[automerger skipped] Merge "Fix potential OOB write in libbluetooth" into oc-dev am: 59304cd9d9 am: 16814b34fb -s ours am: 27feab80ac -s ours am: 8679ae4002 -s ours am: e089c72645 -s ours am: 206766da1a -s ours

am skip reason: Change-Id I90834b920d61bfb2df9414a25d73ba40033e4748 with SHA-1 ccbe059808 is in history

Original change: https://googleplex-android-review.googlesource.com/c/platform/system/bt/+/12781555

Change-Id: I47164d343b5e6e04ab8e26244efe2ece23027bd2
diff --git a/.style.yapf b/.style.yapf
new file mode 100644
index 0000000..3c8af88
--- /dev/null
+++ b/.style.yapf
@@ -0,0 +1,4 @@
+[style]
+# http://google.github.io/styleguide/pyguide.html
+based_on_style: google
+indent_width: 4
diff --git a/Android.bp b/Android.bp
index 49af99c..113be7b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1,4 +1,5 @@
 subdirs = [
+    "apex",
     "binder",
     "build",
     "btif",
@@ -28,8 +29,8 @@
 ]
 
 filegroup {
-  name: "BluetoothTestConfigTemplate",
-  srcs: [
-    "AndroidTestTemplate.xml"
-  ]
+    name: "BluetoothTestConfigTemplate",
+    srcs: [
+        "AndroidTestTemplate.xml",
+    ],
 }
diff --git a/OWNERS b/OWNERS
index 5596ce1..ab11e8c 100644
--- a/OWNERS
+++ b/OWNERS
@@ -2,11 +2,11 @@
 
 # Project owners
 zachoverflow@google.com
-apanicke@google.com
 cmanton@google.com
 hsz@google.com
 jpawlowski@google.com
 mylesgw@google.com
 optedoblivion@google.com
+rahulsabnis@google.com
 siyuanh@google.com
 stng@google.com
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 380216c..a473040 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -6,4 +6,5 @@
 
 [Hook Scripts]
 aosp_first = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} ".*$"
+yapf_hook = ./tools/scripts/yapf_checker.py
 
diff --git a/TEST_MAPPING b/TEST_MAPPING
old mode 100644
new mode 100755
index dc6882f..ccd894b
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -82,8 +82,26 @@
       "host" : true
     },
     {
-        "name" : "net_test_stack_a2dp_native",
-        "host" : true
+      "name" : "net_test_stack_gatt_native",
+      "host" : true
+    },
+    {
+      "name" : "net_test_hci_fragmenter_native",
+      "host" : true
+    },
+    {
+      "name" : "net_test_stack_a2dp_native",
+      "host" : true
+    },
+    {
+      "name" : "net_test_btif_config_cache",
+      "host" : true
+    },
+    {
+      "name" : "net_test_hf_client_add_record"
+    },
+    {
+      "name" : "net_test_btif_hf_client_service"
     }
   ]
 }
diff --git a/apex/Android.bp b/apex/Android.bp
new file mode 100644
index 0000000..fc143ba
--- /dev/null
+++ b/apex/Android.bp
@@ -0,0 +1,27 @@
+apex {
+    name: "com.android.bluetooth.updatable",
+
+    manifest: "apex_manifest.json",
+
+    native_shared_libs: [
+      "libbluetooth_jni",
+      "libbluetooth"
+    ],
+    apps: ["Bluetooth"],
+
+    compile_multilib: "both",
+
+    key: "com.android.bluetooth.updatable.key",
+    certificate: ":com.android.bluetooth.updatable.certificate",
+}
+
+apex_key {
+    name: "com.android.bluetooth.updatable.key",
+    public_key: "com.android.bluetooth.updatable.avbpubkey",
+    private_key: "com.android.bluetooth.updatable.pem",
+}
+
+android_app_certificate {
+    name: "com.android.bluetooth.updatable.certificate",
+    certificate: "com.android.bluetooth.updatable",
+}
diff --git a/apex/apex_manifest.json b/apex/apex_manifest.json
new file mode 100644
index 0000000..11f4d0b
--- /dev/null
+++ b/apex/apex_manifest.json
@@ -0,0 +1,4 @@
+{
+  "name": "com.android.bluetooth.updatable",
+  "version": 2
+}
diff --git a/apex/com.android.bluetooth.updatable.avbpubkey b/apex/com.android.bluetooth.updatable.avbpubkey
new file mode 100644
index 0000000..969211f
--- /dev/null
+++ b/apex/com.android.bluetooth.updatable.avbpubkey
Binary files differ
diff --git a/apex/com.android.bluetooth.updatable.pem b/apex/com.android.bluetooth.updatable.pem
new file mode 100644
index 0000000..dcbd2be
--- /dev/null
+++ b/apex/com.android.bluetooth.updatable.pem
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKQIBAAKCAgEA1+wvczWqiosiDtTkbA3nEOUz1fjZhfsWGT5blDOTZFEq4mP6
+heTlEwEc/kYy9UTtG3zrzGtGR+6JueffuI9exjvsVWBdAZBcmb5cgJAkZkpqOMjl
+sS0nHLHtZUWaniASFJnoqFZPFsqR5f5nXCcDUQH2ltVV5+lApxnMgldgvn8nEAYv
+YvOT+N4zwkYv/iYp+/jRv3XRsDehJf62iLhXZYkJdHtqGSq52W5OZFSN61QLd1Zd
+t+8zOQE+nshagnbFdJXXZpITZ/WOAQUzuM9pZzh155yPXhzlE0dB532qUkwCUmcv
+EK09FkS/wEy2Dc9aduAvtRIiS4MobawZSRvq+I/MZp68ioxbdDAW3buB9URBC0k8
+eq7z4TaRgYY2QtQdRecyCP26GeZPP/kz9gYIAslGTY3+B58tRMYp9YwWnQOl37Jj
+2vTg9hmHFRo7gaB0gPj+YORtFhILppx/hu9ASklWV5X6vVqoExM3SFnb05LEGwmc
+Orgp0iKGFfqpRH796Yk2gu+qnupVoLyHRKeq2WKpDlxekRNF+hF27BXlDZMFR5eu
+JYwK7fAq2WndlUz0t2bFSsmTr/S0brr4938F2UUMObQqVr/ynwjE368ADPrihwZX
+gsjW/xXFwW/ftU1BSvdz+Ps+OkoqfM3ac3hTVjT06jtXOk9EJZIqgKUWmTcCAwEA
+AQKCAgEAgF+XxnN9tWkLEq5YMDYDzvO2YdzV1uZ1OQTuBaq0Usw/JuIyLDDuUOI0
+jqjF2zTjk5gtyRdubY3QrIrDZEM5Ibq2w/vK0Ac4Rt/6tyT7vyU3ChPHHBOwUUsn
+zTIYvzJqDX2D6bGGzBIbtBYQ9aTsP9le9kZYEM2I9tBL2qKAjkGiw9AplDclHq1d
+MH/yQLQH+hrw+VffF1m7oY9Sju6qZ1+WE37ckaHpZPBEAzqSNft95bGffp/kYFvx
+Ew4jYbWjp7D+o6jtiL1hdBHXsmT+UbQrxUw7oMSlHVUcRblc7hjquHtIbC3TR84Q
+AxCYnJVPP6YCNzFU2nhcLPhJAEJs68stR2JB6lFD/RLfESmkgGieobxV4UMHYlMc
+rTZgIwhpKTDAMDmBmfZdRVYa7n3euCHZ/dMiLLkMeeXqHpfZ4kDWfUo89DfRdgp2
+XLb0MZ+RriZDZS7QY7IN0aW9M2UBfNtK9XVuXmMhE7haYtEVxp5UZX2IUicYvi6j
+pko/oyCQJjEBQopYYU1to8izGpPsxARLSl3mn6RoRTCaHmXZniu6HztBzuf6cxH4
+Ze78tHKN/PnSOehGAiLGxVAjlah/D6rxrnmDnvwbF0dUSqSiSho78X2JcpeC+jJz
+XbfassFqvKxsZzEz8ZKdciGydcRJHv5kk0Yf6DDZBXeICId+kqECggEBAPRSpBu7
+3hqT75LIXrE7OHd93Zn61+l/ww0K/s8TSuIVYOZjYMSEUXjVJHsm2DHxaMb7/47P
+IR+LU9zcRuNMa6o0e3w9pACXl/VVbECY5F8oNTYa8tkZA1C4nhWjKJiCGsCSzHMA
+D3+tcwGb2sFJe8UYAkyoZozh4rSxphTLS1OgvQ/T24ofJ5afPT4ZX/vTcs4P4skK
+ScwCG9mBsAw03DONVvj7fWTLQLEOrIBwQkWemlTUU3+gWYXzDi+7Ab7oiCNVYCNc
+yMCv05XpZFW1/nOGXgj1z1EAyj09G8BSgnTH47n/7pmHzSVRxTWK3px5dQfDOoEd
+wae5bkGMrrsHSHsCggEBAOI+D0Ebw+RmhuiMGREE/v5VhSCODK0qfyyN5LIpIp8j
+7O1nLwg8uXljHSA6PIuZJptVvSgaKncpjmVLp8LHPEphFV1fH1lglm9hXUjZWmYL
+AONVuma/n6E5x5BbD2dLP4H8EXsZdHmPYaSOB8nD4k1FeMgBNSzCjW62GmT5WUbH
+ii72TTrzb0ytGoX6qEIlFjUL3ivNmywyVA4tHSHJmyuJNpHrLB+DaIUSnCK6VJWG
+a6aJlGDXcCFGJX+YAxL/wAXfySv5/fRd+RUQgNXagOzQ9AZFgERebyc77s2+zKOq
+o7emvQ2sNhGxw6nWEC0KFUSc7UkJ/kKrLwvHDc+gm3UCggEBAICqOLRGRlv2xjal
+gICTMFR1G0tot2XHG5/1x2Sjw062dXY+pYo4KHOaw4B7X2VFvaj0souxdr8W744j
+Ds3Kw/Q5eUJfb8vECYlwd/a4zaNzGDqrDHLZ4k4TO2UnrExMT+xUIoj5YjAZ1rPl
+MNmTajPMRgG6uW690lbYKx9ORBgBUS9RoY7mg0GmEGlwkYSbwRzVbtfyrBRck/AC
+hQSYndIkP8YVIt7+zs2vbZaiB7SEJsA/pM1UU3DgI/ts3yl7aZ456swUo8AdmC6d
+X3Jnyl1qSIEtegUthlOjp8arbPo0/i9IoY2G37kki9d7j2oV6FSzMk8mrYI3e+HX
+RXlHB5sCggEAJtRf6dzKEjlGjkL9Sl6BJUWoQ54drtrMWOlBhxJoTsfrshMzj0Zc
+zuij5xQXsB3o8mAUxv02rJ0FQ31onZV0U4+2CwB4cO6S4yrix84GJd1dmabtBxV8
+YD96cNwwGJphm/2XQnlr9DEXoRZ/X7Gu9XQm9qy+Y6BAhe/bN9lT6UH2BXlgZ+2/
+/Hj5RGVw22liFlijGQGA0GUS6Bv2FAZ2C1LRetnSCNaU6cH36j3wpnkboMmcHcLc
+C9nuNafxXyFl45w0+sc9PuOiDGt4sTt/RSXXu/vRt+o9SY6PveAxXyW6U5rad65G
+2WKZIl0LCa/hVpVYZGDCss6OlIblZ+04wQKCAQAPnQZKHYRAbMwf3hISiD3HVLQO
+ZRegAF1Jxfw19jSAzAuC4KTChilS2ta5N+ppigbdrXmMhQNloX3dNWf5Qfn83Wk6
+/1oVsZSGL0GAOXYfMFSB6d+o3V0NTCf3SDfV/Yk38ffRp0J1GIV5BOveS6vQICPV
+yGr7AjZzXdqkSkQNsn2Wy4Z9kWQyDBmCkqYBbTn6gw+1Qlg028q9YClQFRrANDx4
+VuORCKP2BCoYexICU+awXao/JtgO1fSBnD52vKjGYaaPnqKbYtDwBBo9MaQ1VSFC
+Jj9v2jUoc0j1uEO13IBAvzebnVuSp4S0EW8nM+oal+enrzTLwgWnGw6hTmwT
+-----END RSA PRIVATE KEY-----
diff --git a/apex/com.android.bluetooth.updatable.pk8 b/apex/com.android.bluetooth.updatable.pk8
new file mode 100644
index 0000000..59f489f
--- /dev/null
+++ b/apex/com.android.bluetooth.updatable.pk8
Binary files differ
diff --git a/apex/com.android.bluetooth.updatable.x509.pem b/apex/com.android.bluetooth.updatable.x509.pem
new file mode 100644
index 0000000..71c169c
--- /dev/null
+++ b/apex/com.android.bluetooth.updatable.x509.pem
@@ -0,0 +1,35 @@
+-----BEGIN CERTIFICATE-----
+MIIGKTCCBBGgAwIBAgIUZNVGFCcxcNXIbScs78HfUDxmLbEwDQYJKoZIhvcNAQEL
+BQAwgaIxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH
+DA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRy
+b2lkMR4wHAYDVQQDDBVjb20uYW5kcm9pZC5ibHVldG9vdGgxIjAgBgkqhkiG9w0B
+CQEWE2FuZHJvaWRAYW5kcm9pZC5jb20wIBcNMTkxMTEwMDIyMzI5WhgPNDc1NzEw
+MDYwMjIzMjlaMIGiMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEW
+MBQGA1UEBwwNTW91bnRhaW4gVmlldzEQMA4GA1UECgwHQW5kcm9pZDEQMA4GA1UE
+CwwHQW5kcm9pZDEeMBwGA1UEAwwVY29tLmFuZHJvaWQuYmx1ZXRvb3RoMSIwIAYJ
+KoZIhvcNAQkBFhNhbmRyb2lkQGFuZHJvaWQuY29tMIICIjANBgkqhkiG9w0BAQEF
+AAOCAg8AMIICCgKCAgEAs4zDyliENHT++R/FRTzSoCv6103aAHme/pSrX0qWx8A7
+5IBEtevCaYR/DqCH/QlAZOndf3MPzf1eeGYSJ+YcBGXBNQ2nikUrpZd6a0cBaOXp
+4SQV3qzTasPCBzcOESEanijcSYdB31S5nAtFKQ1Qu3VtgmwyOw3C29A0DeSN1uDQ
+oZXL+11crJ+ZiVyvRA5OdNoKLppcX45fMFEf0Vel3Cp+XbdEBu3p1DkEN0PxKmPO
+43hS2/2hpaEmy3MM2J8v1SzRdyTj2Ftcv/H2JU1D9RUPKOO8iJkTWLgCewE8VVdC
+hZR4/h6vYKBT4v/fE4zTHS0sLlJhrCAz08BbnxNIUAB/WzcwHMy+EO0AaqZ4PLwh
+WfB2UBX4/mru/2BsoL47/aeXsfQi5+M8NrfnUR+KDUBu9SCqY9nSpvDVWLDJKMVh
+OQm2QBYCiPHoDRBClkkQxtBkQJ3CeH5Peufoamrd9oBcpJi8ZJCDtMWoi0Ie4dJR
+c9b4A/25GkxOwhSyNeIGmdnq3osSK6f1grDefi6GmxvTckyvBMQ0OS4BjfaMwqpw
+GpHe6NFZnDB3qfQPqwGOWcwChTFaoajkO/ETgv9m6RQelOBNQbB1lK3PmcIb1IC3
+9xq5Bp3bcb0DdTA/cKunE4kjDNE+zFSaTDKoexB6TtKjTWMMB6hQgx6KwZNWcc8C
+AwEAAaNTMFEwHQYDVR0OBBYEFAC1MgICiq6wSQUio2PzkQfMzAwmMB8GA1UdIwQY
+MBaAFAC1MgICiq6wSQUio2PzkQfMzAwmMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI
+hvcNAQELBQADggIBAJmeqnXuhvxKmwEpsAGN2Y5moMNuch09KYssnqU7Nb94VxyT
+1qyrUHdiHxOffN0NKrxlgeoNqtbDIRf0LgmgOxRKGm0gf3+XEmw0KtBD29NyzxDy
+RfAvb+m7biTYhq6+V0wkCoT7HZqeuiSC+l+Gv03Qa3CHc613O5fJCpDJwAXIfRux
+/fvA5k/Pci3upra8kAu7xmZad6EMiY+HO4eK4ph8Kr3T8tFjkKMXzmYgj70zA4MD
+bNkZdUsGSsfVxrqfBJC0aCh0VwDskGXwIbs0q1Uc1RCpLLjdY0gMjkWVkgRKNq+u
+W/HiCaqLbjBk2QVOWugwIo6qqVhMOqCekpQjlIc+zJ8WZDunzFmF2OfmVKV4IkWU
+oHoU8Q2jo0211aO/jO8L7LuzsJb0oeGtOkMDaTm7IRE/cQOdN6bCxKV6da6EdZXw
+YTpTcfqg+D7q1/oYK4DzQsRfdfHrAh9PH/S8BU8buScST5ULdT6N+G4lcE5jAc3C
+c2/CTM3l6CWBAgYK+zDqh2Izjns3br25OT5WEtAcdrEU3igLsn3TU30gsK1+pefi
+Pa0ki00pGYj8Et6bIyBfHZ9nuu0SVAgIKNgtPWIu7QnxqpBHTsNH0GgLI/fCRNps
+XxeFStsf+Nj3NSGXAVUBgQZGAWgeMNjiG8siJJKa3A6ouRCkgiRrqcypYCCi
+-----END CERTIFICATE-----
diff --git a/apex/key.pem b/apex/key.pem
new file mode 100644
index 0000000..527a032
--- /dev/null
+++ b/apex/key.pem
@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQCzjMPKWIQ0dP75
+H8VFPNKgK/rXTdoAeZ7+lKtfSpbHwDvkgES168JphH8OoIf9CUBk6d1/cw/N/V54
+ZhIn5hwEZcE1DaeKRSull3prRwFo5enhJBXerNNqw8IHNw4RIRqeKNxJh0HfVLmc
+C0UpDVC7dW2CbDI7DcLb0DQN5I3W4NChlcv7XVysn5mJXK9EDk502goumlxfjl8w
+UR/RV6XcKn5dt0QG7enUOQQ3Q/EqY87jeFLb/aGloSbLcwzYny/VLNF3JOPYW1y/
+8fYlTUP1FQ8o47yImRNYuAJ7ATxVV0KFlHj+Hq9goFPi/98TjNMdLSwuUmGsIDPT
+wFufE0hQAH9bNzAczL4Q7QBqpng8vCFZ8HZQFfj+au7/YGygvjv9p5ex9CLn4zw2
+t+dRH4oNQG71IKpj2dKm8NVYsMkoxWE5CbZAFgKI8egNEEKWSRDG0GRAncJ4fk96
+5+hqat32gFykmLxkkIO0xaiLQh7h0lFz1vgD/bkaTE7CFLI14gaZ2ereixIrp/WC
+sN5+LoabG9NyTK8ExDQ5LgGN9ozCqnAakd7o0VmcMHep9A+rAY5ZzAKFMVqhqOQ7
+8ROC/2bpFB6U4E1BsHWUrc+ZwhvUgLf3GrkGndtxvQN1MD9wq6cTiSMM0T7MVJpM
+Mqh7EHpO0qNNYwwHqFCDHorBk1ZxzwIDAQABAoICAGXfODdWgaxBtWkj3YmrONYo
+HeqLAWXDm7JWJ+WpLGOpblH3dQTC/0tfIbfq3T10QlT/W+00G4OEflVRlHuO09aq
+5TR0ytpo5JrPl2XGo8YgrNJQ5xewd0PcTfpKAnE1lySlilctpvJP3//pZRQlueii
+d7/II7fd1vFg8CfxxYhlhfooQ9Qa5LoM09BcBhVXCKzECYLnkgGlhdykKqlcUX7Z
+nVkO4koMMt92ei3hHrQubPQjEOBAx7Zx+ND2NhOyPjF1fGNdSuk27Sj63+3KKQSl
+LjTFAfN+5rsvrDieOt0w5U1lMED+qALq9K2W/7rX6/Bh8O9XTpOasc0zFsjq59uI
+b+uBsZIPaGDpTjkYBKK3xrhNqIuq2ylN+0D14uAK4/z4sXsvP1XymjWkVNR9ii45
+doiDxANvZzqYoXcR5aEkJcO126RbDIw/SIglWTag6mRvNYCnLVcTzVf7uZXFyLXc
+Yk0NpTGAphVl4uVjXFgr8ZXfQIoM51zFvVVh3+dhosNGms1lgcvGHzJRKsOCzaA8
+EdWm3mGZrM8kvviqFoS4PndIIz6hB71MGhISEWbTUssF/AJSDWKdvJEWpTSLUjyY
+OoWaTABjZEjyCXpaIvssQilyOb+UVP6ayMAyfDjz5lDKB2216PhoQ9sOb9fIDrdK
+24XwqdMUqDZJV6GY8nQBAoIBAQDnS1DTUYeQwO1C831d1m8P8PhEDpwioBPv53y/
+owbmbwADTMxWvRfGnmXM5BYAeiDFfECp2HPnB5SGpkXOJOQ8H8WStgkhRglNHBbK
+yDaoLYTZqkk49enYEO6MNuFgTqiOWwgO+MVXPKmjreenhsoAQAKFFBPqyNU6v9a2
+WEHuIMc6Gv+2GkT7wkVwgmTZA1L58Y2EWX0wdDAYLS+0+6u+xJ5lPQI014Y7YNsa
+qvu7PG5gQ8fdU1fPmMRk9OOxiHilLc0GAQPtnF4fQYcNdcZ66FARMjHjZnFqHCPI
+irddmFReMkKj+HHufO6EVf8KkWgyIr9CzS9wh43PWW/CILwZAoIBAQDGuoM4DEV5
+tr+FmF3Rri1P0G+iC3Bm9oTa1S1AC4r8yPiY9Q8/lV009am2gbz6R5TFma+oFU1y
+R3pm6AaijeKmusmi2NGtdbqa9cUL98HtVAT7ZQmzyz9UepyZQmDy/0B4q2fGOUX1
+QnxKIOU2j8CFVUkbijUooRypZG7mIs/PsnTtmDb0JdOr54E26BLaBXbFcrxLuJxX
+STpQVbU3U/Y2jaLnRcPF4e6bCrQbrsH37wMLM3JTes8PRr0IcPDs4sp30Gj55rcL
+vSYZqfeVKadA312In5p4OPJV7/HSzRl4JE4JSWlCRLZepmAy36kFpQzI6N3LUVMp
+RuZtf+UXIVonAoIBAQCVLEIoyICCn6tmbtwAVXiz81prqnCQ9GVnaQlQH7knjZeY
+iUOQ/cwD0c0eZEy8aggQ1p5t7Khz7LOiVELZPXnmPSeUA8vHpgABt4gLqS13MkRo
+jidDkXcMX38693pMPu7/QT7lSRUduoY+hr7NkWVe2+nEIrrlxjmf9nJokGuVRZa4
+pwkdTbwIE7ftZGveewJdKal4Hq9bPNR4A0ytkVOnafAuozZ1FjwAt+sYPAa3L7aQ
+Z6bT3BjaT1D7O+ZObhJBllSQ9r6t1RfvWLfduoQUaRiavqDmZP/oy9VYVf8FYmWn
+iwrn7iitA+5hc4VWL4ngLADm+KcMEKEphilKLwqhAoIBAAC7FNpy6Rp+eovSOJ4Z
+xt7hRFfTNPGb/HcVi5oNsNAnKQre89RnBzW3pY0fQwOkmb50RzoWAffmnWOdfNDC
+NtAoJa+snnDF2w7Q0o2tto/Z3D7Ua0m2+J+l58eEf/jEyYboEnSfJ0u0l+Jp5o3h
+z3JuEtvAEVv547IXxXShMiRwYo+xHJqfPP0H1+jMx6z3ki09s8WPgzuq8ET1W7o9
+W81tjejNz999ajQ1wN3NMbdosJks8kGuVO6Ycv+B8tDcMKRqJsiseYXYhzhW8Ksi
+wROy/pQPCjFg/Dsmq7v7txlDAOp106ZDvGvyrq3hNqzno3llqdMilGy1bwl+C+ie
+3ccCggEBAISSVWD3owRf5n8unlVTmg64miLv3rM3s67F+Hn62qeyYmW3AhMSPyt2
+RIkEr2YrOKQ5D8Ijk3Gad4ae5UdyeCOPg495qk7bD5thcYG4Mo/MVvCQXxgdkeaK
+TFHpzzUttgKUe2EBfCbkxPjzWqf3ba0mvcnW621vUVA+VDqIUlBU9aosRMgDOFUi
+N+667Kj5P382oZHHxFnUZnCa3PczyGG3WLYBLRFAHl3n8kJ9mOUAAlVC8sRgcYmZ
+z2ZYfc22sUREah+kdxvDvR2ayopl86XoyrdTnK0XKEo+lU8Ghovc5Kbopf15KdWz
+/cAk+1ZkOAzn5RIZNaFGw2FKHds1ODQ=
+-----END PRIVATE KEY-----
diff --git a/audio_a2dp_hw/Android.bp b/audio_a2dp_hw/Android.bp
index 7a23e27..fac3655 100644
--- a/audio_a2dp_hw/Android.bp
+++ b/audio_a2dp_hw/Android.bp
@@ -5,7 +5,7 @@
         "system/bt",
         "system/bt/include",
         "system/bt/audio_a2dp_hw/include",
-    ]
+    ],
 }
 
 // Audio A2DP shared library for target
diff --git a/audio_bluetooth_hw/Android.bp b/audio_bluetooth_hw/Android.bp
index 9cdd64a..de1effd 100644
--- a/audio_bluetooth_hw/Android.bp
+++ b/audio_bluetooth_hw/Android.bp
@@ -19,7 +19,6 @@
         "libcutils",
         "libfmq",
         "libhidlbase",
-        "libhidltransport",
         "liblog",
         "libutils",
     ],
diff --git a/audio_bluetooth_hw/audio_bluetooth_hw.cc b/audio_bluetooth_hw/audio_bluetooth_hw.cc
index e32d88b..887c4e3 100644
--- a/audio_bluetooth_hw/audio_bluetooth_hw.cc
+++ b/audio_bluetooth_hw/audio_bluetooth_hw.cc
@@ -18,7 +18,6 @@
 
 #include <android-base/logging.h>
 #include <errno.h>
-#include <hardware/audio.h>
 #include <hardware/hardware.h>
 #include <log/log.h>
 #include <malloc.h>
@@ -28,10 +27,31 @@
 #include "stream_apis.h"
 #include "utils.h"
 
+using ::android::bluetooth::audio::utils::GetAudioParamString;
+using ::android::bluetooth::audio::utils::ParseAudioParams;
+
 static int adev_set_parameters(struct audio_hw_device* dev,
                                const char* kvpairs) {
   LOG(VERBOSE) << __func__ << ": kevpairs=[" << kvpairs << "]";
-  return -ENOSYS;
+  std::unordered_map<std::string, std::string> params =
+      ParseAudioParams(kvpairs);
+  if (params.empty()) return 0;
+
+  LOG(VERBOSE) << __func__ << ": ParamsMap=[" << GetAudioParamString(params)
+               << "]";
+  if (params.find("A2dpSuspended") == params.end()) {
+    return -ENOSYS;
+  }
+
+  auto* bluetooth_device = reinterpret_cast<BluetoothAudioDevice*>(dev);
+  std::lock_guard<std::mutex> guard(bluetooth_device->mutex_);
+  for (auto sout : bluetooth_device->opened_stream_outs_) {
+    if (sout->stream_out_.common.set_parameters != nullptr) {
+      sout->stream_out_.common.set_parameters(&sout->stream_out_.common,
+                                              kvpairs);
+    }
+  }
+  return 0;
 }
 
 static char* adev_get_parameters(const struct audio_hw_device* dev,
@@ -82,7 +102,8 @@
 static int adev_dump(const audio_hw_device_t* device, int fd) { return 0; }
 
 static int adev_close(hw_device_t* device) {
-  free(device);
+  auto* bluetooth_device = reinterpret_cast<BluetoothAudioDevice*>(device);
+  delete bluetooth_device;
   return 0;
 }
 
@@ -91,8 +112,8 @@
   LOG(VERBOSE) << __func__ << ": name=[" << name << "]";
   if (strcmp(name, AUDIO_HARDWARE_INTERFACE) != 0) return -EINVAL;
 
-  struct audio_hw_device* adev =
-      (struct audio_hw_device*)calloc(1, sizeof(struct audio_hw_device));
+  auto bluetooth_audio_device = new BluetoothAudioDevice{};
+  struct audio_hw_device* adev = &bluetooth_audio_device->audio_device_;
   if (!adev) return -ENOMEM;
 
   adev->common.tag = HARDWARE_DEVICE_TAG;
diff --git a/audio_bluetooth_hw/device_port_proxy.cc b/audio_bluetooth_hw/device_port_proxy.cc
index 6e1fda2..d3a9e82 100644
--- a/audio_bluetooth_hw/device_port_proxy.cc
+++ b/audio_bluetooth_hw/device_port_proxy.cc
@@ -38,7 +38,6 @@
 using ::android::hardware::bluetooth::audio::V2_0::ChannelMode;
 using ::android::hardware::bluetooth::audio::V2_0::PcmParameters;
 using ::android::hardware::bluetooth::audio::V2_0::SampleRate;
-using ::android::hardware::bluetooth::audio::V2_0::SessionType;
 using BluetoothAudioStatus =
     ::android::hardware::bluetooth::audio::V2_0::Status;
 using ControlResultCallback = std::function<void(
diff --git a/audio_bluetooth_hw/device_port_proxy.h b/audio_bluetooth_hw/device_port_proxy.h
index 16db274..160df4b 100644
--- a/audio_bluetooth_hw/device_port_proxy.h
+++ b/audio_bluetooth_hw/device_port_proxy.h
@@ -28,6 +28,8 @@
 namespace bluetooth {
 namespace audio {
 
+using ::android::hardware::bluetooth::audio::V2_0::SessionType;
+
 // Proxy for Bluetooth Audio HW Module to communicate with Bluetooth Audio
 // Session Control. All methods are not thread safe, so users must acquire a
 // lock. Note: currently, in stream_apis.cc, if GetState() is only used for
@@ -84,9 +86,14 @@
   // Set the current BluetoothStreamState
   void SetState(BluetoothStreamState state);
 
+  bool IsA2dp() const {
+    return session_type_ == SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH ||
+           session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH;
+  }
+
  private:
   BluetoothStreamState state_;
-  ::android::hardware::bluetooth::audio::V2_0::SessionType session_type_;
+  SessionType session_type_;
   uint16_t cookie_;
   mutable std::mutex cv_mutex_;
   std::condition_variable internal_cv_;
diff --git a/audio_bluetooth_hw/stream_apis.cc b/audio_bluetooth_hw/stream_apis.cc
index 74b6f2a..d809b57 100644
--- a/audio_bluetooth_hw/stream_apis.cc
+++ b/audio_bluetooth_hw/stream_apis.cc
@@ -36,13 +36,95 @@
 
 namespace {
 
-constexpr unsigned int kMinimumDelayMs = 100;
+constexpr unsigned int kMinimumDelayMs = 50;
 constexpr unsigned int kMaximumDelayMs = 1000;
 constexpr int kExtraAudioSyncMs = 200;
 
 std::ostream& operator<<(std::ostream& os, const audio_config& config) {
   return os << "audio_config[sample_rate=" << config.sample_rate
-            << ", channels=" << StringPrintf("%#x", config.channel_mask) << ", format=" << config.format << "]";
+            << ", channels=" << StringPrintf("%#x", config.channel_mask)
+            << ", format=" << config.format << "]";
+}
+
+void out_calculate_feeding_delay_ms(const BluetoothStreamOut* out,
+                                    uint32_t* latency_ms,
+                                    uint64_t* frames = nullptr,
+                                    struct timespec* timestamp = nullptr) {
+  if (latency_ms == nullptr && frames == nullptr && timestamp == nullptr) {
+    return;
+  }
+
+  // delay_report is the audio delay from the remote headset receiving data to
+  // the headset playing sound in units of nanoseconds
+  uint64_t delay_report_ns = 0;
+  uint64_t delay_report_ms = 0;
+  // absorbed_bytes is the total number of bytes sent by the Bluetooth stack to
+  // a remote headset
+  uint64_t absorbed_bytes = 0;
+  // absorbed_timestamp is the ...
+  struct timespec absorbed_timestamp = {};
+  bool timestamp_fetched = false;
+
+  std::unique_lock<std::mutex> lock(out->mutex_);
+  if (out->bluetooth_output_.GetPresentationPosition(
+          &delay_report_ns, &absorbed_bytes, &absorbed_timestamp)) {
+    delay_report_ms = delay_report_ns / 1000000;
+    // assume kMinimumDelayMs (50ms) < delay_report_ns < kMaximumDelayMs
+    // (1000ms), or it is invalid / ignored and use old delay calculated
+    // by ourselves.
+    if (delay_report_ms > kMinimumDelayMs &&
+        delay_report_ms < kMaximumDelayMs) {
+      timestamp_fetched = true;
+    } else if (delay_report_ms >= kMaximumDelayMs) {
+      LOG(INFO) << __func__ << ": state=" << out->bluetooth_output_.GetState()
+                << ", delay_report=" << delay_report_ns << "ns abnormal";
+    }
+  }
+  if (!timestamp_fetched) {
+    // default to old delay if any failure is found when fetching from ports
+    // audio_a2dp_hw:
+    //   frames_count = buffer_size / frame_size
+    //   latency (sec.) = frames_count / samples_per_second (sample_rate)
+    // Sync from audio_a2dp_hw to add extra delay kExtraAudioSyncMs(+200ms)
+    delay_report_ms =
+        out->frames_count_ * 1000 / out->sample_rate_ + kExtraAudioSyncMs;
+    if (timestamp != nullptr) {
+      clock_gettime(CLOCK_MONOTONIC, &absorbed_timestamp);
+    }
+    LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_.GetState()
+                 << " uses the legacy delay " << delay_report_ms << " ms";
+  }
+  LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_.GetState()
+               << ", delay=" << delay_report_ms << "ms, data=" << absorbed_bytes
+               << " bytes, timestamp=" << absorbed_timestamp.tv_sec << "."
+               << StringPrintf("%09ld", absorbed_timestamp.tv_nsec) << "s";
+
+  if (latency_ms != nullptr) {
+    *latency_ms = delay_report_ms;
+  }
+  if (frames != nullptr) {
+    const uint64_t latency_frames = delay_report_ms * out->sample_rate_ / 1000;
+    *frames = absorbed_bytes / audio_stream_out_frame_size(&out->stream_out_);
+    if (out->frames_presented_ < *frames) {
+      // Are we (the audio HAL) reset?! The stack counter is obsoleted.
+      *frames = out->frames_presented_;
+    } else if ((out->frames_presented_ - *frames) > latency_frames) {
+      // Is the Bluetooth output reset / restarted by AVDTP reconfig?! Its
+      // counter was reset but could not be used.
+      *frames = out->frames_presented_;
+    }
+    // suppose frames would be queued in the headset buffer for delay_report
+    // period, so those frames in buffers should not be included in the number
+    // of presented frames at the timestamp.
+    if (*frames > latency_frames) {
+      *frames -= latency_frames;
+    } else {
+      *frames = 0;
+    }
+  }
+  if (timestamp != nullptr) {
+    *timestamp = absorbed_timestamp;
+  }
 }
 
 }  // namespace
@@ -207,7 +289,8 @@
               << routing_param->second.c_str() << "'";
   }
 
-  if (params.find("A2dpSuspended") != params.end()) {
+  if (params.find("A2dpSuspended") != params.end() &&
+      out->bluetooth_output_.IsA2dp()) {
     if (params["A2dpSuspended"] == "true") {
       LOG(INFO) << __func__ << ": state=" << out->bluetooth_output_.GetState()
                 << " stream param stopped";
@@ -340,17 +423,11 @@
 
 static uint32_t out_get_latency_ms(const struct audio_stream_out* stream) {
   const auto* out = reinterpret_cast<const BluetoothStreamOut*>(stream);
-  std::unique_lock<std::mutex> lock(out->mutex_);
-  /***
-   * audio_a2dp_hw:
-   *   frames_count = buffer_size / frame_size
-   *   latency (sec.) = frames_count / sample_rate
-   */
-  uint32_t latency_ms = out->frames_count_ * 1000 / out->sample_rate_;
+  uint32_t latency_ms = 0;
+  out_calculate_feeding_delay_ms(out, &latency_ms);
   LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_.GetState()
-               << ", latency_ms=" << latency_ms;
-  // Sync from audio_a2dp_hw to add extra +200ms
-  return latency_ms + kExtraAudioSyncMs;
+               << ", latency=" << latency_ms << "ms";
+  return latency_ms;
 }
 
 static int out_set_volume(struct audio_stream_out* stream, float left,
@@ -419,13 +496,11 @@
 
 static int out_get_render_position(const struct audio_stream_out* stream,
                                    uint32_t* dsp_frames) {
-  const auto* out = reinterpret_cast<const BluetoothStreamOut*>(stream);
-  std::unique_lock<std::mutex> lock(out->mutex_);
-
   if (dsp_frames == nullptr) return -EINVAL;
 
-  /* frames = (latency (ms) / 1000) * sample_per_seconds */
-  uint64_t latency_frames =
+  const auto* out = reinterpret_cast<const BluetoothStreamOut*>(stream);
+  // frames = (latency (ms) / 1000) * samples_per_second (sample_rate)
+  const uint64_t latency_frames =
       (uint64_t)out_get_latency_ms(stream) * out->sample_rate_ / 1000;
   if (out->frames_rendered_ >= latency_frames) {
     *dsp_frames = (uint32_t)(out->frames_rendered_ - latency_frames);
@@ -527,52 +602,12 @@
     return -EINVAL;
   }
 
-  // bytes is the total number of bytes sent by the Bluetooth stack to a
-  // remote headset
-  uint64_t bytes = 0;
-  // delay_report is the audio delay from the remote headset receiving data to
-  // the headset playing sound in units of nanoseconds
-  uint64_t delay_report_ns = 0;
   const auto* out = reinterpret_cast<const BluetoothStreamOut*>(stream);
-  std::unique_lock<std::mutex> lock(out->mutex_);
-
-  if (out->bluetooth_output_.GetPresentationPosition(&delay_report_ns, &bytes,
-                                                     timestamp)) {
-    // assume kMinimumDelayMs (100ms) < delay_report_ns < kMaximumDelayMs
-    // (1000ms), or it is invalid / ignored and use old delay calculated
-    // by ourselves.
-    if (delay_report_ns > kMinimumDelayMs * 1000000 &&
-        delay_report_ns < kMaximumDelayMs * 1000000) {
-      *frames = bytes / audio_stream_out_frame_size(stream);
-      timestamp->tv_nsec += delay_report_ns;
-      if (timestamp->tv_nsec > 1000000000) {
-        timestamp->tv_sec += static_cast<int>(timestamp->tv_nsec / 1000000000);
-        timestamp->tv_nsec %= 1000000000;
-      }
-      LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_.GetState() << ", frames=" << *frames << " ("
-                   << bytes << " bytes), timestamp=" << timestamp->tv_sec << "."
-                   << StringPrintf("%09ld", timestamp->tv_nsec) << "s";
-      return 0;
-    } else if (delay_report_ns >= kMaximumDelayMs * 1000000) {
-      LOG(WARNING) << __func__
-                   << ": state=" << out->bluetooth_output_.GetState()
-                   << ", delay_report=" << delay_report_ns << "ns abnormal";
-    }
-  }
-
-  // default to old delay if any failure is found when fetching from ports
-  if (out->frames_presented_ >= out->frames_count_) {
-    clock_gettime(CLOCK_MONOTONIC, timestamp);
-    *frames = out->frames_presented_ - out->frames_count_;
-    LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_.GetState() << ", frames=" << *frames << " ("
-                 << bytes << " bytes), timestamp=" << timestamp->tv_sec << "."
-                 << StringPrintf("%09ld", timestamp->tv_nsec) << "s";
-    return 0;
-  }
-
-  *frames = 0;
-  *timestamp = {};
-  return -EWOULDBLOCK;
+  out_calculate_feeding_delay_ms(out, nullptr, frames, timestamp);
+  LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_.GetState()
+               << ", frames=" << *frames << ", timestamp=" << timestamp->tv_sec
+               << "." << StringPrintf("%09ld", timestamp->tv_nsec) << "s";
+  return 0;
 }
 
 static void out_update_source_metadata(
@@ -600,7 +635,7 @@
                             struct audio_stream_out** stream_out,
                             const char* address __unused) {
   *stream_out = nullptr;
-  auto* out = new BluetoothStreamOut;
+  auto* out = new BluetoothStreamOut{};
   if (!out->bluetooth_output_.SetUp(devices)) {
     delete out;
     return -EINVAL;
@@ -649,6 +684,11 @@
   out->frames_rendered_ = 0;
   out->frames_presented_ = 0;
 
+  {
+    auto* bluetooth_device = reinterpret_cast<BluetoothAudioDevice*>(dev);
+    std::lock_guard<std::mutex> guard(bluetooth_device->mutex_);
+    bluetooth_device->opened_stream_outs_.push_back(out);
+  }
   *stream_out = &out->stream_out_;
   LOG(INFO) << __func__ << ": state=" << out->bluetooth_output_.GetState() << ", sample_rate=" << out->sample_rate_
             << ", channels=" << StringPrintf("%#x", out->channel_mask_) << ", format=" << out->format_
@@ -661,6 +701,11 @@
   auto* out = reinterpret_cast<BluetoothStreamOut*>(stream);
   LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_.GetState()
                << ", stopping";
+  {
+    auto* bluetooth_device = reinterpret_cast<BluetoothAudioDevice*>(dev);
+    std::lock_guard<std::mutex> guard(bluetooth_device->mutex_);
+    bluetooth_device->opened_stream_outs_.remove(out);
+  }
   if (out->bluetooth_output_.GetState() != BluetoothStreamState::DISABLED) {
     out->frames_rendered_ = 0;
     out->frames_presented_ = 0;
diff --git a/audio_bluetooth_hw/stream_apis.h b/audio_bluetooth_hw/stream_apis.h
index 14a955c..c894d1e 100644
--- a/audio_bluetooth_hw/stream_apis.h
+++ b/audio_bluetooth_hw/stream_apis.h
@@ -18,6 +18,7 @@
 
 #include <hardware/audio.h>
 #include <system/audio.h>
+#include <list>
 
 #include "device_port_proxy.h"
 
@@ -45,7 +46,7 @@
 struct BluetoothStreamOut {
   // Must be the first member so it can be cast from audio_stream
   // or audio_stream_out pointer
-  audio_stream_out stream_out_;
+  audio_stream_out stream_out_{};
   ::android::bluetooth::audio::BluetoothAudioPortOut bluetooth_output_;
   int64_t last_write_time_us_;
   // Audio PCM Configs
@@ -62,6 +63,16 @@
   mutable std::mutex mutex_;
 };
 
+struct BluetoothAudioDevice {
+  // Important: device must be first as an audio_hw_device* may be cast to
+  // BluetoothAudioDevice* when the type is implicitly known.
+  audio_hw_device audio_device_{};
+  // protect against device->output and stream_out from being inconsistent
+  std::mutex mutex_;
+  std::list<BluetoothStreamOut*> opened_stream_outs_ =
+      std::list<BluetoothStreamOut*>(0);
+};
+
 int adev_open_output_stream(struct audio_hw_device* dev,
                             audio_io_handle_t handle, audio_devices_t devices,
                             audio_output_flags_t flags,
diff --git a/audio_bluetooth_hw/utils_unittest.cc b/audio_bluetooth_hw/utils_unittest.cc
index a457797..665dea6 100644
--- a/audio_bluetooth_hw/utils_unittest.cc
+++ b/audio_bluetooth_hw/utils_unittest.cc
@@ -25,8 +25,8 @@
 
 class UtilsTest : public testing::Test {
  protected:
-  virtual void SetUp() {}
-  virtual void TearDown() { map_.clear(); }
+  void SetUp() override {}
+  void TearDown() override { map_.clear(); }
 
   std::unordered_map<std::string, std::string> map_;
 };
diff --git a/audio_hal_interface/Android.bp b/audio_hal_interface/Android.bp
index 4b10435..92cf1b1 100644
--- a/audio_hal_interface/Android.bp
+++ b/audio_hal_interface/Android.bp
@@ -13,13 +13,13 @@
     srcs: [
         "a2dp_encoding.cc",
         "client_interface.cc",
+        "codec_status.cc",
         "hearing_aid_software_encoding.cc",
     ],
     shared_libs: [
         "android.hardware.bluetooth.audio@2.0",
         "libfmq",
         "libhidlbase",
-        "libhidltransport",
     ],
     static_libs: [
         "libosi",
@@ -37,6 +37,7 @@
     defaults: ["fluoride_defaults"],
     include_dirs: [
         "system/bt",
+        "system/bt/stack/include",
     ],
     srcs: [
         "client_interface_unittest.cc",
@@ -46,7 +47,6 @@
         "libcutils",
         "libfmq",
         "libhidlbase",
-        "libhidltransport",
         "liblog",
         "libutils",
     ],
diff --git a/audio_hal_interface/a2dp_encoding.cc b/audio_hal_interface/a2dp_encoding.cc
index fc61128..456ba60 100644
--- a/audio_hal_interface/a2dp_encoding.cc
+++ b/audio_hal_interface/a2dp_encoding.cc
@@ -16,11 +16,8 @@
 
 #include "a2dp_encoding.h"
 #include "client_interface.h"
+#include "codec_status.h"
 
-#include "a2dp_aac_constants.h"
-#include "a2dp_sbc_constants.h"
-#include "a2dp_vendor_ldac_constants.h"
-#include "bta/av/bta_av_int.h"
 #include "btif_a2dp_source.h"
 #include "btif_av.h"
 #include "btif_av_co.h"
@@ -29,30 +26,24 @@
 
 namespace {
 
-using ::android::hardware::bluetooth::audio::V2_0::AacObjectType;
-using ::android::hardware::bluetooth::audio::V2_0::AacVariableBitRate;
-using ::android::hardware::bluetooth::audio::V2_0::CodecType;
-using ::android::hardware::bluetooth::audio::V2_0::LdacChannelMode;
-using ::android::hardware::bluetooth::audio::V2_0::LdacQualityIndex;
-using ::android::hardware::bluetooth::audio::V2_0::SbcAllocMethod;
-using ::android::hardware::bluetooth::audio::V2_0::SbcBlockLength;
-using ::android::hardware::bluetooth::audio::V2_0::SbcChannelMode;
-using ::android::hardware::bluetooth::audio::V2_0::SbcNumSubbands;
+using ::bluetooth::audio::AudioCapabilities;
 using ::bluetooth::audio::AudioConfiguration;
 using ::bluetooth::audio::BitsPerSample;
 using ::bluetooth::audio::BluetoothAudioCtrlAck;
 using ::bluetooth::audio::ChannelMode;
-using ::bluetooth::audio::CodecConfiguration;
 using ::bluetooth::audio::PcmParameters;
 using ::bluetooth::audio::SampleRate;
 using ::bluetooth::audio::SessionType;
 
-const CodecConfiguration kInvalidCodecConfiguration = {
-    .codecType = CodecType::UNKNOWN,
-    .encodedAudioBitrate = 0x00000000,
-    .peerMtu = 0xffff,
-    .isScmstEnabled = false,
-    .config = {}};
+using ::bluetooth::audio::BluetoothAudioClientInterface;
+using ::bluetooth::audio::codec::A2dpAacToHalConfig;
+using ::bluetooth::audio::codec::A2dpAptxToHalConfig;
+using ::bluetooth::audio::codec::A2dpCodecToHalBitsPerSample;
+using ::bluetooth::audio::codec::A2dpCodecToHalChannelMode;
+using ::bluetooth::audio::codec::A2dpCodecToHalSampleRate;
+using ::bluetooth::audio::codec::A2dpLdacToHalConfig;
+using ::bluetooth::audio::codec::A2dpSbcToHalConfig;
+using ::bluetooth::audio::codec::CodecConfiguration;
 
 BluetoothAudioCtrlAck a2dp_ack_to_bt_audio_ctrl_ack(tA2DP_CTRL_ACK ack);
 
@@ -61,10 +52,11 @@
  public:
   A2dpTransport(SessionType sessionType)
       : IBluetoothTransportInstance(sessionType, {}),
-        a2dp_pending_cmd_(A2DP_CTRL_CMD_NONE),
-        remote_delay_report_(0),
         total_bytes_read_(0),
-        data_position_({}){};
+        data_position_({}) {
+    a2dp_pending_cmd_ = A2DP_CTRL_CMD_NONE;
+    remote_delay_report_ = 0;
+  }
 
   BluetoothAudioCtrlAck StartRequest() override {
     // Check if a previous request is not finished
@@ -82,11 +74,10 @@
       return a2dp_ack_to_bt_audio_ctrl_ack(A2DP_CTRL_ACK_INCALL_FAILURE);
     }
 
-    if (btif_a2dp_source_is_streaming()) {
-      LOG(ERROR) << __func__ << ": source is busy streaming";
-      return a2dp_ack_to_bt_audio_ctrl_ack(A2DP_CTRL_ACK_FAILURE);
+    if (btif_av_stream_started_ready()) {
+      // Already started, ACK back immediately.
+      return a2dp_ack_to_bt_audio_ctrl_ack(A2DP_CTRL_ACK_SUCCESS);
     }
-
     if (btif_av_stream_ready()) {
       /*
        * Post start event and wait for audio path to open.
@@ -102,11 +93,6 @@
       a2dp_pending_cmd_ = A2DP_CTRL_CMD_NONE;
       return a2dp_ack_to_bt_audio_ctrl_ack(A2DP_CTRL_ACK_SUCCESS);
     }
-
-    if (btif_av_stream_started_ready()) {
-      // Already started, ACK back immediately.
-      return a2dp_ack_to_bt_audio_ctrl_ack(A2DP_CTRL_ACK_SUCCESS);
-    }
     LOG(ERROR) << __func__ << ": AV stream is not ready to start";
     return a2dp_ack_to_bt_audio_ctrl_ack(A2DP_CTRL_ACK_FAILURE);
   }
@@ -137,7 +123,7 @@
 
   void StopRequest() override {
     if (btif_av_get_peer_sep() == AVDT_TSEP_SNK &&
-        !btif_a2dp_source_is_streaming()) {
+        !btif_av_stream_started_ready()) {
       return;
     }
     LOG(INFO) << __func__ << ": handling";
@@ -194,19 +180,21 @@
   }
 
  private:
-  tA2DP_CTRL_CMD a2dp_pending_cmd_;
-  uint16_t remote_delay_report_;
+  static tA2DP_CTRL_CMD a2dp_pending_cmd_;
+  static uint16_t remote_delay_report_;
   uint64_t total_bytes_read_;
   timespec data_position_;
 };
 
-A2dpTransport* a2dp_sink = nullptr;
+tA2DP_CTRL_CMD A2dpTransport::a2dp_pending_cmd_ = A2DP_CTRL_CMD_NONE;
+uint16_t A2dpTransport::remote_delay_report_ = 0;
 
 // Common interface to call-out into Bluetooth Audio HAL
-bluetooth::audio::BluetoothAudioClientInterface* a2dp_hal_clientif = nullptr;
-auto session_type = SessionType::UNKNOWN;
+BluetoothAudioClientInterface* software_hal_interface = nullptr;
+BluetoothAudioClientInterface* offloading_hal_interface = nullptr;
+BluetoothAudioClientInterface* active_hal_interface = nullptr;
 
-// Save the value if the remote reports its delay before a2dp_sink is
+// Save the value if the remote reports its delay before this interface is
 // initialized
 uint16_t remote_delay = 0;
 
@@ -232,301 +220,43 @@
   }
 }
 
-SampleRate a2dp_codec_to_hal_sample_rate(
-    const btav_a2dp_codec_config_t& a2dp_codec_config) {
-  switch (a2dp_codec_config.sample_rate) {
-    case BTAV_A2DP_CODEC_SAMPLE_RATE_44100:
-      return SampleRate::RATE_44100;
-    case BTAV_A2DP_CODEC_SAMPLE_RATE_48000:
-      return SampleRate::RATE_48000;
-    case BTAV_A2DP_CODEC_SAMPLE_RATE_88200:
-      return SampleRate::RATE_88200;
-    case BTAV_A2DP_CODEC_SAMPLE_RATE_96000:
-      return SampleRate::RATE_96000;
-    case BTAV_A2DP_CODEC_SAMPLE_RATE_176400:
-      return SampleRate::RATE_176400;
-    case BTAV_A2DP_CODEC_SAMPLE_RATE_192000:
-      return SampleRate::RATE_192000;
-    case BTAV_A2DP_CODEC_SAMPLE_RATE_16000:
-      return SampleRate::RATE_16000;
-    case BTAV_A2DP_CODEC_SAMPLE_RATE_24000:
-      return SampleRate::RATE_24000;
-    default:
-      return SampleRate::RATE_UNKNOWN;
-  }
-}
-
-BitsPerSample a2dp_codec_to_hal_bits_per_sample(
-    const btav_a2dp_codec_config_t& a2dp_codec_config) {
-  switch (a2dp_codec_config.bits_per_sample) {
-    case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16:
-      return BitsPerSample::BITS_16;
-    case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24:
-      return BitsPerSample::BITS_24;
-    case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32:
-      return BitsPerSample::BITS_32;
-    default:
-      return BitsPerSample::BITS_UNKNOWN;
-  }
-}
-
-ChannelMode a2dp_codec_to_hal_channel_mode(
-    const btav_a2dp_codec_config_t& a2dp_codec_config) {
-  switch (a2dp_codec_config.channel_mode) {
-    case BTAV_A2DP_CODEC_CHANNEL_MODE_MONO:
-      return ChannelMode::MONO;
-    case BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO:
-      return ChannelMode::STEREO;
-    default:
-      return ChannelMode::UNKNOWN;
-  }
-}
-
 bool a2dp_get_selected_hal_codec_config(CodecConfiguration* codec_config) {
-  A2dpCodecConfig* a2dp_codec_configs = bta_av_get_a2dp_current_codec();
-  if (a2dp_codec_configs == nullptr) {
+  A2dpCodecConfig* a2dp_config = bta_av_get_a2dp_current_codec();
+  if (a2dp_config == nullptr) {
     LOG(WARNING) << __func__ << ": failure to get A2DP codec config";
-    *codec_config = kInvalidCodecConfiguration;
+    *codec_config = ::bluetooth::audio::codec::kInvalidCodecConfiguration;
     return false;
   }
-  btav_a2dp_codec_config_t current_codec = a2dp_codec_configs->getCodecConfig();
-  tBT_A2DP_OFFLOAD a2dp_offload;
-  a2dp_codec_configs->getCodecSpecificConfig(&a2dp_offload);
+  btav_a2dp_codec_config_t current_codec = a2dp_config->getCodecConfig();
   switch (current_codec.codec_type) {
     case BTAV_A2DP_CODEC_INDEX_SOURCE_SBC:
       [[fallthrough]];
     case BTAV_A2DP_CODEC_INDEX_SINK_SBC: {
-      codec_config->codecType = CodecType::SBC;
-      codec_config->config.sbcConfig({});
-      auto sbc_config = codec_config->config.sbcConfig();
-      sbc_config.sampleRate = a2dp_codec_to_hal_sample_rate(current_codec);
-      if (sbc_config.sampleRate == SampleRate::RATE_UNKNOWN) {
-        LOG(ERROR) << __func__
-                   << ": Unknown SBC sample_rate=" << current_codec.sample_rate;
+      if (!A2dpSbcToHalConfig(codec_config, a2dp_config)) {
         return false;
       }
-      uint8_t channel_mode = a2dp_offload.codec_info[3] & A2DP_SBC_IE_CH_MD_MSK;
-      switch (channel_mode) {
-        case A2DP_SBC_IE_CH_MD_JOINT:
-          sbc_config.channelMode = SbcChannelMode::JOINT_STEREO;
-          break;
-        case A2DP_SBC_IE_CH_MD_STEREO:
-          sbc_config.channelMode = SbcChannelMode::STEREO;
-          break;
-        case A2DP_SBC_IE_CH_MD_DUAL:
-          sbc_config.channelMode = SbcChannelMode::DUAL;
-          break;
-        case A2DP_SBC_IE_CH_MD_MONO:
-          sbc_config.channelMode = SbcChannelMode::MONO;
-          break;
-        default:
-          LOG(ERROR) << __func__
-                     << ": Unknown SBC channel_mode=" << channel_mode;
-          sbc_config.channelMode = SbcChannelMode::UNKNOWN;
-          return false;
-      }
-      uint8_t block_length =
-          a2dp_offload.codec_info[0] & A2DP_SBC_IE_BLOCKS_MSK;
-      switch (block_length) {
-        case A2DP_SBC_IE_BLOCKS_4:
-          sbc_config.blockLength = SbcBlockLength::BLOCKS_4;
-          break;
-        case A2DP_SBC_IE_BLOCKS_8:
-          sbc_config.blockLength = SbcBlockLength::BLOCKS_8;
-          break;
-        case A2DP_SBC_IE_BLOCKS_12:
-          sbc_config.blockLength = SbcBlockLength::BLOCKS_12;
-          break;
-        case A2DP_SBC_IE_BLOCKS_16:
-          sbc_config.blockLength = SbcBlockLength::BLOCKS_16;
-          break;
-        default:
-          LOG(ERROR) << __func__
-                     << ": Unknown SBC block_length=" << block_length;
-          return false;
-      }
-      uint8_t sub_bands = a2dp_offload.codec_info[0] & A2DP_SBC_IE_SUBBAND_MSK;
-      switch (sub_bands) {
-        case A2DP_SBC_IE_SUBBAND_4:
-          sbc_config.numSubbands = SbcNumSubbands::SUBBAND_4;
-          break;
-        case A2DP_SBC_IE_SUBBAND_8:
-          sbc_config.numSubbands = SbcNumSubbands::SUBBAND_8;
-          break;
-        default:
-          LOG(ERROR) << __func__ << ": Unknown SBC Subbands=" << sub_bands;
-          return false;
-      }
-      uint8_t alloc_method =
-          a2dp_offload.codec_info[0] & A2DP_SBC_IE_ALLOC_MD_MSK;
-      switch (alloc_method) {
-        case A2DP_SBC_IE_ALLOC_MD_S:
-          sbc_config.allocMethod = SbcAllocMethod::ALLOC_MD_S;
-          break;
-        case A2DP_SBC_IE_ALLOC_MD_L:
-          sbc_config.allocMethod = SbcAllocMethod::ALLOC_MD_L;
-          break;
-        default:
-          LOG(ERROR) << __func__
-                     << ": Unknown SBC alloc_method=" << alloc_method;
-          return false;
-      }
-      sbc_config.minBitpool = a2dp_offload.codec_info[1];
-      sbc_config.maxBitpool = a2dp_offload.codec_info[2];
-      sbc_config.bitsPerSample =
-          a2dp_codec_to_hal_bits_per_sample(current_codec);
-      if (sbc_config.bitsPerSample == BitsPerSample::BITS_UNKNOWN) {
-        LOG(ERROR) << __func__ << ": Unknown SBC bits_per_sample="
-                   << current_codec.bits_per_sample;
-        return false;
-      }
-      codec_config->config.sbcConfig(sbc_config);
       break;
     }
     case BTAV_A2DP_CODEC_INDEX_SOURCE_AAC:
       [[fallthrough]];
     case BTAV_A2DP_CODEC_INDEX_SINK_AAC: {
-      codec_config->codecType = CodecType::AAC;
-      codec_config->config.aacConfig({});
-      auto aac_config = codec_config->config.aacConfig();
-      uint8_t object_type = a2dp_offload.codec_info[0];
-      switch (object_type) {
-        case A2DP_AAC_OBJECT_TYPE_MPEG2_LC:
-          aac_config.objectType = AacObjectType::MPEG2_LC;
-          break;
-        case A2DP_AAC_OBJECT_TYPE_MPEG4_LC:
-          aac_config.objectType = AacObjectType::MPEG4_LC;
-          break;
-        case A2DP_AAC_OBJECT_TYPE_MPEG4_LTP:
-          aac_config.objectType = AacObjectType::MPEG4_LTP;
-          break;
-        case A2DP_AAC_OBJECT_TYPE_MPEG4_SCALABLE:
-          aac_config.objectType = AacObjectType::MPEG4_SCALABLE;
-          break;
-        default:
-          LOG(ERROR) << __func__
-                     << ": Unknown AAC object_type=" << +object_type;
-          return false;
-      }
-      aac_config.sampleRate = a2dp_codec_to_hal_sample_rate(current_codec);
-      if (aac_config.sampleRate == SampleRate::RATE_UNKNOWN) {
-        LOG(ERROR) << __func__
-                   << ": Unknown AAC sample_rate=" << current_codec.sample_rate;
+      if (!A2dpAacToHalConfig(codec_config, a2dp_config)) {
         return false;
       }
-      aac_config.channelMode = a2dp_codec_to_hal_channel_mode(current_codec);
-      if (aac_config.channelMode == ChannelMode::UNKNOWN) {
-        LOG(ERROR) << __func__ << ": Unknown AAC channel_mode="
-                   << current_codec.channel_mode;
-        return false;
-      }
-      uint8_t vbr_enabled =
-          a2dp_offload.codec_info[1] & A2DP_AAC_VARIABLE_BIT_RATE_MASK;
-      switch (vbr_enabled) {
-        case A2DP_AAC_VARIABLE_BIT_RATE_ENABLED:
-          aac_config.variableBitRateEnabled = AacVariableBitRate::ENABLED;
-          break;
-        case A2DP_AAC_VARIABLE_BIT_RATE_DISABLED:
-          aac_config.variableBitRateEnabled = AacVariableBitRate::DISABLED;
-          break;
-        default:
-          LOG(ERROR) << __func__ << ": Unknown AAC VBR=" << +vbr_enabled;
-          return false;
-      }
-      aac_config.bitsPerSample =
-          a2dp_codec_to_hal_bits_per_sample(current_codec);
-      if (aac_config.bitsPerSample == BitsPerSample::BITS_UNKNOWN) {
-        LOG(ERROR) << __func__ << ": Unknown AAC bits_per_sample="
-                   << current_codec.bits_per_sample;
-        return false;
-      }
-      codec_config->config.aacConfig(aac_config);
       break;
     }
     case BTAV_A2DP_CODEC_INDEX_SOURCE_APTX:
       [[fallthrough]];
     case BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_HD: {
-      if (current_codec.codec_type == BTAV_A2DP_CODEC_INDEX_SOURCE_APTX) {
-        codec_config->codecType = CodecType::APTX;
-      } else {
-        codec_config->codecType = CodecType::APTX_HD;
-      }
-      codec_config->config.aptxConfig({});
-      auto aptx_config = codec_config->config.aptxConfig();
-      aptx_config.sampleRate = a2dp_codec_to_hal_sample_rate(current_codec);
-      if (aptx_config.sampleRate == SampleRate::RATE_UNKNOWN) {
-        LOG(ERROR) << __func__ << ": Unknown aptX sample_rate="
-                   << current_codec.sample_rate;
+      if (!A2dpAptxToHalConfig(codec_config, a2dp_config)) {
         return false;
       }
-      aptx_config.channelMode = a2dp_codec_to_hal_channel_mode(current_codec);
-      if (aptx_config.channelMode == ChannelMode::UNKNOWN) {
-        LOG(ERROR) << __func__ << ": Unknown aptX channel_mode="
-                   << current_codec.channel_mode;
-        return false;
-      }
-      aptx_config.bitsPerSample =
-          a2dp_codec_to_hal_bits_per_sample(current_codec);
-      if (aptx_config.bitsPerSample == BitsPerSample::BITS_UNKNOWN) {
-        LOG(ERROR) << __func__ << ": Unknown aptX bits_per_sample="
-                   << current_codec.bits_per_sample;
-        return false;
-      }
-      codec_config->config.aptxConfig(aptx_config);
       break;
     }
     case BTAV_A2DP_CODEC_INDEX_SOURCE_LDAC: {
-      codec_config->codecType = CodecType::LDAC;
-      codec_config->config.ldacConfig({});
-      auto ldac_config = codec_config->config.ldacConfig();
-      ldac_config.sampleRate = a2dp_codec_to_hal_sample_rate(current_codec);
-      if (ldac_config.sampleRate == SampleRate::RATE_UNKNOWN) {
-        LOG(ERROR) << __func__ << ": Unknown LDAC sample_rate="
-                   << current_codec.sample_rate;
+      if (!A2dpLdacToHalConfig(codec_config, a2dp_config)) {
         return false;
       }
-      switch (a2dp_offload.codec_info[7]) {
-        case A2DP_LDAC_CHANNEL_MODE_STEREO:
-          ldac_config.channelMode = LdacChannelMode::STEREO;
-          break;
-        case A2DP_LDAC_CHANNEL_MODE_DUAL:
-          ldac_config.channelMode = LdacChannelMode::DUAL;
-          break;
-        case A2DP_LDAC_CHANNEL_MODE_MONO:
-          ldac_config.channelMode = LdacChannelMode::MONO;
-          break;
-        default:
-          LOG(ERROR) << __func__ << ": Unknown LDAC channel_mode="
-                     << a2dp_offload.codec_info[7];
-          ldac_config.channelMode = LdacChannelMode::UNKNOWN;
-          return false;
-      }
-      switch (a2dp_offload.codec_info[6]) {
-        case A2DP_LDAC_QUALITY_HIGH:
-          ldac_config.qualityIndex = LdacQualityIndex::QUALITY_HIGH;
-          break;
-        case A2DP_LDAC_QUALITY_MID:
-          ldac_config.qualityIndex = LdacQualityIndex::QUALITY_MID;
-          break;
-        case A2DP_LDAC_QUALITY_LOW:
-          ldac_config.qualityIndex = LdacQualityIndex::QUALITY_LOW;
-          break;
-        case A2DP_LDAC_QUALITY_ABR_OFFLOAD:
-          ldac_config.qualityIndex = LdacQualityIndex::QUALITY_ABR;
-          break;
-        default:
-          LOG(ERROR) << __func__ << ": Unknown LDAC QualityIndex="
-                     << a2dp_offload.codec_info[6];
-          return false;
-      }
-      ldac_config.bitsPerSample =
-          a2dp_codec_to_hal_bits_per_sample(current_codec);
-      if (ldac_config.bitsPerSample == BitsPerSample::BITS_UNKNOWN) {
-        LOG(ERROR) << __func__ << ": Unknown LDAC bits_per_sample="
-                   << current_codec.bits_per_sample;
-        return false;
-      }
-      codec_config->config.ldacConfig(ldac_config);
       break;
     }
     case BTAV_A2DP_CODEC_INDEX_MAX:
@@ -534,16 +264,15 @@
     default:
       LOG(ERROR) << __func__
                  << ": Unknown codec_type=" << current_codec.codec_type;
-      codec_config->codecType = CodecType::UNKNOWN;
-      codec_config->config = {};
+      *codec_config = ::bluetooth::audio::codec::kInvalidCodecConfiguration;
       return false;
   }
-  codec_config->encodedAudioBitrate = a2dp_codec_configs->getTrackBitRate();
+  codec_config->encodedAudioBitrate = a2dp_config->getTrackBitRate();
   // Obtain the MTU
   RawAddress peer_addr = btif_av_source_active_peer();
   tA2DP_ENCODER_INIT_PEER_PARAMS peer_param;
   bta_av_co_get_peer_params(peer_addr, &peer_param);
-  int effectiveMtu = a2dp_codec_configs->getEffectiveMtu();
+  int effectiveMtu = a2dp_config->getEffectiveMtu();
   if (effectiveMtu > 0 && effectiveMtu < peer_param.peer_mtu) {
     codec_config->peerMtu = effectiveMtu;
   } else {
@@ -564,9 +293,9 @@
   }
 
   btav_a2dp_codec_config_t current_codec = a2dp_codec_configs->getCodecConfig();
-  pcm_config->sampleRate = a2dp_codec_to_hal_sample_rate(current_codec);
-  pcm_config->bitsPerSample = a2dp_codec_to_hal_bits_per_sample(current_codec);
-  pcm_config->channelMode = a2dp_codec_to_hal_channel_mode(current_codec);
+  pcm_config->sampleRate = A2dpCodecToHalSampleRate(current_codec);
+  pcm_config->bitsPerSample = A2dpCodecToHalBitsPerSample(current_codec);
+  pcm_config->channelMode = A2dpCodecToHalChannelMode(current_codec);
   return (pcm_config->sampleRate != SampleRate::RATE_UNKNOWN &&
           pcm_config->bitsPerSample != BitsPerSample::BITS_UNKNOWN &&
           pcm_config->channelMode != ChannelMode::UNKNOWN);
@@ -580,15 +309,29 @@
   }
   return btaudio_a2dp_disabled;
 }
-
 }  // namespace
 
 namespace bluetooth {
 namespace audio {
 namespace a2dp {
 
+bool update_codec_offloading_capabilities(
+    const std::vector<btav_a2dp_codec_config_t>& framework_preference) {
+  return ::bluetooth::audio::codec::UpdateOffloadingCapabilities(
+      framework_preference);
+}
+
 // Checking if new bluetooth_audio is enabled
-bool is_hal_2_0_enabled() { return a2dp_hal_clientif != nullptr; }
+bool is_hal_2_0_enabled() { return active_hal_interface != nullptr; }
+
+// Check if new bluetooth_audio is running with offloading encoders
+bool is_hal_2_0_offloading() {
+  if (!is_hal_2_0_enabled()) {
+    return false;
+  }
+  return active_hal_interface->GetTransportInstance()->GetSessionType() ==
+         SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH;
+}
 
 // Initialize BluetoothAudio HAL: openProvider
 bool init(bluetooth::common::MessageLoopThread* message_loop) {
@@ -599,27 +342,47 @@
     return false;
   }
 
-  if (btif_av_is_a2dp_offload_enabled()) {
-    session_type = SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH;
-  } else {
-    session_type = SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH;
-  }
-  a2dp_sink = new A2dpTransport(session_type);
-  a2dp_hal_clientif = new bluetooth::audio::BluetoothAudioClientInterface(
+  auto a2dp_sink =
+      new A2dpTransport(SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH);
+  software_hal_interface = new bluetooth::audio::BluetoothAudioClientInterface(
       a2dp_sink, message_loop);
-  if (!a2dp_hal_clientif->IsValid()) {
-    LOG(WARNING) << __func__ << ": BluetoothAudio HAL for A2DP session=" << toString(session_type) << " is invalid?!";
-    delete a2dp_hal_clientif;
-    a2dp_hal_clientif = nullptr;
+  if (!software_hal_interface->IsValid()) {
+    LOG(WARNING) << __func__ << ": BluetoothAudio HAL for A2DP is invalid?!";
+    delete software_hal_interface;
+    software_hal_interface = nullptr;
     delete a2dp_sink;
-    a2dp_sink = nullptr;
     return false;
   }
 
+  if (btif_av_is_a2dp_offload_enabled()) {
+    a2dp_sink = new A2dpTransport(SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH);
+    offloading_hal_interface =
+        new bluetooth::audio::BluetoothAudioClientInterface(a2dp_sink,
+                                                            message_loop);
+    if (!offloading_hal_interface->IsValid()) {
+      LOG(FATAL) << __func__
+                 << ": BluetoothAudio HAL for A2DP offloading is invalid?!";
+      delete offloading_hal_interface;
+      offloading_hal_interface = nullptr;
+      delete a2dp_sink;
+      a2dp_sink = static_cast<A2dpTransport*>(
+          software_hal_interface->GetTransportInstance());
+      delete software_hal_interface;
+      software_hal_interface = nullptr;
+      delete a2dp_sink;
+      return false;
+    }
+  }
+
+  active_hal_interface =
+      (offloading_hal_interface != nullptr ? offloading_hal_interface
+                                           : software_hal_interface);
+
   if (remote_delay != 0) {
     LOG(INFO) << __func__ << ": restore DELAY "
               << static_cast<float>(remote_delay / 10.0) << " ms";
-    a2dp_sink->SetRemoteDelay(remote_delay);
+    static_cast<A2dpTransport*>(active_hal_interface->GetTransportInstance())
+        ->SetRemoteDelay(remote_delay);
     remote_delay = 0;
   }
   return true;
@@ -629,11 +392,23 @@
 void cleanup() {
   if (!is_hal_2_0_enabled()) return;
   end_session();
-  delete a2dp_hal_clientif;
-  a2dp_hal_clientif = nullptr;
+
+  auto a2dp_sink = active_hal_interface->GetTransportInstance();
+  static_cast<A2dpTransport*>(a2dp_sink)->ResetPendingCmd();
+  static_cast<A2dpTransport*>(a2dp_sink)->ResetPresentationPosition();
+  active_hal_interface = nullptr;
+
+  a2dp_sink = software_hal_interface->GetTransportInstance();
+  delete software_hal_interface;
+  software_hal_interface = nullptr;
   delete a2dp_sink;
-  a2dp_sink = nullptr;
-  session_type = SessionType::UNKNOWN;
+  if (offloading_hal_interface != nullptr) {
+    a2dp_sink = offloading_hal_interface->GetTransportInstance();
+    delete offloading_hal_interface;
+    offloading_hal_interface = nullptr;
+    delete a2dp_sink;
+  }
+
   remote_delay = 0;
 }
 
@@ -643,15 +418,28 @@
     LOG(ERROR) << __func__ << ": BluetoothAudio HAL is not enabled";
     return false;
   }
+  CodecConfiguration codec_config{};
+  if (!a2dp_get_selected_hal_codec_config(&codec_config)) {
+    LOG(ERROR) << __func__ << ": Failed to get CodecConfiguration";
+    return false;
+  }
+  bool should_codec_offloading =
+      bluetooth::audio::codec::IsCodecOffloadingEnabled(codec_config);
+  if (should_codec_offloading && !is_hal_2_0_offloading()) {
+    LOG(WARNING) << __func__ << ": Switching BluetoothAudio HAL to Hardware";
+    end_session();
+    active_hal_interface = offloading_hal_interface;
+  } else if (!should_codec_offloading && is_hal_2_0_offloading()) {
+    LOG(WARNING) << __func__ << ": Switching BluetoothAudio HAL to Software";
+    end_session();
+    active_hal_interface = software_hal_interface;
+  }
+
   AudioConfiguration audio_config{};
-  if (session_type == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
-    CodecConfiguration codec_config{};
-    if (!a2dp_get_selected_hal_codec_config(&codec_config)) {
-      LOG(ERROR) << __func__ << ": Failed to get CodecConfiguration";
-      return false;
-    }
+  if (active_hal_interface->GetTransportInstance()->GetSessionType() ==
+      SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
     audio_config.codecConfig(codec_config);
-  } else if (session_type == SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH) {
+  } else {
     PcmParameters pcm_config{};
     if (!a2dp_get_selected_hal_pcm_config(&pcm_config)) {
       LOG(ERROR) << __func__ << ": Failed to get PcmConfiguration";
@@ -659,7 +447,7 @@
     }
     audio_config.pcmConfig(pcm_config);
   }
-  return a2dp_hal_clientif->UpdateAudioConfig(audio_config);
+  return active_hal_interface->UpdateAudioConfig(audio_config);
 }
 
 void start_session() {
@@ -667,7 +455,7 @@
     LOG(ERROR) << __func__ << ": BluetoothAudio HAL is not enabled";
     return;
   }
-  a2dp_hal_clientif->StartSession();
+  active_hal_interface->StartSession();
 }
 
 void end_session() {
@@ -675,15 +463,19 @@
     LOG(ERROR) << __func__ << ": BluetoothAudio HAL is not enabled";
     return;
   }
-  a2dp_hal_clientif->EndSession();
+  active_hal_interface->EndSession();
+  static_cast<A2dpTransport*>(active_hal_interface->GetTransportInstance())
+      ->ResetPresentationPosition();
 }
 
 void ack_stream_started(const tA2DP_CTRL_ACK& ack) {
   auto ctrl_ack = a2dp_ack_to_bt_audio_ctrl_ack(ack);
   LOG(INFO) << __func__ << ": result=" << ctrl_ack;
+  auto a2dp_sink =
+      static_cast<A2dpTransport*>(active_hal_interface->GetTransportInstance());
   auto pending_cmd = a2dp_sink->GetPendingCmd();
   if (pending_cmd == A2DP_CTRL_CMD_START) {
-    a2dp_hal_clientif->StreamStarted(ctrl_ack);
+    active_hal_interface->StreamStarted(ctrl_ack);
   } else {
     LOG(WARNING) << __func__ << ": pending=" << pending_cmd
                  << " ignore result=" << ctrl_ack;
@@ -697,9 +489,11 @@
 void ack_stream_suspended(const tA2DP_CTRL_ACK& ack) {
   auto ctrl_ack = a2dp_ack_to_bt_audio_ctrl_ack(ack);
   LOG(INFO) << __func__ << ": result=" << ctrl_ack;
+  auto a2dp_sink =
+      static_cast<A2dpTransport*>(active_hal_interface->GetTransportInstance());
   auto pending_cmd = a2dp_sink->GetPendingCmd();
   if (pending_cmd == A2DP_CTRL_CMD_SUSPEND) {
-    a2dp_hal_clientif->StreamSuspended(ctrl_ack);
+    active_hal_interface->StreamSuspended(ctrl_ack);
   } else if (pending_cmd == A2DP_CTRL_CMD_STOP) {
     LOG(INFO) << __func__ << ": A2DP_CTRL_CMD_STOP result=" << ctrl_ack;
   } else {
@@ -717,12 +511,14 @@
   if (!is_hal_2_0_enabled()) {
     LOG(ERROR) << __func__ << ": BluetoothAudio HAL is not enabled";
     return 0;
-  } else if (session_type != SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH) {
-    LOG(ERROR) << __func__ << ": session_type=" << toString(session_type)
+  } else if (is_hal_2_0_offloading()) {
+    LOG(ERROR) << __func__ << ": session_type="
+               << toString(active_hal_interface->GetTransportInstance()
+                               ->GetSessionType())
                << " is not A2DP_SOFTWARE_ENCODING_DATAPATH";
     return 0;
   }
-  return a2dp_hal_clientif->ReadAudioData(p_buf, len);
+  return active_hal_interface->ReadAudioData(p_buf, len);
 }
 
 // Update A2DP delay report to BluetoothAudio HAL
@@ -733,9 +529,10 @@
     remote_delay = delay_report;
     return;
   }
-  LOG(INFO) << __func__ << ": DELAY " << static_cast<float>(delay_report / 10.0)
-            << " ms";
-  a2dp_sink->SetRemoteDelay(delay_report);
+  VLOG(1) << __func__ << ": DELAY " << static_cast<float>(delay_report / 10.0)
+          << " ms";
+  static_cast<A2dpTransport*>(active_hal_interface->GetTransportInstance())
+      ->SetRemoteDelay(delay_report);
 }
 
 }  // namespace a2dp
diff --git a/audio_hal_interface/a2dp_encoding.h b/audio_hal_interface/a2dp_encoding.h
index 7b104ff..fbecde7 100644
--- a/audio_hal_interface/a2dp_encoding.h
+++ b/audio_hal_interface/a2dp_encoding.h
@@ -16,6 +16,8 @@
 
 #pragma once
 
+#include <vector>
+
 #include "audio_a2dp_hw/include/audio_a2dp_hw.h"
 #include "common/message_loop_thread.h"
 
@@ -23,9 +25,15 @@
 namespace audio {
 namespace a2dp {
 
+bool update_codec_offloading_capabilities(
+    const std::vector<btav_a2dp_codec_config_t>& framework_preference);
+
 // Check if new bluetooth_audio is enabled
 bool is_hal_2_0_enabled();
 
+// Check if new bluetooth_audio is running with offloading encoders
+bool is_hal_2_0_offloading();
+
 // Initialize BluetoothAudio HAL: openProvider
 bool init(bluetooth::common::MessageLoopThread* message_loop);
 
diff --git a/audio_hal_interface/client_interface.cc b/audio_hal_interface/client_interface.cc
index 501e113..e14a102 100644
--- a/audio_hal_interface/client_interface.cc
+++ b/audio_hal_interface/client_interface.cc
@@ -73,7 +73,7 @@
                          const android::sp<IBluetoothAudioProvider>& provider)
       : sink_(sink), provider_(provider){};
 
-  Return<void> startStream() {
+  Return<void> startStream() override {
     BluetoothAudioCtrlAck ack = sink_->StartRequest();
     if (ack != BluetoothAudioCtrlAck::PENDING) {
       auto hidl_retval =
@@ -85,7 +85,7 @@
     return Void();
   }
 
-  Return<void> suspendStream() {
+  Return<void> suspendStream() override {
     BluetoothAudioCtrlAck ack = sink_->SuspendRequest();
     if (ack != BluetoothAudioCtrlAck::PENDING) {
       auto hidl_retval =
@@ -97,12 +97,13 @@
     return Void();
   }
 
-  Return<void> stopStream() {
+  Return<void> stopStream() override {
     sink_->StopRequest();
     return Void();
   }
 
-  Return<void> getPresentationPosition(getPresentationPosition_cb _hidl_cb) {
+  Return<void> getPresentationPosition(
+      getPresentationPosition_cb _hidl_cb) override {
     uint64_t remote_delay_report_ns;
     uint64_t total_bytes_read;
     timespec data_position;
@@ -128,7 +129,7 @@
     return Void();
   }
 
-  Return<void> updateMetadata(const SourceMetadata& sourceMetadata) {
+  Return<void> updateMetadata(const SourceMetadata& sourceMetadata) override {
     LOG(INFO) << __func__ << ": " << sourceMetadata.tracks.size()
               << " track(s)";
     // refer to StreamOut.impl.h within Audio HAL (AUDIO_HAL_VERSION_5_0)
@@ -166,7 +167,8 @@
       : bluetooth_audio_clientif_(clientif), message_loop_(message_loop) {}
   void serviceDied(
       uint64_t /*cookie*/,
-      const ::android::wp<::android::hidl::base::V1_0::IBase>& /*who*/) {
+      const ::android::wp<::android::hidl::base::V1_0::IBase>& /*who*/)
+      override {
     LOG(WARNING) << __func__ << ": restarting connection with new Audio Hal";
     if (bluetooth_audio_clientif_ != nullptr && message_loop_ != nullptr) {
       // restart the session on the correct thread
@@ -189,19 +191,8 @@
                                                              bluetooth::common::MessageLoopThread* message_loop)
     : sink_(sink), provider_(nullptr), session_started_(false), mDataMQ(nullptr),
       death_recipient_(new BluetoothAudioDeathRecipient(this, message_loop)) {
-  auto service_manager = android::hardware::defaultServiceManager1_2();
-  CHECK(service_manager != nullptr);
-  size_t instance_count = 0;
-  auto listManifestByInterface_cb = [&instance_count](const hidl_vec<android::hardware::hidl_string>& instanceNames) {
-    instance_count = instanceNames.size();
-    LOG(INFO) << "listManifestByInterface_cb returns " << instance_count << " instance(s)";
-  };
-  auto hidl_retval = service_manager->listManifestByInterface(kFullyQualifiedInterfaceName, listManifestByInterface_cb);
-  if (!hidl_retval.isOk()) {
-    LOG(FATAL) << __func__ << ": IServiceManager::listByInterface failure: " << hidl_retval.description();
-  }
-  if (instance_count > 0) {
-    fetch_audio_provider();
+  if (IsSupported()) {
+    FetchAudioProvider();
   } else {
     LOG(WARNING) << "IBluetoothAudioProvidersFactory not declared";
   }
@@ -221,7 +212,57 @@
   return capabilities_;
 }
 
-void BluetoothAudioClientInterface::fetch_audio_provider() {
+bool BluetoothAudioClientInterface::IsSupported() {
+  auto service_manager = android::hardware::defaultServiceManager1_2();
+  CHECK(service_manager != nullptr);
+  size_t instance_count = 0;
+  auto listManifestByInterface_cb =
+      [&instance_count](
+          const hidl_vec<android::hardware::hidl_string>& instanceNames) {
+        instance_count = instanceNames.size();
+        LOG(INFO) << "listManifestByInterface_cb returns " << instance_count
+                  << " instance(s)";
+      };
+  auto hidl_retval = service_manager->listManifestByInterface(
+      kFullyQualifiedInterfaceName, listManifestByInterface_cb);
+  if (!hidl_retval.isOk()) {
+    LOG(FATAL) << __func__ << ": IServiceManager::listByInterface failure: "
+               << hidl_retval.description();
+    return false;
+  }
+  return (instance_count > 0);
+}
+
+std::vector<AudioCapabilities>
+BluetoothAudioClientInterface::GetAudioCapabilities(SessionType session_type) {
+  std::vector<AudioCapabilities> capabilities(0);
+  if (!IsSupported()) return capabilities;
+
+  android::sp<IBluetoothAudioProvidersFactory> providersFactory =
+      IBluetoothAudioProvidersFactory::getService();
+  CHECK(providersFactory != nullptr)
+      << "IBluetoothAudioProvidersFactory::getService() failed";
+  LOG(INFO) << "IBluetoothAudioProvidersFactory::getService() returned "
+            << providersFactory.get()
+            << (providersFactory->isRemote() ? " (remote)" : " (local)");
+
+  auto getProviderCapabilities_cb =
+      [&capabilities](const hidl_vec<AudioCapabilities>& audioCapabilities) {
+        for (auto capability : audioCapabilities) {
+          capabilities.push_back(capability);
+        }
+      };
+  auto hidl_retval = providersFactory->getProviderCapabilities(
+      session_type, getProviderCapabilities_cb);
+  if (!hidl_retval.isOk()) {
+    LOG(FATAL) << __func__
+               << ": BluetoothAudioHal::getProviderCapabilities failure: "
+               << hidl_retval.description();
+  }
+  return capabilities;
+}
+
+void BluetoothAudioClientInterface::FetchAudioProvider() {
   if (provider_ != nullptr) {
     LOG(WARNING) << __func__ << ": reflash";
   }
@@ -233,20 +274,16 @@
             << providersFactory.get()
             << (providersFactory->isRemote() ? " (remote)" : " (local)");
 
-  std::promise<void> getProviderCapabilities_promise;
-  auto getProviderCapabilities_future =
-      getProviderCapabilities_promise.get_future();
   auto getProviderCapabilities_cb =
-      [& capabilities = this->capabilities_, &getProviderCapabilities_promise](
+      [& capabilities = this->capabilities_](
           const hidl_vec<AudioCapabilities>& audioCapabilities) {
+        capabilities.clear();
         for (auto capability : audioCapabilities) {
           capabilities.push_back(capability);
         }
-        getProviderCapabilities_promise.set_value();
       };
   auto hidl_retval = providersFactory->getProviderCapabilities(
       sink_->GetSessionType(), getProviderCapabilities_cb);
-  getProviderCapabilities_future.get();
   if (!hidl_retval.isOk()) {
     LOG(FATAL) << __func__ << ": BluetoothAudioHal::getProviderCapabilities failure: " << hidl_retval.description();
     return;
@@ -490,9 +527,12 @@
 void BluetoothAudioClientInterface::RenewAudioProviderAndSession() {
   // NOTE: must be invoked on the same thread where this
   // BluetoothAudioClientInterface is running
-  fetch_audio_provider();
-  session_started_ = false;
-  StartSession();
+  FetchAudioProvider();
+  if (session_started_) {
+    LOG(INFO) << __func__ << ": Restart the session while audio HAL recovering";
+    session_started_ = false;
+    StartSession();
+  }
 }
 
 }  // namespace audio
diff --git a/audio_hal_interface/client_interface.h b/audio_hal_interface/client_interface.h
index 11984aa..1484fc9 100644
--- a/audio_hal_interface/client_interface.h
+++ b/audio_hal_interface/client_interface.h
@@ -18,6 +18,7 @@
 
 #include <time.h>
 #include <mutex>
+#include <vector>
 
 #include <android/hardware/bluetooth/audio/2.0/IBluetoothAudioProvider.h>
 #include <android/hardware/bluetooth/audio/2.0/types.h>
@@ -35,8 +36,6 @@
 using ::android::hardware::bluetooth::audio::V2_0::AudioConfiguration;
 using ::android::hardware::bluetooth::audio::V2_0::BitsPerSample;
 using ::android::hardware::bluetooth::audio::V2_0::ChannelMode;
-using ::android::hardware::bluetooth::audio::V2_0::CodecConfiguration;
-using ::android::hardware::bluetooth::audio::V2_0::CodecType;
 using ::android::hardware::bluetooth::audio::V2_0::IBluetoothAudioProvider;
 using ::android::hardware::bluetooth::audio::V2_0::PcmParameters;
 using ::android::hardware::bluetooth::audio::V2_0::SampleRate;
@@ -135,7 +134,11 @@
     return provider_ != nullptr;
   }
 
+  IBluetoothTransportInstance* GetTransportInstance() const { return sink_; }
+
   std::vector<AudioCapabilities> GetAudioCapabilities() const;
+  static std::vector<AudioCapabilities> GetAudioCapabilities(
+      SessionType session_type);
 
   bool UpdateAudioConfig(const AudioConfiguration& audioConfig);
 
@@ -158,12 +161,15 @@
 
   static constexpr PcmParameters kInvalidPcmConfiguration = {
       .sampleRate = SampleRate::RATE_UNKNOWN,
+      .channelMode = ChannelMode::UNKNOWN,
       .bitsPerSample = BitsPerSample::BITS_UNKNOWN,
-      .channelMode = ChannelMode::UNKNOWN};
+  };
 
  private:
+  static bool IsSupported();
+
   // Helper function to connect to an IBluetoothAudioProvider
-  void fetch_audio_provider();
+  void FetchAudioProvider();
 
   mutable std::mutex internal_mutex_;
   IBluetoothTransportInstance* sink_;
diff --git a/audio_hal_interface/client_interface_unittest.cc b/audio_hal_interface/client_interface_unittest.cc
index de6ba52..f487f85 100644
--- a/audio_hal_interface/client_interface_unittest.cc
+++ b/audio_hal_interface/client_interface_unittest.cc
@@ -17,6 +17,7 @@
 #include <gtest/gtest.h>
 
 #include "client_interface.h"
+#include "codec_status.h"
 
 namespace {
 
@@ -25,7 +26,6 @@
 using ::android::hardware::bluetooth::audio::V2_0::AacVariableBitRate;
 using ::android::hardware::bluetooth::audio::V2_0::AptxParameters;
 using ::android::hardware::bluetooth::audio::V2_0::CodecCapabilities;
-using ::android::hardware::bluetooth::audio::V2_0::CodecConfiguration;
 using ::android::hardware::bluetooth::audio::V2_0::CodecType;
 using ::android::hardware::bluetooth::audio::V2_0::LdacChannelMode;
 using ::android::hardware::bluetooth::audio::V2_0::LdacParameters;
@@ -38,24 +38,76 @@
 
 using ::bluetooth::audio::AudioCapabilities;
 using ::bluetooth::audio::AudioConfiguration;
-using ::bluetooth::audio::BitsPerSample;
 using ::bluetooth::audio::BluetoothAudioClientInterface;
 using ::bluetooth::audio::BluetoothAudioStatus;
-using ::bluetooth::audio::ChannelMode;
 using ::bluetooth::audio::PcmParameters;
-using ::bluetooth::audio::SampleRate;
 using ::bluetooth::audio::SessionType;
+using ::bluetooth::audio::codec::A2dpCodecToHalBitsPerSample;
+using ::bluetooth::audio::codec::A2dpCodecToHalChannelMode;
+using ::bluetooth::audio::codec::A2dpCodecToHalSampleRate;
+using ::bluetooth::audio::codec::BitsPerSample;
+using ::bluetooth::audio::codec::ChannelMode;
+using ::bluetooth::audio::codec::CodecConfiguration;
+using ::bluetooth::audio::codec::IsCodecOffloadingEnabled;
+using ::bluetooth::audio::codec::SampleRate;
+using ::bluetooth::audio::codec::UpdateOffloadingCapabilities;
 using ::testing::Test;
 
-constexpr SampleRate kSampleRates[9] = {
-    SampleRate::RATE_UNKNOWN, SampleRate::RATE_44100, SampleRate::RATE_48000,
-    SampleRate::RATE_88200,   SampleRate::RATE_96000, SampleRate::RATE_176400,
-    SampleRate::RATE_192000,  SampleRate::RATE_16000, SampleRate::RATE_24000};
-constexpr BitsPerSample kBitsPerSamples[4] = {
-    BitsPerSample::BITS_UNKNOWN, BitsPerSample::BITS_16, BitsPerSample::BITS_24,
-    BitsPerSample::BITS_32};
-constexpr ChannelMode kChannelModes[3] = {
-    ChannelMode::UNKNOWN, ChannelMode::MONO, ChannelMode::STEREO};
+struct SampleRatePair {
+  SampleRate hal_sample_rate_;
+  btav_a2dp_codec_sample_rate_t btav_sample_rate_;
+};
+constexpr SampleRatePair kSampleRatePairs[9] = {
+    {.hal_sample_rate_ = SampleRate::RATE_UNKNOWN,
+     .btav_sample_rate_ = BTAV_A2DP_CODEC_SAMPLE_RATE_NONE},
+    {.hal_sample_rate_ = SampleRate::RATE_44100,
+     .btav_sample_rate_ = BTAV_A2DP_CODEC_SAMPLE_RATE_44100},
+    {.hal_sample_rate_ = SampleRate::RATE_48000,
+     .btav_sample_rate_ = BTAV_A2DP_CODEC_SAMPLE_RATE_48000},
+    {.hal_sample_rate_ = SampleRate::RATE_88200,
+     .btav_sample_rate_ = BTAV_A2DP_CODEC_SAMPLE_RATE_88200},
+    {.hal_sample_rate_ = SampleRate::RATE_96000,
+     .btav_sample_rate_ = BTAV_A2DP_CODEC_SAMPLE_RATE_96000},
+    {.hal_sample_rate_ = SampleRate::RATE_176400,
+     .btav_sample_rate_ = BTAV_A2DP_CODEC_SAMPLE_RATE_176400},
+    {.hal_sample_rate_ = SampleRate::RATE_192000,
+     .btav_sample_rate_ = BTAV_A2DP_CODEC_SAMPLE_RATE_192000},
+    {.hal_sample_rate_ = SampleRate::RATE_16000,
+     .btav_sample_rate_ = BTAV_A2DP_CODEC_SAMPLE_RATE_16000},
+    {.hal_sample_rate_ = SampleRate::RATE_24000,
+     .btav_sample_rate_ = BTAV_A2DP_CODEC_SAMPLE_RATE_24000}};
+
+struct BitsPerSamplePair {
+  BitsPerSample hal_bits_per_sample_;
+  btav_a2dp_codec_bits_per_sample_t btav_bits_per_sample_;
+};
+constexpr BitsPerSamplePair kBitsPerSamplePairs[4] = {
+    {.hal_bits_per_sample_ = BitsPerSample::BITS_UNKNOWN,
+     .btav_bits_per_sample_ = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE},
+    {.hal_bits_per_sample_ = BitsPerSample::BITS_16,
+     .btav_bits_per_sample_ = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16},
+    {.hal_bits_per_sample_ = BitsPerSample::BITS_24,
+     .btav_bits_per_sample_ = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24},
+    {.hal_bits_per_sample_ = BitsPerSample::BITS_32,
+     .btav_bits_per_sample_ = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32}};
+
+struct ChannelModePair {
+  ChannelMode hal_channel_mode_;
+  btav_a2dp_codec_channel_mode_t btav_channel_mode_;
+};
+constexpr ChannelModePair kChannelModePairs[3] = {
+    {.hal_channel_mode_ = ChannelMode::UNKNOWN,
+     .btav_channel_mode_ = BTAV_A2DP_CODEC_CHANNEL_MODE_NONE},
+    {.hal_channel_mode_ = ChannelMode::MONO,
+     .btav_channel_mode_ = BTAV_A2DP_CODEC_CHANNEL_MODE_MONO},
+    {.hal_channel_mode_ = ChannelMode::STEREO,
+     .btav_channel_mode_ = BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO}};
+
+constexpr btav_a2dp_codec_index_t codec_indexes[] = {
+    BTAV_A2DP_CODEC_INDEX_SOURCE_SBC,  BTAV_A2DP_CODEC_INDEX_SOURCE_AAC,
+    BTAV_A2DP_CODEC_INDEX_SOURCE_APTX, BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_HD,
+    BTAV_A2DP_CODEC_INDEX_SOURCE_LDAC, BTAV_A2DP_CODEC_INDEX_SINK_SBC,
+    BTAV_A2DP_CODEC_INDEX_SINK_AAC,    BTAV_A2DP_CODEC_INDEX_SINK_LDAC};
 constexpr uint16_t kPeerMtus[5] = {660, 663, 883, 1005, 1500};
 
 class TestTransport : public bluetooth::audio::IBluetoothTransportInstance {
@@ -65,16 +117,16 @@
  public:
   TestTransport(SessionType session_type)
       : bluetooth::audio::IBluetoothTransportInstance(session_type, {}){};
-  bluetooth::audio::BluetoothAudioCtrlAck StartRequest() {
+  bluetooth::audio::BluetoothAudioCtrlAck StartRequest() override {
     return bluetooth::audio::BluetoothAudioCtrlAck::SUCCESS_FINISHED;
   }
-  bluetooth::audio::BluetoothAudioCtrlAck SuspendRequest() {
+  bluetooth::audio::BluetoothAudioCtrlAck SuspendRequest() override {
     return bluetooth::audio::BluetoothAudioCtrlAck::SUCCESS_FINISHED;
   }
-  void StopRequest() {}
+  void StopRequest() override {}
   bool GetPresentationPosition(uint64_t* remote_delay_report_ns,
                                uint64_t* total_bytes_readed,
-                               timespec* data_position) {
+                               timespec* data_position) override {
     if (remote_delay_report_ns) {
       *remote_delay_report_ns = kRemoteDelayReportMs * 1000000;
     }
@@ -86,22 +138,25 @@
     }
     return true;
   }
-  void MetadataChanged(const source_metadata_t& source_metadata __unused) {}
-  void ResetPresentationPosition(){};
-  void LogBytesRead(size_t bytes_readed __unused){};
+  void MetadataChanged(
+      const source_metadata_t& source_metadata __unused) override {}
+  void ResetPresentationPosition() override{};
+  void LogBytesRead(size_t bytes_readed __unused) override{};
 };
 
 class BluetoothAudioClientInterfaceTest : public Test {
  protected:
-  TestTransport* test_transport_;
-  BluetoothAudioClientInterface* clientif_;
+  TestTransport* test_transport_ = nullptr;
+  BluetoothAudioClientInterface* clientif_ = nullptr;
 
   static constexpr int kClientIfReturnSuccess = 0;
 
-  virtual void SetUp() override {}
+  void SetUp() override {}
 
-  virtual void TearDown() override {
+  void TearDown() override {
+    if (clientif_ != nullptr) delete clientif_;
     clientif_ = nullptr;
+    if (test_transport_ != nullptr) delete test_transport_;
     test_transport_ = nullptr;
   }
 
@@ -120,8 +175,7 @@
     return (is_pcm_config_valid && is_pcm_config_supported);
   }
 
-  bool IsOffloadCodecConfigurationSupported(
-      const CodecConfiguration& codec_config) {
+  bool IsCodecOffloadingSupported(const CodecConfiguration& codec_config) {
     CodecCapabilities codec_capability = {};
     for (auto audio_capability : clientif_->GetAudioCapabilities()) {
       if (audio_capability.codecCapabilities().codecType ==
@@ -196,18 +250,38 @@
 
 }  // namespace
 
+TEST_F(BluetoothAudioClientInterfaceTest, A2dpCodecToHalPcmConfig) {
+  btav_a2dp_codec_config_t a2dp_codec_config = {};
+  for (auto sample_rate_pair : kSampleRatePairs) {
+    a2dp_codec_config.sample_rate = sample_rate_pair.btav_sample_rate_;
+    for (auto bits_per_sample_pair : kBitsPerSamplePairs) {
+      a2dp_codec_config.bits_per_sample =
+          bits_per_sample_pair.btav_bits_per_sample_;
+      for (auto channel_mode_pair : kChannelModePairs) {
+        a2dp_codec_config.channel_mode = channel_mode_pair.btav_channel_mode_;
+        EXPECT_EQ(A2dpCodecToHalSampleRate(a2dp_codec_config),
+                  sample_rate_pair.hal_sample_rate_);
+        EXPECT_EQ(A2dpCodecToHalBitsPerSample(a2dp_codec_config),
+                  bits_per_sample_pair.hal_bits_per_sample_);
+        EXPECT_EQ(A2dpCodecToHalChannelMode(a2dp_codec_config),
+                  channel_mode_pair.hal_channel_mode_);
+      }  // ChannelMode
+    }    // BitsPerSampple
+  }      // SampleRate
+}
+
 TEST_F(BluetoothAudioClientInterfaceTest, StartAndEndA2dpSoftwareSession) {
   test_transport_ =
       new TestTransport(SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH);
   clientif_ = new BluetoothAudioClientInterface(test_transport_, nullptr);
   AudioConfiguration audio_config = {};
   PcmParameters pcm_config = {};
-  for (auto sample_rate : kSampleRates) {
-    pcm_config.sampleRate = sample_rate;
-    for (auto bits_per_sample : kBitsPerSamples) {
-      pcm_config.bitsPerSample = bits_per_sample;
-      for (auto channel_mode : kChannelModes) {
-        pcm_config.channelMode = channel_mode;
+  for (auto sample_rate_pair : kSampleRatePairs) {
+    pcm_config.sampleRate = sample_rate_pair.hal_sample_rate_;
+    for (auto bits_per_sample_pair : kBitsPerSamplePairs) {
+      pcm_config.bitsPerSample = bits_per_sample_pair.hal_bits_per_sample_;
+      for (auto channel_mode_pair : kChannelModePairs) {
+        pcm_config.channelMode = channel_mode_pair.hal_channel_mode_;
         audio_config.pcmConfig(pcm_config);
         clientif_->UpdateAudioConfig(audio_config);
         if (IsSoftwarePcmParametersSupported(pcm_config)) {
@@ -221,11 +295,36 @@
   }      // SampleRate
 }
 
-TEST_F(BluetoothAudioClientInterfaceTest, StartAndEndA2dpOffloadSbcSession) {
-  test_transport_ =
-      new TestTransport(SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH);
-  clientif_ = new BluetoothAudioClientInterface(test_transport_, nullptr);
-  AudioConfiguration audio_config = {};
+struct CodecOffloadingPreference {
+  bool is_target_codec_included_;
+  std::vector<btav_a2dp_codec_config_t> preference_;
+};
+
+std::vector<CodecOffloadingPreference> CodecOffloadingPreferenceGenerator(
+    btav_a2dp_codec_index_t target_codec_index) {
+  std::vector<CodecOffloadingPreference> codec_offloading_preferences = {
+      {.is_target_codec_included_ = false,
+       .preference_ = std::vector<btav_a2dp_codec_config_t>(0)}};
+  btav_a2dp_codec_config_t a2dp_codec_config = {};
+  for (auto codec_index : codec_indexes) {
+    a2dp_codec_config.codec_type = codec_index;
+    auto duplicated_preferences = codec_offloading_preferences;
+    for (auto iter = duplicated_preferences.begin();
+         iter != duplicated_preferences.end(); ++iter) {
+      if (codec_index == target_codec_index) {
+        iter->is_target_codec_included_ = true;
+      }
+      iter->preference_.push_back(a2dp_codec_config);
+    }
+    codec_offloading_preferences.insert(codec_offloading_preferences.end(),
+                                        duplicated_preferences.begin(),
+                                        duplicated_preferences.end());
+  }
+  return codec_offloading_preferences;
+}
+
+std::vector<CodecConfiguration> SbcCodecConfigurationsGenerator() {
+  std::vector<CodecConfiguration> sbc_codec_configs;
   CodecConfiguration codec_config = {};
   SbcBlockLength block_lengths[4] = {
       SbcBlockLength::BLOCKS_4, SbcBlockLength::BLOCKS_8,
@@ -234,9 +333,9 @@
                                     SbcNumSubbands::SUBBAND_8};
   SbcAllocMethod alloc_methods[2] = {SbcAllocMethod::ALLOC_MD_S,
                                      SbcAllocMethod::ALLOC_MD_L};
-  for (auto sample_rate : kSampleRates) {
-    for (auto bits_per_sample : kBitsPerSamples) {
-      for (auto channel_mode : kChannelModes) {
+  for (auto sample_rate_pair : kSampleRatePairs) {
+    for (auto bits_per_sample_pair : kBitsPerSamplePairs) {
+      for (auto channel_mode_pair : kChannelModePairs) {
         for (auto peer_mtu : kPeerMtus) {
           for (auto block_length : block_lengths) {
             for (auto num_subband : num_subbands) {
@@ -247,25 +346,19 @@
                 // A2DP_SBC_DEFAULT_BITRATE
                 codec_config.encodedAudioBitrate = 328000;
                 SbcParameters sbc = {
-                    .sampleRate = sample_rate,
-                    .channelMode = (channel_mode == ChannelMode::MONO
+                    .sampleRate = sample_rate_pair.hal_sample_rate_,
+                    .channelMode = (channel_mode_pair.hal_channel_mode_ ==
+                                            ChannelMode::MONO
                                         ? SbcChannelMode::MONO
                                         : SbcChannelMode::JOINT_STEREO),
                     .blockLength = block_length,
                     .numSubbands = num_subband,
                     .allocMethod = alloc_method,
-                    .bitsPerSample = bits_per_sample,
+                    .bitsPerSample = bits_per_sample_pair.hal_bits_per_sample_,
                     .minBitpool = 2,
                     .maxBitpool = 53};
                 codec_config.config.sbcConfig(sbc);
-                audio_config.codecConfig(codec_config);
-                clientif_->UpdateAudioConfig(audio_config);
-                if (IsOffloadCodecConfigurationSupported(codec_config)) {
-                  EXPECT_EQ(clientif_->StartSession(), kClientIfReturnSuccess);
-                } else {
-                  EXPECT_NE(clientif_->StartSession(), kClientIfReturnSuccess);
-                }
-                EXPECT_EQ(clientif_->EndSession(), kClientIfReturnSuccess);
+                sbc_codec_configs.push_back(codec_config);
               }  // SbcAllocMethod
             }    // SbcNumSubbands
           }      // SbcBlockLength
@@ -273,22 +366,56 @@
       }          // ChannelMode
     }            // BitsPerSampple
   }              // SampleRate
+  return sbc_codec_configs;
 }
 
-TEST_F(BluetoothAudioClientInterfaceTest, StartAndEndA2dpOffloadAacSession) {
+TEST_F(BluetoothAudioClientInterfaceTest, A2dpSbcCodecOffloadingState) {
+  test_transport_ =
+      new TestTransport(SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH);
+  clientif_ = new BluetoothAudioClientInterface(test_transport_, nullptr);
+  auto sbc_codec_configs = SbcCodecConfigurationsGenerator();
+  for (auto codec_offloading_preference :
+       CodecOffloadingPreferenceGenerator(BTAV_A2DP_CODEC_INDEX_SOURCE_SBC)) {
+    UpdateOffloadingCapabilities(codec_offloading_preference.preference_);
+    for (CodecConfiguration codec_config : sbc_codec_configs) {
+      if (IsCodecOffloadingSupported(codec_config) &&
+          codec_offloading_preference.is_target_codec_included_) {
+        ASSERT_TRUE(IsCodecOffloadingEnabled(codec_config));
+      } else {
+        ASSERT_FALSE(IsCodecOffloadingEnabled(codec_config));
+      }
+    }
+  }
+}
+
+TEST_F(BluetoothAudioClientInterfaceTest, StartAndEndA2dpOffloadSbcSession) {
   test_transport_ =
       new TestTransport(SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH);
   clientif_ = new BluetoothAudioClientInterface(test_transport_, nullptr);
   AudioConfiguration audio_config = {};
+  for (CodecConfiguration codec_config : SbcCodecConfigurationsGenerator()) {
+    audio_config.codecConfig(codec_config);
+    clientif_->UpdateAudioConfig(audio_config);
+    if (IsCodecOffloadingSupported(codec_config)) {
+      EXPECT_EQ(clientif_->StartSession(), kClientIfReturnSuccess);
+    } else {
+      EXPECT_NE(clientif_->StartSession(), kClientIfReturnSuccess);
+    }
+    EXPECT_EQ(clientif_->EndSession(), kClientIfReturnSuccess);
+  }
+}
+
+std::vector<CodecConfiguration> AacCodecConfigurationsGenerator() {
+  std::vector<CodecConfiguration> aac_codec_configs;
   CodecConfiguration codec_config = {};
   AacObjectType object_types[4] = {
       AacObjectType::MPEG2_LC, AacObjectType::MPEG4_LC,
       AacObjectType::MPEG4_LTP, AacObjectType::MPEG4_SCALABLE};
   AacVariableBitRate variable_bitrates[2] = {AacVariableBitRate::DISABLED,
                                              AacVariableBitRate::ENABLED};
-  for (auto sample_rate : kSampleRates) {
-    for (auto bits_per_sample : kBitsPerSamples) {
-      for (auto channel_mode : kChannelModes) {
+  for (auto sample_rate_pair : kSampleRatePairs) {
+    for (auto bits_per_sample_pair : kBitsPerSamplePairs) {
+      for (auto channel_mode_pair : kChannelModePairs) {
         for (auto peer_mtu : kPeerMtus) {
           for (auto object_type : object_types) {
             for (auto variable_bitrate : variable_bitrates) {
@@ -297,40 +424,68 @@
               codec_config.isScmstEnabled = false;
               // A2DP_AAC_DEFAULT_BITRATE
               codec_config.encodedAudioBitrate = 320000;
-              AacParameters aac = {.objectType = object_type,
-                                   .sampleRate = sample_rate,
-                                   .channelMode = channel_mode,
-                                   .variableBitRateEnabled = variable_bitrate,
-                                   .bitsPerSample = bits_per_sample};
+              AacParameters aac = {
+                  .objectType = object_type,
+                  .sampleRate = sample_rate_pair.hal_sample_rate_,
+                  .channelMode = channel_mode_pair.hal_channel_mode_,
+                  .variableBitRateEnabled = variable_bitrate,
+                  .bitsPerSample = bits_per_sample_pair.hal_bits_per_sample_};
               codec_config.config.aacConfig(aac);
-              audio_config.codecConfig(codec_config);
-              clientif_->UpdateAudioConfig(audio_config);
-              if (IsOffloadCodecConfigurationSupported(codec_config)) {
-                EXPECT_EQ(clientif_->StartSession(), kClientIfReturnSuccess);
-              } else {
-                EXPECT_NE(clientif_->StartSession(), kClientIfReturnSuccess);
-              }
-              EXPECT_EQ(clientif_->EndSession(), kClientIfReturnSuccess);
+              aac_codec_configs.push_back(codec_config);
             }  // AacVariableBitRate
           }    // AacObjectType
         }      // peerMtu
       }        // ChannelMode
     }          // BitsPerSampple
   }            // SampleRate
+  return aac_codec_configs;
 }
 
-TEST_F(BluetoothAudioClientInterfaceTest, StartAndEndA2dpOffloadLdacSession) {
+TEST_F(BluetoothAudioClientInterfaceTest, A2dpAacCodecOffloadingState) {
+  test_transport_ =
+      new TestTransport(SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH);
+  clientif_ = new BluetoothAudioClientInterface(test_transport_, nullptr);
+  auto aac_codec_configs = AacCodecConfigurationsGenerator();
+  for (auto codec_offloading_preference :
+       CodecOffloadingPreferenceGenerator(BTAV_A2DP_CODEC_INDEX_SOURCE_AAC)) {
+    UpdateOffloadingCapabilities(codec_offloading_preference.preference_);
+    for (CodecConfiguration codec_config : aac_codec_configs) {
+      if (IsCodecOffloadingSupported(codec_config) &&
+          codec_offloading_preference.is_target_codec_included_) {
+        ASSERT_TRUE(IsCodecOffloadingEnabled(codec_config));
+      } else {
+        ASSERT_FALSE(IsCodecOffloadingEnabled(codec_config));
+      }
+    }
+  }
+}
+
+TEST_F(BluetoothAudioClientInterfaceTest, StartAndEndA2dpOffloadAacSession) {
   test_transport_ =
       new TestTransport(SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH);
   clientif_ = new BluetoothAudioClientInterface(test_transport_, nullptr);
   AudioConfiguration audio_config = {};
+  for (CodecConfiguration codec_config : AacCodecConfigurationsGenerator()) {
+    audio_config.codecConfig(codec_config);
+    clientif_->UpdateAudioConfig(audio_config);
+    if (IsCodecOffloadingSupported(codec_config)) {
+      EXPECT_EQ(clientif_->StartSession(), kClientIfReturnSuccess);
+    } else {
+      EXPECT_NE(clientif_->StartSession(), kClientIfReturnSuccess);
+    }
+    EXPECT_EQ(clientif_->EndSession(), kClientIfReturnSuccess);
+  }
+}
+
+std::vector<CodecConfiguration> LdacCodecConfigurationsGenerator() {
+  std::vector<CodecConfiguration> ldac_codec_configs;
   CodecConfiguration codec_config = {};
   LdacQualityIndex quality_indexes[4] = {
       LdacQualityIndex::QUALITY_HIGH, LdacQualityIndex::QUALITY_MID,
       LdacQualityIndex::QUALITY_LOW, LdacQualityIndex::QUALITY_ABR};
-  for (auto sample_rate : kSampleRates) {
-    for (auto bits_per_sample : kBitsPerSamples) {
-      for (auto channel_mode : kChannelModes) {
+  for (auto sample_rate_pair : kSampleRatePairs) {
+    for (auto bits_per_sample_pair : kBitsPerSamplePairs) {
+      for (auto channel_mode_pair : kChannelModePairs) {
         for (auto peer_mtu : kPeerMtus) {
           for (auto quality_index : quality_indexes) {
             codec_config.codecType = CodecType::LDAC;
@@ -338,26 +493,104 @@
             codec_config.isScmstEnabled = false;
             codec_config.encodedAudioBitrate = 990000;
             LdacParameters ldac = {
-                .sampleRate = sample_rate,
-                .channelMode = (channel_mode == ChannelMode::MONO
-                                    ? LdacChannelMode::MONO
-                                    : LdacChannelMode::STEREO),
+                .sampleRate = sample_rate_pair.hal_sample_rate_,
+                .channelMode =
+                    (channel_mode_pair.hal_channel_mode_ == ChannelMode::MONO
+                         ? LdacChannelMode::MONO
+                         : LdacChannelMode::STEREO),
                 .qualityIndex = quality_index,
-                .bitsPerSample = bits_per_sample};
+                .bitsPerSample = bits_per_sample_pair.hal_bits_per_sample_};
             codec_config.config.ldacConfig(ldac);
-            audio_config.codecConfig(codec_config);
-            clientif_->UpdateAudioConfig(audio_config);
-            if (IsOffloadCodecConfigurationSupported(codec_config)) {
-              EXPECT_EQ(clientif_->StartSession(), kClientIfReturnSuccess);
-            } else {
-              EXPECT_NE(clientif_->StartSession(), kClientIfReturnSuccess);
-            }
-            EXPECT_EQ(clientif_->EndSession(), kClientIfReturnSuccess);
+            ldac_codec_configs.push_back(codec_config);
           }  // LdacQualityIndex
         }    // peerMtu
       }      // ChannelMode
     }        // BitsPerSampple
   }          // SampleRate
+  return ldac_codec_configs;
+}
+
+TEST_F(BluetoothAudioClientInterfaceTest, A2dpLdacCodecOffloadingState) {
+  test_transport_ =
+      new TestTransport(SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH);
+  clientif_ = new BluetoothAudioClientInterface(test_transport_, nullptr);
+  auto ldac_codec_configs = LdacCodecConfigurationsGenerator();
+  for (auto codec_offloading_preference :
+       CodecOffloadingPreferenceGenerator(BTAV_A2DP_CODEC_INDEX_SOURCE_LDAC)) {
+    UpdateOffloadingCapabilities(codec_offloading_preference.preference_);
+    for (CodecConfiguration codec_config : ldac_codec_configs) {
+      if (IsCodecOffloadingSupported(codec_config) &&
+          codec_offloading_preference.is_target_codec_included_) {
+        ASSERT_TRUE(IsCodecOffloadingEnabled(codec_config));
+      } else {
+        ASSERT_FALSE(IsCodecOffloadingEnabled(codec_config));
+      }
+    }
+  }
+}
+
+TEST_F(BluetoothAudioClientInterfaceTest, StartAndEndA2dpOffloadLdacSession) {
+  test_transport_ =
+      new TestTransport(SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH);
+  clientif_ = new BluetoothAudioClientInterface(test_transport_, nullptr);
+  AudioConfiguration audio_config = {};
+  for (CodecConfiguration codec_config : LdacCodecConfigurationsGenerator()) {
+    audio_config.codecConfig(codec_config);
+    clientif_->UpdateAudioConfig(audio_config);
+    if (IsCodecOffloadingSupported(codec_config)) {
+      EXPECT_EQ(clientif_->StartSession(), kClientIfReturnSuccess);
+    } else {
+      EXPECT_NE(clientif_->StartSession(), kClientIfReturnSuccess);
+    }
+    EXPECT_EQ(clientif_->EndSession(), kClientIfReturnSuccess);
+  }
+}
+
+std::vector<CodecConfiguration> AptxCodecConfigurationsGenerator(
+    CodecType codec_type) {
+  std::vector<CodecConfiguration> aptx_codec_configs;
+  if (codec_type != CodecType::APTX && codec_type != CodecType::APTX_HD)
+    return aptx_codec_configs;
+  CodecConfiguration codec_config = {};
+  for (auto sample_rate_pair : kSampleRatePairs) {
+    for (auto bits_per_sample_pair : kBitsPerSamplePairs) {
+      for (auto channel_mode_pair : kChannelModePairs) {
+        for (auto peer_mtu : kPeerMtus) {
+          codec_config.codecType = codec_type;
+          codec_config.peerMtu = peer_mtu;
+          codec_config.isScmstEnabled = false;
+          codec_config.encodedAudioBitrate =
+              (codec_type == CodecType::APTX ? 352000 : 576000);
+          AptxParameters aptx = {
+              .sampleRate = sample_rate_pair.hal_sample_rate_,
+              .channelMode = channel_mode_pair.hal_channel_mode_,
+              .bitsPerSample = bits_per_sample_pair.hal_bits_per_sample_};
+          codec_config.config.aptxConfig(aptx);
+          aptx_codec_configs.push_back(codec_config);
+        }  // peerMtu
+      }    // ChannelMode
+    }      // BitsPerSampple
+  }        // SampleRate
+  return aptx_codec_configs;
+}
+
+TEST_F(BluetoothAudioClientInterfaceTest, A2dpAptxCodecOffloadingState) {
+  test_transport_ =
+      new TestTransport(SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH);
+  clientif_ = new BluetoothAudioClientInterface(test_transport_, nullptr);
+  auto aptx_codec_configs = AptxCodecConfigurationsGenerator(CodecType::APTX);
+  for (auto codec_offloading_preference :
+       CodecOffloadingPreferenceGenerator(BTAV_A2DP_CODEC_INDEX_SOURCE_APTX)) {
+    UpdateOffloadingCapabilities(codec_offloading_preference.preference_);
+    for (CodecConfiguration codec_config : aptx_codec_configs) {
+      if (IsCodecOffloadingSupported(codec_config) &&
+          codec_offloading_preference.is_target_codec_included_) {
+        ASSERT_TRUE(IsCodecOffloadingEnabled(codec_config));
+      } else {
+        ASSERT_FALSE(IsCodecOffloadingEnabled(codec_config));
+      }
+    }
+  }
 }
 
 TEST_F(BluetoothAudioClientInterfaceTest, StartAndEndA2dpOffloadAptxSession) {
@@ -365,31 +598,37 @@
       new TestTransport(SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH);
   clientif_ = new BluetoothAudioClientInterface(test_transport_, nullptr);
   AudioConfiguration audio_config = {};
-  CodecConfiguration codec_config = {};
-  for (auto sample_rate : kSampleRates) {
-    for (auto bits_per_sample : kBitsPerSamples) {
-      for (auto channel_mode : kChannelModes) {
-        for (auto peer_mtu : kPeerMtus) {
-          codec_config.codecType = CodecType::APTX;
-          codec_config.peerMtu = peer_mtu;
-          codec_config.isScmstEnabled = false;
-          codec_config.encodedAudioBitrate = 352000;
-          AptxParameters aptx = {.sampleRate = sample_rate,
-                                 .channelMode = channel_mode,
-                                 .bitsPerSample = bits_per_sample};
-          codec_config.config.aptxConfig(aptx);
-          audio_config.codecConfig(codec_config);
-          clientif_->UpdateAudioConfig(audio_config);
-          if (IsOffloadCodecConfigurationSupported(codec_config)) {
-            EXPECT_EQ(clientif_->StartSession(), kClientIfReturnSuccess);
-          } else {
-            EXPECT_NE(clientif_->StartSession(), kClientIfReturnSuccess);
-          }
-          EXPECT_EQ(clientif_->EndSession(), kClientIfReturnSuccess);
-        }  // peerMtu
-      }    // ChannelMode
-    }      // BitsPerSampple
-  }        // SampleRate
+  for (CodecConfiguration codec_config :
+       AptxCodecConfigurationsGenerator(CodecType::APTX)) {
+    audio_config.codecConfig(codec_config);
+    clientif_->UpdateAudioConfig(audio_config);
+    if (IsCodecOffloadingSupported(codec_config)) {
+      EXPECT_EQ(clientif_->StartSession(), kClientIfReturnSuccess);
+    } else {
+      EXPECT_NE(clientif_->StartSession(), kClientIfReturnSuccess);
+    }
+    EXPECT_EQ(clientif_->EndSession(), kClientIfReturnSuccess);
+  }
+}
+
+TEST_F(BluetoothAudioClientInterfaceTest, A2dpAptxHdCodecOffloadingState) {
+  test_transport_ =
+      new TestTransport(SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH);
+  clientif_ = new BluetoothAudioClientInterface(test_transport_, nullptr);
+  auto aptx_hd_codec_configs =
+      AptxCodecConfigurationsGenerator(CodecType::APTX_HD);
+  for (auto codec_offloading_preference : CodecOffloadingPreferenceGenerator(
+           BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_HD)) {
+    UpdateOffloadingCapabilities(codec_offloading_preference.preference_);
+    for (CodecConfiguration codec_config : aptx_hd_codec_configs) {
+      if (IsCodecOffloadingSupported(codec_config) &&
+          codec_offloading_preference.is_target_codec_included_) {
+        ASSERT_TRUE(IsCodecOffloadingEnabled(codec_config));
+      } else {
+        ASSERT_FALSE(IsCodecOffloadingEnabled(codec_config));
+      }
+    }
+  }
 }
 
 TEST_F(BluetoothAudioClientInterfaceTest, StartAndEndA2dpOffloadAptxHdSession) {
@@ -397,31 +636,17 @@
       new TestTransport(SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH);
   clientif_ = new BluetoothAudioClientInterface(test_transport_, nullptr);
   AudioConfiguration audio_config = {};
-  CodecConfiguration codec_config = {};
-  for (auto sample_rate : kSampleRates) {
-    for (auto bits_per_sample : kBitsPerSamples) {
-      for (auto channel_mode : kChannelModes) {
-        for (auto peer_mtu : kPeerMtus) {
-          codec_config.codecType = CodecType::APTX_HD;
-          codec_config.peerMtu = peer_mtu;
-          codec_config.isScmstEnabled = false;
-          codec_config.encodedAudioBitrate = 576000;
-          AptxParameters aptx = {.sampleRate = sample_rate,
-                                 .channelMode = channel_mode,
-                                 .bitsPerSample = bits_per_sample};
-          codec_config.config.aptxConfig(aptx);
-          audio_config.codecConfig(codec_config);
-          clientif_->UpdateAudioConfig(audio_config);
-          if (IsOffloadCodecConfigurationSupported(codec_config)) {
-            EXPECT_EQ(clientif_->StartSession(), kClientIfReturnSuccess);
-          } else {
-            EXPECT_NE(clientif_->StartSession(), kClientIfReturnSuccess);
-          }
-          EXPECT_EQ(clientif_->EndSession(), kClientIfReturnSuccess);
-        }  // peerMtu
-      }    // ChannelMode
-    }      // BitsPerSampple
-  }        // SampleRate
+  for (CodecConfiguration codec_config :
+       AptxCodecConfigurationsGenerator(CodecType::APTX_HD)) {
+    audio_config.codecConfig(codec_config);
+    clientif_->UpdateAudioConfig(audio_config);
+    if (IsCodecOffloadingSupported(codec_config)) {
+      EXPECT_EQ(clientif_->StartSession(), kClientIfReturnSuccess);
+    } else {
+      EXPECT_NE(clientif_->StartSession(), kClientIfReturnSuccess);
+    }
+    EXPECT_EQ(clientif_->EndSession(), kClientIfReturnSuccess);
+  }
 }
 
 TEST_F(BluetoothAudioClientInterfaceTest,
@@ -438,7 +663,7 @@
   codec_config.config = {};
   audio_config.codecConfig(codec_config);
   clientif_->UpdateAudioConfig(audio_config);
-  if (IsOffloadCodecConfigurationSupported(codec_config)) {
+  if (IsCodecOffloadingSupported(codec_config)) {
     EXPECT_EQ(clientif_->StartSession(), kClientIfReturnSuccess);
   } else {
     EXPECT_NE(clientif_->StartSession(), kClientIfReturnSuccess);
@@ -453,12 +678,12 @@
   clientif_ = new BluetoothAudioClientInterface(test_transport_, nullptr);
   AudioConfiguration audio_config = {};
   PcmParameters pcm_config = {};
-  for (auto sample_rate : kSampleRates) {
-    pcm_config.sampleRate = sample_rate;
-    for (auto bits_per_sample : kBitsPerSamples) {
-      pcm_config.bitsPerSample = bits_per_sample;
-      for (auto channel_mode : kChannelModes) {
-        pcm_config.channelMode = channel_mode;
+  for (auto sample_rate_pair : kSampleRatePairs) {
+    pcm_config.sampleRate = sample_rate_pair.hal_sample_rate_;
+    for (auto bits_per_sample_pair : kBitsPerSamplePairs) {
+      pcm_config.bitsPerSample = bits_per_sample_pair.hal_bits_per_sample_;
+      for (auto channel_mode_pair : kChannelModePairs) {
+        pcm_config.channelMode = channel_mode_pair.hal_channel_mode_;
         audio_config.pcmConfig(pcm_config);
         clientif_->UpdateAudioConfig(audio_config);
         if (IsSoftwarePcmParametersSupported(pcm_config)) {
diff --git a/audio_hal_interface/codec_status.cc b/audio_hal_interface/codec_status.cc
new file mode 100644
index 0000000..0819e5c
--- /dev/null
+++ b/audio_hal_interface/codec_status.cc
@@ -0,0 +1,569 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "codec_status.h"
+#include "client_interface.h"
+
+#include "a2dp_aac_constants.h"
+#include "a2dp_sbc_constants.h"
+#include "a2dp_vendor_aptx_constants.h"
+#include "a2dp_vendor_aptx_hd_constants.h"
+#include "a2dp_vendor_ldac_constants.h"
+#include "bta/av/bta_av_int.h"
+
+namespace {
+
+using ::android::hardware::bluetooth::audio::V2_0::AacObjectType;
+using ::android::hardware::bluetooth::audio::V2_0::AacParameters;
+using ::android::hardware::bluetooth::audio::V2_0::AacVariableBitRate;
+using ::android::hardware::bluetooth::audio::V2_0::AptxParameters;
+using ::android::hardware::bluetooth::audio::V2_0::AudioCapabilities;
+using ::android::hardware::bluetooth::audio::V2_0::BitsPerSample;
+using ::android::hardware::bluetooth::audio::V2_0::ChannelMode;
+using ::android::hardware::bluetooth::audio::V2_0::CodecType;
+using ::android::hardware::bluetooth::audio::V2_0::LdacChannelMode;
+using ::android::hardware::bluetooth::audio::V2_0::LdacParameters;
+using ::android::hardware::bluetooth::audio::V2_0::LdacQualityIndex;
+using ::android::hardware::bluetooth::audio::V2_0::SampleRate;
+using ::android::hardware::bluetooth::audio::V2_0::SbcAllocMethod;
+using ::android::hardware::bluetooth::audio::V2_0::SbcBlockLength;
+using ::android::hardware::bluetooth::audio::V2_0::SbcChannelMode;
+using ::android::hardware::bluetooth::audio::V2_0::SbcNumSubbands;
+using ::android::hardware::bluetooth::audio::V2_0::SbcParameters;
+
+// capabilities from BluetoothAudioClientInterface::GetAudioCapabilities()
+std::vector<AudioCapabilities> audio_hal_capabilities(0);
+// capabilities that audio HAL supports and frameworks / Bluetooth SoC / runtime
+// preference would like to use.
+std::vector<AudioCapabilities> offloading_preference(0);
+
+bool sbc_offloading_capability_match(const SbcParameters& sbc_capability,
+                                     const SbcParameters& sbc_config) {
+  if ((static_cast<SampleRate>(sbc_capability.sampleRate &
+                               sbc_config.sampleRate) ==
+       SampleRate::RATE_UNKNOWN) ||
+      (static_cast<SbcChannelMode>(sbc_capability.channelMode &
+                                   sbc_config.channelMode) ==
+       SbcChannelMode::UNKNOWN) ||
+      (static_cast<SbcBlockLength>(sbc_capability.blockLength &
+                                   sbc_config.blockLength) ==
+       static_cast<SbcBlockLength>(0)) ||
+      (static_cast<SbcNumSubbands>(sbc_capability.numSubbands &
+                                   sbc_config.numSubbands) ==
+       static_cast<SbcNumSubbands>(0)) ||
+      (static_cast<SbcAllocMethod>(sbc_capability.allocMethod &
+                                   sbc_config.allocMethod) ==
+       static_cast<SbcAllocMethod>(0)) ||
+      (static_cast<BitsPerSample>(sbc_capability.bitsPerSample &
+                                  sbc_config.bitsPerSample) ==
+       BitsPerSample::BITS_UNKNOWN) ||
+      (sbc_config.minBitpool < sbc_capability.minBitpool ||
+       sbc_config.maxBitpool < sbc_config.minBitpool ||
+       sbc_capability.maxBitpool < sbc_config.maxBitpool)) {
+    LOG(WARNING) << __func__ << ": software codec=" << toString(sbc_config)
+                 << " capability=" << toString(sbc_capability);
+    return false;
+  }
+  VLOG(1) << __func__ << ": offloading codec=" << toString(sbc_config)
+          << " capability=" << toString(sbc_capability);
+  return true;
+}
+
+bool aac_offloading_capability_match(const AacParameters& aac_capability,
+                                     const AacParameters& aac_config) {
+  if ((static_cast<AacObjectType>(aac_capability.objectType &
+                                  aac_config.objectType) ==
+       static_cast<AacObjectType>(0)) ||
+      (static_cast<SampleRate>(aac_capability.sampleRate &
+                               aac_config.sampleRate) ==
+       SampleRate::RATE_UNKNOWN) ||
+      (static_cast<ChannelMode>(aac_capability.channelMode &
+                                aac_config.channelMode) ==
+       ChannelMode::UNKNOWN) ||
+      (aac_capability.variableBitRateEnabled != AacVariableBitRate::ENABLED &&
+       aac_config.variableBitRateEnabled != AacVariableBitRate::DISABLED) ||
+      (static_cast<BitsPerSample>(aac_capability.bitsPerSample &
+                                  aac_config.bitsPerSample) ==
+       BitsPerSample::BITS_UNKNOWN)) {
+    LOG(WARNING) << __func__ << ": software codec=" << toString(aac_config)
+                 << " capability=" << toString(aac_capability);
+    return false;
+  }
+  VLOG(1) << __func__ << ": offloading codec=" << toString(aac_config)
+          << " capability=" << toString(aac_capability);
+  return true;
+}
+
+bool aptx_offloading_capability_match(const AptxParameters& aptx_capability,
+                                      const AptxParameters& aptx_config) {
+  if ((static_cast<SampleRate>(aptx_capability.sampleRate &
+                               aptx_config.sampleRate) ==
+       SampleRate::RATE_UNKNOWN) ||
+      (static_cast<ChannelMode>(aptx_capability.channelMode &
+                                aptx_config.channelMode) ==
+       ChannelMode::UNKNOWN) ||
+      (static_cast<BitsPerSample>(aptx_capability.bitsPerSample &
+                                  aptx_config.bitsPerSample) ==
+       BitsPerSample::BITS_UNKNOWN)) {
+    LOG(WARNING) << __func__ << ": software codec=" << toString(aptx_config)
+                 << " capability=" << toString(aptx_capability);
+    return false;
+  }
+  VLOG(1) << __func__ << ": offloading codec=" << toString(aptx_config)
+          << " capability=" << toString(aptx_capability);
+  return true;
+}
+
+bool ldac_offloading_capability_match(const LdacParameters& ldac_capability,
+                                      const LdacParameters& ldac_config) {
+  if ((static_cast<SampleRate>(ldac_capability.sampleRate &
+                               ldac_config.sampleRate) ==
+       SampleRate::RATE_UNKNOWN) ||
+      (static_cast<LdacChannelMode>(ldac_capability.channelMode &
+                                    ldac_config.channelMode) ==
+       LdacChannelMode::UNKNOWN) ||
+      (static_cast<BitsPerSample>(ldac_capability.bitsPerSample &
+                                  ldac_config.bitsPerSample) ==
+       BitsPerSample::BITS_UNKNOWN)) {
+    LOG(WARNING) << __func__ << ": software codec=" << toString(ldac_config)
+                 << " capability=" << toString(ldac_capability);
+    return false;
+  }
+  VLOG(1) << __func__ << ": offloading codec=" << toString(ldac_config)
+          << " capability=" << toString(ldac_capability);
+  return true;
+}
+}  // namespace
+
+namespace bluetooth {
+namespace audio {
+namespace codec {
+
+const CodecConfiguration kInvalidCodecConfiguration = {
+    .codecType = CodecType::UNKNOWN,
+    .encodedAudioBitrate = 0x00000000,
+    .peerMtu = 0xffff,
+    .isScmstEnabled = false,
+    .config = {}};
+
+SampleRate A2dpCodecToHalSampleRate(
+    const btav_a2dp_codec_config_t& a2dp_codec_config) {
+  switch (a2dp_codec_config.sample_rate) {
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_44100:
+      return SampleRate::RATE_44100;
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_48000:
+      return SampleRate::RATE_48000;
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_88200:
+      return SampleRate::RATE_88200;
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_96000:
+      return SampleRate::RATE_96000;
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_176400:
+      return SampleRate::RATE_176400;
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_192000:
+      return SampleRate::RATE_192000;
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_16000:
+      return SampleRate::RATE_16000;
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_24000:
+      return SampleRate::RATE_24000;
+    default:
+      return SampleRate::RATE_UNKNOWN;
+  }
+}
+
+BitsPerSample A2dpCodecToHalBitsPerSample(
+    const btav_a2dp_codec_config_t& a2dp_codec_config) {
+  switch (a2dp_codec_config.bits_per_sample) {
+    case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16:
+      return BitsPerSample::BITS_16;
+    case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24:
+      return BitsPerSample::BITS_24;
+    case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32:
+      return BitsPerSample::BITS_32;
+    default:
+      return BitsPerSample::BITS_UNKNOWN;
+  }
+}
+
+ChannelMode A2dpCodecToHalChannelMode(
+    const btav_a2dp_codec_config_t& a2dp_codec_config) {
+  switch (a2dp_codec_config.channel_mode) {
+    case BTAV_A2DP_CODEC_CHANNEL_MODE_MONO:
+      return ChannelMode::MONO;
+    case BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO:
+      return ChannelMode::STEREO;
+    default:
+      return ChannelMode::UNKNOWN;
+  }
+}
+
+bool A2dpSbcToHalConfig(CodecConfiguration* codec_config,
+                        A2dpCodecConfig* a2dp_config) {
+  btav_a2dp_codec_config_t current_codec = a2dp_config->getCodecConfig();
+  if (current_codec.codec_type != BTAV_A2DP_CODEC_INDEX_SOURCE_SBC &&
+      current_codec.codec_type != BTAV_A2DP_CODEC_INDEX_SINK_SBC) {
+    *codec_config = {};
+    return false;
+  }
+  tBT_A2DP_OFFLOAD a2dp_offload;
+  a2dp_config->getCodecSpecificConfig(&a2dp_offload);
+  codec_config->codecType = CodecType::SBC;
+  codec_config->config.sbcConfig({});
+  auto sbc_config = codec_config->config.sbcConfig();
+  sbc_config.sampleRate = A2dpCodecToHalSampleRate(current_codec);
+  if (sbc_config.sampleRate == SampleRate::RATE_UNKNOWN) {
+    LOG(ERROR) << __func__
+               << ": Unknown SBC sample_rate=" << current_codec.sample_rate;
+    return false;
+  }
+  uint8_t channel_mode = a2dp_offload.codec_info[3] & A2DP_SBC_IE_CH_MD_MSK;
+  switch (channel_mode) {
+    case A2DP_SBC_IE_CH_MD_JOINT:
+      sbc_config.channelMode = SbcChannelMode::JOINT_STEREO;
+      break;
+    case A2DP_SBC_IE_CH_MD_STEREO:
+      sbc_config.channelMode = SbcChannelMode::STEREO;
+      break;
+    case A2DP_SBC_IE_CH_MD_DUAL:
+      sbc_config.channelMode = SbcChannelMode::DUAL;
+      break;
+    case A2DP_SBC_IE_CH_MD_MONO:
+      sbc_config.channelMode = SbcChannelMode::MONO;
+      break;
+    default:
+      LOG(ERROR) << __func__ << ": Unknown SBC channel_mode=" << channel_mode;
+      sbc_config.channelMode = SbcChannelMode::UNKNOWN;
+      return false;
+  }
+  uint8_t block_length = a2dp_offload.codec_info[0] & A2DP_SBC_IE_BLOCKS_MSK;
+  switch (block_length) {
+    case A2DP_SBC_IE_BLOCKS_4:
+      sbc_config.blockLength = SbcBlockLength::BLOCKS_4;
+      break;
+    case A2DP_SBC_IE_BLOCKS_8:
+      sbc_config.blockLength = SbcBlockLength::BLOCKS_8;
+      break;
+    case A2DP_SBC_IE_BLOCKS_12:
+      sbc_config.blockLength = SbcBlockLength::BLOCKS_12;
+      break;
+    case A2DP_SBC_IE_BLOCKS_16:
+      sbc_config.blockLength = SbcBlockLength::BLOCKS_16;
+      break;
+    default:
+      LOG(ERROR) << __func__ << ": Unknown SBC block_length=" << block_length;
+      return false;
+  }
+  uint8_t sub_bands = a2dp_offload.codec_info[0] & A2DP_SBC_IE_SUBBAND_MSK;
+  switch (sub_bands) {
+    case A2DP_SBC_IE_SUBBAND_4:
+      sbc_config.numSubbands = SbcNumSubbands::SUBBAND_4;
+      break;
+    case A2DP_SBC_IE_SUBBAND_8:
+      sbc_config.numSubbands = SbcNumSubbands::SUBBAND_8;
+      break;
+    default:
+      LOG(ERROR) << __func__ << ": Unknown SBC Subbands=" << sub_bands;
+      return false;
+  }
+  uint8_t alloc_method = a2dp_offload.codec_info[0] & A2DP_SBC_IE_ALLOC_MD_MSK;
+  switch (alloc_method) {
+    case A2DP_SBC_IE_ALLOC_MD_S:
+      sbc_config.allocMethod = SbcAllocMethod::ALLOC_MD_S;
+      break;
+    case A2DP_SBC_IE_ALLOC_MD_L:
+      sbc_config.allocMethod = SbcAllocMethod::ALLOC_MD_L;
+      break;
+    default:
+      LOG(ERROR) << __func__ << ": Unknown SBC alloc_method=" << alloc_method;
+      return false;
+  }
+  sbc_config.minBitpool = a2dp_offload.codec_info[1];
+  sbc_config.maxBitpool = a2dp_offload.codec_info[2];
+  sbc_config.bitsPerSample = A2dpCodecToHalBitsPerSample(current_codec);
+  if (sbc_config.bitsPerSample == BitsPerSample::BITS_UNKNOWN) {
+    LOG(ERROR) << __func__ << ": Unknown SBC bits_per_sample="
+               << current_codec.bits_per_sample;
+    return false;
+  }
+  codec_config->config.sbcConfig(sbc_config);
+  return true;
+}
+
+bool A2dpAacToHalConfig(CodecConfiguration* codec_config,
+                        A2dpCodecConfig* a2dp_config) {
+  btav_a2dp_codec_config_t current_codec = a2dp_config->getCodecConfig();
+  if (current_codec.codec_type != BTAV_A2DP_CODEC_INDEX_SOURCE_AAC &&
+      current_codec.codec_type != BTAV_A2DP_CODEC_INDEX_SINK_AAC) {
+    *codec_config = {};
+    return false;
+  }
+  tBT_A2DP_OFFLOAD a2dp_offload;
+  a2dp_config->getCodecSpecificConfig(&a2dp_offload);
+  codec_config->codecType = CodecType::AAC;
+  codec_config->config.aacConfig({});
+  auto aac_config = codec_config->config.aacConfig();
+  uint8_t object_type = a2dp_offload.codec_info[0];
+  switch (object_type) {
+    case A2DP_AAC_OBJECT_TYPE_MPEG2_LC:
+      aac_config.objectType = AacObjectType::MPEG2_LC;
+      break;
+    case A2DP_AAC_OBJECT_TYPE_MPEG4_LC:
+      aac_config.objectType = AacObjectType::MPEG4_LC;
+      break;
+    case A2DP_AAC_OBJECT_TYPE_MPEG4_LTP:
+      aac_config.objectType = AacObjectType::MPEG4_LTP;
+      break;
+    case A2DP_AAC_OBJECT_TYPE_MPEG4_SCALABLE:
+      aac_config.objectType = AacObjectType::MPEG4_SCALABLE;
+      break;
+    default:
+      LOG(ERROR) << __func__ << ": Unknown AAC object_type=" << +object_type;
+      return false;
+  }
+  aac_config.sampleRate = A2dpCodecToHalSampleRate(current_codec);
+  if (aac_config.sampleRate == SampleRate::RATE_UNKNOWN) {
+    LOG(ERROR) << __func__
+               << ": Unknown AAC sample_rate=" << current_codec.sample_rate;
+    return false;
+  }
+  aac_config.channelMode = A2dpCodecToHalChannelMode(current_codec);
+  if (aac_config.channelMode == ChannelMode::UNKNOWN) {
+    LOG(ERROR) << __func__
+               << ": Unknown AAC channel_mode=" << current_codec.channel_mode;
+    return false;
+  }
+  uint8_t vbr_enabled =
+      a2dp_offload.codec_info[1] & A2DP_AAC_VARIABLE_BIT_RATE_MASK;
+  switch (vbr_enabled) {
+    case A2DP_AAC_VARIABLE_BIT_RATE_ENABLED:
+      aac_config.variableBitRateEnabled = AacVariableBitRate::ENABLED;
+      break;
+    case A2DP_AAC_VARIABLE_BIT_RATE_DISABLED:
+      aac_config.variableBitRateEnabled = AacVariableBitRate::DISABLED;
+      break;
+    default:
+      LOG(ERROR) << __func__ << ": Unknown AAC VBR=" << +vbr_enabled;
+      return false;
+  }
+  aac_config.bitsPerSample = A2dpCodecToHalBitsPerSample(current_codec);
+  if (aac_config.bitsPerSample == BitsPerSample::BITS_UNKNOWN) {
+    LOG(ERROR) << __func__ << ": Unknown AAC bits_per_sample="
+               << current_codec.bits_per_sample;
+    return false;
+  }
+  codec_config->config.aacConfig(aac_config);
+  return true;
+}
+
+bool A2dpAptxToHalConfig(CodecConfiguration* codec_config,
+                         A2dpCodecConfig* a2dp_config) {
+  btav_a2dp_codec_config_t current_codec = a2dp_config->getCodecConfig();
+  if (current_codec.codec_type != BTAV_A2DP_CODEC_INDEX_SOURCE_APTX &&
+      current_codec.codec_type != BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_HD) {
+    *codec_config = {};
+    return false;
+  }
+  tBT_A2DP_OFFLOAD a2dp_offload;
+  a2dp_config->getCodecSpecificConfig(&a2dp_offload);
+  if (current_codec.codec_type == BTAV_A2DP_CODEC_INDEX_SOURCE_APTX) {
+    codec_config->codecType = CodecType::APTX;
+  } else {
+    codec_config->codecType = CodecType::APTX_HD;
+  }
+  codec_config->config.aptxConfig({});
+  auto aptx_config = codec_config->config.aptxConfig();
+  aptx_config.sampleRate = A2dpCodecToHalSampleRate(current_codec);
+  if (aptx_config.sampleRate == SampleRate::RATE_UNKNOWN) {
+    LOG(ERROR) << __func__
+               << ": Unknown aptX sample_rate=" << current_codec.sample_rate;
+    return false;
+  }
+  aptx_config.channelMode = A2dpCodecToHalChannelMode(current_codec);
+  if (aptx_config.channelMode == ChannelMode::UNKNOWN) {
+    LOG(ERROR) << __func__
+               << ": Unknown aptX channel_mode=" << current_codec.channel_mode;
+    return false;
+  }
+  aptx_config.bitsPerSample = A2dpCodecToHalBitsPerSample(current_codec);
+  if (aptx_config.bitsPerSample == BitsPerSample::BITS_UNKNOWN) {
+    LOG(ERROR) << __func__ << ": Unknown aptX bits_per_sample="
+               << current_codec.bits_per_sample;
+    return false;
+  }
+  codec_config->config.aptxConfig(aptx_config);
+  return true;
+}
+
+bool A2dpLdacToHalConfig(CodecConfiguration* codec_config,
+                         A2dpCodecConfig* a2dp_config) {
+  btav_a2dp_codec_config_t current_codec = a2dp_config->getCodecConfig();
+  if (current_codec.codec_type != BTAV_A2DP_CODEC_INDEX_SOURCE_LDAC) {
+    codec_config = {};
+    return false;
+  }
+  tBT_A2DP_OFFLOAD a2dp_offload;
+  a2dp_config->getCodecSpecificConfig(&a2dp_offload);
+  codec_config->codecType = CodecType::LDAC;
+  codec_config->config.ldacConfig({});
+  auto ldac_config = codec_config->config.ldacConfig();
+  ldac_config.sampleRate = A2dpCodecToHalSampleRate(current_codec);
+  if (ldac_config.sampleRate == SampleRate::RATE_UNKNOWN) {
+    LOG(ERROR) << __func__
+               << ": Unknown LDAC sample_rate=" << current_codec.sample_rate;
+    return false;
+  }
+  switch (a2dp_offload.codec_info[7]) {
+    case A2DP_LDAC_CHANNEL_MODE_STEREO:
+      ldac_config.channelMode = LdacChannelMode::STEREO;
+      break;
+    case A2DP_LDAC_CHANNEL_MODE_DUAL:
+      ldac_config.channelMode = LdacChannelMode::DUAL;
+      break;
+    case A2DP_LDAC_CHANNEL_MODE_MONO:
+      ldac_config.channelMode = LdacChannelMode::MONO;
+      break;
+    default:
+      LOG(ERROR) << __func__ << ": Unknown LDAC channel_mode="
+                 << a2dp_offload.codec_info[7];
+      ldac_config.channelMode = LdacChannelMode::UNKNOWN;
+      return false;
+  }
+  switch (a2dp_offload.codec_info[6]) {
+    case A2DP_LDAC_QUALITY_HIGH:
+      ldac_config.qualityIndex = LdacQualityIndex::QUALITY_HIGH;
+      break;
+    case A2DP_LDAC_QUALITY_MID:
+      ldac_config.qualityIndex = LdacQualityIndex::QUALITY_MID;
+      break;
+    case A2DP_LDAC_QUALITY_LOW:
+      ldac_config.qualityIndex = LdacQualityIndex::QUALITY_LOW;
+      break;
+    case A2DP_LDAC_QUALITY_ABR_OFFLOAD:
+      ldac_config.qualityIndex = LdacQualityIndex::QUALITY_ABR;
+      break;
+    default:
+      LOG(ERROR) << __func__ << ": Unknown LDAC QualityIndex="
+                 << a2dp_offload.codec_info[6];
+      return false;
+  }
+  ldac_config.bitsPerSample = A2dpCodecToHalBitsPerSample(current_codec);
+  if (ldac_config.bitsPerSample == BitsPerSample::BITS_UNKNOWN) {
+    LOG(ERROR) << __func__ << ": Unknown LDAC bits_per_sample="
+               << current_codec.bits_per_sample;
+    return false;
+  }
+  codec_config->config.ldacConfig(ldac_config);
+  return true;
+}
+
+bool UpdateOffloadingCapabilities(
+    const std::vector<btav_a2dp_codec_config_t>& framework_preference) {
+  audio_hal_capabilities = BluetoothAudioClientInterface::GetAudioCapabilities(
+      SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH);
+  uint32_t codec_type_masks = static_cast<uint32_t>(CodecType::UNKNOWN);
+  for (auto preference : framework_preference) {
+    switch (preference.codec_type) {
+      case BTAV_A2DP_CODEC_INDEX_SOURCE_SBC:
+        codec_type_masks |= CodecType::SBC;
+        break;
+      case BTAV_A2DP_CODEC_INDEX_SOURCE_AAC:
+        codec_type_masks |= CodecType::AAC;
+        break;
+      case BTAV_A2DP_CODEC_INDEX_SOURCE_APTX:
+        codec_type_masks |= CodecType::APTX;
+        break;
+      case BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_HD:
+        codec_type_masks |= CodecType::APTX_HD;
+        break;
+      case BTAV_A2DP_CODEC_INDEX_SOURCE_LDAC:
+        codec_type_masks |= CodecType::LDAC;
+        break;
+      case BTAV_A2DP_CODEC_INDEX_SINK_SBC:
+        [[fallthrough]];
+      case BTAV_A2DP_CODEC_INDEX_SINK_AAC:
+        [[fallthrough]];
+      case BTAV_A2DP_CODEC_INDEX_SINK_LDAC:
+        LOG(WARNING) << __func__
+                     << ": Ignore sink codec_type=" << preference.codec_type;
+        break;
+      case BTAV_A2DP_CODEC_INDEX_MAX:
+        [[fallthrough]];
+      default:
+        LOG(ERROR) << __func__
+                   << ": Unknown codec_type=" << preference.codec_type;
+        return false;
+    }
+  }
+  offloading_preference.clear();
+  for (auto capability : audio_hal_capabilities) {
+    if (static_cast<CodecType>(capability.codecCapabilities().codecType &
+                               codec_type_masks) != CodecType::UNKNOWN) {
+      LOG(INFO) << __func__
+                << ": enabled offloading capability=" << toString(capability);
+      offloading_preference.push_back(capability);
+    } else {
+      LOG(INFO) << __func__
+                << ": disabled offloading capability=" << toString(capability);
+    }
+  }
+  // TODO: Bluetooth SoC and runtime property
+  return true;
+}
+
+// Check whether this codec is supported by the audio HAL and is allowed to use
+// by prefernece of framework / Bluetooth SoC / runtime property.
+bool IsCodecOffloadingEnabled(const CodecConfiguration& codec_config) {
+  for (auto preference : offloading_preference) {
+    if (codec_config.codecType != preference.codecCapabilities().codecType)
+      continue;
+    auto codec_capability = preference.codecCapabilities();
+    switch (codec_capability.codecType) {
+      case CodecType::SBC: {
+        auto sbc_capability = codec_capability.capabilities.sbcCapabilities();
+        auto sbc_config = codec_config.config.sbcConfig();
+        return sbc_offloading_capability_match(sbc_capability, sbc_config);
+      }
+      case CodecType::AAC: {
+        auto aac_capability = codec_capability.capabilities.aacCapabilities();
+        auto aac_config = codec_config.config.aacConfig();
+        return aac_offloading_capability_match(aac_capability, aac_config);
+      }
+      case CodecType::APTX:
+        [[fallthrough]];
+      case CodecType::APTX_HD: {
+        auto aptx_capability = codec_capability.capabilities.aptxCapabilities();
+        auto aptx_config = codec_config.config.aptxConfig();
+        return aptx_offloading_capability_match(aptx_capability, aptx_config);
+      }
+      case CodecType::LDAC: {
+        auto ldac_capability = codec_capability.capabilities.ldacCapabilities();
+        auto ldac_config = codec_config.config.ldacConfig();
+        return ldac_offloading_capability_match(ldac_capability, ldac_config);
+      }
+      case CodecType::UNKNOWN:
+        [[fallthrough]];
+      default:
+        LOG(ERROR) << __func__ << ": Unknown codecType="
+                   << toString(codec_capability.codecType);
+        return false;
+    }
+  }
+  LOG(INFO) << __func__ << ": software codec=" << toString(codec_config);
+  return false;
+}
+
+}  // namespace codec
+}  // namespace audio
+}  // namespace bluetooth
diff --git a/audio_hal_interface/codec_status.h b/audio_hal_interface/codec_status.h
new file mode 100644
index 0000000..e0e0744
--- /dev/null
+++ b/audio_hal_interface/codec_status.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <vector>
+
+#include <android/hardware/bluetooth/audio/2.0/types.h>
+
+#include "a2dp_codec_api.h"
+
+namespace bluetooth {
+namespace audio {
+namespace codec {
+
+using ::android::hardware::bluetooth::audio::V2_0::BitsPerSample;
+using ::android::hardware::bluetooth::audio::V2_0::ChannelMode;
+using ::android::hardware::bluetooth::audio::V2_0::CodecConfiguration;
+using ::android::hardware::bluetooth::audio::V2_0::SampleRate;
+
+extern const CodecConfiguration kInvalidCodecConfiguration;
+
+SampleRate A2dpCodecToHalSampleRate(
+    const btav_a2dp_codec_config_t& a2dp_codec_config);
+BitsPerSample A2dpCodecToHalBitsPerSample(
+    const btav_a2dp_codec_config_t& a2dp_codec_config);
+ChannelMode A2dpCodecToHalChannelMode(
+    const btav_a2dp_codec_config_t& a2dp_codec_config);
+
+bool A2dpSbcToHalConfig(CodecConfiguration* codec_config,
+                        A2dpCodecConfig* a2dp_config);
+bool A2dpAacToHalConfig(CodecConfiguration* codec_config,
+                        A2dpCodecConfig* a2dp_config);
+bool A2dpAptxToHalConfig(CodecConfiguration* codec_config,
+                         A2dpCodecConfig* a2dp_config);
+bool A2dpLdacToHalConfig(CodecConfiguration* codec_config,
+                         A2dpCodecConfig* a2dp_config);
+
+bool UpdateOffloadingCapabilities(
+    const std::vector<btav_a2dp_codec_config_t>& framework_preference);
+// Check whether this codec is supported by the audio HAL and is allowed to use
+// by prefernece of framework / Bluetooth SoC / runtime property.
+bool IsCodecOffloadingEnabled(const CodecConfiguration& codec_config);
+
+}  // namespace codec
+}  // namespace audio
+}  // namespace bluetooth
diff --git a/audio_hearing_aid_hw/Android.bp b/audio_hearing_aid_hw/Android.bp
index 229fc25..4ed9e76 100644
--- a/audio_hearing_aid_hw/Android.bp
+++ b/audio_hearing_aid_hw/Android.bp
@@ -5,7 +5,7 @@
         "system/bt",
         "system/bt/include",
         "system/bt/audio_hearing_aid_hw/include",
-    ]
+    ],
 }
 
 // Audio A2DP shared library for target
diff --git a/binder/Android.bp b/binder/Android.bp
index 650906c..e4beadb 100644
--- a/binder/Android.bp
+++ b/binder/Android.bp
@@ -9,7 +9,7 @@
         "android/bluetooth/bluetooth_device.cc",
         "android/bluetooth/IBluetoothSocketManager.aidl",
         "android/os/parcel_uuid.cc",
-/* TODO: Uncomment this files as they get converted one-by-one into native implementation
+        /* TODO: Uncomment this files as they get converted one-by-one into native implementation
         "android/bluetooth/IBluetooth.aidl",
         "android/bluetooth/IBluetoothA2dp.aidl",
         "android/bluetooth/IBluetoothA2dpSink.aidl",
@@ -40,21 +40,20 @@
         "android/bluetooth/le/IAdvertisingSetCallback.aidl",
         "android/bluetooth/le/IPeriodicAdvertisingCallback.aidl",
         "android/bluetooth/le/IScannerCallback.aidl"
-*/
-     ],
-    export_include_dirs: [ "./"],
+        */
+    ],
+    export_include_dirs: ["./"],
     aidl: {
         export_aidl_headers: true,
         include_dirs: [
             "frameworks/native/aidl/binder",
 
-             /* required for android.os.ParcelUuid, and android.os.ParcelFileDescriptor */
+            /* required for android.os.ParcelUuid, and android.os.ParcelFileDescriptor */
             "frameworks/base/core/java",
             "system/bt/binder",
         ],
     },
     include_dirs: [
-        "libnativehelper/include/nativehelper",
         "system/bt/types",
     ],
     shared_libs: [
diff --git a/binder/android/bluetooth/IBluetooth.aidl b/binder/android/bluetooth/IBluetooth.aidl
index 6bda14b..0e554b7 100644
--- a/binder/android/bluetooth/IBluetooth.aidl
+++ b/binder/android/bluetooth/IBluetooth.aidl
@@ -35,12 +35,11 @@
  */
 interface IBluetooth
 {
-    boolean isEnabled();
     int getState();
-    boolean enable();
-    boolean enableNoAutoConnect();
+    boolean enable(boolean quietMode);
     boolean disable();
 
+    @UnsupportedAppUsage
     String getAddress();
     ParcelUuid[] getUuids();
     boolean setName(in String name);
@@ -59,7 +58,7 @@
     int getDiscoverableTimeout();
     boolean setDiscoverableTimeout(int timeout);
 
-    boolean startDiscovery(String callingPackage);
+    boolean startDiscovery(String callingPackage, String callingFeatureId);
     boolean cancelDiscovery();
     boolean isDiscovering();
     long getDiscoveryEndMillis();
@@ -68,8 +67,7 @@
     int getProfileConnectionState(int profile);
 
     BluetoothDevice[] getBondedDevices();
-    boolean createBond(in BluetoothDevice device, in int transport);
-    boolean createBondOutOfBand(in BluetoothDevice device, in int transport, in OobData oobData);
+    boolean createBond(in BluetoothDevice device, in int transport, in OobData oobData);
     boolean cancelBondProcess(in BluetoothDevice device);
     boolean removeBond(in BluetoothDevice device);
     int getBondState(in BluetoothDevice device);
@@ -79,10 +77,12 @@
 
     String getRemoteName(in BluetoothDevice device);
     int getRemoteType(in BluetoothDevice device);
+    @UnsupportedAppUsage
     String getRemoteAlias(in BluetoothDevice device);
     boolean setRemoteAlias(in BluetoothDevice device, in String name);
     int getRemoteClass(in BluetoothDevice device);
     ParcelUuid[] getRemoteUuids(in BluetoothDevice device);
+    @UnsupportedAppUsage
     boolean fetchRemoteUuids(in BluetoothDevice device);
     boolean sdpSearch(in BluetoothDevice device, in ParcelUuid uuid);
     int getBatteryLevel(in BluetoothDevice device);
@@ -102,8 +102,6 @@
     int getSimAccessPermission(in BluetoothDevice device);
     boolean setSimAccessPermission(in BluetoothDevice device, int value);
 
-    void sendConnectionStateChange(in BluetoothDevice device, int profile, int state, int prevState);
-
     void registerCallback(in IBluetoothCallback callback);
     void unregisterCallback(in IBluetoothCallback callback);
 
@@ -141,4 +139,13 @@
 
     void onLeServiceUp();
     void onBrEdrDown();
+
+    boolean connectAllEnabledProfiles(in BluetoothDevice device);
+    boolean disconnectAllEnabledProfiles(in BluetoothDevice device);
+
+    boolean setActiveDevice(in BluetoothDevice device, in int profiles);
+
+    List<BluetoothDevice> getMostRecentlyConnectedDevices();
+
+    boolean removeActiveDevice(in int profiles);
 }
diff --git a/binder/android/bluetooth/IBluetoothA2dp.aidl b/binder/android/bluetooth/IBluetoothA2dp.aidl
index 6606a1b..39157e5 100644
--- a/binder/android/bluetooth/IBluetoothA2dp.aidl
+++ b/binder/android/bluetooth/IBluetoothA2dp.aidl
@@ -27,15 +27,20 @@
  */
 interface IBluetoothA2dp {
     // Public API
+    @UnsupportedAppUsage
     boolean connect(in BluetoothDevice device);
+    @UnsupportedAppUsage
     boolean disconnect(in BluetoothDevice device);
+    @UnsupportedAppUsage
     List<BluetoothDevice> getConnectedDevices();
+    @UnsupportedAppUsage
     List<BluetoothDevice> getDevicesMatchingConnectionStates(in int[] states);
+    @UnsupportedAppUsage
     int getConnectionState(in BluetoothDevice device);
     boolean setActiveDevice(in BluetoothDevice device);
     BluetoothDevice getActiveDevice();
-    boolean setPriority(in BluetoothDevice device, int priority);
-    int getPriority(in BluetoothDevice device);
+    boolean setConnectionPolicy(in BluetoothDevice device, int connectionPolicy);
+    int getConnectionPolicy(in BluetoothDevice device);
     boolean isAvrcpAbsoluteVolumeSupported();
     oneway void setAvrcpAbsoluteVolume(int volume);
     boolean isA2dpPlaying(in BluetoothDevice device);
@@ -47,4 +52,5 @@
     int supportsOptionalCodecs(in BluetoothDevice device);
     int getOptionalCodecsEnabled(in BluetoothDevice device);
     oneway void setOptionalCodecsEnabled(in BluetoothDevice device, int value);
+    int getPriority(in BluetoothDevice device);
 }
diff --git a/binder/android/bluetooth/IBluetoothA2dpSink.aidl b/binder/android/bluetooth/IBluetoothA2dpSink.aidl
index 557eefc..946b8f4 100644
--- a/binder/android/bluetooth/IBluetoothA2dpSink.aidl
+++ b/binder/android/bluetooth/IBluetoothA2dpSink.aidl
@@ -31,7 +31,7 @@
     List<BluetoothDevice> getDevicesMatchingConnectionStates(in int[] states);
     int getConnectionState(in BluetoothDevice device);
     BluetoothAudioConfig getAudioConfig(in BluetoothDevice device);
-    boolean setPriority(in BluetoothDevice device, int priority);
-    int getPriority(in BluetoothDevice device);
+    boolean setConnectionPolicy(in BluetoothDevice device, int connectionPolicy);
+    int getConnectionPolicy(in BluetoothDevice device);
     boolean isA2dpPlaying(in BluetoothDevice device);
 }
diff --git a/binder/android/bluetooth/IBluetoothGatt.aidl b/binder/android/bluetooth/IBluetoothGatt.aidl
index c9e1c4b..78d0261 100644
--- a/binder/android/bluetooth/IBluetoothGatt.aidl
+++ b/binder/android/bluetooth/IBluetoothGatt.aidl
@@ -46,9 +46,9 @@
     void registerScanner(in IScannerCallback callback, in WorkSource workSource);
     void unregisterScanner(in int scannerId);
     void startScan(in int scannerId, in ScanSettings settings, in List<ScanFilter> filters,
-                   in List scanStorages, in String callingPackage);
+                   in List scanStorages, in String callingPackage, String callingFeatureId);
     void startScanForIntent(in PendingIntent intent, in ScanSettings settings, in List<ScanFilter> filters,
-                            in String callingPackage);
+                            in String callingPackage, String callingFeatureId);
     void stopScanForIntent(in PendingIntent intent, in String callingPackage);
     void stopScan(in int scannerId);
     void flushPendingBatchResults(in int scannerId);
@@ -71,8 +71,10 @@
     void registerSync(in ScanResult scanResult, in int skip, in int timeout, in IPeriodicAdvertisingCallback callback);
     void unregisterSync(in IPeriodicAdvertisingCallback callback);
 
+    @UnsupportedAppUsage
     void registerClient(in ParcelUuid appId, in IBluetoothGattCallback callback);
 
+    @UnsupportedAppUsage
     void unregisterClient(in int clientIf);
     void clientConnect(in int clientIf, in String address, in boolean isDirect, in int transport, in boolean opportunistic, in int phy);
     void clientDisconnect(in int clientIf, in String address);
diff --git a/binder/android/bluetooth/IBluetoothHeadset.aidl b/binder/android/bluetooth/IBluetoothHeadset.aidl
index 0f6954c..0edac14 100644
--- a/binder/android/bluetooth/IBluetoothHeadset.aidl
+++ b/binder/android/bluetooth/IBluetoothHeadset.aidl
@@ -29,8 +29,10 @@
  */
 interface IBluetoothHeadset {
     // Public API
+    @UnsupportedAppUsage
     List<BluetoothDevice> getConnectedDevices();
     List<BluetoothDevice> getDevicesMatchingConnectionStates(in int[] states);
+    @UnsupportedAppUsage
     int getConnectionState(in BluetoothDevice device);
     boolean startVoiceRecognition(in BluetoothDevice device);
     boolean stopVoiceRecognition(in BluetoothDevice device);
@@ -40,10 +42,12 @@
                                          in String arg);
 
     // Hidden API
+    @UnsupportedAppUsage
     boolean connect(in BluetoothDevice device);
+    @UnsupportedAppUsage
     boolean disconnect(in BluetoothDevice device);
-    boolean setPriority(in BluetoothDevice device, int priority);
-    int getPriority(in BluetoothDevice device);
+    boolean setConnectionPolicy(in BluetoothDevice device, int connectionPolicy);
+    int getConnectionPolicy(in BluetoothDevice device);
     int getAudioState(in BluetoothDevice device);
     boolean isAudioOn();
     boolean connectAudio();
@@ -59,4 +63,6 @@
     boolean setActiveDevice(in BluetoothDevice device);
     BluetoothDevice getActiveDevice();
     boolean isInbandRingingEnabled();
+    boolean setPriority(in BluetoothDevice device, int connectionPolicy);
+    int getPriority(in BluetoothDevice device);
 }
diff --git a/binder/android/bluetooth/IBluetoothHeadsetClient.aidl b/binder/android/bluetooth/IBluetoothHeadsetClient.aidl
index 13495ae..f767b27 100644
--- a/binder/android/bluetooth/IBluetoothHeadsetClient.aidl
+++ b/binder/android/bluetooth/IBluetoothHeadsetClient.aidl
@@ -32,8 +32,8 @@
     List<BluetoothDevice> getConnectedDevices();
     List<BluetoothDevice> getDevicesMatchingConnectionStates(in int[] states);
     int getConnectionState(in BluetoothDevice device);
-    boolean setPriority(in BluetoothDevice device, int priority);
-    int getPriority(in BluetoothDevice device);
+    boolean setConnectionPolicy(in BluetoothDevice device, int connectionPolicy);
+    int getConnectionPolicy(in BluetoothDevice device);
 
     boolean startVoiceRecognition(in BluetoothDevice device);
     boolean stopVoiceRecognition(in BluetoothDevice device);
@@ -59,6 +59,7 @@
     boolean disconnectAudio(in BluetoothDevice device);
     void setAudioRouteAllowed(in BluetoothDevice device, boolean allowed);
     boolean getAudioRouteAllowed(in BluetoothDevice device);
+    boolean sendVendorAtCommand(in BluetoothDevice device, int vendorId, String atCommand);
 
     Bundle getCurrentAgFeatures(in BluetoothDevice device);
 }
diff --git a/binder/android/bluetooth/IBluetoothHearingAid.aidl b/binder/android/bluetooth/IBluetoothHearingAid.aidl
index ad14192..b6e02c1 100644
--- a/binder/android/bluetooth/IBluetoothHearingAid.aidl
+++ b/binder/android/bluetooth/IBluetoothHearingAid.aidl
@@ -32,11 +32,9 @@
     int getConnectionState(in BluetoothDevice device);
     boolean setActiveDevice(in BluetoothDevice device);
     List<BluetoothDevice> getActiveDevices();
-    boolean setPriority(in BluetoothDevice device, int priority);
-    int getPriority(in BluetoothDevice device);
-    void adjustVolume(int direction);
+    boolean setConnectionPolicy(in BluetoothDevice device, int connectionPolicy);
+    int getConnectionPolicy(in BluetoothDevice device);
     void setVolume(int volume);
-    int getVolume();
 
     const int HI_SYNC_ID_INVALID = 0;
     long getHiSyncId(in BluetoothDevice device);
diff --git a/binder/android/bluetooth/IBluetoothHidDevice.aidl b/binder/android/bluetooth/IBluetoothHidDevice.aidl
index 5246262..cefb324 100644
--- a/binder/android/bluetooth/IBluetoothHidDevice.aidl
+++ b/binder/android/bluetooth/IBluetoothHidDevice.aidl
@@ -37,4 +37,5 @@
     List<BluetoothDevice> getDevicesMatchingConnectionStates(in int[] states);
     int getConnectionState(in BluetoothDevice device);
     String getUserAppName();
+    boolean setConnectionPolicy(in BluetoothDevice device, int connectionPolicy);
 }
diff --git a/binder/android/bluetooth/IBluetoothHidHost.aidl b/binder/android/bluetooth/IBluetoothHidHost.aidl
index 609da1b..ca3e33c 100644
--- a/binder/android/bluetooth/IBluetoothHidHost.aidl
+++ b/binder/android/bluetooth/IBluetoothHidHost.aidl
@@ -30,8 +30,8 @@
     List<BluetoothDevice> getConnectedDevices();
     List<BluetoothDevice> getDevicesMatchingConnectionStates(in int[] states);
     int getConnectionState(in BluetoothDevice device);
-    boolean setPriority(in BluetoothDevice device, int priority);
-    int getPriority(in BluetoothDevice device);
+    boolean setConnectionPolicy(in BluetoothDevice device, int connectionPolicy);
+    int getConnectionPolicy(in BluetoothDevice device);
     /**
     * @hide
     */
diff --git a/binder/android/bluetooth/IBluetoothManager.aidl b/binder/android/bluetooth/IBluetoothManager.aidl
index 8a80d49..e94fe64 100644
--- a/binder/android/bluetooth/IBluetoothManager.aidl
+++ b/binder/android/bluetooth/IBluetoothManager.aidl
@@ -31,13 +31,16 @@
 {
     IBluetooth registerAdapter(in IBluetoothManagerCallback callback);
     void unregisterAdapter(in IBluetoothManagerCallback callback);
+    @UnsupportedAppUsage
     void registerStateChangeCallback(in IBluetoothStateChangeCallback callback);
+    @UnsupportedAppUsage
     void unregisterStateChangeCallback(in IBluetoothStateChangeCallback callback);
     boolean isEnabled();
     boolean enable(String packageName);
     boolean enableNoAutoConnect(String packageName);
     boolean disable(String packageName, boolean persist);
     int getState();
+    @UnsupportedAppUsage
     IBluetoothGatt getBluetoothGatt();
 
     boolean bindBluetoothProfileService(int profile, IBluetoothProfileServiceConnection proxy);
@@ -49,8 +52,11 @@
     boolean onFactoryReset();
 
     boolean isBleScanAlwaysAvailable();
-    int updateBleAppCount(IBinder b, boolean enable, String packageName);
+    boolean enableBle(String packageName, IBinder b);
+    boolean disableBle(String packageName, IBinder b);
     boolean isBleAppPresent();
     boolean isHearingAidProfileSupported();
+
+    List<String> getSystemConfigEnabledProfilesForPackage(String packageName);
 }
 
diff --git a/binder/android/bluetooth/IBluetoothMap.aidl b/binder/android/bluetooth/IBluetoothMap.aidl
index 562490e..4f221e4 100644
--- a/binder/android/bluetooth/IBluetoothMap.aidl
+++ b/binder/android/bluetooth/IBluetoothMap.aidl
@@ -26,12 +26,11 @@
 interface IBluetoothMap {
     int getState();
     BluetoothDevice getClient();
-    boolean connect(in BluetoothDevice device);
     boolean disconnect(in BluetoothDevice device);
     boolean isConnected(in BluetoothDevice device);
     List<BluetoothDevice> getConnectedDevices();
     List<BluetoothDevice> getDevicesMatchingConnectionStates(in int[] states);
     int getConnectionState(in BluetoothDevice device);
-    boolean setPriority(in BluetoothDevice device, int priority);
-    int getPriority(in BluetoothDevice device);
+    boolean setConnectionPolicy(in BluetoothDevice device, int connectionPolicy);
+    int getConnectionPolicy(in BluetoothDevice device);
 }
diff --git a/binder/android/bluetooth/IBluetoothMapClient.aidl b/binder/android/bluetooth/IBluetoothMapClient.aidl
index 54e2dd7..3da40d3 100644
--- a/binder/android/bluetooth/IBluetoothMapClient.aidl
+++ b/binder/android/bluetooth/IBluetoothMapClient.aidl
@@ -32,8 +32,8 @@
     List<BluetoothDevice> getConnectedDevices();
     List<BluetoothDevice> getDevicesMatchingConnectionStates(in int[] states);
     int getConnectionState(in BluetoothDevice device);
-    boolean setPriority(in BluetoothDevice device,in int priority);
-    int getPriority(in BluetoothDevice device);
+    boolean setConnectionPolicy(in BluetoothDevice device,in int connectionPolicy);
+    int getConnectionPolicy(in BluetoothDevice device);
     boolean sendMessage(in BluetoothDevice device, in Uri[] contacts, in  String message,
         in PendingIntent sentIntent, in PendingIntent deliveryIntent);
     boolean getUnreadMessages(in BluetoothDevice device);
diff --git a/binder/android/bluetooth/IBluetoothPan.aidl b/binder/android/bluetooth/IBluetoothPan.aidl
index 4052aa4..292c127 100644
--- a/binder/android/bluetooth/IBluetoothPan.aidl
+++ b/binder/android/bluetooth/IBluetoothPan.aidl
@@ -32,4 +32,5 @@
     List<BluetoothDevice> getConnectedDevices();
     List<BluetoothDevice> getDevicesMatchingConnectionStates(in int[] states);
     int getConnectionState(in BluetoothDevice device);
+    boolean setConnectionPolicy(in BluetoothDevice device, int connectionPolicy);
 }
diff --git a/binder/android/bluetooth/IBluetoothPbap.aidl b/binder/android/bluetooth/IBluetoothPbap.aidl
index 52caf77..3cb6a6f 100644
--- a/binder/android/bluetooth/IBluetoothPbap.aidl
+++ b/binder/android/bluetooth/IBluetoothPbap.aidl
@@ -28,4 +28,5 @@
     List<BluetoothDevice> getDevicesMatchingConnectionStates(in int[] states);
     int getConnectionState(in BluetoothDevice device);
     void disconnect(in BluetoothDevice device);
+    boolean setConnectionPolicy(in BluetoothDevice device, int connectionPolicy);
 }
diff --git a/binder/android/bluetooth/IBluetoothPbapClient.aidl b/binder/android/bluetooth/IBluetoothPbapClient.aidl
index 52f9845..e4bfc4e 100644
--- a/binder/android/bluetooth/IBluetoothPbapClient.aidl
+++ b/binder/android/bluetooth/IBluetoothPbapClient.aidl
@@ -29,6 +29,6 @@
     List<BluetoothDevice> getConnectedDevices();
     List<BluetoothDevice> getDevicesMatchingConnectionStates(in int[] states);
     int getConnectionState(in BluetoothDevice device);
-    boolean setPriority(in BluetoothDevice device, int priority);
-    int getPriority(in BluetoothDevice device);
+    boolean setConnectionPolicy(in BluetoothDevice device, int connectionPolicy);
+    int getConnectionPolicy(in BluetoothDevice device);
 }
diff --git a/binder/android/bluetooth/IBluetoothSap.aidl b/binder/android/bluetooth/IBluetoothSap.aidl
index 0ea7afb..00f1f1a 100644
--- a/binder/android/bluetooth/IBluetoothSap.aidl
+++ b/binder/android/bluetooth/IBluetoothSap.aidl
@@ -32,6 +32,6 @@
     List<BluetoothDevice> getConnectedDevices();
     List<BluetoothDevice> getDevicesMatchingConnectionStates(in int[] states);
     int getConnectionState(in BluetoothDevice device);
-    boolean setPriority(in BluetoothDevice device, int priority);
-    int getPriority(in BluetoothDevice device);
+    boolean setConnectionPolicy(in BluetoothDevice device, int connectionPolicy);
+    int getConnectionPolicy(in BluetoothDevice device);
 }
diff --git a/bta/Android.bp b/bta/Android.bp
old mode 100644
new mode 100755
index 0e7bfe8..ac6b733
--- a/bta/Android.bp
+++ b/bta/Android.bp
@@ -34,7 +34,6 @@
 cc_library_static {
     name: "libbt-bta",
     defaults: ["fluoride_bta_defaults"],
-    cflags: ["-Wno-implicit-fallthrough"],
     srcs: [
         "ag/bta_ag_act.cc",
         "ag/bta_ag_api.cc",
@@ -143,3 +142,33 @@
         "libbt-common",
     ],
 }
+
+// bta hf client add record tests for target
+// ========================================================
+cc_test {
+    name: "net_test_hf_client_add_record",
+    defaults: ["fluoride_defaults"],
+    test_suites: ["device-tests"],
+    include_dirs: [
+        "system/bt",
+        "system/bt/bta/include",
+        "system/bt/bta/sys",
+        "system/bt/btif/include",
+        "system/bt/internal_include",
+        "system/bt/stack/include",
+        "system/bt/utils/include",
+    ],
+    srcs: [
+        "test/bta_hf_client_add_record_test.cc",
+    ],
+    header_libs: ["libbluetooth_headers"],
+    shared_libs: [
+        "libcutils",
+        "liblog",
+    ],
+    static_libs: [
+        "libbluetooth-types",
+        "libosi",
+    ],
+    cflags: ["-DBUILDCFG"],
+}
diff --git a/bta/ag/bta_ag_main.cc b/bta/ag/bta_ag_main.cc
index f70027f..f7bb8be 100644
--- a/bta/ag/bta_ag_main.cc
+++ b/bta/ag/bta_ag_main.cc
@@ -564,8 +564,8 @@
   if (p_scb->state == BTA_AG_INIT_ST) {
     LOG(INFO) << __func__ << ": Resume connection to " << p_scb->peer_addr
               << ", handle" << bta_ag_scb_to_idx(p_scb);
-    tBTA_AG_DATA open_data = {.api_open.bd_addr = p_scb->peer_addr,
-                              .api_open.sec_mask = p_scb->cli_sec_mask};
+    tBTA_AG_DATA open_data = {.api_open = {.bd_addr = p_scb->peer_addr,
+                                           .sec_mask = p_scb->cli_sec_mask}};
     bta_ag_sm_execute(p_scb, BTA_AG_API_OPEN_EVT, open_data);
   } else {
     VLOG(1) << __func__ << ": device " << p_scb->peer_addr
diff --git a/bta/ag/bta_ag_sdp.cc b/bta/ag/bta_ag_sdp.cc
index ba955d3..88b3bdb 100644
--- a/bta/ag/bta_ag_sdp.cc
+++ b/bta/ag/bta_ag_sdp.cc
@@ -87,7 +87,7 @@
     } else {
       event = BTA_AG_DISC_INT_RES_EVT;
     }
-    tBTA_AG_DATA disc_result = {.disc_result.status = status};
+    tBTA_AG_DATA disc_result = {.disc_result = {.status = status}};
     do_in_main_thread(FROM_HERE, base::Bind(&bta_ag_sm_execute_by_handle, idx,
                                             event, disc_result));
   }
diff --git a/bta/ar/bta_ar.cc b/bta/ar/bta_ar.cc
index bcfa89d..8e02aa4 100644
--- a/bta/ar/bta_ar.cc
+++ b/bta/ar/bta_ar.cc
@@ -102,17 +102,17 @@
   } else if (sys_id == BTA_ID_AVK) {
     bta_ar_cb.p_avk_conn_cback = p_cback;
     mask = BTA_AR_AVK_MASK;
+  } else {
+    APPL_TRACE_ERROR("%s: the registration is from wrong sys_id:%d", __func__,
+                     sys_id);
   }
-#if (BTA_AR_DEBUG == TRUE)
-  else {
-    APPL_TRACE_ERROR(
-        "bta_ar_reg_avdt: the registration is from wrong sys_id:%d", sys_id);
-  }
-#endif
 
   if (mask) {
     if (bta_ar_cb.avdt_registered == 0) {
       AVDT_Register(p_reg, bta_ar_avdt_cback);
+    } else {
+      APPL_TRACE_WARNING("%s: sys_id:%d doesn't register again (registered:%d)",
+                         __func__, sys_id, bta_ar_cb.avdt_registered);
     }
     bta_ar_cb.avdt_registered |= mask;
   }
diff --git a/bta/ar/bta_ar_int.h b/bta/ar/bta_ar_int.h
index ece8378..320fee9 100644
--- a/bta/ar/bta_ar_int.h
+++ b/bta/ar/bta_ar_int.h
@@ -27,10 +27,6 @@
 
 #include "bta_av_api.h"
 
-#ifndef BTA_AR_DEBUG
-#define BTA_AR_DEBUG TRUE
-#endif
-
 #define BTA_AR_AV_MASK 0x01
 #define BTA_AR_AVK_MASK 0x02
 
diff --git a/bta/av/bta_av_aact.cc b/bta/av/bta_av_aact.cc
index 7cd7056..29dcea0 100644
--- a/bta/av/bta_av_aact.cc
+++ b/bta/av/bta_av_aact.cc
@@ -250,7 +250,7 @@
   for (int i = 0; i < BTAV_A2DP_CODEC_INDEX_MAX; i++) {
     if (p_scb->seps[i].av_handle == tavdt_handle) return (p_scb->seps[i].tsep);
   }
-  APPL_TRACE_DEBUG("%s: handle %d not found", __func__, tavdt_handle)
+  APPL_TRACE_DEBUG("%s: avdt_handle %d not found", __func__, tavdt_handle)
   return AVDT_TSEP_INVALID;
 }
 
@@ -293,9 +293,9 @@
  *
  ******************************************************************************/
 static void notify_start_failed(tBTA_AV_SCB* p_scb) {
-  LOG_ERROR(LOG_TAG, "%s: peer %s role:0x%x channel:%d handle:0x%x", __func__,
-            p_scb->PeerAddress().ToString().c_str(), p_scb->role, p_scb->chnl,
-            p_scb->hndl);
+  LOG_ERROR(LOG_TAG, "%s: peer %s role:0x%x bta_channel:%d bta_handle:0x%x",
+            __func__, p_scb->PeerAddress().ToString().c_str(), p_scb->role,
+            p_scb->chnl, p_scb->hndl);
   tBTA_AV_START start;
   /* if start failed, clear role */
   p_scb->role &= ~BTA_AV_ROLE_START_INT;
@@ -477,7 +477,8 @@
     p_msg->initiator = false;
     if (event == AVDT_SUSPEND_CFM_EVT) p_msg->initiator = true;
 
-    APPL_TRACE_VERBOSE("%s: hndl:0x%x", __func__, p_scb->hndl);
+    APPL_TRACE_VERBOSE("%s: bta_handle:0x%x avdt_handle:%d", __func__,
+                       p_scb->hndl, handle);
     p_msg->hdr.layer_specific = p_scb->hndl;
     p_msg->handle = handle;
     p_msg->avdt_event = event;
@@ -523,8 +524,8 @@
     return;
   }
   p_pkt->event = BTA_AV_SINK_MEDIA_DATA_EVT;
-  p_scb->seps[p_scb->sep_idx].p_app_sink_data_cback(BTA_AV_SINK_MEDIA_DATA_EVT,
-                                                    (tBTA_AV_MEDIA*)p_pkt);
+  p_scb->seps[p_scb->sep_idx].p_app_sink_data_cback(
+      p_scb->PeerAddress(), BTA_AV_SINK_MEDIA_DATA_EVT, (tBTA_AV_MEDIA*)p_pkt);
   /* Free the buffer: a copy of the packet has been delivered */
   osi_free(p_pkt);
 }
@@ -543,12 +544,22 @@
   APPL_TRACE_DEBUG("%s: peer %s : found=%s", __func__,
                    peer_address.ToString().c_str(), (found) ? "true" : "false");
 
-  tBTA_AV_SCB* p_scb = bta_av_hndl_to_scb(bta_av_cb.handle);
+  tBTA_AV_SCB* p_scb = NULL;
+  if (peer_address != RawAddress::kEmpty) {
+    p_scb = bta_av_addr_to_scb(peer_address);
+  }
   if (p_scb == NULL) {
-    APPL_TRACE_ERROR("%s: no scb found for handle(0x%x)", __func__,
+    p_scb = bta_av_hndl_to_scb(bta_av_cb.handle);
+  }
+  if (p_scb == NULL) {
+    APPL_TRACE_ERROR("%s: no scb found for SDP handle(0x%x)", __func__,
                      bta_av_cb.handle);
     return;
   }
+  if (bta_av_cb.handle != p_scb->hndl) {
+    APPL_TRACE_WARNING("%s: SDP bta_handle expected=0x%x processing=0x%x",
+                       __func__, bta_av_cb.handle, p_scb->hndl);
+  }
 
   if (!found) {
     APPL_TRACE_ERROR("%s: peer %s A2DP service discovery failed", __func__,
@@ -583,7 +594,7 @@
   } else {
     p_scb->SetAvdtpVersion(0);
   }
-  p_msg->hdr.layer_specific = bta_av_cb.handle;
+  p_msg->hdr.layer_specific = p_scb->hndl;
 
   bta_sys_sendmsg(p_msg);
 }
@@ -601,7 +612,7 @@
   APPL_TRACE_DEBUG("%s: codec: %s", __func__,
                    A2DP_CodecName(p_scb->cfg.codec_info));
   for (int i = 0; i < BTAV_A2DP_CODEC_INDEX_MAX; i++) {
-    APPL_TRACE_DEBUG("%s: av_handle: %d codec: %s", __func__,
+    APPL_TRACE_DEBUG("%s: avdt_handle: %d codec: %s", __func__,
                      p_scb->seps[i].av_handle,
                      A2DP_CodecName(p_scb->seps[i].codec_info));
     if (p_scb->seps[i].av_handle && (p_scb->seps[i].av_handle == avdt_handle) &&
@@ -751,7 +762,7 @@
  *
  ******************************************************************************/
 void bta_av_delay_co(tBTA_AV_SCB* p_scb, tBTA_AV_DATA* p_data) {
-  APPL_TRACE_DEBUG("%s: peer %s handle:%d delay:%d", __func__,
+  APPL_TRACE_DEBUG("%s: peer %s bta_handle:0x%x delay:%d", __func__,
                    p_scb->PeerAddress().ToString().c_str(), p_scb->hndl,
                    p_data->str_msg.msg.delay_rpt_cmd.delay);
   p_scb->p_cos->delay(p_scb->hndl, p_scb->PeerAddress(),
@@ -847,9 +858,6 @@
 
   bta_sys_app_open(BTA_ID_AV, p_scb->app_id, p_scb->PeerAddress());
 
-  /* only one A2DP find service is active at a time */
-  bta_av_cb.handle = p_scb->hndl;
-
   /* set up parameters */
   db_params.db_len = BTA_AV_DISC_BUF_SIZE;
   db_params.num_attr = 3;
@@ -874,7 +882,10 @@
         "sdp_uuid=0x%x : status=%d",
         __func__, p_scb->PeerAddress().ToString().c_str(), p_scb->uuid_int,
         sdp_uuid, find_service_status);
-    bta_av_a2dp_sdp_cback(false, nullptr, RawAddress::kEmpty);
+    bta_av_a2dp_sdp_cback(false, nullptr, p_scb->PeerAddress());
+  } else {
+    /* only one A2DP find service is active at a time */
+    bta_av_cb.handle = p_scb->hndl;
   }
 }
 
@@ -911,6 +922,7 @@
   p_scb->cur_psc_mask = 0;
   p_scb->wait = 0;
   p_scb->num_disc_snks = 0;
+  p_scb->coll_mask = 0;
   alarm_cancel(p_scb->avrc_ct_timer);
 
   /* TODO(eisenbach): RE-IMPLEMENT USING VSC OR HAL EXTENSION
@@ -976,7 +988,7 @@
   local_sep = bta_av_get_scb_sep_type(p_scb, p_msg->handle);
   p_scb->avdt_label = p_data->str_msg.msg.hdr.label;
 
-  APPL_TRACE_DEBUG("%s: peer %s handle:%d local_sep:%d", __func__,
+  APPL_TRACE_DEBUG("%s: peer %s bta_handle:0x%x local_sep:%d", __func__,
                    p_scb->PeerAddress().ToString().c_str(), p_scb->hndl,
                    local_sep);
   APPL_TRACE_DEBUG("%s: codec: %s", __func__,
@@ -1050,18 +1062,21 @@
                            UNUSED_ATTR tBTA_AV_DATA* p_data) {
   tBTA_AV_RCB* p_rcb;
 
-  APPL_TRACE_WARNING("%s: conn_lcb: 0x%x peer_addr: %s", __func__,
-                     bta_av_cb.conn_lcb,
-                     p_scb->PeerAddress().ToString().c_str());
+  APPL_TRACE_API("%s: conn_lcb: 0x%x peer_addr: %s", __func__,
+                 bta_av_cb.conn_lcb, p_scb->PeerAddress().ToString().c_str());
 
   alarm_cancel(bta_av_cb.link_signalling_timer);
   alarm_cancel(p_scb->avrc_ct_timer);
 
-  if (bta_av_cb.conn_lcb) {
+  // conn_lcb is the index bitmask of all used LCBs, and since LCB and SCB use
+  // the same index, it should be safe to use SCB index here.
+  if ((bta_av_cb.conn_lcb & (1 << p_scb->hdi)) != 0) {
     p_rcb = bta_av_get_rcb_by_shdl((uint8_t)(p_scb->hdi + 1));
     if (p_rcb) bta_av_del_rc(p_rcb);
     AVDT_DisconnectReq(p_scb->PeerAddress(), &bta_av_proc_stream_evt);
   } else {
+    APPL_TRACE_WARNING("%s: conn_lcb=0x%x bta_handle=0x%x (hdi=%u) no link",
+                       __func__, bta_av_cb.conn_lcb, p_scb->hndl, p_scb->hdi);
     bta_av_ssm_execute(p_scb, BTA_AV_AVDT_DISCONNECT_EVT, NULL);
   }
 }
@@ -1122,9 +1137,11 @@
   /* we like this codec_type. find the sep_idx */
   local_sep = bta_av_get_scb_sep_type(p_scb, avdt_handle);
   bta_av_adjust_seps_idx(p_scb, avdt_handle);
-  APPL_TRACE_DEBUG("%s: peer %s handle: sep_idx: %d cur_psc_mask:0x%x",
-                   __func__, p_scb->PeerAddress().ToString().c_str(),
-                   p_scb->sep_idx, p_scb->cur_psc_mask);
+  LOG_DEBUG(
+      LOG_TAG,
+      "%s: peer %s bta_handle=0x%x avdt_handle=%d sep_idx=%d cur_psc_mask:0x%x",
+      __func__, p_scb->PeerAddress().ToString().c_str(), p_scb->hndl,
+      p_scb->avdt_handle, p_scb->sep_idx, p_scb->cur_psc_mask);
 
   if ((AVDT_TSEP_SNK == local_sep) &&
       (p_data->ci_setconfig.err_code == AVDT_SUCCESS) &&
@@ -1132,8 +1149,8 @@
     tBTA_AV_MEDIA av_sink_codec_info;
     av_sink_codec_info.avk_config.bd_addr = p_scb->PeerAddress();
     av_sink_codec_info.avk_config.codec_info = p_scb->cfg.codec_info;
-    p_scb->seps[p_scb->sep_idx].p_app_sink_data_cback(BTA_AV_SINK_MEDIA_CFG_EVT,
-                                                      &av_sink_codec_info);
+    p_scb->seps[p_scb->sep_idx].p_app_sink_data_cback(
+        p_scb->PeerAddress(), BTA_AV_SINK_MEDIA_CFG_EVT, &av_sink_codec_info);
   }
 
   AVDT_ConfigRsp(p_scb->avdt_handle, p_scb->avdt_label,
@@ -1167,6 +1184,7 @@
     } else {
       /* we do not know the peer device and it is using non-SBC codec
        * we need to know all the SEPs on SNK */
+      if (p_scb->uuid_int == 0) p_scb->uuid_int = p_scb->open_api.uuid;
       bta_av_discover_req(p_scb, NULL);
       return;
     }
@@ -1203,7 +1221,7 @@
   tBTA_AV_CONN_CHG msg;
   uint8_t* p;
 
-  APPL_TRACE_DEBUG("%s: peer %s handle: %d", __func__,
+  APPL_TRACE_DEBUG("%s: peer %s bta_handle: 0x%x", __func__,
                    p_scb->PeerAddress().ToString().c_str(), p_scb->hndl);
 
   msg.hdr.layer_specific = p_scb->hndl;
@@ -1384,15 +1402,16 @@
  *
  ******************************************************************************/
 void bta_av_connect_req(tBTA_AV_SCB* p_scb, UNUSED_ATTR tBTA_AV_DATA* p_data) {
-  APPL_TRACE_DEBUG("%s: peer %s coll_mask:0x%x", __func__,
+  APPL_TRACE_DEBUG("%s: peer %s coll_mask=0x%02x", __func__,
                    p_scb->PeerAddress().ToString().c_str(), p_scb->coll_mask);
   p_scb->sdp_discovery_started = false;
   if (p_scb->coll_mask & BTA_AV_COLL_INC_TMR) {
     /* SNK initiated L2C connection while SRC was doing SDP.    */
     /* Wait until timeout to check if SNK starts signalling.    */
-    APPL_TRACE_EVENT("%s: coll_mask = 0x%2X", __func__, p_scb->coll_mask);
+    APPL_TRACE_WARNING("%s: coll_mask=0x%02x incoming timer is up", __func__,
+                       p_scb->coll_mask);
     p_scb->coll_mask |= BTA_AV_COLL_API_CALLED;
-    APPL_TRACE_EVENT("%s: updated coll_mask = 0x%2X", __func__,
+    APPL_TRACE_EVENT("%s: updated coll_mask=0x%02x", __func__,
                      p_scb->coll_mask);
     return;
   }
@@ -1438,7 +1457,7 @@
   /* our uuid in case we initiate connection */
   uint16_t uuid_int = p_scb->uuid_int;
 
-  APPL_TRACE_DEBUG("%s: peer %s handle: %d initiator UUID 0x%x", __func__,
+  APPL_TRACE_DEBUG("%s: peer %s bta_handle: 0x%x initiator UUID 0x%x", __func__,
                    p_scb->PeerAddress().ToString().c_str(), p_scb->hndl,
                    uuid_int);
 
@@ -1494,7 +1513,7 @@
 void bta_av_disc_res_as_acp(tBTA_AV_SCB* p_scb, tBTA_AV_DATA* p_data) {
   uint8_t num_snks = 0, i;
 
-  APPL_TRACE_DEBUG("%s: peer %s handle: %d", __func__,
+  APPL_TRACE_DEBUG("%s: peer %s bta_handle: 0x%x", __func__,
                    p_scb->PeerAddress().ToString().c_str(), p_scb->hndl);
 
   /* store number of stream endpoints returned */
@@ -1545,9 +1564,9 @@
   bool getcap_done = false;
 
   APPL_TRACE_DEBUG(
-      "%s: peer %s handle:%d num_seps:%d sep_info_idx:%d wait:0x%x", __func__,
-      p_scb->PeerAddress().ToString().c_str(), p_scb->hndl, p_scb->num_seps,
-      p_scb->sep_info_idx, p_scb->wait);
+      "%s: peer %s bta_handle:0x%x num_seps:%d sep_info_idx:%d wait:0x%x",
+      __func__, p_scb->PeerAddress().ToString().c_str(), p_scb->hndl,
+      p_scb->num_seps, p_scb->sep_info_idx, p_scb->wait);
   APPL_TRACE_DEBUG("%s: codec: %s", __func__,
                    A2DP_CodecInfoString(p_scb->peer_cap.codec_info).c_str());
 
@@ -1612,7 +1631,7 @@
  *
  ******************************************************************************/
 void bta_av_cco_close(tBTA_AV_SCB* p_scb, UNUSED_ATTR tBTA_AV_DATA* p_data) {
-  APPL_TRACE_DEBUG("%s: peer %s handle:%d", __func__,
+  APPL_TRACE_DEBUG("%s: peer %s bta_handle:0x%x", __func__,
                    p_scb->PeerAddress().ToString().c_str(), p_scb->hndl);
   p_scb->p_cos->close(p_scb->hndl, p_scb->PeerAddress());
 }
@@ -1667,7 +1686,7 @@
 
     APPL_TRACE_ERROR(
         "%s: there is already an active connection: peer_addr=%s chnl=%d "
-        "hndl=%d status=%d starting=%d edr=%d",
+        "hndl=0x%x status=%d starting=%d edr=%d",
         __func__, open.bd_addr.ToString().c_str(), open.chnl, open.hndl,
         open.status, open.starting, open.edr);
 
@@ -1700,9 +1719,9 @@
   memcpy(cfg.codec_info, p_scb->peer_cap.codec_info, AVDT_CODEC_SIZE);
   memcpy(cfg.protect_info, p_scb->peer_cap.protect_info, AVDT_PROTECT_SIZE);
 
-  APPL_TRACE_DEBUG("%s: peer %s handle:%d num_codec:%d psc_mask=0x%x", __func__,
-                   p_scb->PeerAddress().ToString().c_str(), p_scb->hndl,
-                   p_scb->peer_cap.num_codec, p_scb->cfg.psc_mask);
+  APPL_TRACE_DEBUG("%s: peer %s bta_handle:0x%x num_codec:%d psc_mask=0x%x",
+                   __func__, p_scb->PeerAddress().ToString().c_str(),
+                   p_scb->hndl, p_scb->peer_cap.num_codec, p_scb->cfg.psc_mask);
   APPL_TRACE_DEBUG("%s: media type 0x%x, 0x%x", __func__, media_type,
                    p_scb->media_type);
   APPL_TRACE_DEBUG("%s: codec: %s", __func__,
@@ -1732,12 +1751,14 @@
     else if (uuid_int == UUID_SERVCLASS_AUDIO_SINK)
       bta_av_adjust_seps_idx(p_scb,
                              bta_av_get_scb_handle(p_scb, AVDT_TSEP_SNK));
+    LOG_DEBUG(LOG_TAG, "%s: sep_idx=%d avdt_handle=%d bta_handle=0x%x",
+              __func__, p_scb->sep_idx, p_scb->avdt_handle, p_scb->hndl);
 
     /* use only the services peer supports */
     cfg.psc_mask &= p_scb->peer_cap.psc_mask;
     p_scb->cur_psc_mask = cfg.psc_mask;
     APPL_TRACE_DEBUG(
-        "%s: peer %s handle:%d sep_idx:%d sep_info_idx:%d "
+        "%s: peer %s bta_handle:0x%x sep_idx:%d sep_info_idx:%d "
         "cur_psc_mask:0x%x",
         __func__, p_scb->PeerAddress().ToString().c_str(), p_scb->hndl,
         p_scb->sep_idx, p_scb->sep_info_idx, p_scb->cur_psc_mask);
@@ -1749,7 +1770,7 @@
       av_sink_codec_info.avk_config.bd_addr = p_scb->PeerAddress();
       av_sink_codec_info.avk_config.codec_info = p_scb->cfg.codec_info;
       p_scb->seps[p_scb->sep_idx].p_app_sink_data_cback(
-          BTA_AV_SINK_MEDIA_CFG_EVT, &av_sink_codec_info);
+          p_scb->PeerAddress(), BTA_AV_SINK_MEDIA_CFG_EVT, &av_sink_codec_info);
     }
 
     if (uuid_int == UUID_SERVCLASS_AUDIO_SOURCE) {
@@ -1780,7 +1801,8 @@
   uint8_t avdt_handle = p_data->ci_setconfig.avdt_handle;
 
   bta_av_adjust_seps_idx(p_scb, avdt_handle);
-  APPL_TRACE_DEBUG("%s: sep_idx: %d", __func__, p_scb->sep_idx);
+  LOG_DEBUG(LOG_TAG, "%s: sep_idx=%d avdt_handle=%d bta_handle=0x%x", __func__,
+            p_scb->sep_idx, p_scb->avdt_handle, p_scb->hndl);
   AVDT_ConfigRsp(p_scb->avdt_handle, p_scb->avdt_label, AVDT_ERR_UNSUP_CFG, 0);
 
   reject.bd_addr = p_data->str_msg.bd_addr;
@@ -1894,6 +1916,7 @@
   if (result != AVDT_SUCCESS) {
     LOG_ERROR(LOG_TAG, "%s: AVDT_StartReq failed for peer %s result:%d",
               __func__, p_scb->PeerAddress().ToString().c_str(), result);
+    bta_av_start_failed(p_scb, p_data);
   }
   LOG_INFO(LOG_TAG,
            "%s: peer %s start requested: sco_occupied:%s role:0x%x "
@@ -1920,8 +1943,8 @@
   uint8_t set_policy = HCI_ENABLE_SNIFF_MODE;
 
   APPL_TRACE_ERROR(
-      "%s: peer %s handle:%d audio_open_cnt:%d, p_data %p start:%d", __func__,
-      p_scb->PeerAddress().ToString().c_str(), p_scb->hndl,
+      "%s: peer %s bta_handle:0x%x audio_open_cnt:%d, p_data %p start:%d",
+      __func__, p_scb->PeerAddress().ToString().c_str(), p_scb->hndl,
       bta_av_cb.audio_open_cnt, p_data, start);
 
   bta_sys_idle(BTA_ID_AV, bta_av_cb.audio_open_cnt, p_scb->PeerAddress());
@@ -2101,6 +2124,8 @@
   uint8_t m_pt = 0x60;
   tAVDT_DATA_OPT_MASK opt;
 
+  if (!p_scb->started) return;
+
   if (p_scb->cong) return;
 
   if (p_scb->use_rtp_header_marker_bit) {
@@ -2228,7 +2253,8 @@
   uint8_t cur_role;
   uint8_t local_tsep = p_scb->seps[p_scb->sep_idx].tsep;
 
-  LOG_INFO(LOG_TAG, "%s: peer %s handle:%d wait:0x%x role:0x%x local_tsep:%d",
+  LOG_INFO(LOG_TAG,
+           "%s: peer %s bta_handle:0x%x wait:0x%x role:0x%x local_tsep:%d",
            __func__, p_scb->PeerAddress().ToString().c_str(), p_scb->hndl,
            p_scb->wait, p_scb->role, local_tsep);
 
@@ -2258,7 +2284,7 @@
   if (p_scb->wait & BTA_AV_WAIT_ROLE_SW_FAILED) {
     /* role switch has failed */
     APPL_TRACE_ERROR(
-        "%s: peer %s role switch failed: handle:%d wait:0x%x, role:0x%x",
+        "%s: peer %s role switch failed: bta_handle:0x%x wait:0x%x, role:0x%x",
         __func__, p_scb->PeerAddress().ToString().c_str(), p_scb->hndl,
         p_scb->wait, p_scb->role);
     p_scb->wait &= ~BTA_AV_WAIT_ROLE_SW_FAILED;
@@ -2418,7 +2444,7 @@
   uint8_t set_policy = (HCI_ENABLE_SNIFF_MODE | HCI_ENABLE_MASTER_SLAVE_SWITCH);
 
   APPL_TRACE_ERROR(
-      "%s: peer %s handle:%d audio_open_cnt:%d started:%s co_started:%d",
+      "%s: peer %s bta_handle:0x%x audio_open_cnt:%d started:%s co_started:%d",
       __func__, p_scb->PeerAddress().ToString().c_str(), p_scb->hndl,
       bta_av_cb.audio_open_cnt, logbool(p_scb->started).c_str(),
       p_scb->co_started);
@@ -2447,9 +2473,9 @@
   uint8_t set_policy = HCI_ENABLE_SNIFF_MODE;
 
   APPL_TRACE_WARNING(
-      "%s: peer %s handle:%d open_status:%d chnl:%d co_started:%d", __func__,
-      p_scb->PeerAddress().ToString().c_str(), p_scb->hndl, p_scb->open_status,
-      p_scb->chnl, p_scb->co_started);
+      "%s: peer %s bta_handle:0x%x open_status:%d chnl:%d co_started:%d",
+      __func__, p_scb->PeerAddress().ToString().c_str(), p_scb->hndl,
+      p_scb->open_status, p_scb->chnl, p_scb->co_started);
 
   if ((bta_av_cb.features & BTA_AV_FEAT_MASTER) == 0 ||
       bta_av_cb.audio_open_cnt == 1) {
@@ -2528,7 +2554,7 @@
   uint8_t err_code = p_data->str_msg.msg.hdr.err_code;
   uint8_t set_policy = HCI_ENABLE_SNIFF_MODE;
 
-  APPL_TRACE_DEBUG("%s: peer %s handle:%d audio_open_cnt:%d err_code:%d",
+  APPL_TRACE_DEBUG("%s: peer %s bta_handle:0x%x audio_open_cnt:%d err_code:%d",
                    __func__, p_scb->PeerAddress().ToString().c_str(),
                    p_scb->hndl, bta_av_cb.audio_open_cnt, err_code);
 
@@ -2607,7 +2633,7 @@
  ******************************************************************************/
 void bta_av_rcfg_str_ok(tBTA_AV_SCB* p_scb, tBTA_AV_DATA* p_data) {
   p_scb->l2c_cid = AVDT_GetL2CapChannel(p_scb->avdt_handle);
-  APPL_TRACE_DEBUG("%s: peer %s handle:%d l2c_cid:%d", __func__,
+  APPL_TRACE_DEBUG("%s: peer %s bta_handle:0x%x l2c_cid:%d", __func__,
                    p_scb->PeerAddress().ToString().c_str(), p_scb->hndl,
                    p_scb->l2c_cid);
 
@@ -2670,9 +2696,13 @@
   } else {
     /* open failed. try again */
     p_scb->num_recfg++;
-    if (bta_av_cb.conn_lcb) {
+    // conn_lcb is the index bitmask of all used LCBs, and since LCB and SCB use
+    // the same index, it should be safe to use SCB index here.
+    if ((bta_av_cb.conn_lcb & (1 << p_scb->hdi)) != 0) {
       AVDT_DisconnectReq(p_scb->PeerAddress(), &bta_av_proc_stream_evt);
     } else {
+      APPL_TRACE_WARNING("%s: conn_lcb=0x%x bta_handle=0x%x (hdi=%u) no link",
+                         __func__, bta_av_cb.conn_lcb, p_scb->hndl, p_scb->hdi);
       bta_av_connect_req(p_scb, NULL);
     }
   }
@@ -2854,7 +2884,7 @@
  *
  ******************************************************************************/
 void bta_av_rcfg_open(tBTA_AV_SCB* p_scb, UNUSED_ATTR tBTA_AV_DATA* p_data) {
-  APPL_TRACE_DEBUG("%s: peer %s handle:%d num_disc_snks:%d", __func__,
+  APPL_TRACE_DEBUG("%s: peer %s bta_handle:0x%x num_disc_snks:%d", __func__,
                    p_scb->PeerAddress().ToString().c_str(), p_scb->hndl,
                    p_scb->num_disc_snks);
 
@@ -2873,6 +2903,8 @@
     /* we may choose to use a different SEP at reconfig.
      * adjust the sep_idx now */
     bta_av_adjust_seps_idx(p_scb, bta_av_get_scb_handle(p_scb, AVDT_TSEP_SRC));
+    LOG_DEBUG(LOG_TAG, "%s: sep_idx=%d avdt_handle=%d bta_handle=0x%x",
+              __func__, p_scb->sep_idx, p_scb->avdt_handle, p_scb->hndl);
 
     /* open the stream with the new config */
     p_scb->sep_info_idx = p_scb->rcfg_idx;
@@ -3035,7 +3067,7 @@
 void bta_av_open_at_inc(tBTA_AV_SCB* p_scb, tBTA_AV_DATA* p_data) {
   memcpy(&(p_scb->open_api), &(p_data->api_open), sizeof(tBTA_AV_API_OPEN));
 
-  APPL_TRACE_DEBUG("%s: peer %s coll_mask:0x%x", __func__,
+  APPL_TRACE_DEBUG("%s: peer %s coll_mask=0x%02x", __func__,
                    p_scb->PeerAddress().ToString().c_str(), p_scb->coll_mask);
 
   if (p_scb->coll_mask & BTA_AV_COLL_INC_TMR) {
@@ -3106,7 +3138,7 @@
   ARRAY_TO_STREAM(p_param, offload_start->codec_info,
                   (int8_t)sizeof(offload_start->codec_info));
   p_scb->offload_started = true;
-  BTM_VendorSpecificCommand(HCI_CONTROLLER_A2DP_OPCODE_OCF, p_param - param,
+  BTM_VendorSpecificCommand(HCI_CONTROLLER_A2DP, p_param - param,
                             param, offload_vendor_callback);
 }
 
@@ -3114,7 +3146,7 @@
   uint8_t param[sizeof(tBT_A2DP_OFFLOAD)];
   APPL_TRACE_DEBUG("%s", __func__);
   param[0] = VS_HCI_A2DP_OFFLOAD_STOP;
-  BTM_VendorSpecificCommand(HCI_CONTROLLER_A2DP_OPCODE_OCF, 1, param,
+  BTM_VendorSpecificCommand(HCI_CONTROLLER_A2DP, 1, param,
                             offload_vendor_callback);
 }
 /*******************************************************************************
diff --git a/bta/av/bta_av_act.cc b/bta/av/bta_av_act.cc
index a727842..827cdbb 100644
--- a/bta/av/bta_av_act.cc
+++ b/bta/av/bta_av_act.cc
@@ -362,6 +362,7 @@
   p_rcb->shdl = shdl;
   p_rcb->lidx = lidx;
   p_rcb->peer_features = 0;
+  p_rcb->cover_art_psm = 0;
   if (lidx == (BTA_AV_NUM_LINKS + 1)) {
     /* this LIDX is reserved for the AVRCP ACP connection */
     p_cb->rc_acp_handle = p_rcb->handle;
@@ -560,9 +561,11 @@
 
   rc_open.peer_addr = p_data->rc_conn_chg.peer_addr;
   rc_open.peer_features = p_cb->rcb[i].peer_features;
+  rc_open.cover_art_psm = p_cb->rcb[i].cover_art_psm;
   rc_open.status = BTA_AV_SUCCESS;
   APPL_TRACE_DEBUG("%s: local features:x%x peer_features:x%x", __func__,
                    p_cb->features, rc_open.peer_features);
+  APPL_TRACE_DEBUG("%s: cover art psm:x%x", __func__, rc_open.cover_art_psm);
   if (rc_open.peer_features == 0) {
     /* we have not done SDP on peer RC capabilities.
      * peer must have initiated the RC connection */
@@ -1301,6 +1304,7 @@
  ******************************************************************************/
 void bta_av_disable(tBTA_AV_CB* p_cb, UNUSED_ATTR tBTA_AV_DATA* p_data) {
   BT_HDR hdr;
+  bool disabling_in_progress = false;
   uint16_t xx;
 
   p_cb->disabling = true;
@@ -1315,8 +1319,13 @@
     if (p_cb->p_scb[xx] != NULL) {
       hdr.layer_specific = xx + 1;
       bta_av_api_deregister((tBTA_AV_DATA*)&hdr);
+      disabling_in_progress = true;
     }
   }
+  // Since All channels are deregistering by API_DEREGISTER, the DEREG_COMP_EVT
+  // would come first before API_DISABLE if there is no connections, and it is
+  // no needed to setup this disabling flag.
+  p_cb->disabling = disabling_in_progress;
 
   alarm_free(p_cb->link_signalling_timer);
   p_cb->link_signalling_timer = NULL;
@@ -1474,7 +1483,7 @@
 #endif
   else {
     /* disconnected. */
-    APPL_TRACE_DEBUG("%s: bta_av_cb.conn_lcb is %d", __func__,
+    APPL_TRACE_DEBUG("%s: bta_av_cb.conn_lcb=0x%x", __func__,
                      bta_av_cb.conn_lcb);
 
     p_lcb = bta_av_find_lcb(p_data->str_msg.bd_addr, BTA_AV_LCB_FREE);
@@ -1500,7 +1509,8 @@
       }
     }
   }
-  APPL_TRACE_DEBUG("%s: sig_chg conn_lcb: 0x%x", __func__, p_cb->conn_lcb);
+  APPL_TRACE_DEBUG("%s: bta_av_cb.conn_lcb=0x%x after sig_chg", __func__,
+                   p_cb->conn_lcb);
 }
 
 /*******************************************************************************
@@ -1567,7 +1577,7 @@
     p_scb = p_cb->p_scb[inx];
   }
   if (p_scb) {
-    APPL_TRACE_DEBUG("%s: coll_mask = 0x%02X", __func__, p_scb->coll_mask);
+    APPL_TRACE_DEBUG("%s: coll_mask=0x%02x", __func__, p_scb->coll_mask);
 
     if (p_scb->coll_mask & BTA_AV_COLL_INC_TMR) {
       p_scb->coll_mask &= ~BTA_AV_COLL_INC_TMR;
@@ -1719,26 +1729,32 @@
       if (peer_rc_version >= AVRC_REV_1_3)
         peer_features |= (BTA_AV_FEAT_VENDOR | BTA_AV_FEAT_METADATA);
 
-      /*
-       * Though Absolute Volume came after in 1.4 and above, but there are few
-       * devices
-       * in market which supports absolute Volume and they are still 1.3
-       * TO avoid IOT issuses with those devices, we check for 1.3 as minimum
-       * version
-       */
-      if (peer_rc_version >= AVRC_REV_1_3) {
-        /* get supported features */
-        tSDP_DISC_ATTR* p_attr =
-            SDP_FindAttributeInRec(p_rec, ATTR_ID_SUPPORTED_FEATURES);
-        if (p_attr != NULL) {
-          uint16_t categories = p_attr->attr_value.v.u16;
-          if (categories & AVRC_SUPF_CT_CAT2)
+      /* Get supported features */
+      tSDP_DISC_ATTR* p_attr =
+          SDP_FindAttributeInRec(p_rec, ATTR_ID_SUPPORTED_FEATURES);
+      if (p_attr != NULL) {
+        uint16_t categories = p_attr->attr_value.v.u16;
+        /*
+         * Though Absolute Volume came after in 1.4 and above, but there are
+         * few devices in market which supports absolute Volume and they are
+         * still 1.3. To avoid IOP issuses with those devices, we check for
+         * 1.3 as minimum version
+         */
+        if (peer_rc_version >= AVRC_REV_1_3) {
+          if (categories & AVRC_SUPF_TG_CAT2)
             peer_features |= (BTA_AV_FEAT_ADV_CTRL);
-          if (categories & AVRC_SUPF_CT_APP_SETTINGS)
+          if (categories & AVRC_SUPF_TG_APP_SETTINGS)
             peer_features |= (BTA_AV_FEAT_APP_SETTING);
-          if (categories & AVRC_SUPF_CT_BROWSE)
+          if (categories & AVRC_SUPF_TG_BROWSE)
             peer_features |= (BTA_AV_FEAT_BROWSE);
         }
+
+        /* AVRCP Cover Artwork over BIP */
+        if (peer_rc_version >= AVRC_REV_1_6) {
+          if (service_uuid == UUID_SERVCLASS_AV_REM_CTRL_TARGET &&
+              categories & AVRC_SUPF_TG_PLAYER_COVER_ART)
+            peer_features |= (BTA_AV_FEAT_COVER_ARTWORK);
+        }
       }
     }
     /* get next record; if none found, we're done */
@@ -1748,6 +1764,108 @@
   return peer_features;
 }
 
+/******************************************************************************
+ *
+ * Function         bta_avk_get_cover_art_psm
+ *
+ * Description      Get the PSM associated with the AVRCP Target cover art
+ *                  feature
+ *
+ * Returns          uint16_t PSM value used to get cover artwork, or 0x0000 if
+ *                  one does not exist.
+ *
+ *****************************************************************************/
+uint16_t bta_avk_get_cover_art_psm() {
+  APPL_TRACE_DEBUG("%s: searching for cover art psm", __func__);
+  /* Cover Art L2CAP PSM is only available on a target device */
+  tBTA_AV_CB* p_cb = &bta_av_cb;
+  tSDP_DISC_REC* p_rec =
+      SDP_FindServiceInDb(p_cb->p_disc_db, UUID_SERVCLASS_AV_REM_CTRL_TARGET,
+          NULL);
+  while (p_rec) {
+    tSDP_DISC_ATTR* p_attr =
+        (SDP_FindAttributeInRec(p_rec, ATTR_ID_ADDITION_PROTO_DESC_LISTS));
+    /*
+     * If we have the Additional Protocol Description Lists attribute then we
+     * specifically want the list that is an L2CAP protocol leading to OBEX.
+     * Because the is a case where cover art is supported and browsing isn't
+     * we need to check each list for the one we want.
+     *
+     * This means we need to do drop down into the protocol list and do a
+     * "for each protocol, for each protocol element, for each protocol element
+     * list parameter, if the parameter is L2CAP then find the PSM associated
+     * with it, then make sure we see OBEX in that same protocol"
+     */
+    if (p_attr != NULL && SDP_DISC_ATTR_TYPE(p_attr->attr_len_type)
+        == DATA_ELE_SEQ_DESC_TYPE) {
+      // Point to first in List of protocols (i.e [(L2CAP -> AVCTP),
+      // (L2CAP -> OBEX)])
+      tSDP_DISC_ATTR* p_protocol_list = p_attr->attr_value.v.p_sub_attr;
+      while (p_protocol_list != NULL) {
+        if (SDP_DISC_ATTR_TYPE(p_protocol_list->attr_len_type)
+            == DATA_ELE_SEQ_DESC_TYPE) {
+          // Point to fist in list of protocol elements (i.e. [L2CAP, AVCTP])
+          tSDP_DISC_ATTR* p_protocol =
+              p_protocol_list->attr_value.v.p_sub_attr;
+          bool protocol_has_obex = false;
+          bool protocol_has_l2cap = false;
+          uint16_t psm = 0x0000;
+          while (p_protocol) {
+            if (SDP_DISC_ATTR_TYPE(p_protocol->attr_len_type)
+                == DATA_ELE_SEQ_DESC_TYPE) {
+              // Point to first item protocol parameters list (i.e [UUID=L2CAP,
+              // PSM=0x1234])
+              tSDP_DISC_ATTR* p_protocol_param =
+                  p_protocol->attr_value.v.p_sub_attr;
+              /*
+               * Currently there's only ever one UUID and one parameter. L2cap
+               * has a single PSM, AVCTP has a version and OBEX has nothing.
+               * Change this if that ever changes.
+               */
+              uint16_t protocol_uuid = 0;
+              uint16_t protocol_param = 0;
+              while (p_protocol_param) {
+                uint16_t param_type =
+                    SDP_DISC_ATTR_TYPE(p_protocol_param->attr_len_type);
+                uint16_t param_len =
+                    SDP_DISC_ATTR_LEN(p_protocol_param->attr_len_type);
+                if (param_type == UUID_DESC_TYPE) {
+                  protocol_uuid = p_protocol_param->attr_value.v.u16;
+                } else if (param_type == UINT_DESC_TYPE) {
+                    protocol_param = (param_len == 2)
+                      ? p_protocol_param->attr_value.v.u16
+                      : p_protocol_param->attr_value.v.u8;
+                } /* else dont care */
+                p_protocol_param = p_protocol_param->p_next_attr;  // next
+              }
+              // If we've found L2CAP then the parameter is a PSM
+              if (protocol_uuid == UUID_PROTOCOL_L2CAP) {
+                protocol_has_l2cap = true;
+                psm = protocol_param;
+              } else if (protocol_uuid == UUID_PROTOCOL_OBEX) {
+                protocol_has_obex = true;
+              }
+            }
+            // If this protocol has l2cap and obex then we're found the BIP PSM
+            if (protocol_has_l2cap && protocol_has_obex) {
+              APPL_TRACE_DEBUG("%s: found psm 0x%x", __func__, psm);
+              return psm;
+            }
+            p_protocol = p_protocol->p_next_attr;  // next protocol element
+          }
+        }
+        p_protocol_list = p_protocol_list->p_next_attr;  // next protocol
+      }
+    }
+    /* get next record; if none found, we're done */
+    p_rec = SDP_FindServiceInDb(p_cb->p_disc_db,
+        UUID_SERVCLASS_AV_REM_CTRL_TARGET, p_rec);
+  }
+  /* L2CAP PSM range is 0x1000-0xFFFF so 0x0000 is safe default invalid */
+  APPL_TRACE_DEBUG("%s: could not find a BIP psm", __func__);
+  return 0x0000;
+}
+
 /*******************************************************************************
  *
  * Function         bta_av_rc_disc_done
@@ -1764,6 +1882,7 @@
   tBTA_AV_LCB* p_lcb;
   uint8_t rc_handle;
   tBTA_AV_FEAT peer_features = 0; /* peer features mask */
+  uint16_t cover_art_psm = 0x0000;
 
   APPL_TRACE_DEBUG("%s: bta_av_rc_disc_done disc:x%x", __func__, p_cb->disc);
   if (!p_cb->disc) {
@@ -1797,6 +1916,12 @@
     if (BTA_AV_FEAT_ADV_CTRL &
         bta_avk_check_peer_features(UUID_SERVCLASS_AV_REMOTE_CONTROL))
       peer_features |= (BTA_AV_FEAT_ADV_CTRL | BTA_AV_FEAT_RCCT);
+
+    if (peer_features & BTA_AV_FEAT_COVER_ARTWORK)
+      cover_art_psm = bta_avk_get_cover_art_psm();
+
+    APPL_TRACE_DEBUG("%s: populating rem ctrl target bip psm 0x%x", __func__,
+                     cover_art_psm);
   } else
 #endif
       if (p_cb->sdp_a2dp_handle) {
@@ -1849,6 +1974,7 @@
           rc_handle = bta_av_rc_create(p_cb, AVCT_INT,
                                        (uint8_t)(p_scb->hdi + 1), p_lcb->lidx);
           p_cb->rcb[rc_handle].peer_features = peer_features;
+          p_cb->rcb[rc_handle].cover_art_psm = cover_art_psm;
         } else {
           APPL_TRACE_ERROR("%s: can not find LCB!!", __func__);
         }
@@ -1858,6 +1984,7 @@
         tBTA_AV_RC_OPEN rc_open;
         rc_open.peer_addr = p_scb->PeerAddress();
         rc_open.peer_features = 0;
+        rc_open.cover_art_psm = 0;
         rc_open.status = BTA_AV_FAIL_SDP;
         tBTA_AV bta_av_data;
         bta_av_data.rc_open = rc_open;
@@ -1879,9 +2006,28 @@
     } else {
       rc_feat.peer_addr = p_scb->PeerAddress();
     }
-    tBTA_AV bta_av_data;
-    bta_av_data.rc_feat = rc_feat;
-    (*p_cb->p_cback)(BTA_AV_RC_FEAT_EVT, &bta_av_data);
+
+    tBTA_AV bta_av_feat;
+    bta_av_feat.rc_feat = rc_feat;
+    (*p_cb->p_cback)(BTA_AV_RC_FEAT_EVT, &bta_av_feat);
+
+    // Send PSM data
+    APPL_TRACE_DEBUG("%s: Send PSM data", __func__);
+    tBTA_AV_RC_PSM rc_psm;
+    p_cb->rcb[rc_handle].cover_art_psm = cover_art_psm;
+    rc_psm.rc_handle = rc_handle;
+    rc_psm.cover_art_psm = cover_art_psm;
+    if (p_scb == NULL) {
+      rc_psm.peer_addr = p_cb->lcb[p_cb->rcb[rc_handle].lidx - 1].addr;
+    } else {
+      rc_psm.peer_addr = p_scb->PeerAddress();
+    }
+
+    APPL_TRACE_DEBUG("%s: rc_psm = 0x%x", __func__, rc_psm.cover_art_psm);
+
+    tBTA_AV bta_av_psm;
+    bta_av_psm.rc_cover_art_psm = rc_psm;
+    (*p_cb->p_cback)(BTA_AV_RC_PSM_EVT, &bta_av_psm);
   }
 }
 
@@ -1915,6 +2061,7 @@
       rc_close.rc_handle = i;
       p_rcb->status &= ~BTA_AV_RC_CONN_MASK;
       p_rcb->peer_features = 0;
+      p_rcb->cover_art_psm = 0;
       APPL_TRACE_DEBUG("%s: shdl:%d, lidx:%d", __func__, p_rcb->shdl,
                        p_rcb->lidx);
       if (p_rcb->shdl) {
@@ -2037,7 +2184,8 @@
   tAVRC_SDP_DB_PARAMS db_params;
   uint16_t attr_list[] = {ATTR_ID_SERVICE_CLASS_ID_LIST,
                           ATTR_ID_BT_PROFILE_DESC_LIST,
-                          ATTR_ID_SUPPORTED_FEATURES};
+                          ATTR_ID_SUPPORTED_FEATURES,
+                          ATTR_ID_ADDITION_PROTO_DESC_LISTS};
   uint8_t hdi;
   tBTA_AV_SCB* p_scb;
   RawAddress peer_addr = RawAddress::kEmpty;
@@ -2070,7 +2218,7 @@
 
     /* set up parameters */
     db_params.db_len = BTA_AV_DISC_BUF_SIZE;
-    db_params.num_attr = 3;
+    db_params.num_attr = sizeof(attr_list) / sizeof(uint16_t);
     db_params.p_db = p_cb->p_disc_db;
     db_params.p_attrs = attr_list;
 
@@ -2108,9 +2256,9 @@
                      p_scb->hndl);
     mask = BTA_AV_HNDL_TO_MSK(p_scb->hdi);
     p_cb->reg_audio &= ~mask;
-    if ((p_cb->conn_audio & mask) && bta_av_cb.audio_open_cnt) {
+    if ((p_cb->conn_audio & mask) && p_cb->audio_open_cnt) {
       /* this channel is still marked as open. decrease the count */
-      bta_av_cb.audio_open_cnt--;
+      p_cb->audio_open_cnt--;
     }
     p_cb->conn_audio &= ~mask;
 
@@ -2161,7 +2309,9 @@
 
     if (p_cb->disabling) {
       p_cb->disabling = false;
-      bta_av_cb.features = 0;
+      // reset enabling parameters
+      p_cb->features = 0;
+      p_cb->sec_mask = 0;
     }
 
     /* Clear the Capturing service class bit */
diff --git a/bta/av/bta_av_api.cc b/bta/av/bta_av_api.cc
index bb5cca4..aed5b12 100644
--- a/bta/av/bta_av_api.cc
+++ b/bta/av/bta_av_api.cc
@@ -156,7 +156,8 @@
  ******************************************************************************/
 void BTA_AvOpen(const RawAddress& bd_addr, tBTA_AV_HNDL handle, bool use_rc,
                 tBTA_SEC sec_mask, uint16_t uuid) {
-  LOG_INFO(LOG_TAG, "%s: peer %s handle:0x%x use_rc=%s sec_mask=0x%x uuid=0x%x",
+  LOG_INFO(LOG_TAG,
+           "%s: peer %s bta_handle:0x%x use_rc=%s sec_mask=0x%x uuid=0x%x",
            __func__, bd_addr.ToString().c_str(), handle,
            (use_rc) ? "true" : "false", sec_mask, uuid);
 
@@ -184,7 +185,7 @@
  *
  ******************************************************************************/
 void BTA_AvClose(tBTA_AV_HNDL handle) {
-  LOG_INFO(LOG_TAG, "%s: handle:0x%x", __func__, handle);
+  LOG_INFO(LOG_TAG, "%s: bta_handle:0x%x", __func__, handle);
 
   BT_HDR* p_buf = (BT_HDR*)osi_malloc(sizeof(BT_HDR));
 
@@ -225,7 +226,7 @@
  *
  ******************************************************************************/
 void BTA_AvStart(tBTA_AV_HNDL handle) {
-  LOG_INFO(LOG_TAG, "%s: handle=%d", __func__, handle);
+  LOG_INFO(LOG_TAG, "%s: bta_handle=0x%x", __func__, handle);
 
   BT_HDR* p_buf = (BT_HDR*)osi_malloc(sizeof(BT_HDR));
 
@@ -245,7 +246,7 @@
  *
  ******************************************************************************/
 void BTA_AvOffloadStart(tBTA_AV_HNDL hndl) {
-  LOG_INFO(LOG_TAG, "%s: handle=%d", __func__, hndl);
+  LOG_INFO(LOG_TAG, "%s: bta_handle=0x%x", __func__, hndl);
 
   BT_HDR* p_buf = (BT_HDR*)osi_malloc(sizeof(BT_HDR));
 
@@ -287,7 +288,7 @@
  *
  ******************************************************************************/
 void BTA_AvStop(tBTA_AV_HNDL handle, bool suspend) {
-  LOG_INFO(LOG_TAG, "%s: handle=%d suspend=%s", __func__, handle,
+  LOG_INFO(LOG_TAG, "%s: bta_handle=0x%x suspend=%s", __func__, handle,
            logbool(suspend).c_str());
 
   tBTA_AV_API_STOP* p_buf =
@@ -318,8 +319,8 @@
 void BTA_AvReconfig(tBTA_AV_HNDL hndl, bool suspend, uint8_t sep_info_idx,
                     uint8_t* p_codec_info, uint8_t num_protect,
                     const uint8_t* p_protect_info) {
-  LOG_INFO(LOG_TAG, "%s: handle=%d suspend=%s sep_info_idx=%d", __func__, hndl,
-           logbool(suspend).c_str(), sep_info_idx);
+  LOG_INFO(LOG_TAG, "%s: bta_handle=0x%x suspend=%s sep_info_idx=%d", __func__,
+           hndl, logbool(suspend).c_str(), sep_info_idx);
 
   tBTA_AV_API_RCFG* p_buf =
       (tBTA_AV_API_RCFG*)osi_malloc(sizeof(tBTA_AV_API_RCFG) + num_protect);
diff --git a/bta/av/bta_av_cfg.cc b/bta/av/bta_av_cfg.cc
index 1da38b3..8583496 100644
--- a/bta/av/bta_av_cfg.cc
+++ b/bta/av/bta_av_cfg.cc
@@ -42,7 +42,10 @@
 
 /* AVRCP supported categories */
 #define BTA_AV_RC_SUPF_CT (AVRC_SUPF_CT_CAT2)
-#define BTA_AVK_RC_SUPF_CT (AVRC_SUPF_CT_CAT1 | AVRC_SUPF_CT_BROWSE)
+#define BTA_AVK_RC_SUPF_CT (AVRC_SUPF_CT_CAT1 |                     \
+                            AVRC_SUPF_CT_BROWSE |                   \
+                            AVRC_SUPF_CT_COVER_ART_GET_IMAGE_PROP | \
+                            AVRC_SUPF_CT_COVER_ART_GET_IMAGE)
 #define BTA_AVK_RC_SUPF_TG (AVRC_SUPF_TG_CAT2)
 
 /* AVRCP Controller and Targer default name */
diff --git a/bta/av/bta_av_ci.cc b/bta/av/bta_av_ci.cc
index a3d552d..e92c8d6 100644
--- a/bta/av/bta_av_ci.cc
+++ b/bta/av/bta_av_ci.cc
@@ -71,7 +71,7 @@
                          uint8_t category, uint8_t num_seid, uint8_t* p_seid,
                          bool recfg_needed, uint8_t avdt_handle) {
   LOG_DEBUG(LOG_TAG,
-            "%s: bta_av_handle=%d err_code=%d category=%d "
+            "%s: bta_av_handle=0x%x err_code=%d category=%d "
             "num_seid=%d recfg_needed=%s avdt_handle=%d",
             __func__, bta_av_handle, err_code, category, num_seid,
             recfg_needed ? "true" : "false", avdt_handle);
diff --git a/bta/av/bta_av_int.h b/bta/av/bta_av_int.h
index 17c4bf3..3925e55 100644
--- a/bta/av/bta_av_int.h
+++ b/bta/av/bta_av_int.h
@@ -571,6 +571,7 @@
   uint8_t shdl;               /* stream handle (hdi + 1) */
   uint8_t lidx;               /* (index+1) to LCB */
   tBTA_AV_FEAT peer_features; /* peer features mask */
+  uint16_t cover_art_psm;     /* BIP PSM for cover art feature */
 } tBTA_AV_RCB;
 #define BTA_AV_NUM_RCB (BTA_AV_NUM_STRS + 2)
 
@@ -602,6 +603,7 @@
   tBTA_SEC sec_mask;            /* security mask */
   tBTA_AV_HNDL handle;          /* the handle for SDP activity */
   bool disabling;               /* true if api disabled called */
+  uint8_t enabling_attempts;    // counter to wait for previous disabling
   uint8_t
       disc; /* (hdi+1) or (rc_handle|BTA_AV_CHNL_MSK) if p_disc_db is in use */
   uint8_t state;          /* state machine state */
@@ -616,6 +618,10 @@
   uint8_t audio_streams; /* handle mask of streaming audio channels */
 } tBTA_AV_CB;
 
+// total attempts are half seconds
+constexpr uint32_t kEnablingAttemptsIntervalMs = 100;
+constexpr uint8_t kEnablingAttemptsCountMaximum = 5;
+
 // A2DP offload VSC parameters
 class tBT_A2DP_OFFLOAD {
  public:
@@ -664,6 +670,7 @@
  ****************************************************************************/
 /* utility functions */
 extern tBTA_AV_SCB* bta_av_hndl_to_scb(uint16_t handle);
+tBTA_AV_SCB* bta_av_addr_to_scb(const RawAddress& bd_addr);
 extern bool bta_av_chk_start(tBTA_AV_SCB* p_scb);
 extern void bta_av_restore_switch(void);
 extern void bta_av_conn_cback(uint8_t handle, const RawAddress& bd_addr,
diff --git a/bta/av/bta_av_main.cc b/bta/av/bta_av_main.cc
index bdd8a70..61e4b4e 100644
--- a/bta/av/bta_av_main.cc
+++ b/bta/av/bta_av_main.cc
@@ -203,7 +203,7 @@
  ****************************************************************************/
 
 /* AV control block */
-tBTA_AV_CB bta_av_cb;
+tBTA_AV_CB bta_av_cb = {};
 
 static const char* bta_av_st_code(uint8_t state);
 
@@ -218,6 +218,39 @@
  *
  ******************************************************************************/
 static void bta_av_api_enable(tBTA_AV_DATA* p_data) {
+  if (bta_av_cb.disabling) {
+    APPL_TRACE_WARNING(
+        "%s: previous (reg_audio=%#x) is still disabling (attempts=%d)",
+        __func__, bta_av_cb.reg_audio, bta_av_cb.enabling_attempts);
+    if (++bta_av_cb.enabling_attempts <= kEnablingAttemptsCountMaximum) {
+      tBTA_AV_API_ENABLE* p_buf =
+          (tBTA_AV_API_ENABLE*)osi_malloc(sizeof(tBTA_AV_API_ENABLE));
+      memcpy(p_buf, &p_data->api_enable, sizeof(tBTA_AV_API_ENABLE));
+      bta_sys_sendmsg_delayed(p_buf, base::TimeDelta::FromMilliseconds(
+                                         kEnablingAttemptsIntervalMs));
+      return;
+    }
+    if (bta_av_cb.sdp_a2dp_handle) {
+      SDP_DeleteRecord(bta_av_cb.sdp_a2dp_handle);
+      bta_sys_remove_uuid(UUID_SERVCLASS_AUDIO_SOURCE);
+    }
+#if (BTA_AV_SINK_INCLUDED == TRUE)
+    if (bta_av_cb.sdp_a2dp_snk_handle) {
+      SDP_DeleteRecord(bta_av_cb.sdp_a2dp_snk_handle);
+      bta_sys_remove_uuid(UUID_SERVCLASS_AUDIO_SINK);
+    }
+#endif
+#if (BTA_AR_INCLUDED == TRUE)
+    // deregister from AVDT
+    bta_ar_dereg_avdt(BTA_ID_AV);
+
+    // deregister from AVCT
+    bta_ar_dereg_avrc(UUID_SERVCLASS_AV_REMOTE_CONTROL, BTA_ID_AV);
+    bta_ar_dereg_avrc(UUID_SERVCLASS_AV_REM_CTRL_TARGET, BTA_ID_AV);
+    bta_ar_dereg_avct(BTA_ID_AV);
+#endif
+  }
+
   /* initialize control block */
   memset(&bta_av_cb, 0, sizeof(tBTA_AV_CB));
 
@@ -243,9 +276,7 @@
   enable.features = bta_av_cb.features;
 
   /* Register for SCO change event */
-  if (!(bta_av_cb.features & BTA_AV_FEAT_NO_SCO_SSPD)) {
-    bta_sys_sco_register(bta_av_sco_chg_cback);
-  }
+  bta_sys_sco_register(bta_av_sco_chg_cback);
 
   /* call callback with enable event */
   tBTA_AV bta_av_data;
@@ -262,7 +293,7 @@
  * Returns          void
  *
  ******************************************************************************/
-static tBTA_AV_SCB* bta_av_addr_to_scb(const RawAddress& bd_addr) {
+tBTA_AV_SCB* bta_av_addr_to_scb(const RawAddress& bd_addr) {
   tBTA_AV_SCB* p_scb = NULL;
   int xx;
 
@@ -429,7 +460,7 @@
     p_msg->bd_addr = bd_addr;
     p_msg->scb_index = scb_index;
     if (p_scb) {
-      APPL_TRACE_DEBUG("%s: scb hndl x%x, role x%x", __func__, p_scb->hndl,
+      APPL_TRACE_DEBUG("%s: bta_handle x%x, role x%x", __func__, p_scb->hndl,
                        p_scb->role);
     }
     LOG_INFO(LOG_TAG, "%s: conn_cback bd_addr: %s", __func__,
@@ -473,6 +504,21 @@
   char* p_service_name;
   tBTA_UTL_COD cod;
 
+  if (bta_av_cb.disabling ||
+      (bta_av_cb.features == 0 && bta_av_cb.sec_mask == 0)) {
+    APPL_TRACE_WARNING(
+        "%s: AV instance (features=%#x, sec_mask=%#x, reg_audio=%#x) is not "
+        "ready for app_id %d",
+        __func__, bta_av_cb.features, bta_av_cb.sec_mask, bta_av_cb.reg_audio,
+        p_data->api_reg.app_id);
+    tBTA_AV_API_REG* p_buf =
+        (tBTA_AV_API_REG*)osi_malloc(sizeof(tBTA_AV_API_REG));
+    memcpy(p_buf, &p_data->api_reg, sizeof(tBTA_AV_API_REG));
+    bta_sys_sendmsg_delayed(
+        p_buf, base::TimeDelta::FromMilliseconds(kEnablingAttemptsIntervalMs));
+    return;
+  }
+
   avdtp_stream_config.Reset();
 
   registr.status = BTA_AV_FAIL_RESOURCES;
@@ -643,6 +689,9 @@
       }
       if (AVDT_CreateStream(p_scb->app_id, &p_scb->seps[codec_index].av_handle,
                             avdtp_stream_config) != AVDT_SUCCESS) {
+        APPL_TRACE_WARNING(
+            "%s: bta_handle=0x%x (app_id %d) failed to alloc an SEP index:%d",
+            __func__, p_scb->hndl, p_scb->app_id, codec_index);
         continue;
       }
       /* Save a copy of the codec */
@@ -912,8 +961,8 @@
       tBTA_AV_ROLE_RES* p_buf =
           (tBTA_AV_ROLE_RES*)osi_malloc(sizeof(tBTA_AV_ROLE_RES));
       APPL_TRACE_DEBUG(
-          "%s: peer %s found: new_role:%d, hci_status:0x%x hndl:0x%x", __func__,
-          peer_addr.ToString().c_str(), id, app_id, p_scb->hndl);
+          "%s: peer %s found: new_role:%d, hci_status:0x%x bta_handle:0x%x",
+          __func__, peer_addr.ToString().c_str(), id, app_id, p_scb->hndl);
       /*
       if ((id != BTM_ROLE_MASTER) && (app_id != HCI_SUCCESS))
       {
@@ -945,8 +994,8 @@
       p_scb = bta_av_cb.p_scb[bta_av_cb.rs_idx - 1];
     }
     if (p_scb && p_scb->q_tag == BTA_AV_Q_TAG_OPEN) {
-      APPL_TRACE_DEBUG("%s: peer %s rs_idx:%d, hndl:0x%x q_tag:%d", __func__,
-                       p_scb->PeerAddress().ToString().c_str(),
+      APPL_TRACE_DEBUG("%s: peer %s rs_idx:%d, bta_handle:0x%x q_tag:%d",
+                       __func__, p_scb->PeerAddress().ToString().c_str(),
                        bta_av_cb.rs_idx, p_scb->hndl, p_scb->q_tag);
 
       if (HCI_SUCCESS == app_id || HCI_ERR_NO_CONNECTION == app_id) {
@@ -986,16 +1035,20 @@
   int i;
   tBTA_AV_API_STOP stop;
 
-  APPL_TRACE_DEBUG("%s: id:%d status:%d", __func__, id, status);
+  LOG(INFO) << __func__ << ": status=" << +status << ", num_links=" << +id;
   if (id) {
     bta_av_cb.sco_occupied = true;
 
+    if (bta_av_cb.features & BTA_AV_FEAT_NO_SCO_SSPD) {
+      return;
+    }
+
     /* either BTA_SYS_SCO_OPEN or BTA_SYS_SCO_CLOSE with remaining active SCO */
     for (i = 0; i < BTA_AV_NUM_STRS; i++) {
       p_scb = bta_av_cb.p_scb[i];
 
       if (p_scb && p_scb->co_started && (!p_scb->sco_suspend)) {
-        APPL_TRACE_DEBUG("%s: suspending scb:%d", __func__, i);
+        VLOG(1) << __func__ << ": suspending scb:" << i;
         /* scb is used and started, not suspended automatically */
         p_scb->sco_suspend = true;
         stop.flush = false;
@@ -1007,12 +1060,16 @@
   } else {
     bta_av_cb.sco_occupied = false;
 
+    if (bta_av_cb.features & BTA_AV_FEAT_NO_SCO_SSPD) {
+      return;
+    }
+
     for (i = 0; i < BTA_AV_NUM_STRS; i++) {
       p_scb = bta_av_cb.p_scb[i];
 
       if (p_scb && p_scb->sco_suspend) /* scb is used and suspended for SCO */
       {
-        APPL_TRACE_DEBUG("%s: starting scb:%d", __func__, i);
+        VLOG(1) << __func__ << ": starting scb:" << i;
         bta_av_ssm_execute(p_scb, BTA_AV_AP_START_EVT, NULL);
       }
     }
@@ -1088,11 +1145,11 @@
   bool is_ok = true;
 
   if (BTM_GetRole(p_scb->PeerAddress(), &role) == BTM_SUCCESS) {
-    LOG_INFO(
-        LOG_TAG,
-        "%s: peer %s hndl:0x%x role:%d conn_audio:0x%x bits:%d features:0x%x",
-        __func__, p_scb->PeerAddress().ToString().c_str(), p_scb->hndl, role,
-        bta_av_cb.conn_audio, bits, bta_av_cb.features);
+    LOG_INFO(LOG_TAG,
+             "%s: peer %s bta_handle:0x%x role:%d conn_audio:0x%x bits:%d "
+             "features:0x%x",
+             __func__, p_scb->PeerAddress().ToString().c_str(), p_scb->hndl,
+             role, bta_av_cb.conn_audio, bits, bta_av_cb.features);
     if (BTM_ROLE_MASTER != role &&
         (A2DP_BitsSet(bta_av_cb.conn_audio) > bits ||
          (bta_av_cb.features & BTA_AV_FEAT_MASTER))) {
@@ -1108,7 +1165,7 @@
                   "%s: peer %s BTM_SwitchRole(BTM_ROLE_MASTER) error: %d",
                   __func__, p_scb->PeerAddress().ToString().c_str(), status);
       }
-      if (status != BTM_DEV_BLACKLISTED) {
+      if (status != BTM_MODE_UNSUPPORTED && status != BTM_DEV_BLACKLISTED) {
         is_ok = false;
         p_scb->wait |= BTA_AV_WAIT_ROLE_SW_RES_START;
       }
@@ -1220,7 +1277,7 @@
     /* state machine events */
     bta_av_sm_execute(&bta_av_cb, p_msg->event, (tBTA_AV_DATA*)p_msg);
   } else {
-    APPL_TRACE_VERBOSE("%s: handle=0x%x", __func__, p_msg->layer_specific);
+    APPL_TRACE_VERBOSE("%s: bta_handle=0x%x", __func__, p_msg->layer_specific);
     /* stream state machine events */
     bta_av_ssm_execute(bta_av_hndl_to_scb(p_msg->layer_specific), p_msg->event,
                        (tBTA_AV_DATA*)p_msg);
@@ -1348,6 +1405,10 @@
       return "AVDT_DELAY_RPT";
     case BTA_AV_ACP_CONNECT_EVT:
       return "ACP_CONNECT";
+    case BTA_AV_API_OFFLOAD_START_EVT:
+      return "OFFLOAD_START";
+    case BTA_AV_API_OFFLOAD_START_RSP_EVT:
+      return "OFFLOAD_START_RSP";
 
     case BTA_AV_API_ENABLE_EVT:
       return "API_ENABLE";
diff --git a/bta/av/bta_av_ssm.cc b/bta/av/bta_av_ssm.cc
index 80effa3..c9fc37e 100644
--- a/bta/av/bta_av_ssm.cc
+++ b/bta/av/bta_av_ssm.cc
@@ -486,22 +486,32 @@
     return;
   }
 
-  APPL_TRACE_VERBOSE(
-      "%s: peer %s AV event(0x%x)=0x%x(%s) state=%d(%s) p_scb=%p", __func__,
-      p_scb->PeerAddress().ToString().c_str(), p_scb->hndl, event,
-      bta_av_evt_code(event), p_scb->state, bta_av_sst_code(p_scb->state),
-      p_scb);
-
   /* look up the state table for the current state */
   tBTA_AV_SST_TBL state_table = bta_av_sst_tbl[p_scb->state];
 
   /* set next state */
+  auto new_state =
+      state_table[event - BTA_AV_FIRST_SSM_EVT][BTA_AV_SNEXT_STATE];
+  if (p_scb->state != new_state) {
+    APPL_TRACE_WARNING(
+        "%s: peer %s AV event(0x%x)=0x%x(%s) state=%d(%s) -> %d(%s) p_scb=%p",
+        __func__, p_scb->PeerAddress().ToString().c_str(), p_scb->hndl, event,
+        bta_av_evt_code(event), p_scb->state, bta_av_sst_code(p_scb->state),
+        new_state, bta_av_sst_code(new_state), p_scb);
+  } else {
+    APPL_TRACE_VERBOSE(
+        "%s: peer %s AV event(0x%x)=0x%x(%s) state=%d(%s) p_scb=%p", __func__,
+        p_scb->PeerAddress().ToString().c_str(), p_scb->hndl, event,
+        bta_av_evt_code(event), p_scb->state, bta_av_sst_code(p_scb->state),
+        p_scb);
+  }
   event -= BTA_AV_FIRST_SSM_EVT;
   p_scb->state = state_table[event][BTA_AV_SNEXT_STATE];
 
-  APPL_TRACE_VERBOSE("%s: peer %s AV next state=%d(%s) p_scb=%p", __func__,
-                     p_scb->PeerAddress().ToString().c_str(), p_scb->state,
-                     bta_av_sst_code(p_scb->state), p_scb);
+  APPL_TRACE_VERBOSE("%s: peer %s AV next state=%d(%s) p_scb=%p(0x%x)",
+                     __func__, p_scb->PeerAddress().ToString().c_str(),
+                     p_scb->state, bta_av_sst_code(p_scb->state), p_scb,
+                     p_scb->hndl);
 
   /* execute action functions */
   for (int i = 0; i < BTA_AV_SACTIONS; i++) {
diff --git a/bta/dm/bta_dm_act.cc b/bta/dm/bta_dm_act.cc
index 5ca3542..d1b260c 100644
--- a/bta/dm/bta_dm_act.cc
+++ b/bta/dm/bta_dm_act.cc
@@ -42,6 +42,7 @@
 #include "btm_api.h"
 #include "btm_int.h"
 #include "btu.h"
+#include "device/include/interop.h"
 #include "gap_api.h" /* For GAP_BleReadPeerPrefConnParams */
 #include "l2c_api.h"
 #include "osi/include/log.h"
@@ -812,15 +813,12 @@
 }
 
 /** Bonds with peer device */
-void bta_dm_bond(const RawAddress& bd_addr, tBTA_TRANSPORT transport) {
-  tBTM_STATUS status;
+void bta_dm_bond(const RawAddress& bd_addr, tBLE_ADDR_TYPE addr_type,
+                 tBTA_TRANSPORT transport) {
   tBTA_DM_SEC sec_event;
   char* p_name;
 
-  if (transport == BTA_TRANSPORT_UNKNOWN)
-    status = BTM_SecBond(bd_addr, 0, NULL, 0);
-  else
-    status = BTM_SecBondByTransport(bd_addr, transport, 0, NULL, 0);
+  tBTM_STATUS status = BTM_SecBond(bd_addr, addr_type, transport, 0, NULL, 0);
 
   if (bta_dm_cb.p_sec_cback && (status != BTM_CMD_STARTED)) {
     memset(&sec_event, 0, sizeof(tBTA_DM_SEC));
@@ -1356,8 +1354,10 @@
     do {
       p_sdp_rec = NULL;
       if (bta_dm_search_cb.service_index == (BTA_USER_SERVICE_ID + 1)) {
-        p_sdp_rec = SDP_FindServiceUUIDInDb(bta_dm_search_cb.p_sdp_db,
-                                            bta_dm_search_cb.uuid, p_sdp_rec);
+        if (!bta_dm_search_cb.uuid.IsEmpty()) {
+          p_sdp_rec = SDP_FindServiceUUIDInDb(bta_dm_search_cb.p_sdp_db,
+                                              bta_dm_search_cb.uuid, p_sdp_rec);
+        }
 
         if (p_sdp_rec && SDP_FindProtocolListElemInRec(
                              p_sdp_rec, UUID_PROTOCOL_RFCOMM, &pe)) {
@@ -1753,7 +1753,9 @@
   if (bta_dm_search_cb.p_search_cback) {
     bta_dm_search_cb.p_search_cback(BTA_DM_SEARCH_CANCEL_CMPL_EVT, NULL);
   }
-  if (!bta_dm_search_cb.name_discover_done) {
+  if (!bta_dm_search_cb.name_discover_done &&
+      (bta_dm_search_cb.state == BTA_DM_SEARCH_ACTIVE ||
+       bta_dm_search_cb.state == BTA_DM_SEARCH_CANCELLING)) {
     BTM_CancelRemoteDeviceName();
   }
   if (bta_dm_search_cb.gatt_disc_active) {
@@ -1771,7 +1773,6 @@
  *
  ******************************************************************************/
 static void bta_dm_find_services(const RawAddress& bd_addr) {
-
   while (bta_dm_search_cb.service_index < BTA_MAX_SERVICE_ID) {
     Uuid uuid = Uuid::kEmpty;
     if (bta_dm_search_cb.services_to_search &
@@ -1945,10 +1946,13 @@
     APPL_TRACE_DEBUG("%s appl_knows_rem_name %d", __func__,
                      bta_dm_search_cb.p_btm_inq_info->appl_knows_rem_name);
   }
-  if ((bta_dm_search_cb.p_btm_inq_info) &&
-      (bta_dm_search_cb.p_btm_inq_info->results.device_type ==
-       BT_DEVICE_TYPE_BLE) &&
-      (bta_dm_search_cb.state == BTA_DM_SEARCH_ACTIVE)) {
+  if (((bta_dm_search_cb.p_btm_inq_info) &&
+       (bta_dm_search_cb.p_btm_inq_info->results.device_type ==
+        BT_DEVICE_TYPE_BLE) &&
+       (bta_dm_search_cb.state == BTA_DM_SEARCH_ACTIVE)) ||
+      (transport == BT_TRANSPORT_LE &&
+       interop_match_addr(INTEROP_DISABLE_NAME_REQUEST,
+                          &bta_dm_search_cb.peer_bdaddr))) {
     /* Do not perform RNR for LE devices at inquiry complete*/
     bta_dm_search_cb.name_discover_done = true;
   }
@@ -2504,7 +2508,7 @@
       sec_event.cfm_req.loc_io_caps = p_data->cfm_req.loc_io_caps;
       sec_event.cfm_req.rmt_io_caps = p_data->cfm_req.rmt_io_caps;
 
-    /* continue to next case */
+      [[fallthrough]];
     /* Passkey entry mode, mobile device with output capability is very
         unlikely to receive key request, so skip this event */
     /*case BTM_SP_KEY_REQ_EVT: */
@@ -2767,6 +2771,9 @@
                 bta_dm_cb.device_list.peer_device[i].peer_bdaddr))
           issue_unpair_cb = true;
 
+        /* remove all cached GATT information */
+        BTA_GATTC_Refresh(bd_addr);
+
         APPL_TRACE_DEBUG("%s: Unpairing: issue unpair CB = %d ", __func__,
                          issue_unpair_cb);
       }
diff --git a/bta/dm/bta_dm_api.cc b/bta/dm/bta_dm_api.cc
index 979d2f6..1650172 100644
--- a/bta/dm/bta_dm_api.cc
+++ b/bta/dm/bta_dm_api.cc
@@ -210,15 +210,10 @@
 }
 
 /** This function initiates a bonding procedure with a peer device */
-void BTA_DmBond(const RawAddress& bd_addr) {
+void BTA_DmBond(const RawAddress& bd_addr, tBLE_ADDR_TYPE addr_type,
+                tBTA_TRANSPORT transport) {
   do_in_main_thread(FROM_HERE,
-                    base::Bind(bta_dm_bond, bd_addr, BTA_TRANSPORT_UNKNOWN));
-}
-
-/** This function initiates a bonding procedure with a peer device */
-void BTA_DmBondByTransport(const RawAddress& bd_addr,
-                           tBTA_TRANSPORT transport) {
-  do_in_main_thread(FROM_HERE, base::Bind(bta_dm_bond, bd_addr, transport));
+                    base::Bind(bta_dm_bond, bd_addr, addr_type, transport));
 }
 
 /** This function cancels the bonding procedure with a peer device
diff --git a/bta/dm/bta_dm_cfg.cc b/bta/dm/bta_dm_cfg.cc
index 438f329..e1e2cad 100644
--- a/bta/dm/bta_dm_cfg.cc
+++ b/bta/dm/bta_dm_cfg.cc
@@ -130,9 +130,9 @@
         {BTA_ID_CG, BTA_ALL_APP_ID, 1},     /* cg resue ct spec table */
         {BTA_ID_DG, BTA_ALL_APP_ID, 2},     /* dg spec table */
         {BTA_ID_AV, BTA_ALL_APP_ID, 4},     /* av spec table */
-        {BTA_ID_AVK, BTA_ALL_APP_ID, 12},   /* avk spec table */
-        {BTA_ID_FTC, BTA_ALL_APP_ID, 6},    /* ftc spec table */
-        {BTA_ID_FTS, BTA_ALL_APP_ID, 7},    /* fts spec table */
+        {BTA_ID_AVK, BTA_ALL_APP_ID, 13},   /* avk spec table */
+        {BTA_ID_FTC, BTA_ALL_APP_ID, 7},    /* ftc spec table */
+        {BTA_ID_FTS, BTA_ALL_APP_ID, 8},    /* fts spec table */
         {BTA_ID_HD, BTA_ALL_APP_ID, 3},     /* hd spec table */
         {BTA_ID_HH, BTA_HH_APP_ID_JOY, 5},  /* app BTA_HH_APP_ID_JOY,
                                                similar to hh spec table */
@@ -140,19 +140,19 @@
                                                similar to hh spec table */
         {BTA_ID_HH, BTA_ALL_APP_ID, 6},     /* hh spec table */
         {BTA_ID_PBC, BTA_ALL_APP_ID, 2},    /* reuse dg spec table */
-        {BTA_ID_PBS, BTA_ALL_APP_ID, 7},    /* reuse fts spec table */
-        {BTA_ID_OPC, BTA_ALL_APP_ID, 6},    /* reuse ftc spec table */
-        {BTA_ID_OPS, BTA_ALL_APP_ID, 7},    /* reuse fts spec table */
-        {BTA_ID_MSE, BTA_ALL_APP_ID, 7},    /* reuse fts spec table */
+        {BTA_ID_PBS, BTA_ALL_APP_ID, 8},    /* reuse fts spec table */
+        {BTA_ID_OPC, BTA_ALL_APP_ID, 7},    /* reuse ftc spec table */
+        {BTA_ID_OPS, BTA_ALL_APP_ID, 8},    /* reuse fts spec table */
+        {BTA_ID_MSE, BTA_ALL_APP_ID, 8},    /* reuse fts spec table */
         {BTA_ID_JV, BTA_JV_PM_ID_1,
-         6}, /* app BTA_JV_PM_ID_1, reuse ftc spec table */
-        {BTA_ID_JV, BTA_ALL_APP_ID, 7},     /* reuse fts spec table */
-        {BTA_ID_HL, BTA_ALL_APP_ID, 8},     /* reuse fts spec table */
-        {BTA_ID_PAN, BTUI_PAN_ID_PANU, 9},  /* PANU spec table */
-        {BTA_ID_PAN, BTUI_PAN_ID_NAP, 10},  /* NAP spec table */
-        {BTA_ID_HS, BTA_ALL_APP_ID, 11},    /* HS spec table */
-        {BTA_ID_GATTC, BTA_ALL_APP_ID, 13}, /* gattc spec table */
-        {BTA_ID_GATTS, BTA_ALL_APP_ID, 14}  /* gatts spec table */
+         7}, /* app BTA_JV_PM_ID_1, reuse ftc spec table */
+        {BTA_ID_JV, BTA_ALL_APP_ID, 8},     /* reuse fts spec table */
+        {BTA_ID_HL, BTA_ALL_APP_ID, 9},     /* reuse fts spec table */
+        {BTA_ID_PAN, BTUI_PAN_ID_PANU, 10}, /* PANU spec table */
+        {BTA_ID_PAN, BTUI_PAN_ID_NAP, 11},  /* NAP spec table */
+        {BTA_ID_HS, BTA_ALL_APP_ID, 12},    /* HS spec table */
+        {BTA_ID_GATTC, BTA_ALL_APP_ID, 14}, /* gattc spec table */
+        {BTA_ID_GATTS, BTA_ALL_APP_ID, 15}  /* gatts spec table */
 };
 
 tBTA_DM_PM_TYPE_QUALIFIER tBTA_DM_PM_SPEC bta_dm_pm_spec[BTA_DM_NUM_PM_SPEC] = {
diff --git a/bta/dm/bta_dm_int.h b/bta/dm/bta_dm_int.h
index 97d7f80..05abd8f 100644
--- a/bta/dm/bta_dm_int.h
+++ b/bta/dm/bta_dm_int.h
@@ -480,7 +480,7 @@
 extern void bta_dm_set_visibility(tBTA_DM_DISC, tBTA_DM_CONN, uint8_t, uint8_t);
 extern void bta_dm_set_scan_config(tBTA_DM_MSG* p_data);
 extern void bta_dm_vendor_spec_command(tBTA_DM_MSG* p_data);
-extern void bta_dm_bond(const RawAddress&, tBTA_TRANSPORT);
+extern void bta_dm_bond(const RawAddress&, tBLE_ADDR_TYPE, tBTA_TRANSPORT);
 extern void bta_dm_bond_cancel(const RawAddress&);
 extern void bta_dm_pin_reply(std::unique_ptr<tBTA_DM_API_PIN_REPLY> msg);
 extern void bta_dm_add_device(std::unique_ptr<tBTA_DM_API_ADD_DEVICE> msg);
diff --git a/bta/gatt/bta_gattc_act.cc b/bta/gatt/bta_gattc_act.cc
index b009e5a..3ff55b1 100644
--- a/bta/gatt/bta_gattc_act.cc
+++ b/bta/gatt/bta_gattc_act.cc
@@ -213,7 +213,7 @@
   }
 
   /* remove bg connection associated with this rcb */
-  for (uint8_t i = 0; i < BTA_GATTC_KNOWN_SR_MAX; i++) {
+  for (uint8_t i = 0; i < BTM_GetWhiteListSize(); i++) {
     if (!bta_gattc_cb.bg_track[i].in_use) continue;
 
     if (bta_gattc_cb.bg_track[i].cif_mask & (1 << (p_clreg->client_if - 1))) {
diff --git a/bta/gatt/bta_gattc_int.h b/bta/gatt/bta_gattc_int.h
index f0ead49..45a54c3 100644
--- a/bta/gatt/bta_gattc_int.h
+++ b/bta/gatt/bta_gattc_int.h
@@ -71,9 +71,9 @@
 #define BTA_GATTC_CL_MAX 32
 #endif
 
-/* max known devices GATTC can support */
+/* max known devices GATTC can support in Bluetooth spec */
 #ifndef BTA_GATTC_KNOWN_SR_MAX
-#define BTA_GATTC_KNOWN_SR_MAX 10
+#define BTA_GATTC_KNOWN_SR_MAX 255
 #endif
 
 #define BTA_GATTC_CONN_MAX GATT_MAX_PHY_CHANNEL
diff --git a/bta/gatt/bta_gattc_queue.cc b/bta/gatt/bta_gattc_queue.cc
index 6d9fff3..6d8f57c 100644
--- a/bta/gatt/bta_gattc_queue.cc
+++ b/bta/gatt/bta_gattc_queue.cc
@@ -171,9 +171,9 @@
                                        GATT_WRITE_OP_CB cb, void* cb_data) {
   gatt_op_queue[conn_id].push_back({.type = GATT_WRITE_CHAR,
                                     .handle = handle,
-                                    .write_type = write_type,
                                     .write_cb = cb,
                                     .write_cb_data = cb_data,
+                                    .write_type = write_type,
                                     .value = std::move(value)});
   gatt_execute_next_op(conn_id);
 }
@@ -184,9 +184,9 @@
                                    GATT_WRITE_OP_CB cb, void* cb_data) {
   gatt_op_queue[conn_id].push_back({.type = GATT_WRITE_DESC,
                                     .handle = handle,
-                                    .write_type = write_type,
                                     .write_cb = cb,
                                     .write_cb_data = cb_data,
+                                    .write_type = write_type,
                                     .value = std::move(value)});
   gatt_execute_next_op(conn_id);
 }
diff --git a/bta/gatt/bta_gattc_utils.cc b/bta/gatt/bta_gattc_utils.cc
index 2c8e5fd..d0b45c1 100644
--- a/bta/gatt/bta_gattc_utils.cc
+++ b/bta/gatt/bta_gattc_utils.cc
@@ -224,7 +224,7 @@
   tBTA_GATTC_SERV* p_srcb = &bta_gattc_cb.known_server[0];
   uint8_t i;
 
-  for (i = 0; i < BTA_GATTC_KNOWN_SR_MAX; i++, p_srcb++) {
+  for (i = 0; i < BTM_GetWhiteListSize(); i++, p_srcb++) {
     if (p_srcb->in_use && p_srcb->server_bda == bda) return p_srcb;
   }
   return NULL;
@@ -243,7 +243,7 @@
   tBTA_GATTC_SERV* p_srcb = &bta_gattc_cb.known_server[0];
   uint8_t i;
 
-  for (i = 0; i < BTA_GATTC_KNOWN_SR_MAX; i++, p_srcb++) {
+  for (i = 0; i < BTM_GetWhiteListSize(); i++, p_srcb++) {
     if (p_srcb->server_bda == bda) return p_srcb;
   }
   return NULL;
@@ -279,7 +279,7 @@
   bool found = false;
   uint8_t i;
 
-  for (i = 0; i < BTA_GATTC_KNOWN_SR_MAX; i++, p_tcb++) {
+  for (i = 0; i < BTM_GetWhiteListSize(); i++, p_tcb++) {
     if (!p_tcb->in_use) {
       found = true;
       break;
@@ -409,7 +409,7 @@
   uint8_t i = 0;
   tBTA_GATTC_CIF_MASK* p_cif_mask;
 
-  for (i = 0; i < BTA_GATTC_KNOWN_SR_MAX; i++, p_bg_tck++) {
+  for (i = 0; i < BTM_GetWhiteListSize(); i++, p_bg_tck++) {
     if (p_bg_tck->in_use && ((p_bg_tck->remote_bda == remote_bda_ptr) ||
                              (p_bg_tck->remote_bda.IsEmpty()))) {
       p_cif_mask = &p_bg_tck->cif_mask;
@@ -437,7 +437,7 @@
   } else /* adding a new device mask */
   {
     for (i = 0, p_bg_tck = &bta_gattc_cb.bg_track[0];
-         i < BTA_GATTC_KNOWN_SR_MAX; i++, p_bg_tck++) {
+         i < BTM_GetWhiteListSize(); i++, p_bg_tck++) {
       if (!p_bg_tck->in_use) {
         p_bg_tck->in_use = true;
         p_bg_tck->remote_bda = remote_bda_ptr;
@@ -468,7 +468,7 @@
   uint8_t i = 0;
   bool is_bg_conn = false;
 
-  for (i = 0; i < BTA_GATTC_KNOWN_SR_MAX && !is_bg_conn; i++, p_bg_tck++) {
+  for (i = 0; i < BTM_GetWhiteListSize() && !is_bg_conn; i++, p_bg_tck++) {
     if (p_bg_tck->in_use && (p_bg_tck->remote_bda == remote_bda ||
                              p_bg_tck->remote_bda.IsEmpty())) {
       if (((p_bg_tck->cif_mask & (1 << (client_if - 1))) != 0) &&
diff --git a/bta/gatt/database.cc b/bta/gatt/database.cc
index 1486b08..58f7056 100644
--- a/bta/gatt/database.cc
+++ b/bta/gatt/database.cc
@@ -127,11 +127,12 @@
   for (; it != nv_attr.cend(); ++it) {
     const auto& attr = *it;
     if (attr.type != PRIMARY_SERVICE && attr.type != SECONDARY_SERVICE) break;
-    result.services.emplace_back(
-        Service{.handle = attr.handle,
-                .end_handle = attr.value.service.end_handle,
-                .is_primary = (attr.type == PRIMARY_SERVICE),
-                .uuid = attr.value.service.uuid});
+    result.services.emplace_back(Service{
+        .handle = attr.handle,
+        .uuid = attr.value.service.uuid,
+        .is_primary = (attr.type == PRIMARY_SERVICE),
+        .end_handle = attr.value.service.end_handle,
+    });
   }
 
   auto current_service_it = result.services.begin();
@@ -168,11 +169,12 @@
           .end_handle = attr.value.included_service.end_handle,
       });
     } else if (attr.type == CHARACTERISTIC) {
-      current_service_it->characteristics.emplace_back(
-          Characteristic{.declaration_handle = attr.handle,
-                         .value_handle = attr.value.characteristic.value_handle,
-                         .properties = attr.value.characteristic.properties,
-                         .uuid = attr.value.characteristic.uuid});
+      current_service_it->characteristics.emplace_back(Characteristic{
+          .declaration_handle = attr.handle,
+          .uuid = attr.value.characteristic.uuid,
+          .value_handle = attr.value.characteristic.value_handle,
+          .properties = attr.value.characteristic.properties,
+      });
 
     } else {
       current_service_it->characteristics.back().descriptors.emplace_back(
diff --git a/bta/gatt/database_builder.cc b/bta/gatt/database_builder.cc
index ed065ab..98c0a27 100644
--- a/bta/gatt/database_builder.cc
+++ b/bta/gatt/database_builder.cc
@@ -32,10 +32,12 @@
   // general case optimization - we add services in order
   if (database.services.empty() ||
       database.services.back().end_handle < handle) {
-    database.services.emplace_back(Service{.handle = handle,
-                                           .end_handle = end_handle,
-                                           .is_primary = is_primary,
-                                           .uuid = uuid});
+    database.services.emplace_back(Service{
+        .handle = handle,
+        .uuid = uuid,
+        .is_primary = is_primary,
+        .end_handle = end_handle,
+    });
   } else {
     auto& vec = database.services;
 
@@ -45,10 +47,12 @@
         [](Service s, uint16_t handle) { return s.end_handle < handle; });
 
     // Insert new service just before it
-    vec.emplace(it, Service{.handle = handle,
-                            .end_handle = end_handle,
-                            .is_primary = is_primary,
-                            .uuid = uuid});
+    vec.emplace(it, Service{
+                        .handle = handle,
+                        .uuid = uuid,
+                        .is_primary = is_primary,
+                        .end_handle = end_handle,
+                    });
   }
 
   services_to_discover.insert({handle, end_handle});
@@ -90,11 +94,12 @@
                  << loghex(value_handle) << " is after service end_handle="
                  << loghex(service->end_handle);
 
-  service->characteristics.emplace_back(
-      Characteristic{.declaration_handle = handle,
-                     .value_handle = value_handle,
-                     .properties = properties,
-                     .uuid = uuid});
+  service->characteristics.emplace_back(Characteristic{
+      .declaration_handle = handle,
+      .uuid = uuid,
+      .value_handle = value_handle,
+      .properties = properties,
+  });
   return;
 }
 
diff --git a/bta/hd/bta_hd_act.cc b/bta/hd/bta_hd_act.cc
index 0d677ee..a37a051 100644
--- a/bta/hd/bta_hd_act.cc
+++ b/bta/hd/bta_hd_act.cc
@@ -62,7 +62,7 @@
 
       case 0x85:  // Report ID
         *has_report_id = TRUE;
-
+        [[fallthrough]];
       default:
         ptr += (item & 0x03);
         break;
diff --git a/bta/hearing_aid/hearing_aid.cc b/bta/hearing_aid/hearing_aid.cc
index 867c628..5c50c1a 100644
--- a/bta/hearing_aid/hearing_aid.cc
+++ b/bta/hearing_aid/hearing_aid.cc
@@ -232,7 +232,7 @@
   uint16_t overwrite_min_ce_len;
 
  public:
-  virtual ~HearingAidImpl() = default;
+  ~HearingAidImpl() override = default;
 
   HearingAidImpl(bluetooth::hearing_aid::HearingAidCallbacks* callbacks,
                  Closure initCb)
@@ -921,9 +921,6 @@
     }
 
     if (hearingDevice->first_connection) {
-      /* add device into BG connection to accept remote initiated connection */
-      BTA_GATTC_Open(gatt_if, address, false, GATT_TRANSPORT_LE, false);
-
       btif_storage_add_hearing_aid(*hearingDevice);
 
       hearingDevice->first_connection = false;
@@ -1507,7 +1504,10 @@
 
     DoDisconnectCleanUp(hearingDevice);
 
-    // Keep this hearing aid in the list, and allow to reconnect back.
+    // This is needed just for the first connection. After stack is restarted,
+    // code that loads device will add them to whitelist.
+    BTA_GATTC_Open(gatt_if, hearingDevice->address, false, GATT_TRANSPORT_LE,
+                   false);
 
     callbacks->OnConnectionState(ConnectionState::DISCONNECTED, remote_bda);
 
diff --git a/bta/hf_client/bta_hf_client_at.cc b/bta/hf_client/bta_hf_client_at.cc
index 5bcc68e..6e4fe26 100644
--- a/bta/hf_client/bta_hf_client_at.cc
+++ b/bta/hf_client/bta_hf_client_at.cc
@@ -724,9 +724,7 @@
  ******************************************************************************/
 void bta_hf_client_cnum(tBTA_HF_CLIENT_CB* client_cb, char* number,
                         uint16_t service) {
-  tBTA_HF_CLIENT evt;
-
-  memset(&evt, 0, sizeof(evt));
+  tBTA_HF_CLIENT evt = {};
 
   evt.cnum.service = service;
   strlcpy(evt.cnum.number, number, BTA_HF_CLIENT_NUMBER_LEN + 1);
@@ -736,6 +734,18 @@
   bta_hf_client_app_callback(BTA_HF_CLIENT_CNUM_EVT, &evt);
 }
 
+void bta_hf_client_unknown_response(tBTA_HF_CLIENT_CB* client_cb,
+                                    const char* evt_buffer) {
+  tBTA_HF_CLIENT evt = {};
+
+  strlcpy(evt.unknown.event_string, evt_buffer,
+          BTA_HF_CLIENT_UNKOWN_EVENT_LEN + 1);
+  evt.unknown.event_string[BTA_HF_CLIENT_UNKOWN_EVENT_LEN] = '\0';
+
+  evt.unknown.bd_addr = client_cb->peer_addr;
+  bta_hf_client_app_callback(BTA_HF_CLIENT_UNKNOWN_EVT, &evt);
+}
+
 /*******************************************************************************
  *
  * Function         bta_hf_client_binp
@@ -1436,6 +1446,36 @@
   return buffer;
 }
 
+static char* bta_hf_client_process_unknown(tBTA_HF_CLIENT_CB* client_cb,
+                                           char* buffer) {
+  char* start = strstr(buffer, "\r\n");
+  if (start == NULL) {
+    return NULL;
+  }
+  start += sizeof("\r\n") - 1;
+
+  char* end = strstr(start, "\r\n");
+  if (end == NULL) {
+    return NULL;
+  }
+
+  int evt_size = end - start + 1;
+
+  char tmp_buf[BTA_HF_CLIENT_UNKOWN_EVENT_LEN];
+  if (evt_size < BTA_HF_CLIENT_UNKOWN_EVENT_LEN) {
+    strlcpy(tmp_buf, start, evt_size);
+    bta_hf_client_unknown_response(client_cb, tmp_buf);
+    AT_CHECK_RN(end);
+  } else {
+    APPL_TRACE_ERROR("%s: exceed event buffer size. (%d, %d)", __func__,
+                     evt_size, BTA_HF_CLIENT_UNKOWN_EVENT_LEN);
+  }
+
+  APPL_TRACE_DEBUG("%s: %s", __func__, buffer);
+
+  return end;
+}
+
 /******************************************************************************
  *       SUPPORTED EVENT MESSAGES
  ******************************************************************************/
@@ -1461,7 +1501,7 @@
     bta_hf_client_parse_cnum,        bta_hf_client_parse_btrh,
     bta_hf_client_parse_busy,        bta_hf_client_parse_delayed,
     bta_hf_client_parse_no_carrier,  bta_hf_client_parse_no_answer,
-    bta_hf_client_parse_blacklisted, bta_hf_client_skip_unknown};
+    bta_hf_client_parse_blacklisted, bta_hf_client_process_unknown};
 
 /* calculate supported event list length */
 static const uint16_t bta_hf_client_parser_cb_count =
@@ -1996,6 +2036,25 @@
   bta_hf_client_send_at(client_cb, BTA_HF_CLIENT_AT_BIA, buf, at_len);
 }
 
+void bta_hf_client_send_at_vendor_specific_cmd(tBTA_HF_CLIENT_CB* client_cb,
+                                               const char* str) {
+  char buf[BTA_HF_CLIENT_AT_MAX_LEN];
+
+  APPL_TRACE_DEBUG("%s", __func__);
+
+  int at_len = snprintf(buf, sizeof(buf), "AT%s", str);
+
+  if (at_len < 1) {
+    APPL_TRACE_ERROR("%s: AT command Framing error", __func__);
+    return;
+  }
+
+  buf[at_len - 1] = '\r';
+
+  bta_hf_client_send_at(client_cb, BTA_HF_CLIENT_AT_VENDOR_SPECIFIC, buf,
+                        at_len);
+}
+
 void bta_hf_client_at_init(tBTA_HF_CLIENT_CB* client_cb) {
   alarm_free(client_cb->at_cb.resp_timer);
   alarm_free(client_cb->at_cb.hold_timer);
@@ -2087,6 +2146,9 @@
     case BTA_HF_CLIENT_AT_CMD_NREC:
       bta_hf_client_send_at_nrec(client_cb);
       break;
+    case BTA_HF_CLIENT_AT_CMD_VENDOR_SPECIFIC_CMD:
+      bta_hf_client_send_at_vendor_specific_cmd(client_cb, p_val->str);
+      break;
     default:
       APPL_TRACE_ERROR("Default case");
       snprintf(buf, BTA_HF_CLIENT_AT_MAX_LEN,
diff --git a/bta/hf_client/bta_hf_client_int.h b/bta/hf_client/bta_hf_client_int.h
old mode 100644
new mode 100755
index 15ceec2..4cd16fa
--- a/bta/hf_client/bta_hf_client_int.h
+++ b/bta/hf_client/bta_hf_client_int.h
@@ -28,6 +28,7 @@
 #define HFP_VERSION_1_1 0x0101
 #define HFP_VERSION_1_5 0x0105
 #define HFP_VERSION_1_6 0x0106
+#define HFP_VERSION_1_7 0x0107
 
 /* RFCOMM MTU SIZE */
 #define BTA_HF_CLIENT_MTU 256
@@ -97,6 +98,7 @@
   BTA_HF_CLIENT_AT_CNUM,
   BTA_HF_CLIENT_AT_NREC,
   BTA_HF_CLIENT_AT_BINP,
+  BTA_HF_CLIENT_AT_VENDOR_SPECIFIC,
 };
 
 /*****************************************************************************
diff --git a/bta/hf_client/bta_hf_client_sco.cc b/bta/hf_client/bta_hf_client_sco.cc
index 50820bd..956b6d6 100644
--- a/bta/hf_client/bta_hf_client_sco.cc
+++ b/bta/hf_client/bta_hf_client_sco.cc
@@ -305,6 +305,7 @@
         case BTA_HF_CLIENT_SCO_LISTEN_E:
           /* create SCO listen connection */
           bta_hf_client_sco_create(client_cb, false);
+          break;
 
         case BTA_HF_CLIENT_SCO_OPEN_E:
           /* remove listening connection */
diff --git a/bta/hf_client/bta_hf_client_sdp.cc b/bta/hf_client/bta_hf_client_sdp.cc
old mode 100644
new mode 100755
index d68adcc..359f7ac
--- a/bta/hf_client/bta_hf_client_sdp.cc
+++ b/bta/hf_client/bta_hf_client_sdp.cc
@@ -33,6 +33,7 @@
 #include "bta_hf_client_int.h"
 #include "bta_sys.h"
 #include "osi/include/osi.h"
+#include "osi/include/properties.h"
 
 using bluetooth::Uuid;
 
@@ -122,6 +123,10 @@
   profile_uuid = UUID_SERVCLASS_HF_HANDSFREE;
   version = HFP_VERSION_1_6;
 
+  if (osi_property_get_bool("persist.bluetooth.hfpclient.sco_s4_supported",
+                            false))
+    version = HFP_VERSION_1_7;
+
   result &= SDP_AddProfileDescriptorList(sdp_handle, profile_uuid, version);
 
   /* add service name */
diff --git a/bta/hh/bta_hh_act.cc b/bta/hh/bta_hh_act.cc
index f64fbf0..81163c7 100644
--- a/bta/hh/bta_hh_act.cc
+++ b/bta/hh/bta_hh_act.cc
@@ -451,7 +451,7 @@
      */
     if ((status == BTA_HH_ERR_SDP) && (p_cb->incoming_conn) &&
         (p_cb->app_id == 0)) {
-      APPL_TRACE_DEBUG("%s: SDP failed for  incoming conn :hndl %d", __func__,
+      APPL_TRACE_ERROR("%s: SDP failed for  incoming conn hndl: %d", __func__,
                        p_cb->incoming_hid_handle);
       HID_HostRemoveDev(p_cb->incoming_hid_handle);
     }
@@ -470,6 +470,8 @@
     bta_hh_trace_dev_db();
 #endif
   }
+  p_cb->incoming_conn = false;
+  p_cb->incoming_hid_handle = BTA_HH_INVALID_HANDLE;
   return;
 }
 
diff --git a/bta/hh/bta_hh_le.cc b/bta/hh/bta_hh_le.cc
old mode 100644
new mode 100755
index 510e3a4..ba95af1
--- a/bta/hh/bta_hh_le.cc
+++ b/bta/hh/bta_hh_le.cc
@@ -66,10 +66,9 @@
 
 static void bta_hh_gattc_callback(tBTA_GATTC_EVT event, tBTA_GATTC* p_data);
 static void bta_hh_le_add_dev_bg_conn(tBTA_HH_DEV_CB* p_cb, bool check_bond);
-// TODO(jpawlowski): uncomment when fixed
-// static void bta_hh_process_cache_rpt (tBTA_HH_DEV_CB *p_cb,
-//                                       tBTA_HH_RPT_CACHE_ENTRY *p_rpt_cache,
-//                                       uint8_t num_rpt);
+static void bta_hh_process_cache_rpt(tBTA_HH_DEV_CB* p_cb,
+                                     tBTA_HH_RPT_CACHE_ENTRY* p_rpt_cache,
+                                     uint8_t num_rpt);
 
 #if (BTA_HH_DEBUG == TRUE)
 static const char* bta_hh_le_rpt_name[4] = {"UNKNOWN", "INPUT", "OUTPUT",
@@ -1001,14 +1000,12 @@
       APPL_TRACE_DEBUG("bta_hh_security_cmpl no reports loaded, try to load");
 
       /* start loading the cache if not in stack */
-      // TODO(jpawlowski): cache storage is broken, fix it
-      // tBTA_HH_RPT_CACHE_ENTRY     *p_rpt_cache;
-      // uint8_t                       num_rpt = 0;
-      // if ((p_rpt_cache = bta_hh_le_co_cache_load(p_cb->addr, &num_rpt,
-      // p_cb->app_id)) != NULL)
-      // {
-      //     bta_hh_process_cache_rpt(p_cb, p_rpt_cache, num_rpt);
-      // }
+      tBTA_HH_RPT_CACHE_ENTRY* p_rpt_cache;
+      uint8_t num_rpt = 0;
+      if ((p_rpt_cache = bta_hh_le_co_cache_load(p_cb->addr, &num_rpt,
+                                                 p_cb->app_id)) != NULL) {
+        bta_hh_process_cache_rpt(p_cb, p_rpt_cache, num_rpt);
+      }
     }
     /*  discovery has been done for HID service */
     if (p_cb->app_id != 0 && p_cb->hid_srvc.in_use) {
@@ -1576,9 +1573,11 @@
 
   app_id = p_dev_cb->app_id;
 
-  p_rpt =
-      bta_hh_le_find_report_entry(p_dev_cb, p_dev_cb->hid_srvc.srvc_inst_id,
-                                  p_char->uuid.As16Bit(), p_char->value_handle);
+  const gatt::Service* p_svc =
+      BTA_GATTC_GetOwningService(p_dev_cb->conn_id, p_char->value_handle);
+
+  p_rpt = bta_hh_le_find_report_entry(
+      p_dev_cb, p_svc->handle, p_char->uuid.As16Bit(), p_char->value_handle);
   if (p_rpt == NULL) {
     APPL_TRACE_ERROR(
         "%s: notification received for Unknown Report, uuid: %s, handle: "
@@ -2165,52 +2164,40 @@
  * Parameters:
  *
  ******************************************************************************/
-// TODO(jpawlowski): uncomment when fixed
-// static void bta_hh_process_cache_rpt (tBTA_HH_DEV_CB *p_cb,
-//                                       tBTA_HH_RPT_CACHE_ENTRY *p_rpt_cache,
-//                                       uint8_t num_rpt)
-// {
-//     uint8_t                       i = 0;
-//     tBTA_HH_LE_RPT              *p_rpt;
+static void bta_hh_process_cache_rpt(tBTA_HH_DEV_CB* p_cb,
+                                     tBTA_HH_RPT_CACHE_ENTRY* p_rpt_cache,
+                                     uint8_t num_rpt) {
+  uint8_t i = 0;
+  tBTA_HH_LE_RPT* p_rpt;
 
-//     if (num_rpt != 0)  /* no cache is found */
-//     {
-//         p_cb->hid_srvc.in_use = true;
+  if (num_rpt != 0) /* no cache is found */
+  {
+    p_cb->hid_srvc.in_use = true;
 
-//         /* set the descriptor info */
-//         p_cb->hid_srvc.descriptor.dl_len =
-//                 p_cb->dscp_info.descriptor.dl_len;
-//         p_cb->hid_srvc.descriptor.dsc_list =
-//                     p_cb->dscp_info.descriptor.dsc_list;
+    /* set the descriptor info */
+    p_cb->hid_srvc.descriptor.dl_len = p_cb->dscp_info.descriptor.dl_len;
+    p_cb->hid_srvc.descriptor.dsc_list = p_cb->dscp_info.descriptor.dsc_list;
 
-//         for (; i <num_rpt; i ++, p_rpt_cache ++)
-//         {
-//             if ((p_rpt = bta_hh_le_find_alloc_report_entry (p_cb,
-//                                                p_rpt_cache->srvc_inst_id,
-//                                                p_rpt_cache->rpt_uuid,
-//                                                p_rpt_cache->char_inst_id,
-//                                                p_rpt_cache->prop))  == NULL)
-//             {
-//                 APPL_TRACE_ERROR("bta_hh_process_cache_rpt: allocation report
-//                 entry failure");
-//                 break;
-//             }
-//             else
-//             {
-//                 p_rpt->rpt_type =  p_rpt_cache->rpt_type;
-//                 p_rpt->rpt_id   =  p_rpt_cache->rpt_id;
+    for (; i < num_rpt; i++, p_rpt_cache++) {
+      if ((p_rpt = bta_hh_le_find_alloc_report_entry(
+               p_cb, p_rpt_cache->srvc_inst_id, p_rpt_cache->rpt_uuid,
+               p_rpt_cache->char_inst_id)) == NULL) {
+        APPL_TRACE_ERROR(
+            "bta_hh_process_cache_rpt: allocation report entry failure");
+        break;
+      } else {
+        p_rpt->rpt_type = p_rpt_cache->rpt_type;
+        p_rpt->rpt_id = p_rpt_cache->rpt_id;
 
-//                 if (p_rpt->uuid == GATT_UUID_HID_BT_KB_INPUT ||
-//                     p_rpt->uuid == GATT_UUID_HID_BT_MOUSE_INPUT ||
-//                     (p_rpt->uuid == GATT_UUID_HID_REPORT && p_rpt->rpt_type
-//                     == BTA_HH_RPTT_INPUT))
-//                 {
-//                     p_rpt->client_cfg_value =
-//                     GATT_CLT_CONFIG_NOTIFICATION;
-//                 }
-//             }
-//         }
-//     }
-// }
+        if (p_rpt->uuid == GATT_UUID_HID_BT_KB_INPUT ||
+            p_rpt->uuid == GATT_UUID_HID_BT_MOUSE_INPUT ||
+            (p_rpt->uuid == GATT_UUID_HID_REPORT &&
+             p_rpt->rpt_type == BTA_HH_RPTT_INPUT)) {
+          p_rpt->client_cfg_value = GATT_CLT_CONFIG_NOTIFICATION;
+        }
+      }
+    }
+  }
+}
 
 #endif
diff --git a/bta/hl/bta_hl_main.cc b/bta/hl/bta_hl_main.cc
deleted file mode 100644
index 597c5d3..0000000
--- a/bta/hl/bta_hl_main.cc
+++ /dev/null
@@ -1,1869 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 1998-2012 Broadcom Corporation
- *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at:
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- *
- ******************************************************************************/
-
-/******************************************************************************
- *
- *  This file contains the HeaLth device profile main functions and state
- *  machine.
- *
- ******************************************************************************/
-#include <string.h>
-
-#include "bt_target.h"
-#if (HL_INCLUDED == TRUE)
-
-#include "bt_common.h"
-#include "bta_hl_api.h"
-#include "bta_hl_int.h"
-#include "l2c_api.h"
-#include "mca_defs.h"
-#include "osi/include/osi.h"
-#include "utl.h"
-
-#if (BTA_HL_DEBUG == TRUE)
-static const char* bta_hl_cch_state_code(tBTA_HL_CCH_STATE state_code);
-static const char* bta_hl_dch_state_code(tBTA_HL_DCH_STATE state_code);
-#endif
-
-extern uint16_t L2CA_AllocateRandomPsm(void);
-extern uint16_t L2CA_AllocatePsm(void);
-/*****************************************************************************
- * DCH State Table
- ****************************************************************************/
-/*****************************************************************************
- * Constants and types
- ****************************************************************************/
-/* state machine action enumeration list for DCH */
-/* The order of this enumeration must be the same as bta_hl_dch_act_tbl[] */
-enum {
-  BTA_HL_DCH_MCA_CREATE,
-  BTA_HL_DCH_MCA_CREATE_CFM,
-  BTA_HL_DCH_MCA_CREATE_IND,
-  BTA_HL_DCH_MCA_OPEN_CFM,
-  BTA_HL_DCH_MCA_OPEN_IND,
-  BTA_HL_DCH_MCA_CLOSE,
-  BTA_HL_DCH_MCA_CLOSE_CFM,
-  BTA_HL_DCH_MCA_CLOSE_IND,
-  BTA_HL_DCH_CLOSE_CMPL,
-  BTA_HL_DCH_MCA_RCV_DATA,
-
-  BTA_HL_DCH_SDP_INIT,
-  BTA_HL_DCH_MCA_RECONNECT,
-  BTA_HL_DCH_MCA_RECONNECT_IND,
-  BTA_HL_DCH_MCA_RECONNECT_CFM,
-  BTA_HL_DCH_CLOSE_ECHO_TEST,
-  BTA_HL_DCH_CREATE_RSP,
-  BTA_HL_DCH_MCA_ABORT,
-  BTA_HL_DCH_MCA_ABORT_IND,
-  BTA_HL_DCH_MCA_ABORT_CFM,
-  BTA_HL_DCH_MCA_CONG_CHANGE,
-
-  BTA_HL_DCH_SDP_FAIL,
-  BTA_HL_DCH_SEND_DATA,
-  BTA_HL_DCH_CI_GET_TX_DATA,
-  BTA_HL_DCH_CI_PUT_RX_DATA,
-  BTA_HL_DCH_CI_GET_ECHO_DATA,
-  BTA_HL_DCH_ECHO_TEST,
-  BTA_HL_DCH_CI_PUT_ECHO_DATA,
-  BTA_HL_DCH_IGNORE
-};
-
-typedef void (*tBTA_HL_DCH_ACTION)(uint8_t app_idx, uint8_t mcl_idx,
-                                   uint8_t mdl_idx, tBTA_HL_DATA* p_data);
-
-static const tBTA_HL_DCH_ACTION bta_hl_dch_action[] = {
-    bta_hl_dch_mca_create,        bta_hl_dch_mca_create_cfm,
-    bta_hl_dch_mca_create_ind,    bta_hl_dch_mca_open_cfm,
-    bta_hl_dch_mca_open_ind,      bta_hl_dch_mca_close,
-    bta_hl_dch_mca_close_cfm,     bta_hl_dch_mca_close_ind,
-    bta_hl_dch_close_cmpl,        bta_hl_dch_mca_rcv_data,
-
-    bta_hl_dch_sdp_init,          bta_hl_dch_mca_reconnect,
-    bta_hl_dch_mca_reconnect_ind, bta_hl_dch_mca_reconnect_cfm,
-    bta_hl_dch_close_echo_test,   bta_hl_dch_create_rsp,
-    bta_hl_dch_mca_abort,         bta_hl_dch_mca_abort_ind,
-    bta_hl_dch_mca_abort_cfm,     bta_hl_dch_mca_cong_change,
-
-    bta_hl_dch_sdp_fail,          bta_hl_dch_send_data,
-    bta_hl_dch_ci_get_tx_data,    bta_hl_dch_ci_put_rx_data,
-    bta_hl_dch_ci_get_echo_data,  bta_hl_dch_echo_test,
-    bta_hl_dch_ci_put_echo_data,
-};
-
-/* state table information */
-#define BTA_HL_DCH_ACTIONS 1    /* number of actions */
-#define BTA_HL_DCH_ACTION_COL 0 /* position of action */
-#define BTA_HL_DCH_NEXT_STATE 1 /* position of next state */
-#define BTA_HL_DCH_NUM_COLS 2   /* number of columns in state tables */
-
-/* state table for idle state */
-static const uint8_t bta_hl_dch_st_idle[][BTA_HL_DCH_NUM_COLS] = {
-    /* Event                                Action 1                    Next
-       state */
-    /* BTA_HL_DCH_SDP_INIT_EVT   */ {BTA_HL_DCH_SDP_INIT,
-                                     BTA_HL_DCH_OPENING_ST},
-    /* BTA_HL_DCH_OPEN_EVT       */ {BTA_HL_DCH_MCA_CREATE,
-                                     BTA_HL_DCH_OPENING_ST},
-    /* BTA_HL_MCA_CREATE_IND_EVT */ {BTA_HL_DCH_MCA_CREATE_IND,
-                                     BTA_HL_DCH_OPENING_ST},
-    /* BTA_HL_MCA_CREATE_CFM_EVT */ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_IDLE_ST},
-    /* BTA_HL_MCA_OPEN_IND_EVT   */ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_IDLE_ST},
-
-    /* BTA_HL_MCA_OPEN_CFM_EVT   */ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_IDLE_ST},
-    /* BTA_HL_DCH_CLOSE_EVT      */ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_IDLE_ST},
-    /* BTA_HL_MCA_CLOSE_IND_EVT  */ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_IDLE_ST},
-    /* BTA_HL_MCA_CLOSE_CFM_EVT  */ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_IDLE_ST},
-    /* BTA_HL_API_SEND_DATA_EVT  */ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_IDLE_ST},
-
-    /* BTA_HL_MCA_RCV_DATA_EVT   */ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_IDLE_ST},
-    /* BTA_HL_DCH_CLOSE_CMPL_EVT */ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_IDLE_ST},
-    /* BTA_HL_DCH_RECONNECT_EVT  */ {BTA_HL_DCH_MCA_RECONNECT,
-                                     BTA_HL_DCH_OPENING_ST},
-    /* BTA_HL_DCH_SDP_FAIL_EVT   */ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_IDLE_ST},
-    /* BTA_HL_MCA_RECONNECT_IND_EVT*/ {BTA_HL_DCH_MCA_RECONNECT_IND,
-                                       BTA_HL_DCH_OPENING_ST},
-
-    /* BTA_HL_MCA_RECONNECT_CFM_EVT*/ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_IDLE_ST},
-    /* BTA_HL_DCH_CLOSE_ECHO_TEST_EVT*/ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_IDLE_ST},
-    /* BTA_HL_API_DCH_CREATE_RSP_EVT */ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_IDLE_ST},
-    /* BTA_HL_DCH_ABORT_EVT */ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_IDLE_ST},
-    /* BTA_HL_MCA_ABORT_IND_EVT */ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_IDLE_ST},
-
-    /* BTA_HL_MCA_ABORT_CFM_EVT */ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_IDLE_ST},
-    /* BTA_HL_MCA_CONG_CHG_EVT */ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_IDLE_ST},
-    /* BTA_HL_CI_GET_TX_DATA_EVT  */ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_IDLE_ST},
-    /* BTA_HL_CI_PUT_RX_DATA_EVT  */ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_IDLE_ST},
-    /* BTA_HL_CI_GET_ECHO_DATA_EVT  */ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_IDLE_ST},
-    /* BTA_HL_DCH_ECHO_TEST_EVT  */ {BTA_HL_DCH_ECHO_TEST,
-                                     BTA_HL_DCH_OPENING_ST},
-    /* BTA_HL_CI_PUT_ECHO_DATA_EVT  */ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_IDLE_ST}};
-
-/* state table for opening state */
-static const uint8_t bta_hl_dch_st_opening[][BTA_HL_DCH_NUM_COLS] = {
-    /* Event                                Action 1                    Next
-       state */
-    /* BTA_HL_DCH_SDP_INIT_EVT   */ {BTA_HL_DCH_SDP_INIT,
-                                     BTA_HL_DCH_OPENING_ST},
-    /* BTA_HL_DCH_OPEN_EVT       */ {BTA_HL_DCH_MCA_CREATE,
-                                     BTA_HL_DCH_OPENING_ST},
-    /* BTA_HL_MCA_CREATE_IND_EVT */ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_OPENING_ST},
-    /* BTA_HL_MCA_CREATE_CFM_EVT */ {BTA_HL_DCH_MCA_CREATE_CFM,
-                                     BTA_HL_DCH_OPENING_ST},
-    /* BTA_HL_MCA_OPEN_IND_EVT   */ {BTA_HL_DCH_MCA_OPEN_IND,
-                                     BTA_HL_DCH_OPEN_ST},
-    /* BTA_HL_MCA_OPEN_CFM_EVT   */ {BTA_HL_DCH_MCA_OPEN_CFM,
-                                     BTA_HL_DCH_OPEN_ST},
-    /* BTA_HL_DCH_CLOSE_EVT      */ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_OPENING_ST},
-    /* BTA_HL_MCA_CLOSE_IND_EVT  */ {BTA_HL_DCH_MCA_CLOSE_IND,
-                                     BTA_HL_DCH_CLOSING_ST},
-    /* BTA_HL_MCA_CLOSE_CFM_EVT  */ {BTA_HL_DCH_MCA_CLOSE_CFM,
-                                     BTA_HL_DCH_CLOSING_ST},
-    /* BTA_HL_API_SEND_DATA_EVT  */ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_OPEN_ST},
-
-    /* BTA_HL_MCA_RCV_DATA_EVT   */ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_OPEN_ST},
-    /* BTA_HL_DCH_CLOSE_CMPL_EVT */ {BTA_HL_DCH_CLOSE_CMPL, BTA_HL_DCH_IDLE_ST},
-    /* BTA_HL_DCH_RECONNECT_EVT  */ {BTA_HL_DCH_MCA_RECONNECT,
-                                     BTA_HL_DCH_OPENING_ST},
-    /* BTA_HL_DCH_SDP_FAIL_EVT   */ {BTA_HL_DCH_SDP_FAIL,
-                                     BTA_HL_DCH_CLOSING_ST},
-    /* BTA_HL_MCA_RECONNECT_IND_EVT*/ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_IDLE_ST},
-    /* BTA_HL_MCA_RECONNECT_CFM_EVT*/ {BTA_HL_DCH_MCA_RECONNECT_CFM,
-                                       BTA_HL_DCH_OPENING_ST},
-    /* BTA_HL_DCH_CLOSE_ECHO_TEST_EVT*/ {BTA_HL_DCH_IGNORE,
-                                         BTA_HL_DCH_OPENING_ST},
-    /* BTA_HL_API_DCH_CREATE_RSP_EVT */ {BTA_HL_DCH_CREATE_RSP,
-                                         BTA_HL_DCH_OPENING_ST},
-    /* BTA_HL_DCH_ABORT_EVT */ {BTA_HL_DCH_MCA_ABORT, BTA_HL_DCH_OPENING_ST},
-    /* BTA_HL_MCA_ABORT_IND_EVT */ {BTA_HL_DCH_MCA_ABORT_IND,
-                                    BTA_HL_DCH_OPENING_ST},
-
-    /* BTA_HL_MCA_ABORT_CFM_EVT */ {BTA_HL_DCH_MCA_ABORT_CFM,
-                                    BTA_HL_DCH_OPENING_ST},
-    /* BTA_HL_MCA_CONG_CHG_EVT */ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_OPENING_ST},
-    /* BTA_HL_CI_GET_TX_DATA_EVT  */ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_OPENING_ST},
-    /* BTA_HL_CI_PUT_RX_DATA_EVT  */ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_OPENING_ST},
-    /* BTA_HL_CI_GET_ECHO_DATA_EVT  */ {BTA_HL_DCH_CI_GET_ECHO_DATA,
-                                        BTA_HL_DCH_OPENING_ST},
-    /* BTA_HL_DCH_ECHO_TEST_EVT  */ {BTA_HL_DCH_ECHO_TEST,
-                                     BTA_HL_DCH_OPENING_ST},
-    /* BTA_HL_CI_PUT_ECHO_DATA_EVT  */ {BTA_HL_DCH_IGNORE,
-                                        BTA_HL_DCH_OPENING_ST}};
-
-/* state table for open state */
-static const uint8_t bta_hl_dch_st_open[][BTA_HL_DCH_NUM_COLS] = {
-    /* Event                                Action 1                  Next
-       state */
-    /* BTA_HL_DCH_SDP_INIT_EVT   */ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_OPEN_ST},
-    /* BTA_HL_DCH_OPEN_EVT       */ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_OPEN_ST},
-    /* BTA_HL_MCA_CREATE_IND_EVT */ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_OPEN_ST},
-    /* BTA_HL_MCA_CREATE_CFM_EVT */ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_OPEN_ST},
-    /* BTA_HL_MCA_OPEN_IND_EVT   */ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_OPEN_ST},
-    /* BTA_HL_MCA_OPEN_CFM_EVT   */ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_OPEN_ST},
-    /* BTA_HL_DCH_CLOSE_EVT      */ {BTA_HL_DCH_MCA_CLOSE,
-                                     BTA_HL_DCH_CLOSING_ST},
-    /* BTA_HL_MCA_CLOSE_IND_EVT  */ {BTA_HL_DCH_MCA_CLOSE_IND,
-                                     BTA_HL_DCH_CLOSING_ST},
-    /* BTA_HL_MCA_CLOSE_CFM_EVT  */ {BTA_HL_DCH_MCA_CLOSE_CFM,
-                                     BTA_HL_DCH_CLOSING_ST},
-    /* BTA_HL_API_SEND_DATA_EVT  */ {BTA_HL_DCH_SEND_DATA, BTA_HL_DCH_OPEN_ST},
-
-    /* BTA_HL_MCA_RCV_DATA_EVT   */ {BTA_HL_DCH_MCA_RCV_DATA,
-                                     BTA_HL_DCH_OPEN_ST},
-    /* BTA_HL_DCH_CLOSE_CMPL_EVT */ {BTA_HL_DCH_CLOSE_CMPL, BTA_HL_DCH_IDLE_ST},
-    /* BTA_HL_DCH_RECONNECT_EVT  */ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_OPEN_ST},
-    /* BTA_HL_DCH_SDP_FAIL_EVT   */ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_OPEN_ST},
-    /* BTA_HL_MCA_RECONNECT_IND_EVT*/ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_OPEN_ST},
-    /* BTA_HL_MCA_RECONNECT_CFM_EVT*/ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_OPEN_ST},
-    /* BTA_HL_DCH_CLOSE_ECHO_TEST_EVT*/ {BTA_HL_DCH_CLOSE_ECHO_TEST,
-                                         BTA_HL_DCH_CLOSING_ST},
-    /* BTA_HL_API_DCH_CREATE_RSP_EVT */ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_OPEN_ST},
-    /* BTA_HL_DCH_ABORT_EVT */ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_OPEN_ST},
-    /* BTA_HL_MCA_ABORT_IND_EVT */ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_OPEN_ST},
-
-    /* BTA_HL_DCH_ABORT_CFM_EVT */ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_OPEN_ST},
-    /* BTA_HL_MCA_CONG_CHG_EVT */ {BTA_HL_DCH_MCA_CONG_CHANGE,
-                                   BTA_HL_DCH_OPEN_ST},
-    /* BTA_HL_CI_GET_TX_DATA_EVT  */ {BTA_HL_DCH_CI_GET_TX_DATA,
-                                      BTA_HL_DCH_OPEN_ST},
-    /* BTA_HL_CI_PUT_RX_DATA_EVT  */ {BTA_HL_DCH_CI_PUT_RX_DATA,
-                                      BTA_HL_DCH_OPEN_ST},
-    /* BTA_HL_CI_GET_ECHO_DATA_EVT  */ {BTA_HL_DCH_CI_GET_ECHO_DATA,
-                                        BTA_HL_DCH_OPEN_ST},
-    /* BTA_HL_DCH_ECHO_TEST_EVT  */ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_OPEN_ST},
-    /* BTA_HL_CI_PUT_ECHO_DATA_EVT  */ {BTA_HL_DCH_CI_PUT_ECHO_DATA,
-                                        BTA_HL_DCH_OPEN_ST}};
-
-/* state table for closing state */
-static const uint8_t bta_hl_dch_st_closing[][BTA_HL_DCH_NUM_COLS] = {
-    /* Event                                Action 1 Next state */
-    /* BTA_HL_DCH_SDP_INIT_EVT   */ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_CLOSING_ST},
-    /* BTA_HL_DCH_OPEN_EVT       */ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_CLOSING_ST},
-    /* BTA_HL_MCA_CREATE_IND_EVT */ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_CLOSING_ST},
-    /* BTA_HL_MCA_CREATE_CFM_EVT */ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_CLOSING_ST},
-    /* BTA_HL_MCA_OPEN_IND_EVT   */ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_CLOSING_ST},
-    /* BTA_HL_MCA_OPEN_CFM_EVT   */ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_CLOSING_ST},
-    /* BTA_HL_DCH_CLOSE_EVT      */ {BTA_HL_DCH_MCA_CLOSE,
-                                     BTA_HL_DCH_CLOSING_ST},
-    /* BTA_HL_MCA_CLOSE_IND_EVT  */ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_CLOSING_ST},
-    /* BTA_HL_MCA_CLOSE_CFM_EVT  */ {BTA_HL_DCH_MCA_CLOSE_CFM,
-                                     BTA_HL_DCH_CLOSING_ST},
-    /* BTA_HL_API_SEND_DATA_EVT  */ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_CLOSING_ST},
-
-    /* BTA_HL_MCA_RCV_DATA_EVT   */ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_CLOSING_ST},
-    /* BTA_HL_DCH_CLOSE_CMPL_EVT */ {BTA_HL_DCH_CLOSE_CMPL, BTA_HL_DCH_IDLE_ST},
-    /* BTA_HL_DCH_RECONNECT_EVT  */ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_CLOSING_ST},
-    /* BTA_HL_DCH_SDP_FAIL_EVT   */ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_CLOSING_ST},
-    /* BTA_HL_MCA_RECONNECT_IND_EVT*/ {BTA_HL_DCH_IGNORE,
-                                       BTA_HL_DCH_CLOSING_ST},
-    /* BTA_HL_MCA_RECONNECT_CFM_EVT*/ {BTA_HL_DCH_IGNORE,
-                                       BTA_HL_DCH_CLOSING_ST},
-    /* BTA_HL_DCH_CLOSE_ECHO_TEST_EVT*/ {BTA_HL_DCH_IGNORE,
-                                         BTA_HL_DCH_CLOSING_ST},
-    /* BTA_HL_API_DCH_CREATE_RSP_EVT */ {BTA_HL_DCH_IGNORE,
-                                         BTA_HL_DCH_CLOSING_ST},
-    /* BTA_HL_DCH_ABORT_EVT */ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_CLOSING_ST},
-    /* BTA_HL_MCA_ABORT_IND_EVT */ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_CLOSING_ST},
-
-    /* BTA_HL_DCH_ABORT_CFM_EVT */ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_CLOSING_ST},
-    /* BTA_HL_MCA_CONG_CHG_EVT */ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_CLOSING_ST},
-    /* BTA_HL_CI_GET_TX_DATA_EVT  */ {BTA_HL_DCH_CI_GET_TX_DATA,
-                                      BTA_HL_DCH_CLOSING_ST},
-    /* BTA_HL_CI_PUT_RX_DATA_EVT  */ {BTA_HL_DCH_CI_PUT_RX_DATA,
-                                      BTA_HL_DCH_CLOSING_ST},
-    /* BTA_HL_CI_GET_ECHO_DATA_EVT  */ {BTA_HL_DCH_CI_GET_ECHO_DATA,
-                                        BTA_HL_DCH_CLOSING_ST},
-    /* BTA_HL_DCH_ECHO_TEST_EVT  */ {BTA_HL_DCH_IGNORE, BTA_HL_DCH_CLOSING_ST},
-    /* BTA_HL_CI_PUT_ECHO_DATA_EVT  */ {BTA_HL_DCH_CI_PUT_ECHO_DATA,
-                                        BTA_HL_DCH_CLOSING_ST}};
-
-/* type for state table */
-typedef const uint8_t (*tBTA_HL_DCH_ST_TBL)[BTA_HL_DCH_NUM_COLS];
-
-/* state table */
-const tBTA_HL_DCH_ST_TBL bta_hl_dch_st_tbl[] = {
-    bta_hl_dch_st_idle, bta_hl_dch_st_opening, bta_hl_dch_st_open,
-    bta_hl_dch_st_closing};
-
-/*****************************************************************************
- * CCH State Table
- ****************************************************************************/
-/*****************************************************************************
- * Constants and types
- ****************************************************************************/
-/* state machine action enumeration list for CCH */
-enum {
-  BTA_HL_CCH_SDP_INIT,
-  BTA_HL_CCH_MCA_OPEN,
-  BTA_HL_CCH_MCA_CLOSE,
-  BTA_HL_CCH_CLOSE_CMPL,
-  BTA_HL_CCH_MCA_CONNECT,
-  BTA_HL_CCH_MCA_DISCONNECT,
-  BTA_HL_CCH_MCA_RSP_TOUT,
-  BTA_HL_CCH_MCA_DISC_OPEN,
-  BTA_HL_CCH_IGNORE
-};
-
-/* type for action functions */
-typedef void (*tBTA_HL_CCH_ACTION)(uint8_t app_idx, uint8_t mcl_idx,
-                                   tBTA_HL_DATA* p_data);
-
-/* action function list for MAS */
-const tBTA_HL_CCH_ACTION bta_hl_cch_action[] = {
-    bta_hl_cch_sdp_init,     bta_hl_cch_mca_open,     bta_hl_cch_mca_close,
-    bta_hl_cch_close_cmpl,   bta_hl_cch_mca_connect,  bta_hl_cch_mca_disconnect,
-    bta_hl_cch_mca_rsp_tout, bta_hl_cch_mca_disc_open};
-
-/* state table information */
-#define BTA_HL_CCH_ACTIONS 1    /* number of actions */
-#define BTA_HL_CCH_NEXT_STATE 1 /* position of next state */
-#define BTA_HL_CCH_NUM_COLS 2   /* number of columns in state tables */
-
-/* state table for MAS idle state */
-static const uint8_t bta_hl_cch_st_idle[][BTA_HL_CCH_NUM_COLS] = {
-    /* Event                          Action 1                  Next state */
-    /* BTA_HL_CCH_OPEN_EVT           */ {BTA_HL_CCH_SDP_INIT,
-                                         BTA_HL_CCH_OPENING_ST},
-    /* BTA_HL_CCH_SDP_OK_EVT         */ {BTA_HL_CCH_IGNORE, BTA_HL_CCH_IDLE_ST},
-    /* BTA_HL_CCH_SDP_FAIL_EVT       */ {BTA_HL_CCH_IGNORE, BTA_HL_CCH_IDLE_ST},
-    /* BTA_HL_MCA_CONNECT_IND_EVT    */ {BTA_HL_CCH_MCA_CONNECT,
-                                         BTA_HL_CCH_OPEN_ST},
-    /* BTA_HL_MCA_DISCONNECT_IND_EVT */ {BTA_HL_CCH_IGNORE, BTA_HL_CCH_IDLE_ST},
-    /* BTA_HL_CCH_CLOSE_EVT          */ {BTA_HL_CCH_IGNORE, BTA_HL_CCH_IDLE_ST},
-    /* BTA_HL_CCH_CLOSE_CMPL_EVT     */ {BTA_HL_CCH_IGNORE, BTA_HL_CCH_IDLE_ST},
-    /* BTA_HL_CCH_CLOSE_CMPL_EVT     */ {BTA_HL_CCH_IGNORE,
-                                         BTA_HL_CCH_IDLE_ST}};
-
-/* state table for obex/rfcomm connection state */
-static const uint8_t bta_hl_cch_st_opening[][BTA_HL_CCH_NUM_COLS] = {
-    /* Event                          Action 1               Next state */
-    /* BTA_HL_CCH_OPEN_EVT           */ {BTA_HL_CCH_IGNORE,
-                                         BTA_HL_CCH_OPENING_ST},
-    /* BTA_HL_CCH_SDP_OK_EVT         */ {BTA_HL_CCH_MCA_OPEN,
-                                         BTA_HL_CCH_OPENING_ST},
-    /* BTA_HL_CCH_SDP_FAIL_EVT       */ {BTA_HL_CCH_CLOSE_CMPL,
-                                         BTA_HL_CCH_IDLE_ST},
-    /* BTA_HL_MCA_CONNECT_IND_EVT    */ {BTA_HL_CCH_MCA_CONNECT,
-                                         BTA_HL_CCH_OPEN_ST},
-    /* BTA_HL_MCA_DISCONNECT_IND_EVT */ {BTA_HL_CCH_MCA_DISCONNECT,
-                                         BTA_HL_CCH_CLOSING_ST},
-    /* BTA_HL_CCH_CLOSE_EVT          */ {BTA_HL_CCH_MCA_CLOSE,
-                                         BTA_HL_CCH_CLOSING_ST},
-    /* BTA_HL_CCH_CLOSE_CMPL_EVT     */ {BTA_HL_CCH_CLOSE_CMPL,
-                                         BTA_HL_CCH_IDLE_ST},
-    /* BTA_HL_MCA_RSP_TOUT_IND_EVT   */ {BTA_HL_CCH_MCA_RSP_TOUT,
-                                         BTA_HL_CCH_CLOSING_ST}};
-
-/* state table for open state */
-static const uint8_t bta_hl_cch_st_open[][BTA_HL_CCH_NUM_COLS] = {
-    /* Event                          Action 1                  Next state */
-    /* BTA_HL_CCH_OPEN_EVT           */ {BTA_HL_CCH_IGNORE, BTA_HL_CCH_OPEN_ST},
-    /* BTA_HL_CCH_SDP_OK_EVT         */ {BTA_HL_CCH_IGNORE, BTA_HL_CCH_OPEN_ST},
-    /* BTA_HL_CCH_SDP_FAIL_EVT       */ {BTA_HL_CCH_IGNORE, BTA_HL_CCH_OPEN_ST},
-    /* BTA_HL_MCA_CONNECT_IND_EVT    */ {BTA_HL_CCH_IGNORE, BTA_HL_CCH_OPEN_ST},
-    /* BTA_HL_MCA_DISCONNECT_IND_EVT */ {BTA_HL_CCH_MCA_DISCONNECT,
-                                         BTA_HL_CCH_CLOSING_ST},
-    /* BTA_HL_CCH_CLOSE_EVT          */ {BTA_HL_CCH_MCA_CLOSE,
-                                         BTA_HL_CCH_CLOSING_ST},
-    /* BTA_HL_CCH_CLOSE_CMPL_EVT     */ {BTA_HL_CCH_IGNORE, BTA_HL_CCH_OPEN_ST},
-    /* BTA_HL_MCA_RSP_TOUT_IND_EVT   */ {BTA_HL_CCH_MCA_RSP_TOUT,
-                                         BTA_HL_CCH_CLOSING_ST}};
-
-/* state table for closing state */
-static const uint8_t bta_hl_cch_st_closing[][BTA_HL_CCH_NUM_COLS] = {
-    /* Event                          Action 1                  Next state */
-    /* BTA_HL_CCH_OPEN_EVT           */ {BTA_HL_CCH_IGNORE,
-                                         BTA_HL_CCH_CLOSING_ST},
-    /* BTA_HL_CCH_SDP_OK_EVT         */ {BTA_HL_CCH_CLOSE_CMPL,
-                                         BTA_HL_CCH_IDLE_ST},
-    /* BTA_HL_CCH_SDP_FAIL_EVT       */ {BTA_HL_CCH_CLOSE_CMPL,
-                                         BTA_HL_CCH_IDLE_ST},
-    /* BTA_HL_MCA_CONNECT_IND_EVT    */ {BTA_HL_CCH_MCA_DISC_OPEN,
-                                         BTA_HL_CCH_CLOSING_ST},
-    /* BTA_HL_MCA_DISCONNECT_IND_EVT */ {BTA_HL_CCH_MCA_DISCONNECT,
-                                         BTA_HL_CCH_CLOSING_ST},
-    /* BTA_HL_CCH_CLOSE_EVT          */ {BTA_HL_CCH_MCA_CLOSE,
-                                         BTA_HL_CCH_CLOSING_ST},
-    /* BTA_HL_CCH_CLOSE_CMPL_EVT     */ {BTA_HL_CCH_CLOSE_CMPL,
-                                         BTA_HL_CCH_IDLE_ST},
-    /* BTA_HL_MCA_RSP_TOUT_IND_EVT   */ {BTA_HL_CCH_IGNORE,
-                                         BTA_HL_CCH_CLOSING_ST}};
-
-/* type for state table CCH */
-typedef const uint8_t (*tBTA_HL_CCH_ST_TBL)[BTA_HL_CCH_NUM_COLS];
-
-/* MAS state table */
-const tBTA_HL_CCH_ST_TBL bta_hl_cch_st_tbl[] = {
-    bta_hl_cch_st_idle, bta_hl_cch_st_opening, bta_hl_cch_st_open,
-    bta_hl_cch_st_closing};
-
-/*****************************************************************************
- * Global data
- ****************************************************************************/
-
-/* HL control block */
-tBTA_HL_CB bta_hl_cb;
-
-/*******************************************************************************
- *
- * Function         bta_hl_cch_sm_execute
- *
- * Description      State machine event handling function for CCH
- *
- * Returns          void
- *
- ******************************************************************************/
-void bta_hl_cch_sm_execute(uint8_t app_idx, uint8_t mcl_idx, uint16_t event,
-                           tBTA_HL_DATA* p_data) {
-  tBTA_HL_CCH_ST_TBL state_table;
-  uint8_t action;
-  int i;
-  tBTA_HL_MCL_CB* p_cb = BTA_HL_GET_MCL_CB_PTR(app_idx, mcl_idx);
-
-#if (BTA_HL_DEBUG == TRUE)
-  tBTA_HL_CCH_STATE in_state = p_cb->cch_state;
-  uint16_t cur_evt = event;
-  APPL_TRACE_DEBUG("HDP CCH Event Handler: State 0x%02x [%s], Event [%s]",
-                   in_state, bta_hl_cch_state_code(in_state),
-                   bta_hl_evt_code(cur_evt));
-#endif
-
-  /* look up the state table for the current state */
-  state_table = bta_hl_cch_st_tbl[p_cb->cch_state];
-
-  event &= 0x00FF;
-
-  /* set next state */
-  p_cb->cch_state = state_table[event][BTA_HL_CCH_NEXT_STATE];
-
-  for (i = 0; i < BTA_HL_CCH_ACTIONS; i++) {
-    action = state_table[event][i];
-    if (action != BTA_HL_CCH_IGNORE) {
-      (*bta_hl_cch_action[action])(app_idx, mcl_idx, p_data);
-    } else {
-      /* discard HDP data */
-      bta_hl_discard_data(p_data->hdr.event, p_data);
-      break;
-    }
-  }
-#if (BTA_HL_DEBUG == TRUE)
-  if (in_state != p_cb->cch_state) {
-    APPL_TRACE_DEBUG("HL CCH State Change: [%s] -> [%s] after [%s]",
-                     bta_hl_cch_state_code(in_state),
-                     bta_hl_cch_state_code(p_cb->cch_state),
-                     bta_hl_evt_code(cur_evt));
-  }
-#endif
-}
-
-/*******************************************************************************
- *
- * Function         bta_hl_dch_sm_execute
- *
- * Description      State machine event handling function for DCH
- *
- * Returns          void
- *
- ******************************************************************************/
-void bta_hl_dch_sm_execute(uint8_t app_idx, uint8_t mcl_idx, uint8_t mdl_idx,
-                           uint16_t event, tBTA_HL_DATA* p_data) {
-  tBTA_HL_DCH_ST_TBL state_table;
-  uint8_t action;
-  int i;
-  tBTA_HL_MDL_CB* p_cb = BTA_HL_GET_MDL_CB_PTR(app_idx, mcl_idx, mdl_idx);
-
-#if (BTA_HL_DEBUG == TRUE)
-  tBTA_HL_DCH_STATE in_state = p_cb->dch_state;
-  uint16_t cur_evt = event;
-  APPL_TRACE_DEBUG("HDP DCH Event Handler: State 0x%02x [%s], Event [%s]",
-                   in_state, bta_hl_dch_state_code(in_state),
-                   bta_hl_evt_code(cur_evt));
-#endif
-
-  /* look up the state table for the current state */
-  state_table = bta_hl_dch_st_tbl[p_cb->dch_state];
-  event -= BTA_HL_DCH_EVT_MIN;
-
-  /* set next state */
-  p_cb->dch_state = state_table[event][BTA_HL_DCH_NEXT_STATE];
-
-  for (i = 0; i < BTA_HL_DCH_ACTIONS; i++) {
-    action = state_table[event][i];
-    if (action != BTA_HL_DCH_IGNORE) {
-      (*bta_hl_dch_action[action])(app_idx, mcl_idx, mdl_idx, p_data);
-    } else {
-      /* discard mas data */
-      bta_hl_discard_data(p_data->hdr.event, p_data);
-      break;
-    }
-  }
-
-#if (BTA_HL_DEBUG == TRUE)
-  if (in_state != p_cb->dch_state) {
-    APPL_TRACE_DEBUG("HL DCH State Change: [%s] -> [%s] after [%s]",
-                     bta_hl_dch_state_code(in_state),
-                     bta_hl_dch_state_code(p_cb->dch_state),
-                     bta_hl_evt_code(cur_evt));
-  }
-#endif
-}
-/*******************************************************************************
- *
- * Function         bta_hl_api_enable
- *
- * Description      Process the API enable request to enable the HL subsystem
- *
- * Returns          void
- *
- ******************************************************************************/
-static void bta_hl_api_enable(tBTA_HL_CB* p_cb, tBTA_HL_DATA* p_data) {
-  tBTA_HL_CTRL evt_data;
-
-  /* If already enabled then reject this request */
-  if (p_cb->enable) {
-    APPL_TRACE_ERROR("HL is already enabled");
-    evt_data.enable_cfm.status = BTA_HL_STATUS_FAIL;
-    if (p_data->api_enable.p_cback)
-      p_data->api_enable.p_cback(BTA_HL_CTRL_ENABLE_CFM_EVT,
-                                 (tBTA_HL_CTRL*)&evt_data);
-    return;
-  }
-
-  /* Done with checking. now perform the enable oepration*/
-  /* initialize control block */
-  memset(p_cb, 0, sizeof(tBTA_HL_CB));
-  p_cb->enable = true;
-  p_cb->p_ctrl_cback = p_data->api_enable.p_cback;
-  evt_data.enable_cfm.status = BTA_HL_STATUS_OK;
-  if (p_data->api_enable.p_cback)
-    p_data->api_enable.p_cback(BTA_HL_CTRL_ENABLE_CFM_EVT,
-                               (tBTA_HL_CTRL*)&evt_data);
-}
-/*******************************************************************************
- *
- * Function         bta_hl_api_disable
- *
- * Description      Process the API disable request to disable the HL subsystem
- *
- * Returns          void
- *
- ******************************************************************************/
-static void bta_hl_api_disable(tBTA_HL_CB* p_cb, tBTA_HL_DATA* p_data) {
-  tBTA_HL_CTRL evt_data;
-  tBTA_HL_STATUS status = BTA_HL_STATUS_OK;
-
-  if (p_cb->enable) {
-    p_cb->disabling = true;
-    bta_hl_check_disable(p_data);
-  } else {
-    status = BTA_HL_STATUS_FAIL;
-    evt_data.disable_cfm.status = status;
-    if (p_cb->p_ctrl_cback)
-      p_cb->p_ctrl_cback(BTA_HL_CTRL_DISABLE_CFM_EVT, (tBTA_HL_CTRL*)&evt_data);
-  }
-
-#if (BTA_HL_DEBUG == TRUE)
-  if (status != BTA_HL_STATUS_OK) {
-    APPL_TRACE_DEBUG("bta_hl_api_disable status =%s",
-                     bta_hl_status_code(status));
-  }
-#endif
-}
-
-/*******************************************************************************
- *
- * Function         bta_hl_api_update
- *
- * Description      Process the API registration request to register an HDP
- *                  application
- *
- * Returns          void
- *
- ******************************************************************************/
-static void bta_hl_api_update(tBTA_HL_CB* p_cb, tBTA_HL_DATA* p_data) {
-  tBTA_HL evt_data;
-  tBTA_HL_APP_CB* p_acb = BTA_HL_GET_APP_CB_PTR(0);
-  tBTA_HL_STATUS status = BTA_HL_STATUS_FAIL;
-
-  APPL_TRACE_DEBUG("bta_hl_api_update");
-  if (p_cb->enable) {
-    status = bta_hl_app_update(p_data->api_update.app_id,
-                               p_data->api_update.is_register);
-    if (!p_data->api_update.is_register) {
-      APPL_TRACE_DEBUG("Deregister");
-      memset(&evt_data, 0, sizeof(tBTA_HL));
-      evt_data.dereg_cfm.status = status;
-      evt_data.dereg_cfm.app_id = p_data->api_update.app_id;
-      if (status == BTA_HL_STATUS_OK)
-        evt_data.dereg_cfm.app_handle = p_acb->app_handle;
-      if (p_acb->p_cback) {
-        p_acb->p_cback(BTA_HL_DEREGISTER_CFM_EVT, (tBTA_HL*)&evt_data);
-      }
-      return;
-    }
-  }
-
-  if (status != BTA_HL_STATUS_OK) {
-    if ((status != BTA_HL_STATUS_DUPLICATE_APP_ID) &&
-        (status != BTA_HL_STATUS_NO_RESOURCE)) {
-      if (p_acb) memset(p_acb, 0, sizeof(tBTA_HL_APP_CB));
-    }
-  }
-#if (BTA_HL_DEBUG == TRUE)
-  if (status != BTA_HL_STATUS_OK) {
-    APPL_TRACE_DEBUG("bta_hl_api_register status =%s",
-                     bta_hl_status_code(status));
-  }
-#endif
-
-  memset(&evt_data, 0, sizeof(tBTA_HL));
-  evt_data.reg_cfm.status = status;
-  evt_data.reg_cfm.app_id = p_data->api_update.app_id;
-  if (status == BTA_HL_STATUS_OK)
-    evt_data.reg_cfm.app_handle = p_acb->app_handle;
-  if (p_data->api_reg.p_cback) {
-    p_data->api_reg.p_cback(BTA_HL_REGISTER_CFM_EVT, (tBTA_HL*)&evt_data);
-  }
-
-  if (status == BTA_HL_STATUS_OK) {
-    evt_data.sdp_info_ind.app_handle = p_acb->app_handle;
-    evt_data.sdp_info_ind.ctrl_psm = p_acb->ctrl_psm;
-    evt_data.sdp_info_ind.data_psm = p_acb->data_psm;
-    evt_data.sdp_info_ind.data_x_spec = BTA_HL_SDP_IEEE_11073_20601;
-    evt_data.sdp_info_ind.mcap_sup_procs = BTA_HL_MCAP_SUP_PROC_MASK;
-
-    if (p_data->api_reg.p_cback) {
-      p_data->api_reg.p_cback(BTA_HL_SDP_INFO_IND_EVT, (tBTA_HL*)&evt_data);
-    }
-  }
-}
-
-/*******************************************************************************
- *
- * Function         bta_hl_api_register
- *
- * Description      Process the API registration request to register an HDP
- *                  application
- *
- * Returns          void
- *
- ******************************************************************************/
-static void bta_hl_api_register(tBTA_HL_CB* p_cb, tBTA_HL_DATA* p_data) {
-  tBTA_HL evt_data;
-  uint8_t app_idx;
-  tBTA_HL_APP_CB* p_acb = NULL;
-  tBTA_HL_STATUS status = BTA_HL_STATUS_FAIL;
-
-  if (p_cb->enable) {
-    if (!bta_hl_is_a_duplicate_id(p_data->api_reg.app_id)) {
-      if (bta_hl_find_avail_app_idx(&app_idx)) {
-        p_acb = BTA_HL_GET_APP_CB_PTR(app_idx);
-        p_acb->in_use = true;
-        p_acb->app_id = p_data->api_reg.app_id;
-        p_acb->p_cback = p_data->api_reg.p_cback;
-        p_acb->sec_mask = p_data->api_reg.sec_mask;
-        p_acb->dev_type = p_data->api_reg.dev_type;
-        strlcpy(p_acb->srv_name, p_data->api_reg.srv_name,
-                sizeof(p_acb->srv_name));
-        strlcpy(p_acb->srv_desp, p_data->api_reg.srv_desp,
-                sizeof(p_acb->srv_desp));
-        strlcpy(p_acb->provider_name, p_data->api_reg.provider_name,
-                sizeof(p_acb->provider_name));
-        bta_hl_cb.p_alloc_psm = L2CA_AllocatePSM;
-        p_acb->ctrl_psm = bta_hl_cb.p_alloc_psm();
-        p_acb->data_psm = bta_hl_cb.p_alloc_psm();
-        p_acb->p_mcap_cback = bta_hl_mcap_ctrl_cback;
-        status = bta_hl_app_registration(app_idx);
-      } else {
-        status = BTA_HL_STATUS_NO_RESOURCE;
-      }
-    } else {
-      status = BTA_HL_STATUS_DUPLICATE_APP_ID;
-    }
-  }
-
-  if (status != BTA_HL_STATUS_OK) {
-    if ((status != BTA_HL_STATUS_DUPLICATE_APP_ID) &&
-        (status != BTA_HL_STATUS_NO_RESOURCE)) {
-      if (p_acb) memset(p_acb, 0, sizeof(tBTA_HL_APP_CB));
-    }
-  }
-#if (BTA_HL_DEBUG == TRUE)
-  if (status != BTA_HL_STATUS_OK) {
-    APPL_TRACE_DEBUG("bta_hl_api_register status =%s",
-                     bta_hl_status_code(status));
-  }
-#endif
-
-  memset(&evt_data, 0, sizeof(tBTA_HL));
-  evt_data.reg_cfm.status = status;
-  evt_data.reg_cfm.app_id = p_data->api_reg.app_id;
-  if (status == BTA_HL_STATUS_OK)
-    evt_data.reg_cfm.app_handle = p_acb->app_handle;
-  if (p_data->api_reg.p_cback) {
-    p_data->api_reg.p_cback(BTA_HL_REGISTER_CFM_EVT, (tBTA_HL*)&evt_data);
-  }
-
-  if (status == BTA_HL_STATUS_OK) {
-    evt_data.sdp_info_ind.app_handle = p_acb->app_handle;
-    evt_data.sdp_info_ind.ctrl_psm = p_acb->ctrl_psm;
-    evt_data.sdp_info_ind.data_psm = p_acb->data_psm;
-    evt_data.sdp_info_ind.data_x_spec = BTA_HL_SDP_IEEE_11073_20601;
-    evt_data.sdp_info_ind.mcap_sup_procs = BTA_HL_MCAP_SUP_PROC_MASK;
-
-    if (p_data->api_reg.p_cback) {
-      p_data->api_reg.p_cback(BTA_HL_SDP_INFO_IND_EVT, (tBTA_HL*)&evt_data);
-    }
-  }
-}
-
-/*******************************************************************************
- *
- * Function         bta_hl_api_deregister
- *
- * Description      Process the API de-registration request
- *
- * Returns          void
- *
- ******************************************************************************/
-static void bta_hl_api_deregister(UNUSED_ATTR tBTA_HL_CB* p_cb,
-                                  tBTA_HL_DATA* p_data) {
-  uint8_t app_idx;
-  tBTA_HL_APP_CB* p_acb;
-
-  if (bta_hl_find_app_idx_using_handle(p_data->api_dereg.app_handle,
-                                       &app_idx)) {
-    p_acb = BTA_HL_GET_APP_CB_PTR(app_idx);
-    p_acb->deregistering = true;
-    bta_hl_check_deregistration(app_idx, p_data);
-  } else {
-    APPL_TRACE_ERROR("Invalid app_handle=%d", p_data->api_dereg.app_handle);
-  }
-}
-
-/*******************************************************************************
- *
- * Function         bta_hl_api_cch_open
- *
- * Description      Process the API CCH open request
- *
- * Returns          void
- *
- ******************************************************************************/
-static void bta_hl_api_cch_open(UNUSED_ATTR tBTA_HL_CB* p_cb,
-                                tBTA_HL_DATA* p_data) {
-  tBTA_HL evt_data;
-  tBTA_HL_STATUS status = BTA_HL_STATUS_OK;
-  uint8_t app_idx, mcl_idx;
-  tBTA_HL_APP_CB* p_acb;
-  tBTA_HL_MCL_CB* p_mcb;
-
-  if (bta_hl_find_app_idx_using_handle(p_data->api_cch_open.app_handle,
-                                       &app_idx)) {
-    if (!bta_hl_find_mcl_idx(app_idx, p_data->api_cch_open.bd_addr, &mcl_idx)) {
-      if (bta_hl_find_avail_mcl_idx(app_idx, &mcl_idx)) {
-        p_mcb = BTA_HL_GET_MCL_CB_PTR(app_idx, mcl_idx);
-        p_mcb->in_use = true;
-        p_mcb->req_ctrl_psm = p_data->api_cch_open.ctrl_psm;
-        p_mcb->sec_mask = p_data->api_cch_open.sec_mask;
-        p_mcb->bd_addr = p_data->api_cch_open.bd_addr;
-        p_mcb->cch_oper = BTA_HL_CCH_OP_LOCAL_OPEN;
-      } else {
-        status = BTA_HL_STATUS_NO_RESOURCE;
-      }
-    } else {
-      /* Only one MCL per BD_ADDR */
-      status = BTA_HL_STATUS_DUPLICATE_CCH_OPEN;
-      APPL_TRACE_DEBUG("bta_hl_api_cch_open: CCH already open: status =%d",
-                       status)
-      p_acb = BTA_HL_GET_APP_CB_PTR(app_idx);
-      p_mcb = BTA_HL_GET_MCL_CB_PTR(app_idx, mcl_idx);
-      if (p_acb->p_cback) {
-        bta_hl_build_cch_open_cfm(&evt_data, p_data->api_cch_open.app_id,
-                                  p_data->api_cch_open.app_handle,
-                                  p_mcb->mcl_handle,
-                                  p_data->api_cch_open.bd_addr, status);
-        p_acb->p_cback(BTA_HL_CCH_OPEN_CFM_EVT, (tBTA_HL*)&evt_data);
-      } else {
-        APPL_TRACE_ERROR("bta_hl_api_cch_open Null Callback");
-      }
-      return;
-    }
-  } else {
-    status = BTA_HL_STATUS_INVALID_APP_HANDLE;
-  }
-#if (BTA_HL_DEBUG == TRUE)
-  if (status != BTA_HL_STATUS_OK) {
-    APPL_TRACE_DEBUG("bta_hl_api_cch_open status =%s",
-                     bta_hl_status_code(status));
-  }
-#endif
-  switch (status) {
-    case BTA_HL_STATUS_OK:
-      bta_hl_cch_sm_execute(app_idx, mcl_idx, BTA_HL_CCH_OPEN_EVT, p_data);
-      break;
-    case BTA_HL_STATUS_NO_RESOURCE:
-    case BTA_HL_STATUS_FAIL:
-
-      p_acb = BTA_HL_GET_APP_CB_PTR(app_idx);
-      if (p_acb->p_cback) {
-        bta_hl_build_cch_open_cfm(&evt_data, p_data->api_cch_open.app_id,
-                                  p_data->api_cch_open.app_handle, 0,
-                                  p_data->api_cch_open.bd_addr, status);
-        p_acb->p_cback(BTA_HL_CCH_OPEN_CFM_EVT, (tBTA_HL*)&evt_data);
-      } else {
-        APPL_TRACE_ERROR("bta_hl_api_cch_open Null Callback");
-      }
-      break;
-    default:
-      APPL_TRACE_ERROR("status code=%d", status);
-      break;
-  }
-}
-
-/*******************************************************************************
- *
- * Function         bta_hl_api_cch_close
- *
- * Description      Process the API CCH close request
- *
- * Returns          void
- *
- ******************************************************************************/
-static void bta_hl_api_cch_close(UNUSED_ATTR tBTA_HL_CB* p_cb,
-                                 tBTA_HL_DATA* p_data) {
-  tBTA_HL evt_data;
-  tBTA_HL_STATUS status = BTA_HL_STATUS_OK;
-  uint8_t app_idx, mcl_idx;
-  tBTA_HL_APP_CB* p_acb;
-  tBTA_HL_MCL_CB* p_mcb;
-
-  if (bta_hl_find_mcl_idx_using_handle(p_data->api_cch_close.mcl_handle,
-                                       &app_idx, &mcl_idx)) {
-    p_mcb = BTA_HL_GET_MCL_CB_PTR(app_idx, mcl_idx);
-    p_mcb->cch_oper = BTA_HL_CCH_OP_LOCAL_CLOSE;
-  } else {
-    status = BTA_HL_STATUS_INVALID_MCL_HANDLE;
-  }
-#if (BTA_HL_DEBUG == TRUE)
-  if (status != BTA_HL_STATUS_OK) {
-    APPL_TRACE_DEBUG("bta_hl_api_cch_close status =%s",
-                     bta_hl_status_code(status));
-  }
-#endif
-  switch (status) {
-    case BTA_HL_STATUS_OK:
-      bta_hl_check_cch_close(app_idx, mcl_idx, p_data, true);
-      break;
-
-    case BTA_HL_STATUS_INVALID_MCL_HANDLE:
-      p_acb = BTA_HL_GET_APP_CB_PTR(app_idx);
-      if (p_acb->p_cback) {
-        bta_hl_build_cch_close_cfm(&evt_data, p_acb->app_handle,
-                                   p_data->api_cch_close.mcl_handle, status);
-        p_acb->p_cback(BTA_HL_CCH_CLOSE_CFM_EVT, (tBTA_HL*)&evt_data);
-      } else {
-        APPL_TRACE_ERROR("bta_hl_api_cch_close Null Callback");
-      }
-      break;
-    default:
-      APPL_TRACE_ERROR("status code=%d", status);
-      break;
-  }
-}
-
-/*******************************************************************************
- *
- * Function         bta_hl_api_dch_open
- *
- * Description      Process the API DCH open request
- *
- * Returns          void
- *
- ******************************************************************************/
-static void bta_hl_api_dch_open(UNUSED_ATTR tBTA_HL_CB* p_cb,
-                                tBTA_HL_DATA* p_data) {
-  tBTA_HL evt_data;
-  tBTA_HL_STATUS status = BTA_HL_STATUS_OK;
-  uint8_t app_idx, mcl_idx, mdl_idx;
-  tBTA_HL_APP_CB* p_acb;
-  tBTA_HL_MCL_CB* p_mcb = NULL;
-  tBTA_HL_MDL_CB* p_dcb;
-  tBTA_HL_MDEP_CFG* p_mdep_cfg;
-  uint8_t mdep_cfg_idx;
-
-  if (bta_hl_find_mcl_idx_using_handle(p_data->api_dch_open.mcl_handle,
-                                       &app_idx, &mcl_idx)) {
-    p_acb = BTA_HL_GET_APP_CB_PTR(app_idx);
-    p_mcb = BTA_HL_GET_MCL_CB_PTR(app_idx, mcl_idx);
-
-    APPL_TRACE_DEBUG(
-        "bta_hl_api_dch_open: app_ix=%d, mcl_idx=%d, cch_state=%d, "
-        "mcl_handle=%d",
-        app_idx, mcl_idx, p_mcb->cch_state, p_data->api_dch_open.mcl_handle);
-    if (p_mcb->cch_state == BTA_HL_CCH_OPEN_ST) {
-      if (bta_hl_find_avail_mdl_idx(app_idx, mcl_idx, &mdl_idx)) {
-        p_dcb = BTA_HL_GET_MDL_CB_PTR(app_idx, mcl_idx, mdl_idx);
-
-        if (bta_hl_find_mdep_cfg_idx(
-                app_idx, p_data->api_dch_open.local_mdep_id, &mdep_cfg_idx)) {
-          if (mdep_cfg_idx &&
-              (p_acb->sup_feature.mdep[mdep_cfg_idx].mdep_cfg.mdep_role ==
-               BTA_HL_MDEP_ROLE_SINK)) {
-            p_data->api_dch_open.local_cfg = BTA_HL_DCH_CFG_NO_PREF;
-          }
-
-          status = bta_hl_chk_local_cfg(app_idx, mcl_idx, mdep_cfg_idx,
-                                        p_data->api_dch_open.local_cfg);
-          if (status == BTA_HL_STATUS_OK) {
-            if (p_data->api_dch_open.local_mdep_id !=
-                BTA_HL_ECHO_TEST_MDEP_ID) {
-              if (bta_hl_set_ctrl_psm_for_dch(app_idx, mcl_idx, mdl_idx,
-                                              p_data->api_dch_open.ctrl_psm)) {
-                p_mdep_cfg = BTA_HL_GET_MDEP_CFG_PTR(app_idx, mdep_cfg_idx);
-                p_dcb->in_use = true;
-                p_dcb->dch_oper = BTA_HL_DCH_OP_LOCAL_OPEN;
-                p_dcb->sec_mask = p_data->api_dch_open.sec_mask;
-                p_dcb->local_mdep_id = p_data->api_dch_open.local_mdep_id;
-                p_dcb->peer_mdep_id = p_data->api_dch_open.peer_mdep_id;
-
-                if (p_mdep_cfg->mdep_role == BTA_HL_MDEP_ROLE_SINK) {
-                  p_dcb->peer_mdep_role = BTA_HL_MDEP_ROLE_SOURCE;
-                } else {
-                  p_dcb->peer_mdep_role = BTA_HL_MDEP_ROLE_SINK;
-                }
-
-                p_dcb->local_mdep_cfg_idx = mdep_cfg_idx;
-                p_dcb->local_cfg = p_data->api_dch_open.local_cfg;
-
-                bta_hl_find_rxtx_apdu_size(app_idx, mdep_cfg_idx,
-                                           &p_dcb->max_rx_apdu_size,
-                                           &p_dcb->max_tx_apdu_size);
-                p_dcb->mdl_id =
-                    bta_hl_allocate_mdl_id(app_idx, mcl_idx, mdl_idx);
-                p_dcb->mdl_cfg_idx_included = false;
-              } else {
-                status = BTA_HL_STATUS_INVALID_CTRL_PSM;
-              }
-
-            } else {
-              status = BTA_HL_STATUS_INVALID_LOCAL_MDEP_ID;
-            }
-          }
-        } else {
-          status = BTA_HL_STATUS_INVALID_LOCAL_MDEP_ID;
-        }
-      } else {
-        status = BTA_HL_STATUS_NO_RESOURCE;
-      }
-    } else {
-      status = BTA_HL_STATUS_NO_CCH;
-    }
-  } else {
-    status = BTA_HL_STATUS_INVALID_MCL_HANDLE;
-  }
-
-#if (BTA_HL_DEBUG == TRUE)
-  if (status != BTA_HL_STATUS_OK) {
-    APPL_TRACE_DEBUG("bta_hl_api_dch_open status =%s",
-                     bta_hl_status_code(status));
-  }
-#endif
-  switch (status) {
-    case BTA_HL_STATUS_OK:
-      if (p_mcb->sdp.num_recs) {
-        bta_hl_dch_sm_execute(app_idx, mcl_idx, mdl_idx, BTA_HL_DCH_OPEN_EVT,
-                              p_data);
-      } else {
-        bta_hl_dch_sm_execute(app_idx, mcl_idx, mdl_idx,
-                              BTA_HL_DCH_SDP_INIT_EVT, p_data);
-      }
-      break;
-    case BTA_HL_STATUS_INVALID_DCH_CFG:
-    case BTA_HL_STATUS_NO_FIRST_RELIABLE:
-    case BTA_HL_STATUS_NO_CCH:
-    case BTA_HL_STATUS_NO_RESOURCE:
-    case BTA_HL_STATUS_FAIL:
-    case BTA_HL_STATUS_INVALID_LOCAL_MDEP_ID:
-    case BTA_HL_STATUS_INVALID_CTRL_PSM:
-      p_acb = BTA_HL_GET_APP_CB_PTR(app_idx);
-      if (p_acb->p_cback) {
-        bta_hl_build_dch_open_cfm(
-            &evt_data, p_acb->app_handle, p_data->api_dch_open.mcl_handle,
-            BTA_HL_INVALID_MDL_HANDLE, 0, 0, 0, 0, 0, status);
-        p_acb->p_cback(BTA_HL_DCH_OPEN_CFM_EVT, (tBTA_HL*)&evt_data);
-      } else {
-        APPL_TRACE_ERROR("bta_hl_api_dch_open Null Callback");
-      }
-
-      break;
-    default:
-      APPL_TRACE_ERROR("Status code=%d", status);
-      break;
-  }
-}
-/*******************************************************************************
- *
- * Function         bta_hl_api_dch_close
- *
- * Description      Process the API DCH close request
- *
- * Returns          void
- *
- ******************************************************************************/
-static void bta_hl_api_dch_close(UNUSED_ATTR tBTA_HL_CB* p_cb,
-                                 tBTA_HL_DATA* p_data) {
-  tBTA_HL evt_data;
-  tBTA_HL_STATUS status = BTA_HL_STATUS_OK;
-  uint8_t app_idx, mcl_idx, mdl_idx;
-  tBTA_HL_APP_CB* p_acb;
-  tBTA_HL_MCL_CB* p_mcb;
-  tBTA_HL_MDL_CB* p_dcb;
-
-  if (bta_hl_find_mdl_idx_using_handle(p_data->api_dch_close.mdl_handle,
-                                       &app_idx, &mcl_idx, &mdl_idx)) {
-    p_dcb = BTA_HL_GET_MDL_CB_PTR(app_idx, mcl_idx, mdl_idx);
-    if (p_dcb->dch_state != BTA_HL_DCH_OPEN_ST) {
-      status = BTA_HL_STATUS_FAIL;
-    }
-  } else {
-    status = BTA_HL_STATUS_INVALID_MDL_HANDLE;
-  }
-
-#if (BTA_HL_DEBUG == TRUE)
-  if (status != BTA_HL_STATUS_OK) {
-    APPL_TRACE_DEBUG("bta_hl_api_dch_close status =%s",
-                     bta_hl_status_code(status));
-  }
-#endif
-
-  switch (status) {
-    case BTA_HL_STATUS_OK:
-      bta_hl_dch_sm_execute(app_idx, mcl_idx, mdl_idx, BTA_HL_DCH_CLOSE_EVT,
-                            p_data);
-      break;
-    case BTA_HL_STATUS_FAIL:
-      p_acb = BTA_HL_GET_APP_CB_PTR(app_idx);
-      if (p_acb->p_cback) {
-        p_mcb = BTA_HL_GET_MCL_CB_PTR(app_idx, mcl_idx);
-        bta_hl_build_dch_close_cfm(&evt_data, p_acb->app_handle,
-                                   p_mcb->mcl_handle,
-                                   p_data->api_dch_close.mdl_handle, status);
-
-        p_acb->p_cback(BTA_HL_DCH_CLOSE_CFM_EVT, (tBTA_HL*)&evt_data);
-      } else {
-        APPL_TRACE_ERROR("bta_hl_api_dch_close Null Callback");
-      }
-      break;
-    default:
-      APPL_TRACE_ERROR("Status code=%d", status);
-      break;
-  }
-}
-
-/*******************************************************************************
- *
- * Function         bta_hl_api_dch_reconnect
- *
- * Description      Process the API DCH reconnect request
- *
- * Returns          void
- *
- ******************************************************************************/
-static void bta_hl_api_dch_reconnect(UNUSED_ATTR tBTA_HL_CB* p_cb,
-                                     tBTA_HL_DATA* p_data) {
-  tBTA_HL evt_data;
-  tBTA_HL_STATUS status = BTA_HL_STATUS_OK;
-  uint8_t app_idx, mcl_idx, mdl_idx;
-  tBTA_HL_APP_CB* p_acb;
-  tBTA_HL_MCL_CB* p_mcb = NULL;
-  tBTA_HL_MDL_CB* p_dcb;
-  uint8_t mdep_cfg_idx;
-  uint8_t mdl_cfg_idx;
-  tBTA_HL_MDEP_CFG* p_mdep_cfg;
-
-  if (bta_hl_find_mcl_idx_using_handle(p_data->api_dch_reconnect.mcl_handle,
-                                       &app_idx, &mcl_idx)) {
-    p_acb = BTA_HL_GET_APP_CB_PTR(app_idx);
-    p_mcb = BTA_HL_GET_MCL_CB_PTR(app_idx, mcl_idx);
-    if (p_mcb->cch_state == BTA_HL_CCH_OPEN_ST) {
-      if (bta_hl_find_avail_mdl_idx(app_idx, mcl_idx, &mdl_idx)) {
-        p_dcb = BTA_HL_GET_MDL_CB_PTR(app_idx, mcl_idx, mdl_idx);
-        if (bta_hl_validate_reconnect_params(app_idx, mcl_idx,
-                                             &(p_data->api_dch_reconnect),
-                                             &mdep_cfg_idx, &mdl_cfg_idx)) {
-          if (!bta_hl_is_the_first_reliable_existed(app_idx, mcl_idx) &&
-              (p_acb->mdl_cfg[mdl_cfg_idx].dch_mode !=
-               BTA_HL_DCH_MODE_RELIABLE)) {
-            status = BTA_HL_STATUS_NO_FIRST_RELIABLE;
-          } else {
-            if (bta_hl_set_ctrl_psm_for_dch(app_idx, mcl_idx, mdl_idx,
-                                            p_data->api_dch_open.ctrl_psm)) {
-              p_dcb->in_use = true;
-              p_dcb->dch_oper = BTA_HL_DCH_OP_LOCAL_RECONNECT;
-              p_dcb->sec_mask = (BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT);
-              p_dcb->local_mdep_id = p_acb->mdl_cfg[mdl_cfg_idx].local_mdep_id;
-              p_dcb->local_mdep_cfg_idx = mdep_cfg_idx;
-              p_dcb->local_cfg = BTA_HL_DCH_CFG_UNKNOWN;
-              p_dcb->mdl_id = p_data->api_dch_reconnect.mdl_id;
-              p_dcb->mdl_cfg_idx_included = true;
-              p_dcb->mdl_cfg_idx = mdl_cfg_idx;
-              p_dcb->dch_mode = p_acb->mdl_cfg[mdl_cfg_idx].dch_mode;
-
-              p_mdep_cfg = BTA_HL_GET_MDEP_CFG_PTR(app_idx, mdep_cfg_idx);
-
-              if (p_mdep_cfg->mdep_role == BTA_HL_MDEP_ROLE_SINK) {
-                p_dcb->peer_mdep_role = BTA_HL_MDEP_ROLE_SOURCE;
-                APPL_TRACE_DEBUG("peer mdep role = SOURCE ");
-              } else {
-                p_dcb->peer_mdep_role = BTA_HL_MDEP_ROLE_SINK;
-                APPL_TRACE_DEBUG("peer mdep role = SINK ");
-              }
-
-              bta_hl_find_rxtx_apdu_size(app_idx, mdep_cfg_idx,
-                                         &p_dcb->max_rx_apdu_size,
-                                         &p_dcb->max_tx_apdu_size);
-            } else {
-              status = BTA_HL_STATUS_INVALID_CTRL_PSM;
-            }
-          }
-        } else {
-          status = BTA_HL_STATUS_INVALID_RECONNECT_CFG;
-        }
-      } else {
-        status = BTA_HL_STATUS_NO_RESOURCE;
-      }
-    } else {
-      status = BTA_HL_STATUS_NO_CCH;
-    }
-  } else {
-    status = BTA_HL_STATUS_INVALID_MCL_HANDLE;
-  }
-
-#if (BTA_HL_DEBUG == TRUE)
-  if (status != BTA_HL_STATUS_OK) {
-    APPL_TRACE_DEBUG("bta_hl_api_dch_reconnect status=%s",
-                     bta_hl_status_code(status));
-  }
-#endif
-
-  switch (status) {
-    case BTA_HL_STATUS_OK:
-      if (p_mcb->sdp.num_recs) {
-        bta_hl_dch_sm_execute(app_idx, mcl_idx, mdl_idx,
-                              BTA_HL_DCH_RECONNECT_EVT, p_data);
-      } else {
-        bta_hl_dch_sm_execute(app_idx, mcl_idx, mdl_idx,
-                              BTA_HL_DCH_SDP_INIT_EVT, p_data);
-      }
-      break;
-    case BTA_HL_STATUS_INVALID_RECONNECT_CFG:
-    case BTA_HL_STATUS_NO_FIRST_RELIABLE:
-    case BTA_HL_STATUS_NO_CCH:
-    case BTA_HL_STATUS_NO_RESOURCE:
-      p_acb = BTA_HL_GET_APP_CB_PTR(app_idx);
-      if (p_acb->p_cback) {
-        bta_hl_build_dch_open_cfm(
-            &evt_data, p_acb->app_handle, p_data->api_dch_reconnect.mcl_handle,
-            BTA_HL_INVALID_MDL_HANDLE, 0, p_data->api_dch_reconnect.mdl_id, 0,
-            0, 0, status);
-        p_acb->p_cback(BTA_HL_DCH_RECONNECT_CFM_EVT, (tBTA_HL*)&evt_data);
-      } else {
-        APPL_TRACE_ERROR("bta_hl_api_dch_reconnect Null Callback");
-      }
-      break;
-    default:
-      APPL_TRACE_ERROR("Status code=%d", status);
-      break;
-  }
-}
-
-/*******************************************************************************
- *
- * Function         bta_hl_api_dch_echo_test
- *
- * Description      Process the API Echo test request
- *
- * Returns          void
- *
- ******************************************************************************/
-static void bta_hl_api_dch_echo_test(UNUSED_ATTR tBTA_HL_CB* p_cb,
-                                     tBTA_HL_DATA* p_data) {
-  tBTA_HL evt_data;
-  tBTA_HL_STATUS status = BTA_HL_STATUS_OK;
-  uint8_t app_idx, mcl_idx, mdl_idx;
-  tBTA_HL_APP_CB* p_acb;
-  tBTA_HL_MCL_CB* p_mcb = NULL;
-  tBTA_HL_MDL_CB* p_dcb;
-  tBTA_HL_ECHO_CFG* p_echo_cfg;
-
-  if (bta_hl_find_mcl_idx_using_handle(p_data->api_dch_echo_test.mcl_handle,
-                                       &app_idx, &mcl_idx)) {
-    p_acb = BTA_HL_GET_APP_CB_PTR(app_idx);
-    p_mcb = BTA_HL_GET_MCL_CB_PTR(app_idx, mcl_idx);
-    if (p_mcb->cch_state == BTA_HL_CCH_OPEN_ST) {
-      if (!p_mcb->echo_test) {
-        if (bta_hl_find_avail_mdl_idx(app_idx, mcl_idx, &mdl_idx)) {
-          p_dcb = BTA_HL_GET_MDL_CB_PTR(app_idx, mcl_idx, mdl_idx);
-
-          if ((p_data->api_dch_echo_test.local_cfg ==
-               BTA_HL_DCH_CFG_RELIABLE) ||
-              (p_data->api_dch_echo_test.local_cfg ==
-               BTA_HL_DCH_CFG_STREAMING)) {
-            bool fcs_use =
-                (bool)(p_dcb->chnl_cfg.fcs & BTA_HL_MCA_FCS_USE_MASK);
-            p_dcb->p_echo_tx_pkt =
-                bta_hl_get_buf(p_data->api_dch_echo_test.pkt_size, fcs_use);
-            if (p_dcb->p_echo_tx_pkt != NULL) {
-              if (bta_hl_set_ctrl_psm_for_dch(app_idx, mcl_idx, mdl_idx,
-                                              p_data->api_dch_open.ctrl_psm)) {
-                p_dcb->in_use = true;
-                p_dcb->dch_oper = BTA_HL_DCH_OP_LOCAL_OPEN;
-                p_dcb->sec_mask = (BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT);
-                p_dcb->local_mdep_cfg_idx = BTA_HL_ECHO_TEST_MDEP_CFG_IDX;
-                p_dcb->local_cfg = p_data->api_dch_echo_test.local_cfg;
-                p_dcb->local_mdep_id = BTA_HL_ECHO_TEST_MDEP_ID;
-                p_dcb->peer_mdep_id = BTA_HL_ECHO_TEST_MDEP_ID;
-                p_dcb->mdl_id =
-                    bta_hl_allocate_mdl_id(app_idx, mcl_idx, mdl_idx);
-                p_dcb->mdl_cfg_idx_included = false;
-                p_echo_cfg = BTA_HL_GET_ECHO_CFG_PTR(app_idx);
-                p_dcb->max_rx_apdu_size = p_echo_cfg->max_rx_apdu_size;
-                p_dcb->max_tx_apdu_size = p_echo_cfg->max_tx_apdu_size;
-                p_mcb->echo_test = true;
-                p_mcb->echo_mdl_idx = mdl_idx;
-              } else {
-                status = BTA_HL_STATUS_INVALID_CTRL_PSM;
-              }
-            } else {
-              status = BTA_HL_STATUS_NO_RESOURCE;
-            }
-          } else {
-            status = BTA_HL_STATUS_INVALID_DCH_CFG;
-          }
-        } else {
-          status = BTA_HL_STATUS_NO_RESOURCE;
-        }
-      } else {
-        status = BTA_HL_STATUS_ECHO_TEST_BUSY;
-      }
-    } else {
-      status = BTA_HL_STATUS_NO_CCH;
-    }
-  } else {
-    status = BTA_HL_STATUS_NO_MCL;
-  }
-
-#if (BTA_HL_DEBUG == TRUE)
-  if (status != BTA_HL_STATUS_OK) {
-    APPL_TRACE_DEBUG("bta_hl_api_dch_echo_test status=%s",
-                     bta_hl_status_code(status));
-  }
-#endif
-
-  switch (status) {
-    case BTA_HL_STATUS_OK:
-      if (p_mcb->sdp.num_recs) {
-        bta_hl_dch_sm_execute(app_idx, mcl_idx, mdl_idx,
-                              BTA_HL_DCH_ECHO_TEST_EVT, p_data);
-      } else {
-        bta_hl_dch_sm_execute(app_idx, mcl_idx, mdl_idx,
-                              BTA_HL_DCH_SDP_INIT_EVT, p_data);
-      }
-      break;
-    case BTA_HL_STATUS_ECHO_TEST_BUSY:
-    case BTA_HL_STATUS_NO_RESOURCE:
-    case BTA_HL_STATUS_INVALID_DCH_CFG:
-      p_acb = BTA_HL_GET_APP_CB_PTR(app_idx);
-      if (p_acb->p_cback) {
-        bta_hl_build_echo_test_cfm(&evt_data, p_acb->app_handle,
-                                   p_mcb->mcl_handle, status);
-        p_acb->p_cback(BTA_HL_DCH_ECHO_TEST_CFM_EVT, (tBTA_HL*)&evt_data);
-      } else {
-        APPL_TRACE_ERROR("bta_hl_api_dch_echo_test Null Callback");
-      }
-      break;
-
-    default:
-      APPL_TRACE_ERROR("Status code=%s", status);
-      break;
-  }
-}
-
-/*******************************************************************************
- *
- * Function         bta_hl_api_sdp_query
- *
- * Description      Process the API SDP query request
- *
- * Returns          void
- *
- ******************************************************************************/
-static void bta_hl_api_sdp_query(UNUSED_ATTR tBTA_HL_CB* p_cb,
-                                 tBTA_HL_DATA* p_data) {
-  tBTA_HL evt_data;
-  tBTA_HL_STATUS status = BTA_HL_STATUS_OK;
-  uint8_t app_idx, mcl_idx;
-  tBTA_HL_APP_CB* p_acb;
-  tBTA_HL_MCL_CB* p_mcb;
-
-  if (bta_hl_find_app_idx_using_handle(p_data->api_sdp_query.app_handle,
-                                       &app_idx)) {
-    if (!bta_hl_find_mcl_idx(app_idx, p_data->api_sdp_query.bd_addr,
-                             &mcl_idx)) {
-      if (bta_hl_find_avail_mcl_idx(app_idx, &mcl_idx)) {
-        p_mcb = BTA_HL_GET_MCL_CB_PTR(app_idx, mcl_idx);
-        p_mcb->in_use = true;
-        p_mcb->bd_addr = p_data->api_sdp_query.bd_addr;
-        APPL_TRACE_DEBUG(
-            "bta_hl_api_sdp_query p_mcb->app_id %d app_idx %d mcl_idx %d",
-            p_mcb->app_id, app_idx, mcl_idx);
-        p_mcb->app_id = p_data->api_sdp_query.app_id;
-        p_mcb->sdp_oper = BTA_HL_SDP_OP_SDP_QUERY_NEW;
-      } else {
-        status = BTA_HL_STATUS_NO_RESOURCE;
-      }
-    } else {
-      p_mcb = BTA_HL_GET_MCL_CB_PTR(app_idx, mcl_idx);
-      p_mcb->app_id = p_data->api_sdp_query.app_id;
-      if (p_mcb->sdp_oper != BTA_HL_SDP_OP_NONE) {
-        status = BTA_HL_STATUS_SDP_NO_RESOURCE;
-      } else {
-        p_mcb->sdp_oper = BTA_HL_SDP_OP_SDP_QUERY_CURRENT;
-      }
-    }
-  } else {
-    status = BTA_HL_STATUS_INVALID_APP_HANDLE;
-  }
-
-  if (status == BTA_HL_STATUS_OK) {
-    status = bta_hl_init_sdp(p_mcb->sdp_oper, app_idx, mcl_idx, 0xFF);
-    if ((status != BTA_HL_STATUS_OK) &&
-        (p_mcb->sdp_oper == BTA_HL_SDP_OP_SDP_QUERY_NEW)) {
-      memset(p_mcb, 0, sizeof(tBTA_HL_MCL_CB));
-    }
-  }
-#if (BTA_HL_DEBUG == TRUE)
-  if (status != BTA_HL_STATUS_OK) {
-    APPL_TRACE_DEBUG("bta_hl_api_sdp_query status=%s",
-                     bta_hl_status_code(status));
-  }
-#endif
-  switch (status) {
-    case BTA_HL_STATUS_NO_RESOURCE:
-    case BTA_HL_STATUS_FAIL:
-    case BTA_HL_STATUS_SDP_NO_RESOURCE:
-      p_acb = BTA_HL_GET_APP_CB_PTR(app_idx);
-      if (p_acb->p_cback) {
-        bta_hl_build_sdp_query_cfm(&evt_data, p_data->api_sdp_query.app_id,
-                                   p_data->api_sdp_query.app_handle,
-                                   p_data->api_sdp_query.bd_addr, NULL, status);
-        p_acb->p_cback(BTA_HL_SDP_QUERY_CFM_EVT, (tBTA_HL*)&evt_data);
-      } else {
-        APPL_TRACE_ERROR("bta_hl_api_sdp_query Null Callback");
-      }
-      break;
-    case BTA_HL_STATUS_OK:
-      break;
-    default:
-      APPL_TRACE_ERROR("Status code=%d", status);
-      break;
-  }
-}
-
-/*******************************************************************************
- *
- * Function         bta_hl_sdp_query_results
- *
- * Description      Process the SDP query results
- *
- * Returns          void
- *
- ******************************************************************************/
-static void bta_hl_sdp_query_results(UNUSED_ATTR tBTA_HL_CB* p_cb,
-                                     tBTA_HL_DATA* p_data) {
-  tBTA_HL evt_data;
-  tBTA_HL_STATUS status = BTA_HL_STATUS_OK;
-  uint8_t app_idx = p_data->cch_sdp.app_idx;
-  uint8_t mcl_idx = p_data->cch_sdp.mcl_idx;
-  tBTA_HL_APP_CB* p_acb = BTA_HL_GET_APP_CB_PTR(app_idx);
-  tBTA_HL_MCL_CB* p_mcb = BTA_HL_GET_MCL_CB_PTR(app_idx, mcl_idx);
-  tBTA_HL_SDP* p_sdp = NULL;
-  uint16_t event;
-
-  event = p_data->hdr.event;
-
-  if (event == BTA_HL_SDP_QUERY_OK_EVT) {
-    // this is freed in btif_hl_proc_sdp_query_cfm
-    p_sdp = (tBTA_HL_SDP*)osi_malloc(sizeof(tBTA_HL_SDP));
-    memcpy(p_sdp, &p_mcb->sdp, sizeof(tBTA_HL_SDP));
-  } else {
-    status = BTA_HL_STATUS_SDP_FAIL;
-  }
-
-#if (BTA_HL_DEBUG == TRUE)
-  if (status != BTA_HL_STATUS_OK) {
-    APPL_TRACE_DEBUG("bta_hl_sdp_query_results status=%s",
-                     bta_hl_status_code(status));
-  }
-#endif
-
-  APPL_TRACE_DEBUG(
-      "bta_hl_sdp_query_results p_mcb->app_id %d app_idx %d mcl_idx %d",
-      p_mcb->app_id, app_idx, mcl_idx);
-  bta_hl_build_sdp_query_cfm(&evt_data, p_mcb->app_id, p_acb->app_handle,
-                             p_mcb->bd_addr, p_sdp, status);
-  p_acb->p_cback(BTA_HL_SDP_QUERY_CFM_EVT, (tBTA_HL*)&evt_data);
-
-  if (p_data->cch_sdp.release_mcl_cb) {
-    memset(p_mcb, 0, sizeof(tBTA_HL_MCL_CB));
-  } else {
-    if (p_mcb->close_pending)
-      bta_hl_check_cch_close(app_idx, mcl_idx, p_data, true);
-
-    if (!p_mcb->ctrl_psm) {
-      /* Control channel acceptor: do not store the SDP records */
-      memset(&p_mcb->sdp, 0, sizeof(tBTA_HL_SDP));
-    }
-  }
-}
-
-/*******************************************************************************
- *
- * Function         bta_hl_api_delete_mdl
- *
- * Description      Process the API DELETE MDL request
- *
- * Returns          void
- *
- ******************************************************************************/
-static void bta_hl_api_delete_mdl(UNUSED_ATTR tBTA_HL_CB* p_cb,
-                                  tBTA_HL_DATA* p_data) {
-  tBTA_HL evt_data;
-  tBTA_HL_STATUS status = BTA_HL_STATUS_OK;
-  uint8_t app_idx, mcl_idx;
-  tBTA_HL_APP_CB* p_acb;
-  tBTA_HL_MCL_CB* p_mcb;
-
-  if (bta_hl_find_mcl_idx_using_handle(p_data->api_delete_mdl.mcl_handle,
-                                       &app_idx, &mcl_idx)) {
-    if (bta_hl_is_mdl_value_valid(p_data->api_delete_mdl.mdl_id)) {
-      p_mcb = BTA_HL_GET_MCL_CB_PTR(app_idx, mcl_idx);
-      if (bta_hl_is_mdl_exsit_in_mcl(app_idx, p_mcb->bd_addr,
-                                     p_data->api_delete_mdl.mdl_id)) {
-        p_mcb->delete_mdl.mcl_handle = p_data->api_delete_mdl.mcl_handle;
-        p_mcb->delete_mdl.mdl_id = p_data->api_delete_mdl.mdl_id;
-        p_mcb->delete_mdl.delete_req_pending = true;
-
-        if (MCA_Delete((tMCA_CL)p_mcb->mcl_handle,
-                       p_data->api_delete_mdl.mdl_id) != MCA_SUCCESS) {
-          status = BTA_HL_STATUS_FAIL;
-          memset(&p_mcb->delete_mdl, 0, sizeof(tBTA_HL_DELETE_MDL));
-        }
-      } else {
-        status = BTA_HL_STATUS_NO_MDL_ID_FOUND;
-      }
-    } else {
-      status = BTA_HL_STATUS_INVALID_MDL_ID;
-    }
-  } else {
-    status = BTA_HL_STATUS_INVALID_MCL_HANDLE;
-  }
-
-#if (BTA_HL_DEBUG == TRUE)
-  if (status != BTA_HL_STATUS_OK) {
-    APPL_TRACE_DEBUG("bta_hl_api_delete_mdl status=%s",
-                     bta_hl_status_code(status));
-  }
-#endif
-  switch (status) {
-    case BTA_HL_STATUS_OK:
-      break;
-    case BTA_HL_STATUS_FAIL:
-    case BTA_HL_STATUS_NO_MDL_ID_FOUND:
-    case BTA_HL_STATUS_INVALID_MDL_ID:
-      p_acb = BTA_HL_GET_APP_CB_PTR(app_idx);
-      if (p_acb->p_cback) {
-        bta_hl_build_delete_mdl_cfm(&evt_data, p_acb->app_handle,
-                                    p_data->api_delete_mdl.mcl_handle,
-                                    p_data->api_delete_mdl.mdl_id, status);
-        p_acb->p_cback(BTA_HL_DELETE_MDL_CFM_EVT, (tBTA_HL*)&evt_data);
-      } else {
-        APPL_TRACE_ERROR("bta_hl_api_delete_mdl Null Callback");
-      }
-      break;
-    default:
-      APPL_TRACE_ERROR("status code =%d", status);
-      break;
-  }
-}
-
-/*******************************************************************************
- *
- * Function         bta_hl_mca_delete_mdl_cfm
- *
- * Description      Process the DELETE MDL confirmation event
- *
- * Returns          void
- *
- ******************************************************************************/
-static void bta_hl_mca_delete_mdl_cfm(UNUSED_ATTR tBTA_HL_CB* p_cb,
-                                      tBTA_HL_DATA* p_data) {
-  tBTA_HL evt_data;
-  tBTA_HL_STATUS status = BTA_HL_STATUS_OK;
-  uint8_t app_idx, mcl_idx;
-  tMCA_RSP_EVT* p_delete_cfm = &p_data->mca_evt.mca_data.delete_cfm;
-  tBTA_HL_MCL_CB* p_mcb;
-  bool send_cfm_evt = true;
-  tBTA_HL_APP_CB* p_acb;
-
-  if (bta_hl_find_mcl_idx_using_handle(p_data->mca_evt.mcl_handle, &app_idx,
-                                       &mcl_idx)) {
-    p_mcb = BTA_HL_GET_MCL_CB_PTR(app_idx, mcl_idx);
-    if (p_mcb->delete_mdl.delete_req_pending) {
-      if (p_delete_cfm->rsp_code == MCA_RSP_SUCCESS) {
-        if (!bta_hl_delete_mdl_cfg(app_idx, p_mcb->bd_addr,
-                                   p_delete_cfm->mdl_id)) {
-          status = BTA_HL_STATUS_FAIL;
-        }
-      } else {
-        status = BTA_HL_STATUS_FAIL;
-      }
-
-      memset(&p_mcb->delete_mdl, 0, sizeof(tBTA_HL_DELETE_MDL));
-    } else {
-      send_cfm_evt = false;
-    }
-  } else {
-    send_cfm_evt = false;
-  }
-
-#if (BTA_HL_DEBUG == TRUE)
-  if (status != BTA_HL_STATUS_OK) {
-    APPL_TRACE_DEBUG("bta_hl_api_delete_mdl status=%s",
-                     bta_hl_status_code(status));
-  }
-#endif
-
-  if (send_cfm_evt) {
-    p_acb = BTA_HL_GET_APP_CB_PTR(app_idx);
-    if (p_acb->p_cback) {
-      bta_hl_build_delete_mdl_cfm(&evt_data, p_acb->app_handle,
-                                  p_mcb->mcl_handle, p_delete_cfm->mdl_id,
-                                  status);
-
-      p_acb->p_cback(BTA_HL_DELETE_MDL_CFM_EVT, (tBTA_HL*)&evt_data);
-    } else {
-      APPL_TRACE_ERROR("bta_hl_mca_delete_mdl_cfm Null Callback");
-    }
-  }
-}
-
-/*******************************************************************************
- *
- * Function         bta_hl_mca_delete_mdl_ind
- *
- * Description      Process the DELETE MDL indication event
- *
- * Returns          void
- *
- ******************************************************************************/
-static void bta_hl_mca_delete_mdl_ind(UNUSED_ATTR tBTA_HL_CB* p_cb,
-                                      tBTA_HL_DATA* p_data) {
-  tBTA_HL evt_data;
-  uint8_t app_idx, mcl_idx, mdl_idx;
-  tMCA_EVT_HDR* p_delete_ind = &p_data->mca_evt.mca_data.delete_ind;
-  tBTA_HL_MCL_CB* p_mcb;
-  tBTA_HL_MDL_CB* p_dcb;
-  bool send_ind_evt = false;
-  tBTA_HL_APP_CB* p_acb;
-
-  if (bta_hl_find_mcl_idx_using_handle(p_data->mca_evt.mcl_handle, &app_idx,
-                                       &mcl_idx)) {
-    p_mcb = BTA_HL_GET_MCL_CB_PTR(app_idx, mcl_idx);
-
-    if (bta_hl_find_mdl_idx(app_idx, mcl_idx, p_delete_ind->mdl_id, &mdl_idx)) {
-      p_dcb = BTA_HL_GET_MDL_CB_PTR(app_idx, mcl_idx, mdl_idx);
-      p_dcb->dch_oper = BTA_HL_DCH_OP_REMOTE_DELETE;
-    }
-    if (bta_hl_delete_mdl_cfg(app_idx, p_mcb->bd_addr, p_delete_ind->mdl_id)) {
-      send_ind_evt = true;
-    }
-  }
-
-#if (BTA_HL_DEBUG == TRUE)
-  if (!send_ind_evt) {
-    APPL_TRACE_DEBUG("bta_hl_mca_delete_mdl_ind is_send_ind_evt =%d",
-                     send_ind_evt);
-  }
-#endif
-
-  if (send_ind_evt) {
-    p_acb = BTA_HL_GET_APP_CB_PTR(app_idx);
-    if (p_acb->p_cback) {
-      evt_data.delete_mdl_ind.mcl_handle = p_mcb->mcl_handle;
-      evt_data.delete_mdl_ind.app_handle = p_acb->app_handle;
-      evt_data.delete_mdl_ind.mdl_id = p_delete_ind->mdl_id;
-      p_acb->p_cback(BTA_HL_DELETE_MDL_IND_EVT, (tBTA_HL*)&evt_data);
-    } else {
-      APPL_TRACE_ERROR("bta_hl_mca_delete_mdl_ind Null Callback");
-    }
-  }
-}
-
-/*******************************************************************************
- *
- * Function         bta_hl_api_dch_abort
- *
- * Description      Process the API DCH abort request
- *
- * Returns          void
- *
- ******************************************************************************/
-static void bta_hl_api_dch_abort(UNUSED_ATTR tBTA_HL_CB* p_cb,
-                                 tBTA_HL_DATA* p_data) {
-  tBTA_HL_STATUS status = BTA_HL_STATUS_OK;
-  uint8_t app_idx, mcl_idx, mdl_idx;
-  tBTA_HL_APP_CB* p_acb;
-  tBTA_HL_MCL_CB* p_mcb;
-  tBTA_HL_MDL_CB* p_dcb;
-  tBTA_HL evt_data;
-
-  if (bta_hl_find_mcl_idx_using_handle(p_data->api_dch_abort.mcl_handle,
-                                       &app_idx, &mcl_idx)) {
-    if (!bta_hl_find_dch_setup_mdl_idx(app_idx, mcl_idx, &mdl_idx)) {
-      status = BTA_HL_STATUS_NO_MDL_ID_FOUND;
-    } else {
-      p_dcb = BTA_HL_GET_MDL_CB_PTR(app_idx, mcl_idx, mdl_idx);
-      if (p_dcb->abort_oper) {
-        /* abort already in progress*/
-        status = BTA_HL_STATUS_FAIL;
-      } else {
-        p_dcb->abort_oper = BTA_HL_ABORT_LOCAL_MASK;
-      }
-    }
-  } else {
-    status = BTA_HL_STATUS_INVALID_MCL_HANDLE;
-  }
-
-#if (BTA_HL_DEBUG == TRUE)
-  if (status != BTA_HL_STATUS_OK) {
-    APPL_TRACE_DEBUG("bta_hl_api_dch_abort status=%s",
-                     bta_hl_status_code(status));
-  }
-#endif
-  switch (status) {
-    case BTA_HL_STATUS_OK:
-
-      bta_hl_dch_sm_execute(app_idx, mcl_idx, mdl_idx, BTA_HL_DCH_ABORT_EVT,
-                            p_data);
-      break;
-    case BTA_HL_STATUS_NO_MDL_ID_FOUND:
-    case BTA_HL_STATUS_FAIL:
-
-      p_acb = BTA_HL_GET_APP_CB_PTR(app_idx);
-      if (p_acb->p_cback) {
-        p_mcb = BTA_HL_GET_MCL_CB_PTR(app_idx, mcl_idx);
-        bta_hl_build_abort_cfm(&evt_data,
-
-                               p_acb->app_handle, p_mcb->mcl_handle,
-                               BTA_HL_STATUS_FAIL);
-        p_acb->p_cback(BTA_HL_DCH_ABORT_CFM_EVT, (tBTA_HL*)&evt_data);
-      } else {
-        APPL_TRACE_ERROR("bta_hl_api_dch_abort Null Callback");
-      }
-      break;
-    default:
-      APPL_TRACE_ERROR("Status code=%d", status);
-      break;
-  }
-}
-
-/*******************************************************************************
- *
- * Function         bta_hl_hdl_event
- *
- * Description      HL main event handling function.
- *
- * Returns          void
- *
- ******************************************************************************/
-bool bta_hl_hdl_event(BT_HDR* p_msg) {
-  uint8_t app_idx, mcl_idx, mdl_idx;
-  bool success = true;
-
-#if (BTA_HL_DEBUG == TRUE)
-  APPL_TRACE_DEBUG("BTA HL Event Handler: Event [%s]",
-                   bta_hl_evt_code(p_msg->event));
-#endif
-
-  switch (p_msg->event) {
-    case BTA_HL_API_ENABLE_EVT:
-      bta_hl_api_enable(&bta_hl_cb, (tBTA_HL_DATA*)p_msg);
-      break;
-    case BTA_HL_API_DISABLE_EVT:
-      bta_hl_api_disable(&bta_hl_cb, (tBTA_HL_DATA*)p_msg);
-      break;
-    case BTA_HL_API_UPDATE_EVT:
-      bta_hl_api_update(&bta_hl_cb, (tBTA_HL_DATA*)p_msg);
-      break;
-    case BTA_HL_API_REGISTER_EVT:
-      bta_hl_api_register(&bta_hl_cb, (tBTA_HL_DATA*)p_msg);
-      break;
-    case BTA_HL_API_DEREGISTER_EVT:
-      bta_hl_api_deregister(&bta_hl_cb, (tBTA_HL_DATA*)p_msg);
-      break;
-    case BTA_HL_API_CCH_OPEN_EVT:
-      bta_hl_api_cch_open(&bta_hl_cb, (tBTA_HL_DATA*)p_msg);
-      break;
-    case BTA_HL_API_CCH_CLOSE_EVT:
-      bta_hl_api_cch_close(&bta_hl_cb, (tBTA_HL_DATA*)p_msg);
-      break;
-    case BTA_HL_API_DCH_OPEN_EVT:
-      bta_hl_api_dch_open(&bta_hl_cb, (tBTA_HL_DATA*)p_msg);
-      break;
-    case BTA_HL_API_DCH_CLOSE_EVT:
-      bta_hl_api_dch_close(&bta_hl_cb, (tBTA_HL_DATA*)p_msg);
-      break;
-    case BTA_HL_API_DELETE_MDL_EVT:
-      bta_hl_api_delete_mdl(&bta_hl_cb, (tBTA_HL_DATA*)p_msg);
-      break;
-    case BTA_HL_API_DCH_RECONNECT_EVT:
-      bta_hl_api_dch_reconnect(&bta_hl_cb, (tBTA_HL_DATA*)p_msg);
-      break;
-
-    case BTA_HL_API_DCH_ECHO_TEST_EVT:
-      bta_hl_api_dch_echo_test(&bta_hl_cb, (tBTA_HL_DATA*)p_msg);
-      break;
-
-    case BTA_HL_API_SDP_QUERY_EVT:
-      bta_hl_api_sdp_query(&bta_hl_cb, (tBTA_HL_DATA*)p_msg);
-      break;
-
-    case BTA_HL_MCA_DELETE_CFM_EVT:
-      bta_hl_mca_delete_mdl_cfm(&bta_hl_cb, (tBTA_HL_DATA*)p_msg);
-      break;
-
-    case BTA_HL_MCA_DELETE_IND_EVT:
-      bta_hl_mca_delete_mdl_ind(&bta_hl_cb, (tBTA_HL_DATA*)p_msg);
-      break;
-
-    case BTA_HL_SDP_QUERY_OK_EVT:
-    case BTA_HL_SDP_QUERY_FAIL_EVT:
-      bta_hl_sdp_query_results(&bta_hl_cb, (tBTA_HL_DATA*)p_msg);
-      break;
-    case BTA_HL_API_DCH_ABORT_EVT:
-      bta_hl_api_dch_abort(&bta_hl_cb, (tBTA_HL_DATA*)p_msg);
-      break;
-
-    default:
-      if (p_msg->event < BTA_HL_DCH_EVT_MIN) {
-        if (bta_hl_find_cch_cb_indexes((tBTA_HL_DATA*)p_msg, &app_idx,
-                                       &mcl_idx)) {
-          bta_hl_cch_sm_execute(app_idx, mcl_idx, p_msg->event,
-                                (tBTA_HL_DATA*)p_msg);
-        } else {
-#if (BTA_HL_DEBUG == TRUE)
-          APPL_TRACE_ERROR(
-              "unable to find control block indexes for CCH: [event=%s]",
-              bta_hl_evt_code(p_msg->event));
-#else
-          APPL_TRACE_ERROR(
-              "unable to find control block indexes for CCH: [event=%d]",
-              p_msg->event);
-#endif
-          success = false;
-        }
-      } else {
-        if (bta_hl_find_dch_cb_indexes((tBTA_HL_DATA*)p_msg, &app_idx, &mcl_idx,
-                                       &mdl_idx)) {
-          bta_hl_dch_sm_execute(app_idx, mcl_idx, mdl_idx, p_msg->event,
-                                (tBTA_HL_DATA*)p_msg);
-        } else {
-#if (BTA_HL_DEBUG == TRUE)
-          APPL_TRACE_ERROR(
-              "unable to find control block indexes for DCH : [event=%s]",
-              bta_hl_evt_code(p_msg->event));
-#else
-          APPL_TRACE_ERROR(
-              "unable to find control block indexes for DCH: [event=%d]",
-              p_msg->event);
-#endif
-          success = false;
-        }
-      }
-
-      break;
-  }
-
-  return (success);
-}
-
-/*****************************************************************************
- *  Debug Functions
- ****************************************************************************/
-#if (BTA_HL_DEBUG == TRUE)
-
-/*******************************************************************************
- *
- * Function         bta_hl_cch_state_code
- *
- * Description      Map CCH state code to the corresponding state string
- *
- * Returns          string pointer for the associated state name
- *
- ******************************************************************************/
-static const char* bta_hl_cch_state_code(tBTA_HL_CCH_STATE state_code) {
-  switch (state_code) {
-    case BTA_HL_CCH_IDLE_ST:
-      return "BTA_HL_CCH_IDLE_ST";
-    case BTA_HL_CCH_OPENING_ST:
-      return "BTA_HL_CCH_OPENING_ST";
-    case BTA_HL_CCH_OPEN_ST:
-      return "BTA_HL_CCH_OPEN_ST";
-    case BTA_HL_CCH_CLOSING_ST:
-      return "BTA_HL_CCH_CLOSING_ST";
-    default:
-      return "Unknown CCH state code";
-  }
-}
-
-/*******************************************************************************
- *
- * Function         bta_hl_dch_state_code
- *
- * Description      Map DCH state code to the corresponding state string
- *
- * Returns          string pointer for the associated state name
- *
- ******************************************************************************/
-static const char* bta_hl_dch_state_code(tBTA_HL_DCH_STATE state_code) {
-  switch (state_code) {
-    case BTA_HL_DCH_IDLE_ST:
-      return "BTA_HL_DCH_IDLE_ST";
-    case BTA_HL_DCH_OPENING_ST:
-      return "BTA_HL_DCH_OPENING_ST";
-    case BTA_HL_DCH_OPEN_ST:
-      return "BTA_HL_DCH_OPEN_ST";
-    case BTA_HL_DCH_CLOSING_ST:
-      return "BTA_HL_DCH_CLOSING_ST";
-    default:
-      return "Unknown DCH state code";
-  }
-}
-#endif /* Debug Functions */
-#endif /* HL_INCLUDED */
diff --git a/bta/include/bta_api.h b/bta/include/bta_api.h
index b12725a..b7d7d30 100644
--- a/bta/include/bta_api.h
+++ b/bta/include/bta_api.h
@@ -154,13 +154,6 @@
    BTM_SEC_OUT_AUTHENTICATE) /* Authentication required. */
 #define BTA_SEC_ENCRYPT \
   (BTM_SEC_IN_ENCRYPT | BTM_SEC_OUT_ENCRYPT) /* Encryption required. */
-#define BTA_SEC_MODE4_LEVEL4                                               \
-  (BTM_SEC_MODE4_LEVEL4) /* Mode 4 level 4 service, i.e. incoming/outgoing \
-                            MITM and P-256 encryption */
-#define BTA_SEC_MITM \
-  (BTM_SEC_IN_MITM | BTM_SEC_OUT_MITM) /* Man-In-The_Middle protection */
-#define BTA_SEC_IN_16_DIGITS \
-  (BTM_SEC_IN_MIN_16_DIGIT_PIN) /* Min 16 digit for pin code */
 
 typedef uint16_t tBTA_SEC;
 
@@ -1196,20 +1189,6 @@
  * Function         BTA_DmBond
  *
  * Description      This function initiates a bonding procedure with a peer
- *                  device.  The bonding procedure enables authentication
- *                  and optionally encryption on the Bluetooth link.
- *
- *
- * Returns          void
- *
- ******************************************************************************/
-extern void BTA_DmBond(const RawAddress& bd_addr);
-
-/*******************************************************************************
- *
- * Function         BTA_DmBondByTransport
- *
- * Description      This function initiates a bonding procedure with a peer
  *                  device by designated transport.  The bonding procedure
  *                  enables authentication and optionally encryption on the
  *                  Bluetooth link.
@@ -1218,8 +1197,8 @@
  * Returns          void
  *
  ******************************************************************************/
-extern void BTA_DmBondByTransport(const RawAddress& bd_addr,
-                                  tBTA_TRANSPORT transport);
+extern void BTA_DmBond(const RawAddress& bd_addr, tBLE_ADDR_TYPE addr_type,
+                       tBTA_TRANSPORT transport);
 
 /*******************************************************************************
  *
diff --git a/bta/include/bta_av_api.h b/bta/include/bta_av_api.h
index 418f012..82be54d 100644
--- a/bta/include/bta_av_api.h
+++ b/bta/include/bta_av_api.h
@@ -72,6 +72,7 @@
 #define BTA_AV_FEAT_DELAY_RPT 0x0400 /* allow delay reporting */
 #define BTA_AV_FEAT_ACP_START \
   0x0800 /* start stream when 2nd SNK was accepted   */
+#define BTA_AV_FEAT_COVER_ARTWORK 0x1000 /* use cover art feature */
 #define BTA_AV_FEAT_APP_SETTING 0x2000 /* Player app setting support */
 
 /* Internal features */
@@ -144,8 +145,9 @@
 #define BTA_AV_OFFLOAD_START_RSP_EVT 22 /* a2dp offload start response */
 #define BTA_AV_RC_BROWSE_OPEN_EVT 23    /* remote control channel open */
 #define BTA_AV_RC_BROWSE_CLOSE_EVT 24   /* remote control channel closed */
+#define BTA_AV_RC_PSM_EVT 25            /* cover art psm update */
 /* Max BTA event */
-#define BTA_AV_MAX_EVT 25
+#define BTA_AV_MAX_EVT 26
 
 typedef uint8_t tBTA_AV_EVT;
 
@@ -234,6 +236,7 @@
 /* data associated with BTA_AV_RC_OPEN_EVT */
 typedef struct {
   uint8_t rc_handle;
+  uint16_t cover_art_psm;
   tBTA_AV_FEAT peer_features;
   RawAddress peer_addr;
   tBTA_AV_STATUS status;
@@ -265,6 +268,13 @@
   RawAddress peer_addr;
 } tBTA_AV_RC_FEAT;
 
+/* data associated with BTA_AV_RC_PSM_EVT */
+typedef struct {
+  uint8_t rc_handle;
+  uint16_t cover_art_psm;
+  RawAddress peer_addr;
+} tBTA_AV_RC_PSM;
+
 /* data associated with BTA_AV_REMOTE_CMD_EVT */
 typedef struct {
   uint8_t rc_handle;
@@ -342,6 +352,7 @@
   tBTA_AV_META_MSG meta_msg;
   tBTA_AV_REJECT reject;
   tBTA_AV_RC_FEAT rc_feat;
+  tBTA_AV_RC_PSM rc_cover_art_psm;
   tBTA_AV_STATUS status;
 } tBTA_AV;
 
@@ -360,7 +371,8 @@
 
 /* AV callback */
 typedef void(tBTA_AV_CBACK)(tBTA_AV_EVT event, tBTA_AV* p_data);
-typedef void(tBTA_AV_SINK_DATA_CBACK)(tBTA_AV_EVT event, tBTA_AV_MEDIA* p_data);
+typedef void(tBTA_AV_SINK_DATA_CBACK)(const RawAddress&, tBTA_AV_EVT event,
+                                      tBTA_AV_MEDIA* p_data);
 
 /* type for stream state machine action functions */
 struct tBTA_AV_SCB;
diff --git a/bta/include/bta_hf_client_api.h b/bta/include/bta_hf_client_api.h
old mode 100644
new mode 100755
index 69dc109..7f46079
--- a/bta/include/bta_hf_client_api.h
+++ b/bta/include/bta_hf_client_api.h
@@ -45,6 +45,7 @@
 #define BTA_HF_CLIENT_PEER_ECC 0x00000080 /* Enhanced Call Control */
 #define BTA_HF_CLIENT_PEER_EXTERR 0x00000100 /* Extended error codes */
 #define BTA_HF_CLIENT_PEER_CODEC 0x00000200  /* Codec Negotiation */
+#define BTA_HF_CLIENT_PEER_S4 0x00000800     /* ESCO S4 link setting */
 
 typedef uint16_t tBTA_HF_CLIENT_PEER_FEAT;
 
@@ -60,6 +61,7 @@
 #define BTA_HF_CLIENT_FEAT_ECS 0x00000020   /* Enhanced Call Status */
 #define BTA_HF_CLIENT_FEAT_ECC 0x00000040   /* Enhanced Call Control */
 #define BTA_HF_CLIENT_FEAT_CODEC 0x00000080 /* Codec Negotiation */
+#define BTA_HF_CLIENT_FEAT_S4 0x00000200    /* ESCO S4 link setting */
 
 /* HFP HF extended call handling - masks not related to any spec */
 #define BTA_HF_CLIENT_CHLD_REL \
@@ -119,6 +121,9 @@
                                      */
 #define BTA_HF_CLIENT_BINP_EVT 20 /* binp number event */
 #define BTA_HF_CLIENT_RING_INDICATION 21 /* HF Client ring indication */
+
+#define BTA_HF_CLIENT_UNKNOWN_EVT 22 /* Unknown or vendor specific Event */
+
 #define BTA_HF_CLIENT_DISABLE_EVT 30     /* HF Client disabled */
 
 typedef uint8_t tBTA_HF_CLIENT_EVT;
@@ -159,6 +164,7 @@
 #define BTA_HF_CLIENT_AT_CMD_BINP 13
 #define BTA_HF_CLIENT_AT_CMD_BLDN 14
 #define BTA_HF_CLIENT_AT_CMD_NREC 15
+#define BTA_HF_CLIENT_AT_CMD_VENDOR_SPECIFIC_CMD 16
 
 typedef uint8_t tBTA_HF_CLIENT_AT_CMD_TYPE;
 
@@ -234,6 +240,13 @@
   uint16_t value;
 } tBTA_HF_CLIENT_VAL;
 
+/* data associated with BTA_HF_CLIENT_UNKNOWN_EVT event */
+#define BTA_HF_CLIENT_UNKOWN_EVENT_LEN 32
+typedef struct {
+  RawAddress bd_addr;
+  char event_string[BTA_HF_CLIENT_UNKOWN_EVENT_LEN + 1];
+} tBTA_HF_CLIENT_UNKNOWN;
+
 /* union of data associated with AG callback */
 typedef union {
   // Common BD ADDR field for all tyepdefs
@@ -248,6 +261,7 @@
   tBTA_HF_CLIENT_AT_RESULT result;
   tBTA_HF_CLIENT_CLCC clcc;
   tBTA_HF_CLIENT_CNUM cnum;
+  tBTA_HF_CLIENT_UNKNOWN unknown;
 } tBTA_HF_CLIENT;
 
 typedef uint32_t tBTA_HF_CLIENT_FEAT;
diff --git a/bta/include/bta_pan_ci.h b/bta/include/bta_pan_ci.h
index 3711700..afc6377 100644
--- a/bta/include/bta_pan_ci.h
+++ b/bta/include/bta_pan_ci.h
@@ -67,8 +67,8 @@
  * Description      This function is called to enable or disable data flow on
  *                  the TX path.  The phone should call this function to
  *                  disable data flow when it is congested and cannot handle
- *                  any more data sent by bta_pan_co_tx_write() or
- *                  bta_pan_co_tx_writebuf().  This function is used when the
+ *                  any more data sent by bta_pan_co_tx_write().
+ *                  This function is used when the
  *                  TX data path is configured to use a push interface.
  *
  *
diff --git a/bta/include/bta_pan_co.h b/bta/include/bta_pan_co.h
index acd212e..6dc5435 100644
--- a/bta/include/bta_pan_co.h
+++ b/bta/include/bta_pan_co.h
@@ -38,7 +38,6 @@
 #define BTA_PAN_RX_PUSH_BUF 0x01 /* RX push with zero copy. */
 #define BTA_PAN_RX_PULL 0x02     /* RX pull. */
 #define BTA_PAN_TX_PUSH 0x00     /* TX push. */
-#define BTA_PAN_TX_PUSH_BUF 0x10 /* TX push with zero copy. */
 #define BTA_PAN_TX_PULL 0x20     /* TX pull. */
 
 /*****************************************************************************
@@ -140,24 +139,6 @@
 
 /*******************************************************************************
  *
- * Function         bta_pan_co_tx_writebuf
- *
- * Description      This function is called by PAN to send data to the phone
- *                  when the TX path is configured to use a push interface with
- *                  zero copy.  The phone must free the buffer using function
- *                  osi_free() when it is through processing the buffer.
- *
- *
- * Returns          void
- *
- ******************************************************************************/
-extern void bta_pan_co_tx_writebuf(uint16_t handle, uint8_t app_id,
-                                   const RawAddress& src, const RawAddress& dst,
-                                   uint16_t protocol, BT_HDR* p_buf, bool ext,
-                                   bool forward);
-
-/*******************************************************************************
- *
  * Function         bta_pan_co_rx_flow
  *
  * Description      This function is called by PAN to enable or disable
diff --git a/bta/jv/bta_jv_act.cc b/bta/jv/bta_jv_act.cc
index 2ede5c9..27bc7fb 100644
--- a/bta/jv/bta_jv_act.cc
+++ b/bta/jv/bta_jv_act.cc
@@ -1622,6 +1622,9 @@
         p_pcb->handle = BTA_JV_RFC_H_S_TO_HDL(p_cb->handle, si);
         VLOG(2) << __func__ << ": p_pcb->handle=" << loghex(p_pcb->handle)
                 << ", curr_sess=" << p_cb->curr_sess;
+      } else {
+        LOG(ERROR) << __func__ << ": RFCOMM_CreateConnection failed";
+        return NULL;
       }
     } else {
       LOG(ERROR) << __func__ << ": cannot create new rfc listen port";
@@ -1887,16 +1890,16 @@
   static tL2CAP_FIXED_CHNL_REG fcr = {
       .pL2CA_FixedConn_Cb = fcchan_conn_chng_cbk,
       .pL2CA_FixedData_Cb = fcchan_data_cbk,
-      .default_idle_tout = 0xffff,
       .fixed_chnl_opts =
           {
               .mode = L2CAP_FCR_BASIC_MODE,
+              .tx_win_sz = 1,
               .max_transmit = 0xFF,
               .rtrans_tout = 2000,
               .mon_tout = 12000,
               .mps = 670,
-              .tx_win_sz = 1,
           },
+      .default_idle_tout = 0xffff,
   };
 
   while (t && t->chan != chan) t = t->next;
diff --git a/bta/pan/bta_pan_act.cc b/bta/pan/bta_pan_act.cc
index 548d6fd..f887cba 100644
--- a/bta/pan/bta_pan_act.cc
+++ b/bta/pan/bta_pan_act.cc
@@ -598,48 +598,19 @@
  *
  ******************************************************************************/
 void bta_pan_tx_path(tBTA_PAN_SCB* p_scb, UNUSED_ATTR tBTA_PAN_DATA* p_data) {
-  /* if data path configured for tx pull */
-  if ((bta_pan_cb.flow_mask & BTA_PAN_TX_MASK) == BTA_PAN_TX_PULL) {
-    bta_pan_pm_conn_busy(p_scb);
-    /* call application callout function for tx path */
-    bta_pan_co_tx_path(p_scb->handle, p_scb->app_id);
+  bta_pan_pm_conn_busy(p_scb);
+  /* call application callout function for tx path */
+  bta_pan_co_tx_path(p_scb->handle, p_scb->app_id);
 
-    /* free data that exceeds queue level */
-    while (fixed_queue_length(p_scb->data_queue) > bta_pan_cb.q_level)
-      osi_free(fixed_queue_try_dequeue(p_scb->data_queue));
-    bta_pan_pm_conn_idle(p_scb);
-  }
-  /* if configured for zero copy push */
-  else if ((bta_pan_cb.flow_mask & BTA_PAN_TX_MASK) == BTA_PAN_TX_PUSH_BUF) {
-    /* if app can accept data */
-    if (p_scb->app_flow_enable) {
-      BT_HDR* p_buf;
-
-      /* read data from the queue */
-      p_buf = (BT_HDR*)fixed_queue_try_dequeue(p_scb->data_queue);
-      if (p_buf != NULL) {
-        /* send data to application */
-        bta_pan_co_tx_writebuf(p_scb->handle, p_scb->app_id,
-                               ((tBTA_PAN_DATA_PARAMS*)p_buf)->src,
-                               ((tBTA_PAN_DATA_PARAMS*)p_buf)->dst,
-                               ((tBTA_PAN_DATA_PARAMS*)p_buf)->protocol, p_buf,
-                               ((tBTA_PAN_DATA_PARAMS*)p_buf)->ext,
-                               ((tBTA_PAN_DATA_PARAMS*)p_buf)->forward);
-      }
-      /* free data that exceeds queue level  */
-      while (fixed_queue_length(p_scb->data_queue) > bta_pan_cb.q_level)
-        osi_free(fixed_queue_try_dequeue(p_scb->data_queue));
-
-      /* if there is more data to be passed to
-      upper layer */
-      if (!fixed_queue_is_empty(p_scb->data_queue)) {
-        p_buf = (BT_HDR*)osi_malloc(sizeof(BT_HDR));
-        p_buf->layer_specific = p_scb->handle;
-        p_buf->event = BTA_PAN_RX_FROM_BNEP_READY_EVT;
-        bta_sys_sendmsg(p_buf);
-      }
+  /* free data that exceeds queue level */
+  while (fixed_queue_length(p_scb->data_queue) > bta_pan_cb.q_level) {
+    BT_HDR* p_buf = (BT_HDR*)fixed_queue_try_dequeue(p_scb->data_queue);
+    if (p_buf != nullptr) {
+      osi_free(p_buf);
     }
   }
+
+  bta_pan_pm_conn_idle(p_scb);
 }
 
 /*******************************************************************************
diff --git a/bta/pan/bta_pan_ci.cc b/bta/pan/bta_pan_ci.cc
index d2fda39..7ebb0ad 100644
--- a/bta/pan/bta_pan_ci.cc
+++ b/bta/pan/bta_pan_ci.cc
@@ -89,8 +89,8 @@
  * Description      This function is called to enable or disable data flow on
  *                  the TX path.  The phone should call this function to
  *                  disable data flow when it is congested and cannot handle
- *                  any more data sent by bta_pan_co_tx_write() or
- *                  bta_pan_co_tx_writebuf().  This function is used when the
+ *                  any more data sent by bta_pan_co_tx_write().
+ *                  This function is used when the
  *                  TX data path is configured to use a push interface.
  *
  *
diff --git a/bta/sys/bta_sys.h b/bta/sys/bta_sys.h
index 29e9a3f..3ceefc1 100644
--- a/bta/sys/bta_sys.h
+++ b/bta/sys/bta_sys.h
@@ -222,6 +222,7 @@
 extern bool bta_sys_is_register(uint8_t id);
 extern uint16_t bta_sys_get_sys_features(void);
 extern void bta_sys_sendmsg(void* p_msg);
+extern void bta_sys_sendmsg_delayed(void* p_msg, const base::TimeDelta& delay);
 extern void bta_sys_start_timer(alarm_t* alarm, uint64_t interval_ms,
                                 uint16_t event, uint16_t layer_specific);
 extern void bta_sys_disable(tBTA_SYS_HW_MODULE module);
diff --git a/bta/sys/bta_sys_main.cc b/bta/sys/bta_sys_main.cc
index 1036e74..e81320d 100644
--- a/bta/sys/bta_sys_main.cc
+++ b/bta/sys/bta_sys_main.cc
@@ -532,6 +532,14 @@
   }
 }
 
+void bta_sys_sendmsg_delayed(void* p_msg, const base::TimeDelta& delay) {
+  if (do_in_main_thread_delayed(
+          FROM_HERE, base::Bind(&bta_sys_event, static_cast<BT_HDR*>(p_msg)),
+          delay) != BT_STATUS_SUCCESS) {
+    LOG(ERROR) << __func__ << ": do_in_main_thread_delayed failed";
+  }
+}
+
 /*******************************************************************************
  *
  * Function         bta_sys_start_timer
diff --git a/bta/test/bta_hf_client_add_record_test.cc b/bta/test/bta_hf_client_add_record_test.cc
new file mode 100644
index 0000000..e975b56
--- /dev/null
+++ b/bta/test/bta_hf_client_add_record_test.cc
@@ -0,0 +1,52 @@
+#include <base/logging.h>
+#include <gtest/gtest.h>
+
+#include "bta/hf_client/bta_hf_client_sdp.cc"
+#include "btif/src/btif_hf_client.cc"
+
+static uint16_t gVersion;
+
+void LogMsg(uint32_t trace_set_mask, const char* fmt_str, ...) {}
+bool SDP_AddProtocolList(uint32_t handle, uint16_t num_elem,
+                         tSDP_PROTOCOL_ELEM* p_elem_list) {
+  return false;
+}
+bool SDP_AddServiceClassIdList(uint32_t handle, uint16_t num_services,
+                               uint16_t* p_service_uuids) {
+  return false;
+}
+bool SDP_AddProfileDescriptorList(uint32_t handle, uint16_t profile_uuid,
+                                  uint16_t version) {
+  gVersion = version;
+  return false;
+}
+bool SDP_AddAttribute(uint32_t handle, uint16_t attr_id, uint8_t attr_type,
+                      uint32_t attr_len, uint8_t* p_val) {
+  return false;
+}
+bool SDP_AddUuidSequence(uint32_t handle, uint16_t attr_id, uint16_t num_uuids,
+                         uint16_t* p_uuids) {
+  return false;
+}
+
+class BtaHfClientAddRecordTest : public ::testing::Test {
+ protected:
+  void SetUp() override { gVersion = 0; }
+
+  void TearDown() override {}
+};
+
+TEST_F(BtaHfClientAddRecordTest, test_hf_client_add_record) {
+  tBTA_HF_CLIENT_FEAT features = BTIF_HF_CLIENT_FEATURES;
+  uint32_t sdp_handle = 0;
+  uint8_t scn = 0;
+
+  osi_property_set("persist.bluetooth.hfpclient.sco_s4_supported", "true");
+  bta_hf_client_add_record("Handsfree", scn, features, sdp_handle);
+  EXPECT_EQ(gVersion, 0x0107);
+  sdp_handle++;
+  scn++;
+  osi_property_set("persist.bluetooth.hfpclient.sco_s4_supported", "false");
+  bta_hf_client_add_record("Handsfree", scn, features, sdp_handle);
+  EXPECT_EQ(gVersion, 0x0106);
+}
diff --git a/btif/Android.bp b/btif/Android.bp
old mode 100644
new mode 100755
index 0ecf207..dd24121
--- a/btif/Android.bp
+++ b/btif/Android.bp
@@ -24,8 +24,6 @@
     "system/bt/utils/include",
     "system/bt/include",
     "system/libhwbinder/include",
-    "system/security/keystore/include",
-    "hardware/interfaces/keymaster/4.0/support/include",
 ]
 
 // libbtif static library for target
@@ -57,6 +55,7 @@
         "src/btif_ble_scanner.cc",
         "src/btif_bqr.cc",
         "src/btif_config.cc",
+        "src/btif_config_cache.cc",
         "src/btif_config_transcode.cc",
         "src/btif_core.cc",
         "src/btif_debug.cc",
@@ -73,7 +72,6 @@
         "src/btif_hf_client.cc",
         "src/btif_hh.cc",
         "src/btif_hd.cc",
-        "src/btif_keystore.cc",
         "src/btif_mce.cc",
         "src/btif_pan.cc",
         "src/btif_profile_queue.cc",
@@ -90,10 +88,14 @@
         "src/btif_storage.cc",
         "src/btif_uid.cc",
         "src/btif_util.cc",
+        "src/btif_keystore.cc",
         "src/stack_manager.cc",
     ],
+    header_libs: [
+        "libmedia_headers",
+    ],
     shared_libs: [
-        "libaudioclient",
+        "libaaudio",
         "libcutils",
         "libfmq",
         "liblog",
@@ -102,16 +104,8 @@
         "android.hardware.bluetooth.a2dp@1.0",
         "android.hardware.bluetooth.audio@2.0",
         "libhidlbase",
-        "libhidltransport",
-        "libhwbinder",
         "libutils",
         "libcrypto",
-        "android.hardware.keymaster@4.0",
-        "android.hardware.keymaster@3.0",
-        "libkeymaster4support",
-        "libkeystore_aidl",
-        "libkeystore_binder",
-        "libkeystore_parcelables",
     ],
     whole_static_libs: [
         "avrcp-target-service",
@@ -121,7 +115,6 @@
     ],
     cflags: [
         "-DBUILDCFG",
-        "-Wno-implicit-fallthrough",
     ],
 
 }
@@ -135,29 +128,21 @@
     include_dirs: btifCommonIncludes,
     srcs: [
         "test/btif_storage_test.cc",
-        "test/btif_keystore_test.cc"
     ],
     header_libs: ["libbluetooth_headers"],
     shared_libs: [
-        "libaudioclient",
+        "libaaudio",
+        "android.hardware.bluetooth@1.0",
         "android.hardware.bluetooth.a2dp@1.0",
         "android.hardware.bluetooth.audio@2.0",
         "libfmq",
         "libhidlbase",
-        "libhidltransport",
         "liblog",
         "libprotobuf-cpp-lite",
         "libcutils",
         "libprocessgroup",
         "libutils",
         "libcrypto",
-        "android.hardware.keymaster@4.0",
-        "android.hardware.keymaster@3.0",
-        "libkeymaster4support",
-        "libkeystore_aidl",
-        "libkeystore_binder",
-        "libkeystore_parcelables",
-        "libbinder",
     ],
     static_libs: [
         "libbt-bta",
@@ -175,7 +160,7 @@
         "libbluetooth-types",
         "libosi",
         "libbt-protos-lite",
-    ],
+   ],
     whole_static_libs: [
         "libbtif",
         "libbluetooth-for-tests",
@@ -194,8 +179,8 @@
     test_suites: ["device-tests"],
     include_dirs: btifCommonIncludes,
     srcs: [
-      "src/btif_profile_queue.cc",
-      "test/btif_profile_queue_test.cc"
+        "src/btif_profile_queue.cc",
+        "test/btif_profile_queue_test.cc",
     ],
     header_libs: ["libbluetooth_headers"],
     shared_libs: [
@@ -241,3 +226,51 @@
         misc_undefined: ["bounds"],
     },
 }
+
+// btif config cache unit tests for target
+// ========================================================
+cc_test {
+    name: "net_test_btif_config_cache",
+    defaults: ["fluoride_defaults"],
+    test_suites: ["device-tests"],
+    host_supported: true,
+    include_dirs: btifCommonIncludes,
+    srcs: [
+        "src/btif_config_cache.cc",
+        "test/btif_config_cache_test.cc",
+    ],
+    header_libs: ["libbluetooth_headers"],
+    shared_libs: [
+        "liblog",
+        "libcutils",
+    ],
+    static_libs: [
+        "libbluetooth-types",
+        "libosi",
+        "libgmock",
+        "libc++fs",
+    ],
+    cflags: ["-DBUILDCFG"],
+}
+
+// btif hf client service tests for target
+// ========================================================
+cc_test {
+    name: "net_test_btif_hf_client_service",
+    defaults: ["fluoride_defaults"],
+    test_suites: ["device-tests"],
+    include_dirs: btifCommonIncludes,
+    srcs: [
+        "test/btif_hf_client_service_test.cc",
+    ],
+    header_libs: ["libbluetooth_headers"],
+    shared_libs: [
+        "libcutils",
+        "liblog",
+    ],
+    static_libs: [
+        "libbluetooth-types",
+        "libosi",
+    ],
+    cflags: ["-DBUILDCFG"],
+}
diff --git a/btif/co/bta_av_co.cc b/btif/co/bta_av_co.cc
index 9f17d4a..708bb40 100644
--- a/btif/co/bta_av_co.cc
+++ b/btif/co/bta_av_co.cc
@@ -417,10 +417,14 @@
    *
    * @param peer_address the peer address
    * @param codec_user_config the codec user configuration to set
+   * @param p_restart_output if there is a change in the encoder configuration
+   * that requires restarting of the A2DP connection, flag |p_restart_output|
+   * will be set to true.
    * @return true on success, otherwise false
    */
   bool SetCodecUserConfig(const RawAddress& peer_address,
-                          const btav_a2dp_codec_config_t& codec_user_config);
+                          const btav_a2dp_codec_config_t& codec_user_config,
+                          bool* p_restart_output);
 
   /**
    * Set the codec audio configuration.
@@ -954,8 +958,9 @@
       (p_peer->num_sup_sinks != BTA_AV_CO_NUM_ELEMENTS(p_peer->sinks))) {
     return A2DP_FAIL;
   }
-  APPL_TRACE_DEBUG("%s: last Sink codec reached for peer %s", __func__,
-                   p_peer->addr.ToString().c_str());
+  APPL_TRACE_DEBUG("%s: last Sink codec reached for peer %s (local %s)",
+                   __func__, p_peer->addr.ToString().c_str(),
+                   p_peer->acceptor ? "acceptor" : "initiator");
 
   // Select the Source codec
   const BtaAvCoSep* p_sink = nullptr;
@@ -973,6 +978,32 @@
       return A2DP_FAIL;
     }
   } else {
+    if (btif_av_peer_prefers_mandatory_codec(p_peer->addr)) {
+      // Apply user preferred codec directly before first codec selected.
+      p_sink = FindPeerSink(p_peer, BTAV_A2DP_CODEC_INDEX_SOURCE_SBC);
+      if (p_sink != nullptr) {
+        APPL_TRACE_API("%s: mandatory codec preferred for peer %s", __func__,
+                       p_peer->addr.ToString().c_str());
+        btav_a2dp_codec_config_t high_priority_mandatory{
+            .codec_type = BTAV_A2DP_CODEC_INDEX_SOURCE_SBC,
+            .codec_priority = BTAV_A2DP_CODEC_PRIORITY_HIGHEST,
+            // Using default settings for those untouched fields
+        };
+        uint8_t result_codec_config[AVDT_CODEC_SIZE];
+        bool restart_input = false;
+        bool restart_output = false;
+        bool config_updated = false;
+        tA2DP_ENCODER_INIT_PEER_PARAMS peer_params;
+        GetPeerEncoderParameters(p_peer->addr, &peer_params);
+        p_peer->GetCodecs()->setCodecUserConfig(
+            high_priority_mandatory, &peer_params, p_sink->codec_caps,
+            result_codec_config, &restart_input, &restart_output,
+            &config_updated);
+      } else {
+        APPL_TRACE_WARNING("%s: mandatory codec not found for peer %s",
+                           __func__, p_peer->addr.ToString().c_str());
+      }
+    }
     p_sink = SelectSourceCodec(p_peer);
     if (p_sink == nullptr) {
       APPL_TRACE_ERROR("%s: cannot set up codec for peer %s", __func__,
@@ -1005,6 +1036,11 @@
     memcpy(p_codec_info, p_peer->codec_config, AVDT_CODEC_SIZE);
   }
 
+  // report this peer selectable codecs after retrieved all its capabilities.
+  LOG(INFO) << __func__ << ": retrieved " << +p_peer->num_rx_sinks
+            << " capabilities from peer " << p_peer->addr;
+  ReportSourceCodecState(p_peer);
+
   return A2DP_SUCCESS;
 }
 
@@ -1369,28 +1405,26 @@
   APPL_TRACE_DEBUG("%s: peer %s bta_av_handle: 0x%x delay:0x%x", __func__,
                    peer_address.ToString().c_str(), bta_av_handle, delay);
 
-  btif_av_set_audio_delay(delay);
+  btif_av_set_audio_delay(peer_address, delay);
 }
 
 void BtaAvCo::UpdateMtu(tBTA_AV_HNDL bta_av_handle,
                         const RawAddress& peer_address, uint16_t mtu) {
-  APPL_TRACE_DEBUG("%s: peer %s bta_av_handle: 0x%x mtu: %d", __func__,
-                   peer_address.ToString().c_str(), bta_av_handle, mtu);
+  LOG(INFO) << __func__ << ": peer " << peer_address
+            << " bta_av_handle: " << loghex(bta_av_handle) << " mtu: " << mtu;
 
   // Find the peer
   BtaAvCoPeer* p_peer = FindPeerAndUpdate(bta_av_handle, peer_address);
   if (p_peer == nullptr) {
-    APPL_TRACE_ERROR(
-        "%s: could not find peer entry for bta_av_handle 0x%x peer %s",
-        __func__, bta_av_handle, peer_address.ToString().c_str());
+    LOG(ERROR) << __func__ << ": could not find peer entry for bta_av_handle "
+               << loghex(bta_av_handle) << " peer " << peer_address;
     return;
   }
   p_peer->mtu = mtu;
 }
 
 bool BtaAvCo::SetActivePeer(const RawAddress& peer_address) {
-  APPL_TRACE_DEBUG("%s: peer_address=%s", __func__,
-                   peer_address.ToString().c_str());
+  VLOG(1) << __func__ << ": peer_address=" << peer_address;
 
   std::lock_guard<std::recursive_mutex> lock(codec_lock_);
 
@@ -1409,8 +1443,8 @@
 
   active_peer_ = p_peer;
   memcpy(codec_config_, active_peer_->codec_config, AVDT_CODEC_SIZE);
-  APPL_TRACE_DEBUG("%s: codec = %s", __func__,
-                   A2DP_CodecInfoString(codec_config_).c_str());
+  LOG(INFO) << __func__ << ": codec = " << A2DP_CodecInfoString(codec_config_);
+  // report the selected codec configuration of this new active peer.
   ReportSourceCodecState(active_peer_);
   return true;
 }
@@ -1455,7 +1489,7 @@
 
 bool BtaAvCo::SetCodecUserConfig(
     const RawAddress& peer_address,
-    const btav_a2dp_codec_config_t& codec_user_config) {
+    const btav_a2dp_codec_config_t& codec_user_config, bool* p_restart_output) {
   uint8_t result_codec_config[AVDT_CODEC_SIZE];
   const BtaAvCoSep* p_sink = nullptr;
   bool restart_input = false;
@@ -1463,14 +1497,24 @@
   bool config_updated = false;
   bool success = true;
 
-  APPL_TRACE_DEBUG("%s: peer_address=%s codec_user_config=%s", __func__,
-                   peer_address.ToString().c_str(),
-                   codec_user_config.ToString().c_str());
+  VLOG(1) << __func__ << ": peer_address=" << peer_address
+          << " codec_user_config={" << codec_user_config.ToString() << "}";
+
+  *p_restart_output = false;
 
   BtaAvCoPeer* p_peer = FindPeer(peer_address);
   if (p_peer == nullptr) {
-    APPL_TRACE_ERROR("%s: cannot find peer %s to configure", __func__,
-                     peer_address.ToString().c_str());
+    LOG(ERROR) << __func__ << ": cannot find peer " << peer_address
+               << " to configure";
+    success = false;
+    goto done;
+  }
+
+  // Don't call BTA_AvReconfig() prior to retrieving all peer's capabilities
+  if ((p_peer->num_rx_sinks != p_peer->num_sinks) &&
+      (p_peer->num_sup_sinks != BTA_AV_CO_NUM_ELEMENTS(p_peer->sinks))) {
+    LOG(WARNING) << __func__ << ": peer " << p_peer->addr
+                 << " : not all peer's capabilities have been retrieved";
     success = false;
     goto done;
   }
@@ -1483,10 +1527,9 @@
     p_sink = p_peer->p_sink;
   }
   if (p_sink == nullptr) {
-    APPL_TRACE_ERROR(
-        "%s: peer %s : cannot find peer SEP to configure for codec type %d",
-        __func__, p_peer->addr.ToString().c_str(),
-        codec_user_config.codec_type);
+    LOG(ERROR) << __func__ << ": peer " << p_peer->addr
+               << " : cannot find peer SEP to configure for codec type "
+               << codec_user_config.codec_type;
     success = false;
     goto done;
   }
@@ -1509,35 +1552,31 @@
 
     p_sink = SelectSourceCodec(p_peer);
     if (p_sink == nullptr) {
-      APPL_TRACE_ERROR("%s: peer %s : cannot set up codec for the peer SINK",
-                       __func__, p_peer->addr.ToString().c_str());
-      success = false;
-      goto done;
-    }
-    // Don't call BTA_AvReconfig() prior to retrieving all peer's capabilities
-    if ((p_peer->num_rx_sinks != p_peer->num_sinks) &&
-        (p_peer->num_sup_sinks != BTA_AV_CO_NUM_ELEMENTS(p_peer->sinks))) {
-      APPL_TRACE_WARNING(
-          "%s: peer %s : not all peer's capabilities have been retrieved",
-          __func__, p_peer->addr.ToString().c_str());
+      LOG(ERROR) << __func__ << ": peer " << p_peer->addr
+                 << " : cannot set up codec for the peer SINK";
       success = false;
       goto done;
     }
 
     p_peer->acceptor = false;
-    APPL_TRACE_DEBUG("%s: call BTA_AvReconfig(0x%x)", __func__,
-                     p_peer->BtaAvHandle());
+    VLOG(1) << __func__ << ": call BTA_AvReconfig("
+            << loghex(p_peer->BtaAvHandle()) << ")";
     BTA_AvReconfig(p_peer->BtaAvHandle(), true, p_sink->sep_info_idx,
                    p_peer->codec_config, num_protect, bta_av_co_cp_scmst);
+    *p_restart_output = true;
   }
 
 done:
-  // NOTE: We unconditionally send the upcall even if there is no change
-  // or the user config failed. Thus, the caller would always know whether the
-  // request succeeded or failed.
+  // We send the upcall if there is no change or the user config failed for
+  // current active peer, so the caller would know it failed. If there is no
+  // error, the new selected codec configuration would be sent after we are
+  // ready to start a new session with the audio HAL.
+  // For none active peer, we unconditionally send the upcall, so the caller
+  // would always know the result.
   // NOTE: Currently, the input is restarted by sending an upcall
   // and informing the Media Framework about the change.
-  if (p_peer != nullptr) {
+  if (p_peer != nullptr &&
+      (!restart_output || !success || p_peer != active_peer_)) {
     return ReportSourceCodecState(p_peer);
   }
 
@@ -1550,21 +1589,29 @@
   bool restart_output = false;
   bool config_updated = false;
 
-  APPL_TRACE_DEBUG("%s: codec_audio_config: %s", __func__,
-                   codec_audio_config.ToString().c_str());
+  VLOG(1) << __func__
+          << ": codec_audio_config: " << codec_audio_config.ToString();
 
   // Find the peer that is currently open
   BtaAvCoPeer* p_peer = active_peer_;
   if (p_peer == nullptr) {
-    APPL_TRACE_ERROR("%s: no active peer to configure", __func__);
+    LOG(ERROR) << __func__ << ": no active peer to configure";
+    return false;
+  }
+
+  // Don't call BTA_AvReconfig() prior to retrieving all peer's capabilities
+  if ((p_peer->num_rx_sinks != p_peer->num_sinks) &&
+      (p_peer->num_sup_sinks != BTA_AV_CO_NUM_ELEMENTS(p_peer->sinks))) {
+    LOG(WARNING) << __func__ << ": peer " << p_peer->addr
+                 << " : not all peer's capabilities have been retrieved";
     return false;
   }
 
   // Use the current sink codec
   const BtaAvCoSep* p_sink = p_peer->p_sink;
   if (p_sink == nullptr) {
-    APPL_TRACE_ERROR("%s: peer %s : cannot find peer SEP to configure",
-                     __func__, p_peer->addr.ToString().c_str());
+    LOG(ERROR) << __func__ << ": peer " << p_peer->addr
+               << " : cannot find peer SEP to configure";
     return false;
   }
 
@@ -1585,24 +1632,16 @@
     SaveNewCodecConfig(p_peer, result_codec_config, p_sink->num_protect,
                        p_sink->protect_info);
 
-    // Don't call BTA_AvReconfig() prior to retrieving all peer's capabilities
-    if ((p_peer->num_rx_sinks != p_peer->num_sinks) &&
-        (p_peer->num_sup_sinks != BTA_AV_CO_NUM_ELEMENTS(p_peer->sinks))) {
-      APPL_TRACE_WARNING(
-          "%s: peer %s : not all peer's capabilities have been retrieved",
-          __func__, p_peer->addr.ToString().c_str());
-    } else {
-      p_peer->acceptor = false;
-      APPL_TRACE_DEBUG("%s: call BTA_AvReconfig(0x%x)", __func__,
-                       p_peer->BtaAvHandle());
-      BTA_AvReconfig(p_peer->BtaAvHandle(), true, p_sink->sep_info_idx,
-                     p_peer->codec_config, num_protect, bta_av_co_cp_scmst);
-    }
+    p_peer->acceptor = false;
+    VLOG(1) << __func__ << ": call BTA_AvReconfig("
+            << loghex(p_peer->BtaAvHandle()) << ")";
+    BTA_AvReconfig(p_peer->BtaAvHandle(), true, p_sink->sep_info_idx,
+                   p_peer->codec_config, num_protect, bta_av_co_cp_scmst);
   }
 
   if (config_updated) {
     // NOTE: Currently, the input is restarted by sending an upcall
-    // and informing the Media Framework about the change.
+    // and informing the Media Framework about the change of selected codec.
     return ReportSourceCodecState(p_peer);
   }
 
@@ -1614,22 +1653,19 @@
   std::vector<btav_a2dp_codec_config_t> codecs_local_capabilities;
   std::vector<btav_a2dp_codec_config_t> codecs_selectable_capabilities;
 
-  APPL_TRACE_DEBUG("%s: peer_address=%s", __func__,
-                   p_peer->addr.ToString().c_str());
+  VLOG(1) << __func__ << ": peer_address=" << p_peer->addr;
   A2dpCodecs* codecs = p_peer->GetCodecs();
   CHECK(codecs != nullptr);
   if (!codecs->getCodecConfigAndCapabilities(&codec_config,
                                              &codecs_local_capabilities,
                                              &codecs_selectable_capabilities)) {
-    APPL_TRACE_WARNING(
-        "%s: Peer %s : error reporting audio source codec state: "
-        "cannot get codec config and capabilities",
-        __func__, p_peer->addr.ToString().c_str());
+    LOG(WARNING) << __func__ << ": Peer " << p_peer->addr
+                 << " : error reporting audio source codec state: cannot get "
+                    "codec config and capabilities";
     return false;
   }
-  APPL_TRACE_DEBUG("%s: peer %s codec_config=%s", __func__,
-                   p_peer->addr.ToString().c_str(),
-                   codec_config.ToString().c_str());
+  LOG(INFO) << __func__ << ": peer " << p_peer->addr << " codec_config={"
+            << codec_config.ToString() << "}";
   btif_av_report_source_codec_state(p_peer->addr, codec_config,
                                     codecs_local_capabilities,
                                     codecs_selectable_capabilities);
@@ -1736,19 +1772,14 @@
 
   // Select the codec
   for (const auto& iter : p_peer->GetCodecs()->orderedSourceCodecs()) {
-    APPL_TRACE_DEBUG("%s: trying codec %s", __func__, iter->name().c_str());
+    VLOG(1) << __func__ << ": trying codec " << iter->name();
     p_sink = AttemptSourceCodecSelection(*iter, p_peer);
     if (p_sink != nullptr) {
-      APPL_TRACE_DEBUG("%s: selected codec %s", __func__, iter->name().c_str());
+      VLOG(1) << __func__ << ": selected codec " << iter->name();
       break;
     }
-    APPL_TRACE_DEBUG("%s: cannot use codec %s", __func__, iter->name().c_str());
+    VLOG(1) << __func__ << ": cannot use codec " << iter->name();
   }
-
-  // NOTE: Unconditionally dispatch the event to make sure a callback with
-  // the most recent codec info is generated.
-  ReportSourceCodecState(p_peer);
-
   return p_sink;
 }
 
@@ -1988,10 +2019,8 @@
   bool restart_output = false;
   bool config_updated = false;
 
-  APPL_TRACE_DEBUG("%s: peer_address=%s", __func__,
-                   p_peer->addr.ToString().c_str());
-  APPL_TRACE_DEBUG("%s: codec: %s", __func__,
-                   A2DP_CodecInfoString(p_ota_codec_config).c_str());
+  LOG(INFO) << __func__ << ": peer_address=" << p_peer->addr
+            << ", codec: " << A2DP_CodecInfoString(p_ota_codec_config);
 
   *p_restart_output = false;
 
@@ -2002,8 +2031,8 @@
     // There are no peer SEPs if we didn't do the discovery procedure yet.
     // We have all the information we need from the peer, so we can
     // proceed with the OTA codec configuration.
-    APPL_TRACE_ERROR("%s: peer %s : cannot find peer SEP to configure",
-                     __func__, p_peer->addr.ToString().c_str());
+    LOG(ERROR) << __func__ << ": peer " << p_peer->addr
+               << " : cannot find peer SEP to configure";
     return false;
   }
 
@@ -2012,15 +2041,14 @@
   if (!p_peer->GetCodecs()->setCodecOtaConfig(
           p_ota_codec_config, &peer_params, result_codec_config, &restart_input,
           &restart_output, &config_updated)) {
-    APPL_TRACE_ERROR("%s: peer %s : cannot set OTA config", __func__,
-                     p_peer->addr.ToString().c_str());
+    LOG(ERROR) << __func__ << ": peer " << p_peer->addr
+               << " : cannot set OTA config";
     return false;
   }
 
   if (restart_output) {
-    APPL_TRACE_DEBUG("%s: restart output", __func__);
-    APPL_TRACE_DEBUG("%s: codec: %s", __func__,
-                     A2DP_CodecInfoString(result_codec_config).c_str());
+    VLOG(1) << __func__ << ": restart output for codec: "
+            << A2DP_CodecInfoString(result_codec_config);
 
     *p_restart_output = true;
     p_peer->p_sink = p_sink;
@@ -2030,7 +2058,7 @@
 
   if (restart_input || config_updated) {
     // NOTE: Currently, the input is restarted by sending an upcall
-    // and informing the Media Framework about the change.
+    // and informing the Media Framework about the change of selected codec.
     ReportSourceCodecState(p_peer);
   }
 
@@ -2169,8 +2197,9 @@
 
 bool bta_av_co_set_codec_user_config(
     const RawAddress& peer_address,
-    const btav_a2dp_codec_config_t& codec_user_config) {
-  return bta_av_co_cb.SetCodecUserConfig(peer_address, codec_user_config);
+    const btav_a2dp_codec_config_t& codec_user_config, bool* p_restart_output) {
+  return bta_av_co_cb.SetCodecUserConfig(peer_address, codec_user_config,
+                                         p_restart_output);
 }
 
 bool bta_av_co_set_codec_audio_config(
diff --git a/btif/co/bta_hh_co.cc b/btif/co/bta_hh_co.cc
index 5a7cad8..cec3f69 100644
--- a/btif/co/bta_hh_co.cc
+++ b/btif/co/bta_hh_co.cc
@@ -163,7 +163,29 @@
         APPL_TRACE_ERROR("%s: UHID_FEATURE: Invalid report type = %d", __func__,
                          ev.u.feature.rtype);
       break;
+    case UHID_SET_REPORT:
+      if (ret < (ssize_t)(sizeof(ev.type) + sizeof(ev.u.set_report))) {
+          APPL_TRACE_ERROR("%s: Invalid size read from uhid-dev: %zd < %zu",
+                           __func__, ret, sizeof(ev.type) + sizeof(ev.u.set_report));
+            return -EFAULT;
+        }
 
+        APPL_TRACE_DEBUG("UHID_SET_REPORT: Report type = %d, report_size = %d"
+                          , ev.u.set_report.rtype, ev.u.set_report.size);
+
+        if (ev.u.set_report.rtype == UHID_FEATURE_REPORT)
+            btif_hh_setreport(p_dev, BTHH_FEATURE_REPORT,
+                              ev.u.set_report.size, ev.u.set_report.data);
+        else if (ev.u.set_report.rtype == UHID_OUTPUT_REPORT)
+            btif_hh_setreport(p_dev, BTHH_OUTPUT_REPORT,
+                              ev.u.set_report.size, ev.u.set_report.data);
+        else if(ev.u.set_report.rtype == UHID_INPUT_REPORT)
+            btif_hh_setreport(p_dev, BTHH_INPUT_REPORT,
+                              ev.u.set_report.size, ev.u.set_report.data);
+        else
+            APPL_TRACE_ERROR("%s:UHID_SET_REPORT: Invalid Report type = %d"
+                          , __func__, ev.u.set_report.rtype);
+        break;
     default:
       APPL_TRACE_DEBUG("Invalid event from uhid-dev: %u\n", ev.type);
   }
@@ -571,7 +593,7 @@
   }
 
   // Send the HID report to the kernel.
-  if (p_dev->fd >= 0 && p_dev->get_rpt_snt--) {
+  if (p_dev->fd >= 0 && p_dev->get_rpt_snt > 0 && p_dev->get_rpt_snt--) {
     uint32_t* get_rpt_id =
         (uint32_t*)fixed_queue_dequeue(p_dev->get_rpt_id_queue);
     memset(&ev, 0, sizeof(ev));
diff --git a/btif/co/bta_pan_co.cc b/btif/co/bta_pan_co.cc
index a799d93..86499f3 100644
--- a/btif/co/bta_pan_co.cc
+++ b/btif/co/bta_pan_co.cc
@@ -235,29 +235,6 @@
 
 /*******************************************************************************
  *
- * Function         bta_pan_co_tx_writebuf
- *
- * Description      This function is called by PAN to send data to the phone
- *                  when the TX path is configured to use a push interface with
- *                  zero copy.  The phone must free the buffer using function
- *                  osi_free() when it is through processing the buffer.
- *
- *
- * Returns          true if flow enabled
- *
- ******************************************************************************/
-void bta_pan_co_tx_writebuf(UNUSED_ATTR uint16_t handle,
-                            UNUSED_ATTR uint8_t app_id,
-                            UNUSED_ATTR const RawAddress& src,
-                            UNUSED_ATTR const RawAddress& dst,
-                            UNUSED_ATTR uint16_t protocol,
-                            UNUSED_ATTR BT_HDR* p_buf, UNUSED_ATTR bool ext,
-                            UNUSED_ATTR bool forward) {
-  BTIF_TRACE_API("bta_pan_co_tx_writebuf not used");
-}
-
-/*******************************************************************************
- *
  * Function         bta_pan_co_rx_flow
  *
  * Description      This function is called by PAN to enable or disable
diff --git a/btif/include/btif_a2dp_sink.h b/btif/include/btif_a2dp_sink.h
index 0a9038c..b972c79 100644
--- a/btif/include/btif_a2dp_sink.h
+++ b/btif/include/btif_a2dp_sink.h
@@ -109,6 +109,9 @@
 // |tBTA_AV_SUSPEND|.
 void btif_a2dp_sink_on_suspended(tBTA_AV_SUSPEND* p_av_suspend);
 
+// Start the decoder for the A2DP Sink module.
+bool btif_a2dp_sink_on_start(void);
+
 // Enable/disable discarding of received A2DP frames.
 // If |enable| is true, the discarding is enabled, otherwise is disabled.
 void btif_a2dp_sink_set_rx_flush(bool enable);
diff --git a/btif/include/btif_a2dp_source.h b/btif/include/btif_a2dp_source.h
index 0649012..6d2cbb1 100644
--- a/btif/include/btif_a2dp_source.h
+++ b/btif/include/btif_a2dp_source.h
@@ -89,7 +89,8 @@
 // |codec_user_config| contains the preferred codec user configuration.
 void btif_a2dp_source_encoder_user_config_update_req(
     const RawAddress& peer_addr,
-    const btav_a2dp_codec_config_t& codec_user_config);
+    const std::vector<btav_a2dp_codec_config_t>& codec_user_preferences,
+    std::promise<void> peer_ready_promise);
 
 // Process a request to update the A2DP audio encoding with new audio
 // configuration feeding parameters stored in |codec_audio_config|.
diff --git a/btif/include/btif_api.h b/btif/include/btif_api.h
index 50d833f..f9b8886 100644
--- a/btif/include/btif_api.h
+++ b/btif/include/btif_api.h
@@ -105,7 +105,7 @@
 
 /*******************************************************************************
  *
- * Function         is_niap_mode
+ * Function         is_niap_mode_
  *
  * Description      Checks if BT was enabled in single user mode. In this
  *                  mode, use of keystore for key attestation of LTK is limitee
@@ -118,6 +118,22 @@
 
 /*******************************************************************************
  *
+ * Function         get_niap_config_compare_result
+ *
+ * Description      Get the niap config compare result for confirming the config
+ *                  checksum compare result. When the niap mode doesn't enable,
+ *                  it should be all pass (0b11).
+ *                  Bit define:
+ *                    CONFIG_FILE_COMPARE_PASS = 0b01
+ *                    CONFIG_BACKUP_COMPARE_PASS = 0b10
+ *
+ * Returns          int
+ *
+ ******************************************************************************/
+int get_niap_config_compare_result(void);
+
+/*******************************************************************************
+ *
  * Function         is_atv_device
  *
  * Description      Returns true if the local device is an Android TV
diff --git a/btif/include/btif_av.h b/btif/include/btif_av.h
index e42f056..8ddfc6a 100644
--- a/btif/include/btif_av.h
+++ b/btif/include/btif_av.h
@@ -114,6 +114,14 @@
 bool btif_av_peer_supports_3mbps(const RawAddress& peer_address);
 
 /**
+ * Check whether the mandatory codec is more preferred for this peer.
+ *
+ * @param peer_address the target peer address
+ * @return true if optional codecs are not preferred to be used
+ */
+bool btif_av_peer_prefers_mandatory_codec(const RawAddress& peer_address);
+
+/**
  * Report A2DP Source Codec State for a peer.
  *
  * @param peer_address the address of the peer to report
@@ -163,9 +171,16 @@
 /**
  * Set the audio delay for the stream.
  *
+ * @param peer_address the address of the peer to report
  * @param delay the delay to set in units of 1/10ms
  */
-void btif_av_set_audio_delay(uint16_t delay);
+void btif_av_set_audio_delay(const RawAddress& peer_address, uint16_t delay);
+
+/**
+ * Get the audio delay for the stream.
+ *  @param  none
+ */
+uint16_t btif_av_get_audio_delay(void);
 
 /**
  * Reset the audio delay and count of audio bytes sent to zero.
@@ -188,6 +203,12 @@
 bool btif_av_is_a2dp_offload_enabled(void);
 
 /**
+ *  check A2DP offload enabled and running
+ *  @param  none
+ */
+bool btif_av_is_a2dp_offload_running(void);
+
+/**
  * Check whether peer device is silenced
  *
  * @param peer_address to check
diff --git a/btif/include/btif_av_co.h b/btif/include/btif_av_co.h
index 93d6d7e..01b3b2f 100644
--- a/btif/include/btif_av_co.h
+++ b/btif/include/btif_av_co.h
@@ -48,10 +48,11 @@
 // Sets the user preferred codec configuration.
 // The peer address is |peer_addr|.
 // |codec_user_config| contains the preferred codec configuration.
+// |restart_output| is used to know whether AV is reconfiguring with remote.
 // Returns true on success, otherwise false.
 bool bta_av_co_set_codec_user_config(
     const RawAddress& peer_addr,
-    const btav_a2dp_codec_config_t& codec_user_config);
+    const btav_a2dp_codec_config_t& codec_user_config, bool* p_restart_output);
 
 // Sets the Audio HAL selected audio feeding parameters.
 // Those parameters are applied only to the currently selected codec.
diff --git a/btif/include/btif_avrcp_audio_track.h b/btif/include/btif_avrcp_audio_track.h
index 20b21ff..eeca1c8 100644
--- a/btif/include/btif_avrcp_audio_track.h
+++ b/btif/include/btif_avrcp_audio_track.h
@@ -35,8 +35,8 @@
  * should eventually be
  * deleted using BtifAvrcpAudioTrackDelete (see below).
  */
-void* BtifAvrcpAudioTrackCreate(int trackFreq, int bits_per_sample,
-                                int channelType);
+void* BtifAvrcpAudioTrackCreate(int trackFreq, int bitsPerSample,
+                                int channelCount);
 
 /**
  * Starts the audio track.
@@ -66,4 +66,4 @@
  * Used only for debugging.
  */
 int BtifAvrcpAudioTrackWriteData(void* handle, void* audioBuffer,
-                                 int bufferlen);
+                                 int bufferLength);
diff --git a/btif/include/btif_bqr.h b/btif/include/btif_bqr.h
index ecf69f1..f22544b 100644
--- a/btif/include/btif_bqr.h
+++ b/btif/include/btif_bqr.h
@@ -19,6 +19,7 @@
 
 #include "btm_api_types.h"
 #include "common/leaky_bonded_queue.h"
+#include "osi/include/osi.h"
 
 namespace bluetooth {
 namespace bqr {
@@ -44,6 +45,25 @@
 //   [(e)SCO Voice Choppy]
 //     When the controller detects the factors which will cause voice choppy,
 //     the controller shall report (e)SCO Voice Choppy event to the host.
+//
+//   [Root Inflammation]
+//     When the controller encounters an error it shall report Root Inflammation
+//     event indicating the error code to the host.
+//
+//   [LMP/LL message trace]
+//     The controller sends the LMP/LL message handshaking with the remote
+//     device to the host.
+//
+//   [Bluetooth Multi-profile/Coex scheduling trace]
+//     The controller sends its scheduling information on handling the Bluetooth
+//     multiple profiles and wireless coexistence in the 2.4 Ghz band to the
+//     host.
+//
+//   [Enable the Controller Debug Information mechanism]
+//     After enabling the Controller Debug Information mechanism, the controller
+//     just can autonomously report debug logging information via the Controller
+//     Debug Info sub-event to the host.
+//
 
 // Bit masks for the selected quality event reporting.
 static constexpr uint32_t kQualityEventMaskAllOff = 0;
@@ -51,33 +71,64 @@
 static constexpr uint32_t kQualityEventMaskApproachLsto = 0x00000002;
 static constexpr uint32_t kQualityEventMaskA2dpAudioChoppy = 0x00000004;
 static constexpr uint32_t kQualityEventMaskScoVoiceChoppy = 0x00000008;
+static constexpr uint32_t kQualityEventMaskRootInflammation = 0x00000010;
+static constexpr uint32_t kQualityEventMaskLmpMessageTrace = 0x00010000;
+static constexpr uint32_t kQualityEventMaskBtSchedulingTrace = 0x00020000;
+static constexpr uint32_t kQualityEventMaskControllerDbgInfo = 0x00040000;
 static constexpr uint32_t kQualityEventMaskAll =
     kQualityEventMaskMonitorMode | kQualityEventMaskApproachLsto |
-    kQualityEventMaskA2dpAudioChoppy | kQualityEventMaskScoVoiceChoppy;
+    kQualityEventMaskA2dpAudioChoppy | kQualityEventMaskScoVoiceChoppy |
+    kQualityEventMaskRootInflammation | kQualityEventMaskLmpMessageTrace |
+    kQualityEventMaskBtSchedulingTrace | kQualityEventMaskControllerDbgInfo;
 // Define the minimum time interval (in ms) of quality event reporting for the
 // selected quality event(s). Controller Firmware should not report the next
 // event within the defined time interval.
 static constexpr uint16_t kMinReportIntervalNoLimit = 0;
 static constexpr uint16_t kMinReportIntervalMaxMs = 0xFFFF;
-// Total length of all BQR parameters except Vendor Specific Parameters.
-static constexpr uint8_t kBqrParamTotalLen = 48;
+// The maximum count of Log Dump related event can be written in the log file.
+static constexpr uint16_t kLogDumpEventPerFile = 0x00FF;
+// Total length of all parameters of the link Quality related event except
+// Vendor Specific Parameters.
+static constexpr uint8_t kLinkQualityParamTotalLen = 48;
+// Total length of all parameters of the ROOT_INFLAMMATION event except Vendor
+// Specific Parameters.
+static constexpr uint8_t kRootInflammationParamTotalLen = 3;
+// Total length of all parameters of the Log Dump related event except Vendor
+// Specific Parameters.
+static constexpr uint8_t kLogDumpParamTotalLen = 3;
 // Warning criteria of the RSSI value.
 static constexpr int8_t kCriWarnRssi = -80;
 // Warning criteria of the unused AFH channel count.
 static constexpr uint8_t kCriWarnUnusedCh = 55;
 // The queue size of recording the BQR events.
 static constexpr uint8_t kBqrEventQueueSize = 25;
-// The minimum size of the ROOT_INFLAMMATION event
-// HCI_VENDOR_SPECIFIC_EVT(1) + BQR sub event(1) + BQR report ID(1) +
-// error code(1) + vendor error code(1) = 5
-static constexpr uint8_t kRootInflammationPacketMinSize = 5;
-
 // The Property of BQR event mask configuration.
 static constexpr const char* kpPropertyEventMask =
     "persist.bluetooth.bqr.event_mask";
 // The Property of BQR minimum report interval configuration.
 static constexpr const char* kpPropertyMinReportIntervalMs =
     "persist.bluetooth.bqr.min_interval_ms";
+// Path of the LMP/LL message trace log file.
+static constexpr const char* kpLmpLlMessageTraceLogPath =
+    "/data/misc/bluetooth/logs/lmp_ll_message_trace.log";
+// Path of the last LMP/LL message trace log file.
+static constexpr const char* kpLmpLlMessageTraceLastLogPath =
+    "/data/misc/bluetooth/logs/lmp_ll_message_trace.log.last";
+// Path of the Bluetooth Multi-profile/Coex scheduling trace log file.
+static constexpr const char* kpBtSchedulingTraceLogPath =
+    "/data/misc/bluetooth/logs/bt_scheduling_trace.log";
+// Path of the last Bluetooth Multi-profile/Coex scheduling trace log file.
+static constexpr const char* kpBtSchedulingTraceLastLogPath =
+    "/data/misc/bluetooth/logs/bt_scheduling_trace.log.last";
+
+// File Descriptor of LMP/LL message trace log
+static int LmpLlMessageTraceLogFd = INVALID_FD;
+// File Descriptor of Bluetooth Multi-profile/Coex scheduling trace log
+static int BtSchedulingTraceLogFd = INVALID_FD;
+// Counter of LMP/LL message trace
+static uint16_t LmpLlMessageTraceCounter = 0;
+// Counter of Bluetooth Multi-profile/Coex scheduling trace
+static uint16_t BtSchedulingTraceCounter = 0;
 
 // Action definition
 //
@@ -96,7 +147,10 @@
   QUALITY_REPORT_ID_APPROACH_LSTO = 0x02,
   QUALITY_REPORT_ID_A2DP_AUDIO_CHOPPY = 0x03,
   QUALITY_REPORT_ID_SCO_VOICE_CHOPPY = 0x04,
-  QUALITY_REPORT_ID_ROOT_INFLAMMATION = 0x05
+  QUALITY_REPORT_ID_ROOT_INFLAMMATION = 0x05,
+  QUALITY_REPORT_ID_LMP_LL_MESSAGE_TRACE = 0x11,
+  QUALITY_REPORT_ID_BT_SCHEDULING_TRACE = 0x12,
+  QUALITY_REPORT_ID_CONTROLLER_DBG_INFO = 0x13
 };
 
 // Packet Type definition
@@ -138,17 +192,92 @@
   uint16_t minimum_report_interval_ms;
 } BqrConfiguration;
 
+// Link quality related BQR event
+typedef struct {
+  // Quality report ID.
+  uint8_t quality_report_id;
+  // Packet type of the connection.
+  uint8_t packet_types;
+  // Connection handle of the connection.
+  uint16_t connection_handle;
+  // Performing Role for the connection.
+  uint8_t connection_role;
+  // Current Transmit Power Level for the connection. This value is the same as
+  // the controller's response to the HCI_Read_Transmit_Power_Level HCI command.
+  uint8_t tx_power_level;
+  // Received Signal Strength Indication (RSSI) value for the connection. This
+  // value is an absolute receiver signal strength value.
+  int8_t rssi;
+  // Signal-to-Noise Ratio (SNR) value for the connection. It is the average
+  // SNR of all the channels used by the link currently.
+  uint8_t snr;
+  // Indicates the number of unused channels in AFH_channel_map.
+  uint8_t unused_afh_channel_count;
+  // Indicates the number of the channels which are interfered and quality is
+  // bad but are still selected for AFH.
+  uint8_t afh_select_unideal_channel_count;
+  // Current Link Supervision Timeout Setting.
+  // Unit: N * 0.3125 ms (1 Bluetooth Clock)
+  uint16_t lsto;
+  // Piconet Clock for the specified Connection_Handle. This value is the same
+  // as the controller's response to HCI_Read_Clock HCI command with the
+  // parameter "Which_Clock" of 0x01 (Piconet Clock).
+  // Unit: N * 0.3125 ms (1 Bluetooth Clock)
+  uint32_t connection_piconet_clock;
+  // The count of retransmission.
+  uint32_t retransmission_count;
+  // The count of no RX.
+  uint32_t no_rx_count;
+  // The count of NAK (Negative Acknowledge).
+  uint32_t nak_count;
+  // Timestamp of last TX ACK.
+  // Unit: N * 0.3125 ms (1 Bluetooth Clock)
+  uint32_t last_tx_ack_timestamp;
+  // The count of Flow-off (STOP).
+  uint32_t flow_off_count;
+  // Timestamp of last Flow-on (GO).
+  // Unit: N * 0.3125 ms (1 Bluetooth Clock)
+  uint32_t last_flow_on_timestamp;
+  // Buffer overflow count (how many bytes of TX data are dropped) since the
+  // last event.
+  uint32_t buffer_overflow_bytes;
+  // Buffer underflow count (in byte).
+  uint32_t buffer_underflow_bytes;
+  // For the controller vendor to obtain more vendor specific parameters.
+  uint8_t* vendor_specific_parameter;
+} BqrLinkQualityEvent;
+
+// Log dump related BQR event
+typedef struct {
+  // Quality report ID.
+  uint8_t quality_report_id;
+  // Connection handle of the connection.
+  uint16_t connection_handle;
+  // For the controller vendor to obtain more vendor specific parameters.
+  uint8_t* vendor_specific_parameter;
+} BqrLogDumpEvent;
+
 // BQR sub-event of Vendor Specific Event
 class BqrVseSubEvt {
  public:
-  // Parse the Bluetooth Quality Report VSE sub-event.
+  // Parse the Link Quality related BQR event.
   //
   // @param length Total length of all parameters contained in the sub-event.
   // @param p_param_buf A pointer to the parameters contained in the sub-event.
-  // @return false If the parameter total length is abnormal.
-  //         true If all parameters are parsed successfully.
-  bool ParseBqrEvt(uint8_t length, uint8_t* p_param_buf);
-
+  void ParseBqrLinkQualityEvt(uint8_t length, uint8_t* p_param_buf);
+  // Write the LMP/LL message trace to the log file.
+  //
+  // @param fd The File Descriptor of the log file.
+  // @param length Total length of all parameters contained in the sub-event.
+  // @param p_param_buf A pointer to the parameters contained in the sub-event.
+  void WriteLmpLlTraceLogFile(int fd, uint8_t length, uint8_t* p_param_buf);
+  // Write the Bluetooth Multi-profile/Coex scheduling trace to the log file.
+  //
+  // @param fd The File Descriptor of the log file.
+  // @param length Total length of all parameters contained in the sub-event.
+  // @param p_param_buf A pointer to the parameters contained in the sub-event.
+  void WriteBtSchedulingTraceLogFile(int fd, uint8_t length,
+                                     uint8_t* p_param_buf);
   // Get a string representation of the Bluetooth Quality event.
   //
   // @return a string representation of the Bluetooth Quality event.
@@ -159,56 +288,10 @@
   }
 
   virtual ~BqrVseSubEvt() = default;
-
-  // Quality report ID.
-  uint8_t quality_report_id_ = 0;
-  // Packet type of the connection.
-  uint8_t packet_types_ = 0;
-  // Connection handle of the connection.
-  uint16_t connection_handle_ = 0;
-  // Performing Role for the connection.
-  uint8_t connection_role_ = 0;
-  // Current Transmit Power Level for the connection. This value is the same as
-  // the controller's response to the HCI_Read_Transmit_Power_Level HCI command.
-  uint8_t tx_power_level_ = 0;
-  // Received Signal Strength Indication (RSSI) value for the connection. This
-  // value is an absolute receiver signal strength value.
-  int8_t rssi_ = 0;
-  // Signal-to-Noise Ratio (SNR) value for the connection. It is the average
-  // SNR of all the channels used by the link currently.
-  uint8_t snr_ = 0;
-  // Indicates the number of unused channels in AFH_channel_map.
-  uint8_t unused_afh_channel_count_ = 0;
-  // Indicates the number of the channels which are interfered and quality is
-  // bad but are still selected for AFH.
-  uint8_t afh_select_unideal_channel_count_ = 0;
-  // Current Link Supervision Timeout Setting.
-  // Unit: N * 0.3125 ms (1 Bluetooth Clock)
-  uint16_t lsto_ = 0;
-  // Piconet Clock for the specified Connection_Handle. This value is the same
-  // as the controller's response to HCI_Read_Clock HCI command with the
-  // parameter "Which_Clock" of 0x01 (Piconet Clock).
-  // Unit: N * 0.3125 ms (1 Bluetooth Clock)
-  uint32_t connection_piconet_clock_ = 0;
-  // The count of retransmission.
-  uint32_t retransmission_count_ = 0;
-  // The count of no RX.
-  uint32_t no_rx_count_ = 0;
-  // The count of NAK (Negative Acknowledge).
-  uint32_t nak_count_ = 0;
-  // Timestamp of last TX ACK.
-  // Unit: N * 0.3125 ms (1 Bluetooth Clock)
-  uint32_t last_tx_ack_timestamp_ = 0;
-  // The count of Flow-off (STOP).
-  uint32_t flow_off_count_ = 0;
-  // Timestamp of last Flow-on (GO).
-  // Unit: N * 0.3125 ms (1 Bluetooth Clock)
-  uint32_t last_flow_on_timestamp_ = 0;
-  // Buffer overflow count (how many bytes of TX data are dropped) since the
-  // last event.
-  uint32_t buffer_overflow_bytes_ = 0;
-  // Buffer underflow count (in byte).
-  uint32_t buffer_underflow_bytes_ = 0;
+  // Link Quality related BQR event
+  BqrLinkQualityEvent bqr_link_quality_event_ = {};
+  // Log Dump related BQR event
+  BqrLogDumpEvent bqr_log_dump_event_ = {};
   // Local wall clock timestamp of receiving BQR VSE sub-event
   std::tm tm_timestamp_ = {};
 };
@@ -236,23 +319,11 @@
 //   mechanism in the Bluetooth controller.
 void EnableBtQualityReport(bool is_enable);
 
-// Dump Bluetooth Quality Report information.
-//
-// @param fd The file descriptor to use for dumping information.
-void DebugDump(int fd);
-
 // Configure Bluetooth Quality Report setting to the Bluetooth controller.
 //
 // @param bqr_config The struct of configuration parameters.
 void ConfigureBqr(const BqrConfiguration& bqr_config);
 
-// Invoked on completion of Bluetooth Quality Report configuration. Then it will
-// Register/Unregister for receiving VSE - Bluetooth Quality Report sub event.
-//
-// @param current_evt_mask Indicates current quality event bit mask setting in
-//   the Bluetooth controller.
-void ConfigureBqrCmpl(uint32_t current_evt_mask);
-
 // Callback invoked on completion of vendor specific Bluetooth Quality Report
 // command.
 //
@@ -260,13 +331,56 @@
 //   specific command complete event.
 void BqrVscCompleteCallback(tBTM_VSC_CMPL* p_vsc_cmpl_params);
 
-// Record a new incoming Bluetooth Quality Report in quality event queue.
+// Invoked on completion of Bluetooth Quality Report configuration. Then it will
+// Register/Unregister for receiving VSE - Bluetooth Quality Report sub-event.
 //
-// @param len Lengths of the quality report sent from the Bluetooth
+// @param current_evt_mask Indicates current quality event bit mask setting in
+//   the Bluetooth controller.
+void ConfigureBqrCmpl(uint32_t current_evt_mask);
+
+// Categorize the incoming Bluetooth Quality Report.
+//
+// @param length Lengths of the quality report sent from the Bluetooth
 //   controller.
-// @param p_quality_report A pointer to the quality report which is sent from
-//   the Bluetooth controller via Vendor Specific Event.
-void AddBqrEventToQueue(uint8_t length, uint8_t* p_stream);
+// @param p_bqr_event A pointer to the BQR VSE sub-event which is sent from the
+//   Bluetooth controller.
+void CategorizeBqrEvent(uint8_t length, uint8_t* p_bqr_event);
+
+// Record a new incoming Link Quality related BQR event in quality event queue.
+//
+// @param length Lengths of the Link Quality related BQR event.
+// @param p_link_quality_event A pointer to the Link Quality related BQR event.
+void AddLinkQualityEventToQueue(uint8_t length, uint8_t* p_link_quality_event);
+
+// Dump the LMP/LL message handshaking with the remote device to a log file.
+//
+// @param length Lengths of the LMP/LL message trace event.
+// @param p_lmp_ll_message_event A pointer to the LMP/LL message trace event.
+void DumpLmpLlMessage(uint8_t length, uint8_t* p_lmp_ll_message_event);
+
+// Open the LMP/LL message trace log file.
+//
+// @return a file descriptor of the LMP/LL message trace log file.
+int OpenLmpLlTraceLogFile();
+
+// Dump the Bluetooth Multi-profile/Coex scheduling information to a log file.
+//
+// @param length Lengths of the Bluetooth Multi-profile/Coex scheduling trace
+//   event.
+// @param p_bt_scheduling_event A pointer to the Bluetooth Multi-profile/Coex
+//   scheduling trace event.
+void DumpBtScheduling(uint8_t length, uint8_t* p_bt_scheduling_event);
+
+// Open the Bluetooth Multi-profile/Coex scheduling trace log file.
+//
+// @return a file descriptor of the Bluetooth Multi-profile/Coex scheduling
+//   trace log file.
+int OpenBtSchedulingTraceLogFile();
+
+// Dump Bluetooth Quality Report information.
+//
+// @param fd The file descriptor to use for dumping information.
+void DebugDump(int fd);
 
 }  // namespace bqr
 }  // namespace bluetooth
diff --git a/btif/include/btif_common.h b/btif/include/btif_common.h
index 39034c3..67e8e11 100644
--- a/btif/include/btif_common.h
+++ b/btif/include/btif_common.h
@@ -28,7 +28,7 @@
 #include <hardware/bluetooth.h>
 
 #include "bt_types.h"
-#include "bta_api.h"
+#include "bta/include/bta_api.h"
 #include "osi/include/log.h"
 #include "osi/include/osi.h"
 
diff --git a/btif/include/btif_config.h b/btif/include/btif_config.h
index 028afa7..d1678cd 100644
--- a/btif/include/btif_config.h
+++ b/btif/include/btif_config.h
@@ -29,6 +29,18 @@
 
 static const char BTIF_CONFIG_MODULE[] = "btif_config_module";
 
+static const std::string BT_CONFIG_KEY_SDP_DI_MANUFACTURER =
+    "SdpDiManufacturer";
+static const std::string BT_CONFIG_KEY_SDP_DI_MODEL = "SdpDiModel";
+static const std::string BT_CONFIG_KEY_SDP_DI_HW_VERSION =
+    "SdpDiHardwareVersion";
+static const std::string BT_CONFIG_KEY_SDP_DI_VENDOR_ID_SRC =
+    "SdpDiVendorIdSource";
+
+static const std::string BT_CONFIG_KEY_REMOTE_VER_MFCT = "Manufacturer";
+static const std::string BT_CONFIG_KEY_REMOTE_VER_VER = "LmpVer";
+static const std::string BT_CONFIG_KEY_REMOTE_VER_SUBVER = "LmpSubVer";
+
 bool btif_config_has_section(const char* section);
 bool btif_config_exist(const std::string& section, const std::string& key);
 bool btif_config_get_int(const std::string& section, const std::string& key,
@@ -52,7 +64,7 @@
 size_t btif_config_get_bin_length(const std::string& section,
                                   const std::string& key);
 
-std::list<section_t>& btif_config_sections();
+const std::list<section_t>& btif_config_sections();
 
 void btif_config_save(void);
 void btif_config_flush(void);
@@ -64,3 +76,39 @@
 bool btif_get_device_type(const RawAddress& bd_addr, int* p_device_type);
 
 void btif_debug_config_dump(int fd);
+
+typedef struct {
+  std::string (*checksum_read)(const char* filename);
+  bool (*checksum_save)(const std::string& checksum,
+                        const std::string& filename);
+  bool (*config_get_bool)(const config_t& config, const std::string& section,
+                          const std::string& key, bool def_value);
+  int (*config_get_int)(const config_t& config, const std::string& section,
+                        const std::string& key, int def_value);
+  const std::string* (*config_get_string)(const config_t& config,
+                                          const std::string& section,
+                                          const std::string& key,
+                                          const std::string* def_value);
+  uint64_t (*config_get_uint64)(const config_t& config,
+                                const std::string& section,
+                                const std::string& key, uint64_t def_value);
+  bool (*config_has_key)(const config_t& config, const std::string& section,
+                         const std::string& key);
+  bool (*config_has_section)(const config_t& config,
+                             const std::string& section);
+  std::unique_ptr<config_t> (*config_new)(const char* filename);
+  std::unique_ptr<config_t> (*config_new_clone)(const config_t& src);
+  std::unique_ptr<config_t> (*config_new_empty)(void);
+  bool (*config_remove_key)(config_t* config, const std::string& section,
+                            const std::string& key);
+  bool (*config_remove_section)(config_t* config, const std::string& section);
+  bool (*config_save)(const config_t& config, const std::string& filename);
+  void (*config_set_bool)(config_t* config, const std::string& section,
+                          const std::string& key, bool value);
+  void (*config_set_int)(config_t* config, const std::string& section,
+                         const std::string& key, int value);
+  void (*config_set_string)(config_t* config, const std::string& section,
+                            const std::string& key, const std::string& value);
+  void (*config_set_uint64)(config_t* config, const std::string& section,
+                            const std::string& key, uint64_t value);
+} storage_config_t;
diff --git a/btif/include/btif_config_cache.h b/btif/include/btif_config_cache.h
new file mode 100644
index 0000000..aedcd4e
--- /dev/null
+++ b/btif/include/btif_config_cache.h
@@ -0,0 +1,60 @@
+/*
+ *  Copyright 2020 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#pragma once
+
+#include <map>
+#include <unordered_set>
+
+#include "common/lru.h"
+#include "osi/include/config.h"
+#include "osi/include/log.h"
+#include "raw_address.h"
+
+class BtifConfigCache {
+ public:
+  explicit BtifConfigCache(size_t capacity);
+  ~BtifConfigCache();
+
+  void Clear();
+  void Init(std::unique_ptr<config_t> source);
+  const std::list<section_t>& GetPersistentSections();
+  config_t PersistentSectionCopy();
+  bool HasSection(const std::string& section_name);
+  bool HasUnpairedSection(const std::string& section_name);
+  bool HasPersistentSection(const std::string& section_name);
+  bool HasKey(const std::string& section_name, const std::string& key);
+  bool RemoveKey(const std::string& section_name, const std::string& key);
+  void RemovePersistentSectionsWithKey(const std::string& key);
+
+  // Setters and getters
+  void SetString(std::string section_name, std::string key, std::string value);
+  std::optional<std::string> GetString(const std::string& section_name,
+                                       const std::string& key);
+  void SetInt(std::string section_name, std::string key, int value);
+  std::optional<int> GetInt(const std::string& section_name,
+                            const std::string& key);
+  void SetUint64(std::string section_name, std::string key, uint64_t value);
+  std::optional<uint64_t> GetUint64(const std::string& section_name,
+                                    const std::string& key);
+  void SetBool(std::string section_name, std::string key, bool value);
+  std::optional<bool> GetBool(const std::string& section_name,
+                              const std::string& key);
+
+ private:
+  bluetooth::common::LruCache<std::string, section_t> unpaired_devices_cache_;
+  config_t paired_devices_list_;
+};
diff --git a/btif/include/btif_dm.h b/btif/include/btif_dm.h
index a6c39eb..fae8929 100644
--- a/btif/include/btif_dm.h
+++ b/btif/include/btif_dm.h
@@ -19,7 +19,7 @@
 #ifndef BTIF_DM_H
 #define BTIF_DM_H
 
-#include "bta_api.h"
+#include "bta/include/bta_api.h"
 #include "bte_appl.h"
 #include "btif_uid.h"
 
diff --git a/btif/include/btif_keystore.h b/btif/include/btif_keystore.h
index 1b3663d..460f3b8 100644
--- a/btif/include/btif_keystore.h
+++ b/btif/include/btif_keystore.h
@@ -1,76 +1,25 @@
-/******************************************************************************
+/*
+ * Copyright 2020 The Android Open Source Project
  *
- *  Copyright 2019 Google, Inc.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
  *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at:
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- *
- ******************************************************************************/
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 
-#include <base/logging.h>
-#include <keystore/keystore_client_impl.h>
-#include <mutex>
-
-#include "osi/include/alarm.h"
-#include "osi/include/allocator.h"
-#include "osi/include/compat.h"
-#include "osi/include/config.h"
-#include "osi/include/log.h"
-#include "osi/include/osi.h"
-#include "osi/include/properties.h"
+#include <hardware/bt_keystore.h>
 
 namespace bluetooth {
-/**
- * Client wrapper to access AndroidKeystore.
- *
- * <p>Use to encrypt/decrypt data and store to disk.
- */
-class BtifKeystore {
- public:
-  /**
-   * @param keystore_client injected pre-created client object for keystore
-   */
-  BtifKeystore(keystore::KeystoreClient* keystore_client);
+namespace bluetooth_keystore {
 
-  /**
-   * Encrypts given data
-   *
-   * <p>Returns a string representation of the encrypted data
-   *
-   * @param data to be encrypted
-   * @param flags for keystore
-   */
-  std::string Encrypt(const std::string& data, int32_t flags);
+BluetoothKeystoreInterface* getBluetoothKeystoreInterface();
 
-  /**
-   * Returns a decrypted string representation of the encrypted data or empty
-   * string on error.
-   *
-   * @param input encrypted data
-   */
-  std::string Decrypt(const std::string& input_filename);
-
-  /**
-   * Check for existence of keystore key.
-   *
-   * This key can be cleared if a user manually wipes bluetooth storage data
-   * b/133214365
-   */
-  bool DoesKeyExist();
-
- private:
-  std::unique_ptr<keystore::KeystoreClient> keystore_client_;
-  std::mutex api_mutex_;
-  bool GenerateKey(const std::string& name, int32_t flags);
-};
-
-}  // namespace bluetooth
+}  // namespace bluetooth_keystore
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/btif/src/bluetooth.cc b/btif/src/bluetooth.cc
index 158a739..a00c0c6 100644
--- a/btif/src/bluetooth.cc
+++ b/btif/src/bluetooth.cc
@@ -59,12 +59,16 @@
 #include "btif_debug_btsnoop.h"
 #include "btif_debug_conn.h"
 #include "btif_hf.h"
+#include "btif_keystore.h"
 #include "btif_storage.h"
 #include "btsnoop.h"
 #include "btsnoop_mem.h"
 #include "common/address_obfuscator.h"
+#include "common/metric_id_allocator.h"
 #include "common/metrics.h"
 #include "device/include/interop.h"
+#include "main/shim/dumpsys.h"
+#include "main/shim/shim.h"
 #include "osi/include/alarm.h"
 #include "osi/include/allocation_tracker.h"
 #include "osi/include/log.h"
@@ -82,6 +86,8 @@
 bt_callbacks_t* bt_hal_cbacks = NULL;
 bool restricted_mode = false;
 bool niap_mode = false;
+const int CONFIG_COMPARE_ALL_PASS = 0b11;
+int niap_config_compare_result = CONFIG_COMPARE_ALL_PASS;
 bool is_local_device_atv = false;
 
 /*******************************************************************************
@@ -135,9 +141,16 @@
  ****************************************************************************/
 
 static int init(bt_callbacks_t* callbacks, bool start_restricted,
-                bool is_niap_mode, bool is_atv) {
-  LOG_INFO(LOG_TAG, "%s: start restricted = %d ; niap = %d", __func__,
-           start_restricted, is_niap_mode);
+                bool is_niap_mode, int config_compare_result, bool is_atv) {
+  LOG_INFO(LOG_TAG,
+           "%s: start restricted = %d ; niap = %d, config compare result = %d",
+           __func__, start_restricted, is_niap_mode, config_compare_result);
+
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    LOG_INFO(LOG_TAG, "%s Enable Gd bluetooth functionality", __func__);
+  } else {
+    LOG_INFO(LOG_TAG, "%s Preserving legacy bluetooth functionality", __func__);
+  }
 
   if (interface_ready()) return BT_STATUS_DONE;
 
@@ -148,7 +161,9 @@
   bt_hal_cbacks = callbacks;
   restricted_mode = start_restricted;
   niap_mode = is_niap_mode;
+  niap_config_compare_result = config_compare_result;
   is_local_device_atv = is_atv;
+
   stack_manager_get_interface()->init_stack();
   btif_debug_init();
   return BT_STATUS_SUCCESS;
@@ -172,6 +187,11 @@
 
 bool is_restricted_mode() { return restricted_mode; }
 bool is_niap_mode() { return niap_mode; }
+// if niap mode disable, will always return CONFIG_COMPARE_ALL_PASS(0b11)
+// indicate don't check config checksum.
+int get_niap_config_compare_result() {
+  return niap_mode ? niap_config_compare_result : CONFIG_COMPARE_ALL_PASS;
+}
 
 bool is_atv_device() { return is_local_device_atv; }
 
@@ -325,9 +345,13 @@
   HearingAid::DebugDump(fd);
   connection_manager::dump(fd);
   bluetooth::bqr::DebugDump(fd);
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    bluetooth::shim::Dump(fd);
+  } else {
 #if (BTSNOOP_MEM == TRUE)
-  btif_debug_btsnoop_dump(fd);
+    btif_debug_btsnoop_dump(fd);
 #endif
+  }
 }
 
 static void dumpMetrics(std::string* output) {
@@ -379,6 +403,9 @@
 
   if (is_profile(profile_id, BT_PROFILE_HEARING_AID_ID))
     return btif_hearing_aid_get_interface();
+
+  if (is_profile(profile_id, BT_KEYSTORE_ID))
+    return bluetooth::bluetooth_keystore::getBluetoothKeystoreInterface();
   return NULL;
 }
 
@@ -452,6 +479,11 @@
       address);
 }
 
+static int get_metric_id(const RawAddress& address) {
+  return bluetooth::common::MetricIdAllocator::GetInstance().AllocateId(
+      address);
+}
+
 EXPORT_SYMBOL bt_interface_t bluetoothInterface = {
     sizeof(bluetoothInterface),
     init,
@@ -488,4 +520,5 @@
     interop_database_add,
     get_avrcp_service,
     obfuscate_address,
+    get_metric_id,
 };
diff --git a/btif/src/btif_a2dp.cc b/btif/src/btif_a2dp.cc
index e2d6132..c9f4592 100644
--- a/btif/src/btif_a2dp.cc
+++ b/btif/src/btif_a2dp.cc
@@ -60,6 +60,7 @@
     if (bluetooth::audio::a2dp::is_hal_2_0_enabled()) {
       bluetooth::audio::a2dp::ack_stream_started(status);
     } else if (btif_av_is_a2dp_offload_enabled()) {
+      // TODO: BluetoothA2dp@1.0 is deprecated
       btif_a2dp_audio_on_started(status);
     } else {
       btif_a2dp_command_ack(status);
@@ -75,7 +76,7 @@
       LOG(WARNING) << __func__ << ": peer " << peer_addr << " A2DP is suspending and ignores the started event";
       return false;
     }
-    if (btif_av_is_a2dp_offload_enabled()) {
+    if (btif_av_is_a2dp_offload_running()) {
       btif_av_stream_start_offload();
     } else if (bluetooth::audio::a2dp::is_hal_2_0_enabled()) {
       if (btif_av_get_peer_sep() == AVDT_TSEP_SNK) {
@@ -98,6 +99,7 @@
     if (bluetooth::audio::a2dp::is_hal_2_0_enabled()) {
       bluetooth::audio::a2dp::ack_stream_started(A2DP_CTRL_ACK_FAILURE);
     } else if (btif_av_is_a2dp_offload_enabled()) {
+      // TODO: BluetoothA2dp@1.0 is deprecated
       btif_a2dp_audio_on_started(p_av_start->status);
     } else {
       btif_a2dp_command_ack(A2DP_CTRL_ACK_FAILURE);
@@ -116,9 +118,10 @@
     return;
   }
   if (bluetooth::audio::a2dp::is_hal_2_0_enabled() ||
-      !btif_av_is_a2dp_offload_enabled()) {
+      !btif_av_is_a2dp_offload_running()) {
     btif_a2dp_source_on_stopped(p_av_suspend);
   } else if (p_av_suspend != NULL) {
+    // TODO: BluetoothA2dp@1.0 is deprecated
     btif_a2dp_audio_on_stopped(p_av_suspend->status);
   }
 }
@@ -131,9 +134,10 @@
     return;
   }
   if (bluetooth::audio::a2dp::is_hal_2_0_enabled() ||
-      !btif_av_is_a2dp_offload_enabled()) {
+      !btif_av_is_a2dp_offload_running()) {
     btif_a2dp_source_on_suspended(p_av_suspend);
   } else if (p_av_suspend != NULL) {
+    // TODO: BluetoothA2dp@1.0 is deprecated
     btif_a2dp_audio_on_suspended(p_av_suspend->status);
   }
 }
@@ -159,7 +163,7 @@
       ack = A2DP_CTRL_ACK_FAILURE;
       break;
   }
-  if (btif_av_is_a2dp_offload_enabled()) {
+  if (btif_av_is_a2dp_offload_running()) {
     if (ack != BTA_AV_SUCCESS && btif_av_stream_started_ready()) {
       // Offload request will return with failure from btif_av sm if
       // suspend is triggered for remote start. Disconnect only if SoC
@@ -173,6 +177,7 @@
     bluetooth::audio::a2dp::ack_stream_started(ack);
   } else {
     btif_a2dp_command_ack(ack);
+    // TODO: BluetoothA2dp@1.0 is deprecated
     btif_a2dp_audio_on_started(status);
   }
 }
diff --git a/btif/src/btif_a2dp_audio_interface.cc b/btif/src/btif_a2dp_audio_interface.cc
index 7e46818..026baa1 100644
--- a/btif/src/btif_a2dp_audio_interface.cc
+++ b/btif/src/btif_a2dp_audio_interface.cc
@@ -137,15 +137,15 @@
 
 class BluetoothAudioHost : public IBluetoothAudioHost {
  public:
-  Return<void> startStream() {
+  Return<void> startStream() override {
     btif_a2dp_audio_send_start_req();
     return Void();
   }
-  Return<void> suspendStream() {
+  Return<void> suspendStream() override {
     btif_a2dp_audio_send_suspend_req();
     return Void();
   }
-  Return<void> stopStream() {
+  Return<void> stopStream() override {
     btif_a2dp_audio_send_stop_req();
     return Void();
   }
@@ -160,9 +160,9 @@
 
 class BluetoothAudioDeathRecipient : public hidl_death_recipient {
  public:
-  virtual void serviceDied(
+  void serviceDied(
       uint64_t /*cookie*/,
-      const wp<::android::hidl::base::V1_0::IBase>& /*who*/) {
+      const wp<::android::hidl::base::V1_0::IBase>& /*who*/) override {
     LOG_ERROR(LOG_TAG, "%s", __func__);
     // Restart the session on the correct thread
     do_in_main_thread(FROM_HERE,
diff --git a/btif/src/btif_a2dp_sink.cc b/btif/src/btif_a2dp_sink.cc
index aa75d21..0d96f16 100644
--- a/btif/src/btif_a2dp_sink.cc
+++ b/btif/src/btif_a2dp_sink.cc
@@ -64,7 +64,9 @@
   BTIF_MEDIA_SINK_DECODER_UPDATE = 1,
   BTIF_MEDIA_SINK_CLEAR_TRACK,
   BTIF_MEDIA_SINK_SET_FOCUS_STATE,
-  BTIF_MEDIA_SINK_AUDIO_RX_FLUSH
+  BTIF_MEDIA_SINK_AUDIO_RX_FLUSH,
+  BTIF_MEDIA_SINK_START,
+  BTIF_MEDIA_SINK_SUSPEND
 };
 
 typedef struct {
@@ -149,6 +151,8 @@
     btif_a2dp_sink_focus_state_t state);
 static void btif_a2dp_sink_audio_rx_flush_event();
 static void btif_a2dp_sink_clear_track_event_req();
+static void btif_a2dp_sink_on_start_event();
+static void btif_a2dp_sink_on_suspend_event();
 
 UNUSED_ATTR static const char* dump_media_event(uint16_t event) {
   switch (event) {
@@ -156,6 +160,8 @@
     CASE_RETURN_STR(BTIF_MEDIA_SINK_CLEAR_TRACK)
     CASE_RETURN_STR(BTIF_MEDIA_SINK_SET_FOCUS_STATE)
     CASE_RETURN_STR(BTIF_MEDIA_SINK_AUDIO_RX_FLUSH)
+    CASE_RETURN_STR(BTIF_MEDIA_SINK_START)
+    CASE_RETURN_STR(BTIF_MEDIA_SINK_SUSPEND)
     default:
       break;
   }
@@ -364,6 +370,12 @@
     case BTIF_MEDIA_SINK_AUDIO_RX_FLUSH:
       btif_a2dp_sink_audio_rx_flush_event();
       break;
+    case BTIF_MEDIA_SINK_START:
+      btif_a2dp_sink_on_start_event();
+      break;
+    case BTIF_MEDIA_SINK_SUSPEND:
+      btif_a2dp_sink_on_suspend_event();
+      break;
     default:
       LOG_ERROR(LOG_TAG, "%s: unknown event %d", __func__, p_msg->event);
       break;
@@ -392,6 +404,11 @@
 
 void btif_a2dp_sink_on_idle() {
   LOG_INFO(LOG_TAG, "%s", __func__);
+  BT_HDR* p_buf = reinterpret_cast<BT_HDR*>(osi_malloc(sizeof(BT_HDR)));
+  p_buf->event = BTIF_MEDIA_SINK_SUSPEND;
+  btif_a2dp_sink_cb.worker_thread.DoInThread(
+      FROM_HERE, base::BindOnce(btif_a2dp_sink_command_ready, p_buf));
+
   if (btif_a2dp_sink_state == BTIF_A2DP_SINK_STATE_OFF) return;
   btif_a2dp_sink_audio_handle_stop_decoding();
   btif_a2dp_sink_clear_track_event_req();
@@ -399,16 +416,37 @@
 
 void btif_a2dp_sink_on_stopped(UNUSED_ATTR tBTA_AV_SUSPEND* p_av_suspend) {
   LOG_INFO(LOG_TAG, "%s", __func__);
+  BT_HDR* p_buf = reinterpret_cast<BT_HDR*>(osi_malloc(sizeof(BT_HDR)));
+  p_buf->event = BTIF_MEDIA_SINK_SUSPEND;
+  btif_a2dp_sink_cb.worker_thread.DoInThread(
+      FROM_HERE, base::BindOnce(btif_a2dp_sink_command_ready, p_buf));
+
   if (btif_a2dp_sink_state == BTIF_A2DP_SINK_STATE_OFF) return;
   btif_a2dp_sink_audio_handle_stop_decoding();
 }
 
 void btif_a2dp_sink_on_suspended(UNUSED_ATTR tBTA_AV_SUSPEND* p_av_suspend) {
   LOG_INFO(LOG_TAG, "%s", __func__);
+  BT_HDR* p_buf = reinterpret_cast<BT_HDR*>(osi_malloc(sizeof(BT_HDR)));
+  p_buf->event = BTIF_MEDIA_SINK_SUSPEND;
+  btif_a2dp_sink_cb.worker_thread.DoInThread(
+      FROM_HERE, base::BindOnce(btif_a2dp_sink_command_ready, p_buf));
+
   if (btif_a2dp_sink_state == BTIF_A2DP_SINK_STATE_OFF) return;
   btif_a2dp_sink_audio_handle_stop_decoding();
 }
 
+bool btif_a2dp_sink_on_start() {
+  LOG_INFO(LOG_TAG, "%s", __func__);
+
+  BT_HDR* p_buf = reinterpret_cast<BT_HDR*>(osi_malloc(sizeof(BT_HDR)));
+  p_buf->event = BTIF_MEDIA_SINK_START;
+  btif_a2dp_sink_cb.worker_thread.DoInThread(
+      FROM_HERE, base::BindOnce(btif_a2dp_sink_command_ready, p_buf));
+
+  return true;
+}
+
 static void btif_a2dp_sink_audio_handle_stop_decoding() {
   LOG_INFO(LOG_TAG, "%s", __func__);
   alarm_t* old_alarm;
@@ -592,10 +630,14 @@
     return;
   }
 
+  if (btif_a2dp_sink_cb.decoder_interface->decoder_configure != nullptr) {
+    btif_a2dp_sink_cb.decoder_interface->decoder_configure(p_buf->codec_info);
+  }
+
   APPL_TRACE_DEBUG("%s: create audio track", __func__);
   btif_a2dp_sink_cb.audio_track =
 #ifndef OS_GENERIC
-      BtifAvrcpAudioTrackCreate(sample_rate, bits_per_sample, channel_type);
+      BtifAvrcpAudioTrackCreate(sample_rate, bits_per_sample, channel_count);
 #else
       NULL;
 #endif
@@ -694,3 +736,25 @@
   btif_a2dp_sink_cb.worker_thread.DoInThread(
       FROM_HERE, base::BindOnce(btif_a2dp_sink_command_ready, p_buf));
 }
+
+static void btif_a2dp_sink_on_start_event() {
+  LOG_INFO(LOG_TAG, "%s", __func__);
+
+  if ((btif_a2dp_sink_cb.decoder_interface != nullptr) &&
+      (btif_a2dp_sink_cb.decoder_interface->decoder_start != nullptr)) {
+    btif_a2dp_sink_cb.decoder_interface->decoder_start();
+  }
+
+  return;
+}
+
+static void btif_a2dp_sink_on_suspend_event() {
+  LOG_INFO(LOG_TAG, "%s", __func__);
+
+  if ((btif_a2dp_sink_cb.decoder_interface != nullptr) &&
+      (btif_a2dp_sink_cb.decoder_interface->decoder_suspend != nullptr)) {
+    btif_a2dp_sink_cb.decoder_interface->decoder_suspend();
+  }
+
+  return;
+}
diff --git a/btif/src/btif_a2dp_source.cc b/btif/src/btif_a2dp_source.cc
index 9951ce1..cd06a12 100644
--- a/btif/src/btif_a2dp_source.cc
+++ b/btif/src/btif_a2dp_source.cc
@@ -246,7 +246,8 @@
     const RawAddress& peer_address);
 static void btif_a2dp_source_encoder_user_config_update_event(
     const RawAddress& peer_address,
-    const btav_a2dp_codec_config_t& codec_user_config);
+    const std::vector<btav_a2dp_codec_config_t>& codec_user_preferences,
+    std::promise<void> peer_ready_promise);
 static void btif_a2dp_source_audio_feeding_update_event(
     const btav_a2dp_codec_config_t& codec_audio_config);
 static bool btif_a2dp_source_audio_tx_flush_req(void);
@@ -358,6 +359,7 @@
   }
   if (!bluetooth::audio::a2dp::init(&btif_a2dp_source_thread)) {
     if (btif_av_is_a2dp_offload_enabled()) {
+      // TODO: BluetoothA2dp@1.0 is deprecated
       LOG(WARNING) << __func__ << ": Using BluetoothA2dp HAL";
     } else {
       LOG(WARNING) << __func__ << ": Using legacy HAL";
@@ -397,9 +399,11 @@
   }
   if (bluetooth::audio::a2dp::is_hal_2_0_enabled()) {
     bluetooth::audio::a2dp::start_session();
+    bluetooth::audio::a2dp::set_remote_delay(btif_av_get_audio_delay());
     BluetoothMetricsLogger::GetInstance()->LogBluetoothSessionStart(
         bluetooth::common::CONNECTION_TECHNOLOGY_TYPE_BREDR, 0);
   } else if (btif_av_is_a2dp_offload_enabled()) {
+    // TODO: BluetoothA2dp@1.0 is deprecated
     btif_a2dp_audio_interface_start_session();
   } else {
     BluetoothMetricsLogger::GetInstance()->LogBluetoothSessionStart(
@@ -433,12 +437,10 @@
   }
 
   // Start the session.
-  // If audio was streaming before, start audio streaming as well.
   btif_a2dp_source_start_session(new_peer_address,
                                  std::move(peer_ready_promise));
-  if (is_streaming) {
-    btif_a2dp_source_start_audio_req();
-  }
+  // If audio was streaming before, DON'T start audio streaming, but leave the
+  // control to the audio HAL.
   return true;
 }
 
@@ -468,6 +470,7 @@
     BluetoothMetricsLogger::GetInstance()->LogBluetoothSessionEnd(
         bluetooth::common::DISCONNECT_REASON_UNKNOWN, 0);
   } else if (btif_av_is_a2dp_offload_enabled()) {
+    // TODO: BluetoothA2dp@1.0 is deprecated
     btif_a2dp_audio_interface_end_session();
   } else {
     BluetoothMetricsLogger::GetInstance()->LogBluetoothSessionEnd(
@@ -502,6 +505,7 @@
   if (bluetooth::audio::a2dp::is_hal_2_0_enabled()) {
     bluetooth::audio::a2dp::cleanup();
   } else if (btif_av_is_a2dp_offload_enabled()) {
+    // TODO: BluetoothA2dp@1.0 is deprecated
     btif_a2dp_audio_interface_end_session();
   } else {
     btif_a2dp_control_cleanup();
@@ -618,23 +622,57 @@
 
 void btif_a2dp_source_encoder_user_config_update_req(
     const RawAddress& peer_address,
-    const btav_a2dp_codec_config_t& codec_user_config) {
-  LOG_INFO(LOG_TAG, "%s: peer_address=%s state=%s", __func__,
-           peer_address.ToString().c_str(),
-           btif_a2dp_source_cb.StateStr().c_str());
-  btif_a2dp_source_thread.DoInThread(
-      FROM_HERE, base::Bind(&btif_a2dp_source_encoder_user_config_update_event,
-                            peer_address, codec_user_config));
+    const std::vector<btav_a2dp_codec_config_t>& codec_user_preferences,
+    std::promise<void> peer_ready_promise) {
+  LOG(INFO) << __func__ << ": peer_address=" << peer_address
+            << " state=" << btif_a2dp_source_cb.StateStr() << " "
+            << codec_user_preferences.size() << " codec_preference(s)";
+  if (!btif_a2dp_source_thread.DoInThread(
+          FROM_HERE,
+          base::BindOnce(&btif_a2dp_source_encoder_user_config_update_event,
+                         peer_address, codec_user_preferences,
+                         std::move(peer_ready_promise)))) {
+    // cannot set promise but triggers crash
+    LOG(FATAL) << __func__ << ": peer_address=" << peer_address
+               << " state=" << btif_a2dp_source_cb.StateStr()
+               << " fails to context switch";
+  }
 }
 
 static void btif_a2dp_source_encoder_user_config_update_event(
     const RawAddress& peer_address,
-    const btav_a2dp_codec_config_t& codec_user_config) {
-  LOG_INFO(LOG_TAG, "%s: peer_address=%s state=%s", __func__,
-           peer_address.ToString().c_str(),
-           btif_a2dp_source_cb.StateStr().c_str());
-  if (!bta_av_co_set_codec_user_config(peer_address, codec_user_config)) {
-    LOG_ERROR(LOG_TAG, "%s: cannot update codec user configuration", __func__);
+    const std::vector<btav_a2dp_codec_config_t>& codec_user_preferences,
+    std::promise<void> peer_ready_promise) {
+  bool restart_output = false;
+  bool success = false;
+  for (auto codec_user_config : codec_user_preferences) {
+    success = bta_av_co_set_codec_user_config(peer_address, codec_user_config,
+                                              &restart_output);
+    if (success) {
+      LOG(INFO) << __func__ << ": peer_address=" << peer_address
+                << " state=" << btif_a2dp_source_cb.StateStr()
+                << " codec_preference={" << codec_user_config.ToString()
+                << "} restart_output=" << (restart_output ? "true" : "false");
+      break;
+    }
+  }
+  if (success && restart_output) {
+    // Codec reconfiguration is in progress, and it is safe to unlock since
+    // remaining tasks like starting audio session and reporting new codec
+    // will be handled by BTA_AV_RECONFIG_EVT later.
+    peer_ready_promise.set_value();
+    return;
+  }
+  if (!success) {
+    LOG(ERROR) << __func__ << ": cannot update codec user configuration(s)";
+  }
+  if (!peer_address.IsEmpty() && peer_address == btif_av_source_active_peer()) {
+    // No more actions needed with remote, and if succeed, user had changed the
+    // config like the bits per sample only. Let's resume the session now.
+    btif_a2dp_source_start_session(peer_address, std::move(peer_ready_promise));
+  } else {
+    // Unlock for non-active peer
+    peer_ready_promise.set_value();
   }
 }
 
@@ -672,35 +710,33 @@
 
   if (btif_a2dp_source_cb.State() == BtifA2dpSource::kStateOff) return;
 
-  /* allow using this api for other than suspend */
-  if (p_av_suspend != nullptr) {
-    if (p_av_suspend->status != BTA_AV_SUCCESS) {
-      LOG_ERROR(LOG_TAG, "%s: A2DP stop request failed: status=%d", __func__,
-                p_av_suspend->status);
-      if (p_av_suspend->initiator) {
-        LOG_WARN(LOG_TAG, "%s: A2DP stop request failed: status=%d", __func__,
-                 p_av_suspend->status);
-        if (bluetooth::audio::a2dp::is_hal_2_0_enabled()) {
-          bluetooth::audio::a2dp::ack_stream_suspended(A2DP_CTRL_ACK_FAILURE);
-        } else {
-          btif_a2dp_command_ack(A2DP_CTRL_ACK_FAILURE);
-        }
+  // allow using this API for other (acknowledgement and stopping media task)
+  // than suspend
+  if (p_av_suspend != nullptr && p_av_suspend->status != BTA_AV_SUCCESS) {
+    LOG_ERROR(LOG_TAG, "%s: A2DP stop failed: status=%d, initiator=%s",
+              __func__, p_av_suspend->status,
+              (p_av_suspend->initiator ? "true" : "false"));
+    if (p_av_suspend->initiator) {
+      if (bluetooth::audio::a2dp::is_hal_2_0_enabled()) {
+        bluetooth::audio::a2dp::ack_stream_suspended(A2DP_CTRL_ACK_FAILURE);
+      } else {
+        btif_a2dp_command_ack(A2DP_CTRL_ACK_FAILURE);
       }
-      return;
     }
-  }
-  if (btif_av_is_a2dp_offload_enabled()) {
+  } else if (btif_av_is_a2dp_offload_running()) {
     bluetooth::audio::a2dp::ack_stream_suspended(A2DP_CTRL_ACK_SUCCESS);
     return;
   }
-  /* ensure tx frames are immediately suspended */
-  btif_a2dp_source_cb.tx_flush = true;
 
-  /* request to stop media task */
+  // ensure tx frames are immediately suspended
+  btif_a2dp_source_cb.tx_flush = true;
+  // ensure tx frames are immediately flushed
   btif_a2dp_source_audio_tx_flush_req();
+
+  // request to stop media task
   btif_a2dp_source_stop_audio_req();
 
-  /* once stream is fully stopped we will ack back */
+  // once software stream is fully stopped we will ack back
 }
 
 void btif_a2dp_source_on_suspended(tBTA_AV_SUSPEND* p_av_suspend) {
@@ -709,29 +745,32 @@
 
   if (btif_a2dp_source_cb.State() == BtifA2dpSource::kStateOff) return;
 
-  /* check for status failures */
+  CHECK(p_av_suspend != nullptr) << "Suspend result could not be nullptr";
+
+  // check for status failures
   if (p_av_suspend->status != BTA_AV_SUCCESS) {
+    LOG_WARN(LOG_TAG, "%s: A2DP suspend failed: status=%d, initiator=%s",
+             __func__, p_av_suspend->status,
+             (p_av_suspend->initiator ? "true" : "false"));
     if (p_av_suspend->initiator) {
-      LOG_WARN(LOG_TAG, "%s: A2DP suspend request failed: status=%d", __func__,
-               p_av_suspend->status);
       if (bluetooth::audio::a2dp::is_hal_2_0_enabled()) {
         bluetooth::audio::a2dp::ack_stream_suspended(A2DP_CTRL_ACK_FAILURE);
       } else {
         btif_a2dp_command_ack(A2DP_CTRL_ACK_FAILURE);
       }
     }
-  }
-  if (btif_av_is_a2dp_offload_enabled()) {
+  } else if (btif_av_is_a2dp_offload_running()) {
     bluetooth::audio::a2dp::ack_stream_suspended(A2DP_CTRL_ACK_SUCCESS);
     return;
   }
-  /* once stream is fully stopped we will ack back */
 
-  /* ensure tx frames are immediately flushed */
+  // ensure tx frames are immediately suspended
   btif_a2dp_source_cb.tx_flush = true;
 
-  /* stop timer tick */
+  // stop timer tick
   btif_a2dp_source_stop_audio_req();
+
+  // once software stream is fully stopped we will ack back
 }
 
 /* when true media task discards any tx frames */
@@ -742,13 +781,13 @@
 }
 
 static void btif_a2dp_source_audio_tx_start_event(void) {
-  LOG_INFO(LOG_TAG, "%s: media_alarm is %srunning, streaming %s state=%s",
-           __func__,
-           btif_a2dp_source_cb.media_alarm.IsScheduled() ? "" : "not ",
-           btif_a2dp_source_is_streaming() ? "true" : "false",
-           btif_a2dp_source_cb.StateStr().c_str());
+  LOG_INFO(
+      LOG_TAG, "%s: media_alarm is %s, streaming %s state=%s", __func__,
+      btif_a2dp_source_cb.media_alarm.IsScheduled() ? "running" : "stopped",
+      btif_a2dp_source_is_streaming() ? "true" : "false",
+      btif_a2dp_source_cb.StateStr().c_str());
 
-  if (btif_av_is_a2dp_offload_enabled()) return;
+  if (btif_av_is_a2dp_offload_running()) return;
 
   /* Reset the media feeding state */
   CHECK(btif_a2dp_source_cb.encoder_interface != nullptr);
@@ -758,6 +797,9 @@
       "%s: starting timer %" PRIu64 " ms", __func__,
       btif_a2dp_source_cb.encoder_interface->get_encoder_interval_ms());
 
+  /* audio engine starting, reset tx suspended flag */
+  btif_a2dp_source_cb.tx_flush = false;
+
   wakelock_acquire();
   btif_a2dp_source_cb.media_alarm.SchedulePeriodic(
       btif_a2dp_source_thread.GetWeakPtr(), FROM_HERE,
@@ -782,13 +824,13 @@
 }
 
 static void btif_a2dp_source_audio_tx_stop_event(void) {
-  LOG_INFO(LOG_TAG, "%s: media_alarm is %srunning, streaming %s state=%s",
-           __func__,
-           btif_a2dp_source_cb.media_alarm.IsScheduled() ? "" : "not ",
-           btif_a2dp_source_is_streaming() ? "true" : "false",
-           btif_a2dp_source_cb.StateStr().c_str());
+  LOG_INFO(
+      LOG_TAG, "%s: media_alarm is %s, streaming %s state=%s", __func__,
+      btif_a2dp_source_cb.media_alarm.IsScheduled() ? "running" : "stopped",
+      btif_a2dp_source_is_streaming() ? "true" : "false",
+      btif_a2dp_source_cb.StateStr().c_str());
 
-  if (btif_av_is_a2dp_offload_enabled()) return;
+  if (btif_av_is_a2dp_offload_running()) return;
 
   btif_a2dp_source_cb.stats.session_end_us =
       bluetooth::common::time_get_os_boottime_us();
@@ -818,7 +860,7 @@
     UIPC_Close(*a2dp_uipc, UIPC_CH_ID_AV_AUDIO);
 
     /*
-     * Try to send acknowldegment once the media stream is
+     * Try to send acknowledgement once the media stream is
      * stopped. This will make sure that the A2DP HAL layer is
      * un-blocked on wait for acknowledgment for the sent command.
      * This resolves a corner cases AVDTP SUSPEND collision
@@ -842,7 +884,7 @@
 }
 
 static void btif_a2dp_source_audio_handle_timer(void) {
-  if (btif_av_is_a2dp_offload_enabled()) return;
+  if (btif_av_is_a2dp_offload_running()) return;
 
   uint64_t timestamp_us = bluetooth::common::time_get_os_boottime_us();
   log_tstamps_us("A2DP Source tx timer", timestamp_us);
@@ -995,7 +1037,7 @@
   /* Flush all enqueued audio buffers (encoded) */
   LOG_INFO(LOG_TAG, "%s: state=%s", __func__,
            btif_a2dp_source_cb.StateStr().c_str());
-  if (btif_av_is_a2dp_offload_enabled()) return;
+  if (btif_av_is_a2dp_offload_running()) return;
 
   if (btif_a2dp_source_cb.encoder_interface != nullptr)
     btif_a2dp_source_cb.encoder_interface->feeding_flush();
@@ -1255,7 +1297,7 @@
   SchedulingStats enqueue_stats = stats.tx_queue_enqueue_stats;
   A2dpSessionMetrics metrics;
   metrics.codec_index = stats.codec_index;
-  metrics.is_a2dp_offload = btif_av_is_a2dp_offload_enabled();
+  metrics.is_a2dp_offload = btif_av_is_a2dp_offload_running();
   // session_start_us is 0 when btif_a2dp_source_start_audio_req() is not called
   // mark the metric duration as invalid (-1) in this case
   if (stats.session_start_us != 0) {
diff --git a/btif/src/btif_av.cc b/btif/src/btif_av.cc
index 24e32ec..1003b0f 100644
--- a/btif/src/btif_av.cc
+++ b/btif/src/btif_av.cc
@@ -110,6 +110,7 @@
 };
 
 class BtifAvPeer;
+static bt_status_t sink_set_active_device(const RawAddress& peer_address);
 
 // Should not need dedicated Suspend state as actual actions are no
 // different than Open state. Suspend flags are needed however to prevent
@@ -277,9 +278,18 @@
 
   bool IsConnected() const;
   bool IsStreaming() const;
-  bool IsInSilenceMode() const { return is_silenced_; };
+  bool IsInSilenceMode() const { return is_silenced_; }
 
-  void SetSilence(bool silence) { is_silenced_ = silence; };
+  void SetSilence(bool silence) { is_silenced_ = silence; }
+
+  // AVDTP delay reporting in 1/10 milliseconds
+  void SetDelayReport(uint16_t delay) { delay_report_ = delay; }
+  uint16_t GetDelayReport() const { return delay_report_; }
+
+  void SetMandatoryCodecPreferred(bool preferred) {
+    mandatory_codec_preferred_ = preferred;
+  }
+  bool IsMandatoryCodecPreferred() const { return mandatory_codec_preferred_; }
 
   /**
    * Check whether any of the flags specified by the bitlags mask is set.
@@ -329,6 +339,8 @@
   uint8_t flags_;
   bool self_initiated_connection_;
   bool is_silenced_;
+  uint16_t delay_report_;
+  bool mandatory_codec_preferred_ = false;
 };
 
 class BtifAvSource {
@@ -346,7 +358,8 @@
 
   bt_status_t Init(
       btav_source_callbacks_t* callbacks, int max_connected_audio_devices,
-      const std::vector<btav_a2dp_codec_config_t>& codec_priorities);
+      const std::vector<btav_a2dp_codec_config_t>& codec_priorities,
+      const std::vector<btav_a2dp_codec_config_t>& offloading_preference);
   void Cleanup();
 
   btav_source_callbacks_t* Callbacks() { return callbacks_; }
@@ -494,23 +507,12 @@
       const std::vector<btav_a2dp_codec_config_t>& codec_preferences,
       std::promise<void> peer_ready_promise) {
     // Restart the session if the codec for the active peer is updated
-    bool restart_session =
-        ((active_peer_ == peer_address) && !active_peer_.IsEmpty());
-    if (restart_session) {
+    if (!peer_address.IsEmpty() && active_peer_ == peer_address) {
       btif_a2dp_source_end_session(active_peer_);
     }
 
-    for (auto cp : codec_preferences) {
-      BTIF_TRACE_DEBUG("%s: codec_preference=%s", __func__,
-                       cp.ToString().c_str());
-      btif_a2dp_source_encoder_user_config_update_req(peer_address, cp);
-    }
-    if (restart_session) {
-      btif_a2dp_source_start_session(active_peer_,
-                                     std::move(peer_ready_promise));
-    } else {
-      peer_ready_promise.set_value();
-    }
+    btif_a2dp_source_encoder_user_config_update_req(
+        peer_address, codec_preferences, std::move(peer_ready_promise));
   }
 
   const std::map<RawAddress, BtifAvPeer*>& Peers() const { return peers_; }
@@ -665,6 +667,7 @@
   case BTA_AV_VENDOR_CMD_EVT:      \
   case BTA_AV_META_MSG_EVT:        \
   case BTA_AV_RC_FEAT_EVT:         \
+  case BTA_AV_RC_PSM_EVT:          \
   case BTA_AV_REMOTE_RSP_EVT: {    \
     btif_rc_handler(e, d);         \
   } break;
@@ -685,9 +688,12 @@
                                     btav_audio_state_t state);
 static void btif_av_report_sink_audio_config_state(
     const RawAddress& peer_address, int sample_rate, int channel_count);
+static void btif_av_query_mandatory_codec_priority(
+    const RawAddress& peer_address);
 static void btif_av_source_initiate_av_open_timer_timeout(void* data);
 static void btif_av_sink_initiate_av_open_timer_timeout(void* data);
-static void bta_av_sink_media_callback(tBTA_AV_EVT event,
+static void bta_av_sink_media_callback(const RawAddress& peer_address,
+                                       tBTA_AV_EVT event,
                                        tBTA_AV_MEDIA* p_data);
 
 static BtifAvPeer* btif_av_source_find_peer(const RawAddress& peer_address) {
@@ -737,6 +743,7 @@
     CASE_RETURN_STR(BTA_AV_META_MSG_EVT)
     CASE_RETURN_STR(BTA_AV_REJECT_EVT)
     CASE_RETURN_STR(BTA_AV_RC_FEAT_EVT)
+    CASE_RETURN_STR(BTA_AV_RC_PSM_EVT)
     CASE_RETURN_STR(BTA_AV_OFFLOAD_START_RSP_EVT)
     CASE_RETURN_STR(BTIF_AV_CONNECT_REQ_EVT)
     CASE_RETURN_STR(BTIF_AV_DISCONNECT_REQ_EVT)
@@ -870,7 +877,8 @@
       av_open_on_rc_timer_(nullptr),
       edr_(0),
       flags_(0),
-      self_initiated_connection_(false) {}
+      self_initiated_connection_(false),
+      delay_report_(0) {}
 
 BtifAvPeer::~BtifAvPeer() { alarm_free(av_open_on_rc_timer_); }
 
@@ -946,7 +954,8 @@
 
 bt_status_t BtifAvSource::Init(
     btav_source_callbacks_t* callbacks, int max_connected_audio_devices,
-    const std::vector<btav_a2dp_codec_config_t>& codec_priorities) {
+    const std::vector<btav_a2dp_codec_config_t>& codec_priorities,
+    const std::vector<btav_a2dp_codec_config_t>& offloading_preference) {
   LOG_INFO(LOG_TAG, "%s: max_connected_audio_devices=%d", __PRETTY_FUNCTION__,
            max_connected_audio_devices);
   if (enabled_) return BT_STATUS_SUCCESS;
@@ -964,6 +973,10 @@
   BTIF_TRACE_DEBUG("a2dp_offload.enable = %d", a2dp_offload_enabled_);
 
   callbacks_ = callbacks;
+  if (a2dp_offload_enabled_) {
+    bluetooth::audio::a2dp::update_codec_offloading_capabilities(
+        offloading_preference);
+  }
   bta_av_co_init(codec_priorities);
 
   if (!btif_a2dp_source_init()) {
@@ -1041,12 +1054,18 @@
         __PRETTY_FUNCTION__, peer_address.ToString().c_str());
     return nullptr;
   }
+
   // Get the BTA Handle (if known)
   if (bta_handle == kBtaHandleUnknown) {
     auto it = peer_id2bta_handle_.find(peer_id);
-    if (it != peer_id2bta_handle_.end()) {
-      bta_handle = it->second;
+    if (it == peer_id2bta_handle_.end() || it->second == kBtaHandleUnknown) {
+      BTIF_TRACE_ERROR(
+          "%s: Cannot create peer for peer_address=%s : "
+          "cannot convert Peer ID=%d to unique BTA Handle",
+          __PRETTY_FUNCTION__, peer_address.ToString().c_str(), peer_id);
+      return nullptr;
     }
+    bta_handle = it->second;
   }
 
   LOG_INFO(LOG_TAG,
@@ -1135,7 +1154,18 @@
 
   // Set the BTA Handle for the Peer (if exists)
   BtifAvPeer* peer = FindPeerByPeerId(peer_id);
-  if (peer != nullptr) {
+  if (peer != nullptr && peer->BtaHandle() != bta_handle) {
+    if (peer->BtaHandle() == kBtaHandleUnknown) {
+      BTIF_TRACE_EVENT(
+          "%s: Assign peer: peer_address=%s bta_handle=0x%x peer_id=%d",
+          __PRETTY_FUNCTION__, peer->PeerAddress().ToString().c_str(),
+          bta_handle, peer_id);
+    } else {
+      BTIF_TRACE_WARNING(
+          "%s: Correct peer: peer_address=%s bta_handle=0x%x->0x%x peer_id=%d",
+          __PRETTY_FUNCTION__, peer->PeerAddress().ToString().c_str(),
+          peer->BtaHandle(), bta_handle, peer_id);
+    }
     peer->SetBtaHandle(bta_handle);
   }
 }
@@ -1232,9 +1262,14 @@
   // Get the BTA Handle (if known)
   if (bta_handle == kBtaHandleUnknown) {
     auto it = peer_id2bta_handle_.find(peer_id);
-    if (it != peer_id2bta_handle_.end()) {
-      bta_handle = it->second;
+    if (it == peer_id2bta_handle_.end() || it->second == kBtaHandleUnknown) {
+      BTIF_TRACE_ERROR(
+          "%s: Cannot create peer for peer_address=%s : "
+          "cannot convert Peer ID=%d to unique BTA Handle",
+          __PRETTY_FUNCTION__, peer_address.ToString().c_str(), peer_id);
+      return nullptr;
     }
+    bta_handle = it->second;
   }
 
   LOG_INFO(LOG_TAG,
@@ -1325,7 +1360,18 @@
 
   // Set the BTA Handle for the Peer (if exists)
   BtifAvPeer* peer = FindPeerByPeerId(peer_id);
-  if (peer != nullptr) {
+  if (peer != nullptr && peer->BtaHandle() != bta_handle) {
+    if (peer->BtaHandle() == kBtaHandleUnknown) {
+      BTIF_TRACE_EVENT(
+          "%s: Assign peer: peer_address=%s bta_handle=0x%x peer_id=%d",
+          __PRETTY_FUNCTION__, peer->PeerAddress().ToString().c_str(),
+          bta_handle, peer_id);
+    } else {
+      BTIF_TRACE_WARNING(
+          "%s: Correct peer: peer_address=%s bta_handle=0x%x->0x%x peer_id=%d",
+          __PRETTY_FUNCTION__, peer->PeerAddress().ToString().c_str(),
+          peer->BtaHandle(), bta_handle, peer_id);
+    }
     peer->SetBtaHandle(bta_handle);
   }
 }
@@ -1421,6 +1467,7 @@
         }
         break;
       }
+      btif_av_query_mandatory_codec_priority(peer_.PeerAddress());
       BTA_AvOpen(peer_.PeerAddress(), peer_.BtaHandle(), true,
                  BTA_SEC_AUTHENTICATE, peer_.LocalUuidServiceClass());
       peer_.StateMachine().TransitionTo(BtifAvStateMachine::kStateOpening);
@@ -1546,6 +1593,7 @@
     case BTA_AV_VENDOR_CMD_EVT:
     case BTA_AV_META_MSG_EVT:
     case BTA_AV_RC_FEAT_EVT:
+    case BTA_AV_RC_PSM_EVT:
     case BTA_AV_REMOTE_RSP_EVT:
       btif_rc_handler(event, (tBTA_AV*)p_data);
       break;
@@ -1841,17 +1889,26 @@
       // If remote tries to start A2DP when DUT is A2DP Source, then Suspend.
       // If A2DP is Sink and call is active, then disconnect the AVDTP channel.
       bool should_suspend = false;
-      if (peer_.IsSink() && !peer_.CheckFlags(BtifAvPeer::kFlagPendingStart |
-                                              BtifAvPeer::kFlagRemoteSuspend)) {
-        LOG(WARNING) << __PRETTY_FUNCTION__ << ": Peer " << peer_.PeerAddress()
-                     << " : trigger Suspend as remote initiated";
-        should_suspend = true;
-      }
+      if (peer_.IsSink()) {
+        if (!peer_.CheckFlags(BtifAvPeer::kFlagPendingStart |
+                              BtifAvPeer::kFlagRemoteSuspend)) {
+          LOG(WARNING) << __PRETTY_FUNCTION__ << ": Peer "
+                       << peer_.PeerAddress()
+                       << " : trigger Suspend as remote initiated";
+          should_suspend = true;
+        } else if (!peer_.IsActivePeer()) {
+          LOG(WARNING) << __PRETTY_FUNCTION__ << ": Peer "
+                       << peer_.PeerAddress()
+                       << " : trigger Suspend as non-active";
+          should_suspend = true;
+        }
 
-      // If peer is A2DP Source, do ACK commands to audio HAL and start media task
-      if (peer_.IsSink() && btif_a2dp_on_started(peer_.PeerAddress(), &p_av->start)) {
-        // Only clear pending flag after acknowledgement
-        peer_.ClearFlags(BtifAvPeer::kFlagPendingStart);
+        // If peer is A2DP Source, do ACK commands to audio HAL and start media
+        // task
+        if (btif_a2dp_on_started(peer_.PeerAddress(), &p_av->start)) {
+          // Only clear pending flag after acknowledgement
+          peer_.ClearFlags(BtifAvPeer::kFlagPendingStart);
+        }
       }
 
       // Remain in Open state if status failed
@@ -1860,6 +1917,7 @@
       if (peer_.IsSource() && peer_.IsActivePeer()) {
         // Remove flush state, ready for streaming
         btif_a2dp_sink_set_rx_flush(false);
+        btif_a2dp_sink_on_start();
       }
 
       if (should_suspend) {
@@ -1886,17 +1944,20 @@
 
     case BTA_AV_CLOSE_EVT:
       // AVDTP link is closed
-      if (peer_.IsActivePeer()) {
-        btif_a2dp_on_stopped(nullptr);
-      }
-
       // Change state to Idle, send acknowledgement if start is pending
       if (peer_.CheckFlags(BtifAvPeer::kFlagPendingStart)) {
         BTIF_TRACE_WARNING("%s: Peer %s : failed pending start request",
                            __PRETTY_FUNCTION__,
                            peer_.PeerAddress().ToString().c_str());
-        btif_a2dp_command_ack(A2DP_CTRL_ACK_FAILURE);
+        tBTA_AV_START av_start = {.chnl = p_av->close.chnl,
+                                  .hndl = p_av->close.hndl,
+                                  .status = BTA_AV_FAIL_STREAM,
+                                  .initiator = true,
+                                  .suspending = true};
+        btif_a2dp_on_started(peer_.PeerAddress(), &av_start);
         // Pending start flag will be cleared when exit current state
+      } else if (peer_.IsActivePeer()) {
+        btif_a2dp_on_stopped(nullptr);
       }
 
       // Inform the application that we are disconnected
@@ -1906,18 +1967,36 @@
       break;
 
     case BTA_AV_RECONFIG_EVT:
-      if (peer_.CheckFlags(BtifAvPeer::kFlagPendingStart) &&
-          (p_av->reconfig.status == BTA_AV_SUCCESS)) {
-        LOG_INFO(LOG_TAG,
-                 "%s : Peer %s : Reconfig done - calling BTA_AvStart()",
-                 __PRETTY_FUNCTION__, peer_.PeerAddress().ToString().c_str());
+      if (p_av->reconfig.status != BTA_AV_SUCCESS) {
+        LOG(WARNING) << __PRETTY_FUNCTION__ << ": Peer " << peer_.PeerAddress()
+                     << " : failed reconfiguration";
+        if (peer_.CheckFlags(BtifAvPeer::kFlagPendingStart)) {
+          LOG(ERROR) << __PRETTY_FUNCTION__ << ": Peer " << peer_.PeerAddress()
+                     << " : cannot proceed to do AvStart";
+          peer_.ClearFlags(BtifAvPeer::kFlagPendingStart);
+          btif_a2dp_command_ack(A2DP_CTRL_ACK_FAILURE);
+        }
+        if (peer_.IsSink()) {
+          src_disconnect_sink(peer_.PeerAddress());
+        } else if (peer_.IsSource()) {
+          sink_disconnect_src(peer_.PeerAddress());
+        }
+        break;
+      }
+
+      if (peer_.IsActivePeer()) {
+        LOG(INFO) << __PRETTY_FUNCTION__ << " : Peer " << peer_.PeerAddress()
+                  << " : Reconfig done - calling startSession() to audio HAL";
+        std::promise<void> peer_ready_promise;
+        std::future<void> peer_ready_future = peer_ready_promise.get_future();
+        btif_a2dp_source_start_session(peer_.PeerAddress(),
+                                       std::move(peer_ready_promise));
+      }
+      if (peer_.CheckFlags(BtifAvPeer::kFlagPendingStart)) {
+        LOG(INFO) << __PRETTY_FUNCTION__ << " : Peer " << peer_.PeerAddress()
+                  << " : Reconfig done - calling BTA_AvStart("
+                  << loghex(peer_.BtaHandle()) << ")";
         BTA_AvStart(peer_.BtaHandle());
-      } else if (peer_.CheckFlags(BtifAvPeer::kFlagPendingStart)) {
-        BTIF_TRACE_WARNING("%s: Peer %s : failed reconfiguration",
-                           __PRETTY_FUNCTION__,
-                           peer_.PeerAddress().ToString().c_str());
-        peer_.ClearFlags(BtifAvPeer::kFlagPendingStart);
-        btif_a2dp_command_ack(A2DP_CTRL_ACK_FAILURE);
       }
       break;
 
@@ -1965,6 +2044,8 @@
   // We are again in started state, clear any remote suspend flags
   peer_.ClearFlags(BtifAvPeer::kFlagRemoteSuspend);
 
+  btif_a2dp_sink_set_rx_flush(false);
+
   // Report that we have entered the Streaming stage. Usually, this should
   // be followed by focus grant. See update_audio_focus_state()
   btif_report_audio_state(peer_.PeerAddress(), BTAV_AUDIO_STATE_STARTED);
@@ -2013,15 +2094,14 @@
       // always overrides.
       peer_.ClearFlags(BtifAvPeer::kFlagRemoteSuspend);
 
-      if (peer_.IsSink()) {
+      if (peer_.IsSink() &&
+          (peer_.IsActivePeer() || !btif_av_stream_started_ready())) {
         // Immediately stop transmission of frames while suspend is pending
-        if (peer_.IsActivePeer()) {
-          if (event == BTIF_AV_STOP_STREAM_REQ_EVT) {
-            btif_a2dp_on_stopped(nullptr);
-          } else {
-            // (event == BTIF_AV_SUSPEND_STREAM_REQ_EVT)
-            btif_a2dp_source_set_tx_flush(true);
-          }
+        if (event == BTIF_AV_STOP_STREAM_REQ_EVT) {
+          btif_a2dp_on_stopped(nullptr);
+        } else {
+          // ensure tx frames are immediately suspended
+          btif_a2dp_source_set_tx_flush(true);
         }
       } else if (peer_.IsSource()) {
         btif_a2dp_on_stopped(nullptr);
@@ -2056,8 +2136,10 @@
                BtifAvEvent::EventName(event).c_str(), p_av->suspend.status,
                p_av->suspend.initiator, peer_.FlagsToString().c_str());
 
-      // A2DP suspended, stop A2DP encoder/decoder until resumed
-      btif_a2dp_on_suspended(&p_av->suspend);
+      // A2DP suspended, stop A2DP encoder / decoder until resumed
+      if (peer_.IsActivePeer() || !btif_av_stream_started_ready()) {
+        btif_a2dp_on_suspended(&p_av->suspend);
+      }
 
       // If not successful, remain in current state
       if (p_av->suspend.status != BTA_AV_SUCCESS) {
@@ -2083,10 +2165,8 @@
         state = BTAV_AUDIO_STATE_STOPPED;
       }
 
-      // Suspend completed, clear pending status
-      peer_.ClearFlags(BtifAvPeer::kFlagLocalSuspendPending);
-
       btif_report_audio_state(peer_.PeerAddress(), state);
+      // Suspend completed, clear local pending flags while entering Opened
       peer_.StateMachine().TransitionTo(BtifAvStateMachine::kStateOpened);
     } break;
 
@@ -2099,7 +2179,11 @@
       peer_.SetFlags(BtifAvPeer::kFlagPendingStop);
       peer_.ClearFlags(BtifAvPeer::kFlagLocalSuspendPending);
 
-      btif_a2dp_on_stopped(&p_av->suspend);
+      // Don't change the encoder and audio provider state by a non-active peer
+      // since they are shared between peers
+      if (peer_.IsActivePeer() || !btif_av_stream_started_ready()) {
+        btif_a2dp_on_stopped(&p_av->suspend);
+      }
 
       btif_report_audio_state(peer_.PeerAddress(), BTAV_AUDIO_STATE_STOPPED);
 
@@ -2364,6 +2448,36 @@
 }
 
 /**
+ * Call out to JNI / JAVA layers to retrieve whether the mandatory codec is more
+ * preferred than others.
+ *
+ * @param peer_address the peer address
+ */
+static void btif_av_query_mandatory_codec_priority(
+    const RawAddress& peer_address) {
+  auto query_priority = [](const RawAddress& peer_address) {
+    auto apply_priority = [](const RawAddress& peer_address, bool preferred) {
+      BtifAvPeer* peer = btif_av_source_find_peer(peer_address);
+      if (peer == nullptr) {
+        BTIF_TRACE_WARNING(
+            "btif_av_query_mandatory_codec_priority: peer is null");
+        return;
+      }
+      peer->SetMandatoryCodecPreferred(preferred);
+    };
+    bool preferred =
+        btif_av_source.Callbacks()->mandatory_codec_preferred_cb(peer_address);
+    if (preferred) {
+      do_in_main_thread(
+          FROM_HERE, base::BindOnce(apply_priority, peer_address, preferred));
+    }
+  };
+  if (btif_av_source.Enabled()) {
+    do_in_jni_thread(FROM_HERE, base::BindOnce(query_priority, peer_address));
+  }
+}
+
+/**
  * Process BTIF or BTA AV or BTA AVRCP events. The processing is done on the
  * JNI thread.
  *
@@ -2379,10 +2493,11 @@
                                  tBTA_AV_HNDL bta_handle,
                                  const BtifAvEvent& btif_av_event) {
   BtifAvPeer* peer = nullptr;
-  BTIF_TRACE_EVENT("%s: peer_sep=%s (%d) peer_address=%s handle=0x%x event=%s",
-                   __func__, (peer_sep == AVDT_TSEP_SRC) ? "Source" : "Sink",
-                   peer_sep, peer_address.ToString().c_str(), bta_handle,
-                   btif_av_event.ToString().c_str());
+  BTIF_TRACE_EVENT(
+      "%s: peer_sep=%s (%d) peer_address=%s bta_handle=0x%x event=%s", __func__,
+      (peer_sep == AVDT_TSEP_SRC) ? "Source" : "Sink", peer_sep,
+      peer_address.ToString().c_str(), bta_handle,
+      btif_av_event.ToString().c_str());
 
   // Find the peer
   if (peer_address != RawAddress::kEmpty) {
@@ -2400,8 +2515,8 @@
   }
   if (peer == nullptr) {
     BTIF_TRACE_ERROR(
-        "%s: Cannot find or create %s peer for peer_address=%s handle=0x%x : "
-        "event dropped: %s",
+        "%s: Cannot find or create %s peer for peer_address=%s bta_handle=0x%x "
+        ": event dropped: %s",
         __func__, (peer_sep == AVDT_TSEP_SRC) ? "Source" : "Sink",
         peer_address.ToString().c_str(), bta_handle,
         btif_av_event.ToString().c_str());
@@ -2441,7 +2556,7 @@
       const tBTA_AV_REGISTER& registr = p_data->registr;
       bta_handle = registr.hndl;
       uint8_t peer_id = registr.app_id;  // The PeerId is used as AppId
-      BTIF_TRACE_DEBUG("%s: handle=0x%x app_id=%d", __func__, bta_handle,
+      BTIF_TRACE_DEBUG("%s: bta_handle=0x%x app_id=%d", __func__, bta_handle,
                        registr.app_id);
       if (peer_sep == AVDT_TSEP_SNK) {
         btif_av_source.BtaHandleRegistered(peer_id, bta_handle);
@@ -2538,8 +2653,13 @@
       peer_address = rc_feat.peer_addr;
       break;
     }
+    case BTA_AV_RC_PSM_EVT: {
+      const tBTA_AV_RC_PSM& rc_psm = p_data->rc_cover_art_psm;
+      peer_address = rc_psm.peer_addr;
+      break;
+    }
   }
-  BTIF_TRACE_DEBUG("%s: peer_address=%s handle=0x%x", __func__,
+  BTIF_TRACE_DEBUG("%s: peer_address=%s bta_handle=0x%x", __func__,
                    peer_address.ToString().c_str(), bta_handle);
 
   btif_av_handle_event(peer_sep, peer_address, bta_handle, btif_av_event);
@@ -2562,14 +2682,17 @@
 }
 
 // TODO: All processing should be done on the JNI thread
-static void bta_av_sink_media_callback(tBTA_AV_EVT event,
+static void bta_av_sink_media_callback(const RawAddress& peer_address,
+                                       tBTA_AV_EVT event,
                                        tBTA_AV_MEDIA* p_data) {
   BTIF_TRACE_EVENT("%s: event=%d", __func__, event);
+  BTIF_TRACE_EVENT("%s: address=%s", __func__,
+                   (p_data->avk_config.bd_addr.ToString().c_str()));
 
   switch (event) {
     case BTA_AV_SINK_MEDIA_DATA_EVT: {
-      BtifAvPeer* peer = btif_av_sink_find_peer(btif_av_sink.ActivePeer());
-      if (peer != nullptr) {
+      BtifAvPeer* peer = btif_av_sink_find_peer(peer_address);
+      if (peer != nullptr && peer->IsActivePeer()) {
         int state = peer->StateMachine().StateId();
         if ((state == BtifAvStateMachine::kStateStarted) ||
             (state == BtifAvStateMachine::kStateOpened)) {
@@ -2615,10 +2738,11 @@
 // Initializes the AV interface for source mode
 static bt_status_t init_src(
     btav_source_callbacks_t* callbacks, int max_connected_audio_devices,
-    std::vector<btav_a2dp_codec_config_t> codec_priorities) {
+    const std::vector<btav_a2dp_codec_config_t>& codec_priorities,
+    const std::vector<btav_a2dp_codec_config_t>& offloading_preference) {
   BTIF_TRACE_EVENT("%s", __func__);
   return btif_av_source.Init(callbacks, max_connected_audio_devices,
-                             codec_priorities);
+                             codec_priorities, offloading_preference);
 }
 
 // Initializes the AV interface for sink mode
@@ -2644,20 +2768,25 @@
   BTIF_TRACE_EVENT("%s: peer_address=%s uuid=0x%x", __func__,
                    peer_address->ToString().c_str(), uuid);
 
-  BtifAvPeer* peer = nullptr;
-  if (uuid == UUID_SERVCLASS_AUDIO_SOURCE) {
-    peer = btif_av_source.FindOrCreatePeer(*peer_address, kBtaHandleUnknown);
-    if (peer == nullptr) {
-      return BT_STATUS_FAIL;
+  auto connection_task = [](RawAddress* peer_address, uint16_t uuid) {
+    BtifAvPeer* peer = nullptr;
+    if (uuid == UUID_SERVCLASS_AUDIO_SOURCE) {
+      peer = btif_av_source.FindOrCreatePeer(*peer_address, kBtaHandleUnknown);
+    } else if (uuid == UUID_SERVCLASS_AUDIO_SINK) {
+      peer = btif_av_sink.FindOrCreatePeer(*peer_address, kBtaHandleUnknown);
     }
-  } else if (uuid == UUID_SERVCLASS_AUDIO_SINK) {
-    peer = btif_av_sink.FindOrCreatePeer(*peer_address, kBtaHandleUnknown);
     if (peer == nullptr) {
-      return BT_STATUS_FAIL;
+      btif_queue_advance();
+      return;
     }
+    peer->StateMachine().ProcessEvent(BTIF_AV_CONNECT_REQ_EVT, nullptr);
+  };
+  bt_status_t status = do_in_main_thread(
+      FROM_HERE, base::BindOnce(connection_task, peer_address, uuid));
+  if (status != BT_STATUS_SUCCESS) {
+    LOG(ERROR) << __func__ << ": can't post connection task to main_thread";
   }
-  peer->StateMachine().ProcessEvent(BTIF_AV_CONNECT_REQ_EVT, nullptr);
-  return BT_STATUS_SUCCESS;
+  return status;
 }
 
 static void set_source_silence_peer_int(const RawAddress& peer_address,
@@ -2760,6 +2889,28 @@
                             peer_address, kBtaHandleUnknown, btif_av_event));
 }
 
+static bt_status_t sink_set_active_device(const RawAddress& peer_address) {
+  BTIF_TRACE_EVENT("%s: Peer %s", __func__, peer_address.ToString().c_str());
+
+  if (!btif_av_sink.Enabled()) {
+    LOG(WARNING) << __func__ << ": BTIF AV Source is not enabled";
+    return BT_STATUS_NOT_READY;
+  }
+
+  std::promise<void> peer_ready_promise;
+  std::future<void> peer_ready_future = peer_ready_promise.get_future();
+  bt_status_t status = do_in_main_thread(
+      FROM_HERE, base::BindOnce(&set_active_peer_int,
+                                AVDT_TSEP_SRC,  // peer_sep
+                                peer_address, std::move(peer_ready_promise)));
+  if (status == BT_STATUS_SUCCESS) {
+    peer_ready_future.wait();
+  } else {
+    LOG(WARNING) << __func__ << ": BTIF AV Sink fails to change peer";
+  }
+  return status;
+}
+
 static bt_status_t src_set_silence_sink(const RawAddress& peer_address,
                                         bool silence) {
   BTIF_TRACE_EVENT("%s: Peer %s", __func__, peer_address.ToString().c_str());
@@ -2804,6 +2955,11 @@
     return BT_STATUS_NOT_READY;
   }
 
+  if (peer_address.IsEmpty()) {
+    LOG(WARNING) << __func__ << ": BTIF AV Source needs peer to config";
+    return BT_STATUS_PARM_INVALID;
+  }
+
   std::promise<void> peer_ready_promise;
   std::future<void> peer_ready_future = peer_ready_promise.get_future();
   bt_status_t status = do_in_main_thread(
@@ -2843,10 +2999,14 @@
 };
 
 static const btav_sink_interface_t bt_av_sink_interface = {
-    sizeof(btav_sink_interface_t), init_sink,    sink_connect_src,
-    sink_disconnect_src,           cleanup_sink, update_audio_focus_state,
+    sizeof(btav_sink_interface_t),
+    init_sink,
+    sink_connect_src,
+    sink_disconnect_src,
+    cleanup_sink,
+    update_audio_focus_state,
     update_audio_track_gain,
-};
+    sink_set_active_device};
 
 RawAddress btif_av_source_active_peer(void) {
   return btif_av_source.ActivePeer();
@@ -2861,6 +3021,27 @@
                                    BTIF_AV_START_STREAM_REQ_EVT);
 }
 
+void src_do_suspend_in_main_thread(btif_av_sm_event_t event) {
+  if (event != BTIF_AV_SUSPEND_STREAM_REQ_EVT &&
+      event != BTIF_AV_STOP_STREAM_REQ_EVT)
+    return;
+  auto src_do_stream_suspend = [](btif_av_sm_event_t event) {
+    bool is_idle = true;
+    for (auto it : btif_av_source.Peers()) {
+      const BtifAvPeer* peer = it.second;
+      if (peer->StateMachine().StateId() == BtifAvStateMachine::kStateStarted) {
+        btif_av_source_dispatch_sm_event(peer->PeerAddress(), event);
+        is_idle = false;
+      }
+    }
+    if (is_idle) {
+      btif_a2dp_on_stopped(nullptr);
+    }
+  };
+  // switch to main thread to prevent a race condition of accessing peers
+  do_in_main_thread(FROM_HERE, base::Bind(src_do_stream_suspend, event));
+}
+
 void btif_av_stream_stop(const RawAddress& peer_address) {
   LOG_INFO(LOG_TAG, "%s peer %s", __func__, peer_address.ToString().c_str());
 
@@ -2870,23 +3051,15 @@
   }
 
   // The active peer might have changed and we might be in the process
-  // of reconfiguring the stream. We need to stop the appopriate peer(s).
-  for (auto it : btif_av_source.Peers()) {
-    const BtifAvPeer* peer = it.second;
-    btif_av_source_dispatch_sm_event(peer->PeerAddress(),
-                                     BTIF_AV_STOP_STREAM_REQ_EVT);
-  }
+  // of reconfiguring the stream. We need to stop the appropriate peer(s).
+  src_do_suspend_in_main_thread(BTIF_AV_STOP_STREAM_REQ_EVT);
 }
 
 void btif_av_stream_suspend(void) {
   LOG_INFO(LOG_TAG, "%s", __func__);
   // The active peer might have changed and we might be in the process
   // of reconfiguring the stream. We need to suspend the appropriate peer(s).
-  for (auto it : btif_av_source.Peers()) {
-    const BtifAvPeer* peer = it.second;
-    btif_av_source_dispatch_sm_event(peer->PeerAddress(),
-                                     BTIF_AV_SUSPEND_STREAM_REQ_EVT);
-  }
+  src_do_suspend_in_main_thread(BTIF_AV_SUSPEND_STREAM_REQ_EVT);
 }
 
 void btif_av_stream_start_offload(void) {
@@ -3022,7 +3195,7 @@
     tBTA_AV_FEAT features = BTA_AV_FEAT_NO_SCO_SSPD | BTA_AV_FEAT_RCCT |
                             BTA_AV_FEAT_METADATA | BTA_AV_FEAT_VENDOR |
                             BTA_AV_FEAT_ADV_CTRL | BTA_AV_FEAT_RCTG |
-                            BTA_AV_FEAT_BROWSE;
+                            BTA_AV_FEAT_BROWSE | BTA_AV_FEAT_COVER_ARTWORK;
     BTA_AvEnable(BTA_SEC_AUTHENTICATE, features, bta_av_sink_callback);
     btif_av_sink.RegisterAllBtaHandles();
     return BT_STATUS_SUCCESS;
@@ -3121,6 +3294,16 @@
   return (is_connected && is3mbps);
 }
 
+bool btif_av_peer_prefers_mandatory_codec(const RawAddress& peer_address) {
+  BtifAvPeer* peer = btif_av_find_peer(peer_address);
+  if (peer == nullptr) {
+    BTIF_TRACE_WARNING("%s: No peer found for peer_address=%s", __func__,
+                       peer_address.ToString().c_str());
+    return false;
+  }
+  return peer->IsMandatoryCodecPreferred();
+}
+
 void btif_av_acl_disconnected(const RawAddress& peer_address) {
   // Inform the application that ACL is disconnected and move to idle state
   LOG_INFO(LOG_TAG, "%s: Peer %s : ACL Disconnected", __func__,
@@ -3173,6 +3356,9 @@
   dprintf(fd, "    Support 3Mbps: %s\n", peer.Is3Mbps() ? "true" : "false");
   dprintf(fd, "    Self Initiated Connection: %s\n",
           peer.SelfInitiatedConnection() ? "true" : "false");
+  dprintf(fd, "    Delay Reporting: %u\n", peer.GetDelayReport());
+  dprintf(fd, "    Codec Preferred: %s\n",
+          peer.IsMandatoryCodecPreferred() ? "Mandatory" : "Optional");
 }
 
 static void btif_debug_av_source_dump(int fd) {
@@ -3207,9 +3393,23 @@
   btif_debug_av_sink_dump(fd);
 }
 
-void btif_av_set_audio_delay(uint16_t delay) {
+void btif_av_set_audio_delay(const RawAddress& peer_address, uint16_t delay) {
   btif_a2dp_control_set_audio_delay(delay);
-  bluetooth::audio::a2dp::set_remote_delay(delay);
+  BtifAvPeer* peer = btif_av_find_peer(peer_address);
+  if (peer != nullptr && peer->IsSink()) {
+    peer->SetDelayReport(delay);
+    if (peer->IsActivePeer()) {
+      bluetooth::audio::a2dp::set_remote_delay(peer->GetDelayReport());
+    }
+  }
+}
+
+uint16_t btif_av_get_audio_delay() {
+  BtifAvPeer* peer = btif_av_find_active_peer();
+  if (peer != nullptr && peer->IsSink()) {
+    return peer->GetDelayReport();
+  }
+  return 0;
 }
 
 void btif_av_reset_audio_delay(void) { btif_a2dp_control_reset_audio_delay(); }
@@ -3218,6 +3418,18 @@
   return btif_av_source.A2dpOffloadEnabled();
 }
 
+bool btif_av_is_a2dp_offload_running() {
+  if (!btif_av_is_a2dp_offload_enabled()) {
+    return false;
+  }
+  if (!bluetooth::audio::a2dp::is_hal_2_0_enabled()) {
+    // since android::hardware::bluetooth::a2dp::V1_0 deprecated, offloading
+    // is supported by Bluetooth Audio HAL 2.0 only.
+    return false;
+  }
+  return bluetooth::audio::a2dp::is_hal_2_0_offloading();
+}
+
 bool btif_av_is_peer_silenced(const RawAddress& peer_address) {
   return btif_av_source.IsPeerSilenced(peer_address);
 }
diff --git a/btif/src/btif_avrcp_audio_track.cc b/btif/src/btif_avrcp_audio_track.cc
index 4f3cd35..5cf94e2 100644
--- a/btif/src/btif_avrcp_audio_track.cc
+++ b/btif/src/btif_avrcp_audio_track.cc
@@ -19,8 +19,8 @@
 
 #include "btif_avrcp_audio_track.h"
 
+#include <aaudio/AAudio.h>
 #include <base/logging.h>
-#include <media/AudioTrack.h>
 #include <utils/StrongPointer.h>
 
 #include "bt_target.h"
@@ -28,50 +28,49 @@
 
 using namespace android;
 
-typedef struct { android::sp<android::AudioTrack> track; } BtifAvrcpAudioTrack;
+typedef struct {
+  AAudioStream* stream;
+  int bitsPerSample;
+  int channelCount;
+  float* buffer;
+  size_t bufferLength;
+} BtifAvrcpAudioTrack;
 
 #if (DUMP_PCM_DATA == TRUE)
 FILE* outputPcmSampleFile;
 char outputFilename[50] = "/data/misc/bluedroid/output_sample.pcm";
 #endif
 
-void* BtifAvrcpAudioTrackCreate(int trackFreq, int bits_per_sample,
-                                int channelType) {
-  audio_format_t format;
-  switch (bits_per_sample) {
-    default:
-    case 16:
-      format = AUDIO_FORMAT_PCM_16_BIT;
-      break;
-    case 24:
-      format = AUDIO_FORMAT_PCM_24_BIT_PACKED;
-      break;
-    case 32:
-      format = AUDIO_FORMAT_PCM_32_BIT;
-      break;
-  }
-  LOG_VERBOSE(LOG_TAG,
-              "%s Track.cpp: btCreateTrack freq %d format 0x%x channel %d ",
-              __func__, trackFreq, format, channelType);
-  sp<android::AudioTrack> track = new android::AudioTrack(
-      AUDIO_STREAM_MUSIC, trackFreq, format, channelType,
-      (size_t)0 /*frameCount*/, (audio_output_flags_t)AUDIO_OUTPUT_FLAG_FAST,
-      NULL /*callback_t*/, NULL /*void* user*/, 0 /*notificationFrames*/,
-      AUDIO_SESSION_ALLOCATE, android::AudioTrack::TRANSFER_SYNC);
-  CHECK(track != NULL);
+void* BtifAvrcpAudioTrackCreate(int trackFreq, int bitsPerSample,
+                                int channelCount) {
+  LOG_VERBOSE(LOG_TAG, "%s Track.cpp: btCreateTrack freq %d bps %d channel %d ",
+              __func__, trackFreq, bitsPerSample, channelCount);
+
+  AAudioStreamBuilder* builder;
+  AAudioStream* stream;
+  aaudio_result_t result = AAudio_createStreamBuilder(&builder);
+  AAudioStreamBuilder_setSampleRate(builder, trackFreq);
+  AAudioStreamBuilder_setFormat(builder, AAUDIO_FORMAT_PCM_FLOAT);
+  AAudioStreamBuilder_setChannelCount(builder, channelCount);
+  AAudioStreamBuilder_setSessionId(builder, AAUDIO_SESSION_ID_ALLOCATE);
+  AAudioStreamBuilder_setPerformanceMode(builder,
+                                         AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
+  result = AAudioStreamBuilder_openStream(builder, &stream);
+  CHECK(result == AAUDIO_OK);
+  AAudioStreamBuilder_delete(builder);
 
   BtifAvrcpAudioTrack* trackHolder = new BtifAvrcpAudioTrack;
   CHECK(trackHolder != NULL);
-  trackHolder->track = track;
-
-  if (trackHolder->track->initCheck() != 0) {
-    return nullptr;
-  }
+  trackHolder->stream = stream;
+  trackHolder->bitsPerSample = bitsPerSample;
+  trackHolder->channelCount = channelCount;
+  trackHolder->bufferLength =
+      trackHolder->channelCount * AAudioStream_getBufferSizeInFrames(stream);
+  trackHolder->buffer = new float[trackHolder->bufferLength]();
 
 #if (DUMP_PCM_DATA == TRUE)
   outputPcmSampleFile = fopen(outputFilename, "ab");
 #endif
-  trackHolder->track->setVolume(1, 1);
   return (void*)trackHolder;
 }
 
@@ -82,9 +81,9 @@
   }
   BtifAvrcpAudioTrack* trackHolder = static_cast<BtifAvrcpAudioTrack*>(handle);
   CHECK(trackHolder != NULL);
-  CHECK(trackHolder->track != NULL);
+  CHECK(trackHolder->stream != NULL);
   LOG_VERBOSE(LOG_TAG, "%s Track.cpp: btStartTrack", __func__);
-  trackHolder->track->start();
+  AAudioStream_requestStart(trackHolder->stream);
 }
 
 void BtifAvrcpAudioTrackStop(void* handle) {
@@ -93,9 +92,9 @@
     return;
   }
   BtifAvrcpAudioTrack* trackHolder = static_cast<BtifAvrcpAudioTrack*>(handle);
-  if (trackHolder != NULL && trackHolder->track != NULL) {
+  if (trackHolder != NULL && trackHolder->stream != NULL) {
     LOG_VERBOSE(LOG_TAG, "%s Track.cpp: btStartTrack", __func__);
-    trackHolder->track->stop();
+    AAudioStream_requestStop(trackHolder->stream);
   }
 }
 
@@ -105,8 +104,10 @@
     return;
   }
   BtifAvrcpAudioTrack* trackHolder = static_cast<BtifAvrcpAudioTrack*>(handle);
-  if (trackHolder != NULL && trackHolder->track != NULL) {
+  if (trackHolder != NULL && trackHolder->stream != NULL) {
     LOG_VERBOSE(LOG_TAG, "%s Track.cpp: btStartTrack", __func__);
+    AAudioStream_close(trackHolder->stream);
+    delete trackHolder->buffer;
     delete trackHolder;
   }
 
@@ -124,10 +125,10 @@
     return;
   }
   BtifAvrcpAudioTrack* trackHolder = static_cast<BtifAvrcpAudioTrack*>(handle);
-  if (trackHolder != NULL && trackHolder->track != NULL) {
-    LOG_VERBOSE(LOG_TAG, "%s Track.cpp: btStartTrack", __func__);
-    trackHolder->track->pause();
-    trackHolder->track->flush();
+  if (trackHolder != NULL && trackHolder->stream != NULL) {
+    LOG_VERBOSE(LOG_TAG, "%s Track.cpp: btPauseTrack", __func__);
+    AAudioStream_requestPause(trackHolder->stream);
+    AAudioStream_requestFlush(trackHolder->stream);
   }
 }
 
@@ -136,26 +137,90 @@
     LOG_DEBUG(LOG_TAG, "%s handle is null.", __func__);
     return;
   }
-  BtifAvrcpAudioTrack* trackHolder = static_cast<BtifAvrcpAudioTrack*>(handle);
-  if (trackHolder != NULL && trackHolder->track != NULL) {
-    LOG_VERBOSE(LOG_TAG, "%s set gain %f", __func__, gain);
-    trackHolder->track->setVolume(gain);
-  }
+  // Does nothing right now
 }
 
+constexpr float kScaleQ15ToFloat = 1.0f / 32768.0f;
+constexpr float kScaleQ23ToFloat = 1.0f / 8388608.0f;
+constexpr float kScaleQ31ToFloat = 1.0f / 2147483648.0f;
+
+static size_t sampleSizeFor(BtifAvrcpAudioTrack* trackHolder) {
+  return trackHolder->bitsPerSample / 8;
+}
+
+static size_t transcodeQ15ToFloat(uint8_t* buffer, size_t length,
+                                  BtifAvrcpAudioTrack* trackHolder) {
+  size_t sampleSize = sampleSizeFor(trackHolder);
+  size_t i = 0;
+  for (; i <= length / sampleSize; i++) {
+    trackHolder->buffer[i] = ((int16_t*)buffer)[i] * kScaleQ15ToFloat;
+  }
+  return i * sampleSize;
+}
+
+static size_t transcodeQ23ToFloat(uint8_t* buffer, size_t length,
+                                  BtifAvrcpAudioTrack* trackHolder) {
+  size_t sampleSize = sampleSizeFor(trackHolder);
+  size_t i = 0;
+  for (; i <= length / sampleSize; i++) {
+    size_t offset = i * sampleSize;
+    int32_t sample = *((int32_t*)(buffer + offset - 1)) & 0x00FFFFFF;
+    trackHolder->buffer[i] = sample * kScaleQ23ToFloat;
+  }
+  return i * sampleSize;
+}
+
+static size_t transcodeQ31ToFloat(uint8_t* buffer, size_t length,
+                                  BtifAvrcpAudioTrack* trackHolder) {
+  size_t sampleSize = sampleSizeFor(trackHolder);
+  size_t i = 0;
+  for (; i <= length / sampleSize; i++) {
+    trackHolder->buffer[i] = ((int32_t*)buffer)[i] * kScaleQ31ToFloat;
+  }
+  return i * sampleSize;
+}
+
+static size_t transcodeToPcmFloat(uint8_t* buffer, size_t length,
+                                  BtifAvrcpAudioTrack* trackHolder) {
+  switch (trackHolder->bitsPerSample) {
+    case 16:
+      return transcodeQ15ToFloat(buffer, length, trackHolder);
+    case 24:
+      return transcodeQ23ToFloat(buffer, length, trackHolder);
+    case 32:
+      return transcodeQ31ToFloat(buffer, length, trackHolder);
+  }
+  return -1;
+}
+
+constexpr int64_t kTimeoutNanos = 100 * 1000 * 1000;  // 100 ms
+
 int BtifAvrcpAudioTrackWriteData(void* handle, void* audioBuffer,
-                                 int bufferlen) {
+                                 int bufferLength) {
   BtifAvrcpAudioTrack* trackHolder = static_cast<BtifAvrcpAudioTrack*>(handle);
   CHECK(trackHolder != NULL);
-  CHECK(trackHolder->track != NULL);
-  int retval = -1;
+  CHECK(trackHolder->stream != NULL);
+  aaudio_result_t retval = -1;
 #if (DUMP_PCM_DATA == TRUE)
   if (outputPcmSampleFile) {
-    fwrite((audioBuffer), 1, (size_t)bufferlen, outputPcmSampleFile);
+    fwrite((audioBuffer), 1, (size_t)bufferLength, outputPcmSampleFile);
   }
 #endif
-  retval = trackHolder->track->write(audioBuffer, (size_t)bufferlen);
-  LOG_VERBOSE(LOG_TAG, "%s Track.cpp: btWriteData len = %d ret = %d", __func__,
-              bufferlen, retval);
-  return retval;
+
+  size_t sampleSize = sampleSizeFor(trackHolder);
+  int transcodedCount = 0;
+  do {
+    transcodedCount +=
+        transcodeToPcmFloat(((uint8_t*)audioBuffer) + transcodedCount,
+                            bufferLength - transcodedCount, trackHolder);
+
+    retval = AAudioStream_write(
+        trackHolder->stream, trackHolder->buffer,
+        transcodedCount / (sampleSize * trackHolder->channelCount),
+        kTimeoutNanos);
+    LOG_VERBOSE(LOG_TAG, "%s Track.cpp: btWriteData len = %d ret = %d",
+                __func__, bufferLength, retval);
+  } while (transcodedCount < bufferLength);
+
+  return transcodedCount;
 }
diff --git a/btif/src/btif_ble_advertiser.cc b/btif/src/btif_ble_advertiser.cc
index dcbe080..d1923bd 100644
--- a/btif/src/btif_ble_advertiser.cc
+++ b/btif/src/btif_ble_advertiser.cc
@@ -83,7 +83,7 @@
 }
 
 class BleAdvertiserInterfaceImpl : public BleAdvertiserInterface {
-  ~BleAdvertiserInterfaceImpl(){};
+  ~BleAdvertiserInterfaceImpl() override{};
 
   void RegisterAdvertiserCb(IdStatusCallback cb, uint8_t advertiser_id,
                             uint8_t status) {
diff --git a/btif/src/btif_ble_scanner.cc b/btif/src/btif_ble_scanner.cc
index fa6fd4c..b314993 100644
--- a/btif/src/btif_ble_scanner.cc
+++ b/btif/src/btif_ble_scanner.cc
@@ -203,7 +203,7 @@
 void bta_cback(tBTA_GATTC_EVT, tBTA_GATTC*) {}
 
 class BleScannerInterfaceImpl : public BleScannerInterface {
-  ~BleScannerInterfaceImpl(){};
+  ~BleScannerInterfaceImpl() override{};
 
   void RegisterScanner(RegisterCallback cb) override {
     do_in_main_thread(FROM_HERE,
diff --git a/btif/src/btif_bqr.cc b/btif/src/btif_bqr.cc
index 17253c5..848d886 100644
--- a/btif/src/btif_bqr.cc
+++ b/btif/src/btif_bqr.cc
@@ -14,7 +14,12 @@
  * limitations under the License.
  */
 
+#include <base/logging.h>
+#include <errno.h>
+#include <fcntl.h>
 #include <statslog.h>
+#include <stdio.h>
+#include <sys/stat.h>
 
 #include "btif_bqr.h"
 #include "btif_dm.h"
@@ -32,60 +37,110 @@
 static std::unique_ptr<LeakyBondedQueue<BqrVseSubEvt>> kpBqrEventQueue(
     new LeakyBondedQueue<BqrVseSubEvt>(kBqrEventQueueSize));
 
-bool BqrVseSubEvt::ParseBqrEvt(uint8_t length, uint8_t* p_param_buf) {
-  if (length < kBqrParamTotalLen) {
+void BqrVseSubEvt::ParseBqrLinkQualityEvt(uint8_t length,
+                                          uint8_t* p_param_buf) {
+  if (length < kLinkQualityParamTotalLen) {
     LOG(FATAL) << __func__
                << ": Parameter total length: " << std::to_string(length)
                << " is abnormal. It shall be not shorter than: "
-               << std::to_string(kBqrParamTotalLen);
-    return false;
+               << std::to_string(kLinkQualityParamTotalLen);
+    return;
   }
 
-  STREAM_TO_UINT8(quality_report_id_, p_param_buf);
-  STREAM_TO_UINT8(packet_types_, p_param_buf);
-  STREAM_TO_UINT16(connection_handle_, p_param_buf);
-  STREAM_TO_UINT8(connection_role_, p_param_buf);
-  STREAM_TO_UINT8(tx_power_level_, p_param_buf);
-  STREAM_TO_INT8(rssi_, p_param_buf);
-  STREAM_TO_UINT8(snr_, p_param_buf);
-  STREAM_TO_UINT8(unused_afh_channel_count_, p_param_buf);
-  STREAM_TO_UINT8(afh_select_unideal_channel_count_, p_param_buf);
-  STREAM_TO_UINT16(lsto_, p_param_buf);
-  STREAM_TO_UINT32(connection_piconet_clock_, p_param_buf);
-  STREAM_TO_UINT32(retransmission_count_, p_param_buf);
-  STREAM_TO_UINT32(no_rx_count_, p_param_buf);
-  STREAM_TO_UINT32(nak_count_, p_param_buf);
-  STREAM_TO_UINT32(last_tx_ack_timestamp_, p_param_buf);
-  STREAM_TO_UINT32(flow_off_count_, p_param_buf);
-  STREAM_TO_UINT32(last_flow_on_timestamp_, p_param_buf);
-  STREAM_TO_UINT32(buffer_overflow_bytes_, p_param_buf);
-  STREAM_TO_UINT32(buffer_underflow_bytes_, p_param_buf);
+  STREAM_TO_UINT8(bqr_link_quality_event_.quality_report_id, p_param_buf);
+  STREAM_TO_UINT8(bqr_link_quality_event_.packet_types, p_param_buf);
+  STREAM_TO_UINT16(bqr_link_quality_event_.connection_handle, p_param_buf);
+  STREAM_TO_UINT8(bqr_link_quality_event_.connection_role, p_param_buf);
+  STREAM_TO_UINT8(bqr_link_quality_event_.tx_power_level, p_param_buf);
+  STREAM_TO_INT8(bqr_link_quality_event_.rssi, p_param_buf);
+  STREAM_TO_UINT8(bqr_link_quality_event_.snr, p_param_buf);
+  STREAM_TO_UINT8(bqr_link_quality_event_.unused_afh_channel_count,
+                  p_param_buf);
+  STREAM_TO_UINT8(bqr_link_quality_event_.afh_select_unideal_channel_count,
+                  p_param_buf);
+  STREAM_TO_UINT16(bqr_link_quality_event_.lsto, p_param_buf);
+  STREAM_TO_UINT32(bqr_link_quality_event_.connection_piconet_clock,
+                   p_param_buf);
+  STREAM_TO_UINT32(bqr_link_quality_event_.retransmission_count, p_param_buf);
+  STREAM_TO_UINT32(bqr_link_quality_event_.no_rx_count, p_param_buf);
+  STREAM_TO_UINT32(bqr_link_quality_event_.nak_count, p_param_buf);
+  STREAM_TO_UINT32(bqr_link_quality_event_.last_tx_ack_timestamp, p_param_buf);
+  STREAM_TO_UINT32(bqr_link_quality_event_.flow_off_count, p_param_buf);
+  STREAM_TO_UINT32(bqr_link_quality_event_.last_flow_on_timestamp, p_param_buf);
+  STREAM_TO_UINT32(bqr_link_quality_event_.buffer_overflow_bytes, p_param_buf);
+  STREAM_TO_UINT32(bqr_link_quality_event_.buffer_underflow_bytes, p_param_buf);
 
   const auto now = system_clock::to_time_t(system_clock::now());
   localtime_r(&now, &tm_timestamp_);
+}
 
-  return true;
+void BqrVseSubEvt::WriteLmpLlTraceLogFile(int fd, uint8_t length,
+                                          uint8_t* p_param_buf) {
+  const auto now = system_clock::to_time_t(system_clock::now());
+  localtime_r(&now, &tm_timestamp_);
+
+  STREAM_TO_UINT8(bqr_log_dump_event_.quality_report_id, p_param_buf);
+  STREAM_TO_UINT16(bqr_log_dump_event_.connection_handle, p_param_buf);
+  length -= kLogDumpParamTotalLen;
+  bqr_log_dump_event_.vendor_specific_parameter = p_param_buf;
+
+  std::stringstream ss_log;
+  ss_log << "\n"
+         << std::put_time(&tm_timestamp_, "%m-%d %H:%M:%S ")
+         << "Handle: " << loghex(bqr_log_dump_event_.connection_handle)
+         << " VSP: ";
+
+  TEMP_FAILURE_RETRY(write(fd, ss_log.str().c_str(), ss_log.str().size()));
+  TEMP_FAILURE_RETRY(
+      write(fd, bqr_log_dump_event_.vendor_specific_parameter, length));
+  LmpLlMessageTraceCounter++;
+}
+
+void BqrVseSubEvt::WriteBtSchedulingTraceLogFile(int fd, uint8_t length,
+                                                 uint8_t* p_param_buf) {
+  const auto now = system_clock::to_time_t(system_clock::now());
+  localtime_r(&now, &tm_timestamp_);
+
+  STREAM_TO_UINT8(bqr_log_dump_event_.quality_report_id, p_param_buf);
+  STREAM_TO_UINT16(bqr_log_dump_event_.connection_handle, p_param_buf);
+  length -= kLogDumpParamTotalLen;
+  bqr_log_dump_event_.vendor_specific_parameter = p_param_buf;
+
+  std::stringstream ss_log;
+  ss_log << "\n"
+         << std::put_time(&tm_timestamp_, "%m-%d %H:%M:%S ")
+         << "Handle: " << loghex(bqr_log_dump_event_.connection_handle)
+         << " VSP: ";
+
+  TEMP_FAILURE_RETRY(write(fd, ss_log.str().c_str(), ss_log.str().size()));
+  TEMP_FAILURE_RETRY(
+      write(fd, bqr_log_dump_event_.vendor_specific_parameter, length));
+  BtSchedulingTraceCounter++;
 }
 
 std::string BqrVseSubEvt::ToString() const {
-  std::stringstream ss_return_string;
-  ss_return_string << QualityReportIdToString(quality_report_id_)
-                   << ", Handle: " << loghex(connection_handle_) << ", "
-                   << PacketTypeToString(packet_types_) << ", "
-                   << ((connection_role_ == 0) ? "Master" : "Slave ")
-                   << ", PwLv: " << loghex(tx_power_level_)
-                   << ", RSSI: " << std::to_string(rssi_)
-                   << ", SNR: " << std::to_string(snr_) << ", UnusedCh: "
-                   << std::to_string(unused_afh_channel_count_)
-                   << ", UnidealCh: "
-                   << std::to_string(afh_select_unideal_channel_count_)
-                   << ", ReTx: " << std::to_string(retransmission_count_)
-                   << ", NoRX: " << std::to_string(no_rx_count_)
-                   << ", NAK: " << std::to_string(nak_count_)
-                   << ", FlowOff: " << std::to_string(flow_off_count_)
-                   << ", OverFlow: " << std::to_string(buffer_overflow_bytes_)
-                   << ", UndFlow: " << std::to_string(buffer_underflow_bytes_);
-  return ss_return_string.str();
+  std::stringstream ss;
+  ss << QualityReportIdToString(bqr_link_quality_event_.quality_report_id)
+     << ", Handle: " << loghex(bqr_link_quality_event_.connection_handle)
+     << ", " << PacketTypeToString(bqr_link_quality_event_.packet_types) << ", "
+     << ((bqr_link_quality_event_.connection_role == 0) ? "Master" : "Slave ")
+     << ", PwLv: " << loghex(bqr_link_quality_event_.tx_power_level)
+     << ", RSSI: " << std::to_string(bqr_link_quality_event_.rssi)
+     << ", SNR: " << std::to_string(bqr_link_quality_event_.snr)
+     << ", UnusedCh: "
+     << std::to_string(bqr_link_quality_event_.unused_afh_channel_count)
+     << ", UnidealCh: "
+     << std::to_string(bqr_link_quality_event_.afh_select_unideal_channel_count)
+     << ", ReTx: "
+     << std::to_string(bqr_link_quality_event_.retransmission_count)
+     << ", NoRX: " << std::to_string(bqr_link_quality_event_.no_rx_count)
+     << ", NAK: " << std::to_string(bqr_link_quality_event_.nak_count)
+     << ", FlowOff: " << std::to_string(bqr_link_quality_event_.flow_off_count)
+     << ", OverFlow: "
+     << std::to_string(bqr_link_quality_event_.buffer_overflow_bytes)
+     << ", UndFlow: "
+     << std::to_string(bqr_link_quality_event_.buffer_underflow_bytes);
+  return ss.str();
 }
 
 std::string QualityReportIdToString(uint8_t quality_report_id) {
@@ -166,46 +221,6 @@
   }
 }
 
-void AddBqrEventToQueue(uint8_t length, uint8_t* p_stream) {
-  std::unique_ptr<BqrVseSubEvt> p_bqr_event = std::make_unique<BqrVseSubEvt>();
-  if (!p_bqr_event->ParseBqrEvt(length, p_stream)) {
-    LOG(WARNING) << __func__ << ": Fail to parse BQR sub event.";
-    return;
-  }
-
-  LOG(WARNING) << *p_bqr_event;
-  int ret = android::util::stats_write(
-      android::util::BLUETOOTH_QUALITY_REPORT_REPORTED,
-      p_bqr_event->quality_report_id_, p_bqr_event->packet_types_,
-      p_bqr_event->connection_handle_, p_bqr_event->connection_role_,
-      p_bqr_event->tx_power_level_, p_bqr_event->rssi_, p_bqr_event->snr_,
-      p_bqr_event->unused_afh_channel_count_,
-      p_bqr_event->afh_select_unideal_channel_count_, p_bqr_event->lsto_,
-      p_bqr_event->connection_piconet_clock_,
-      p_bqr_event->retransmission_count_, p_bqr_event->no_rx_count_,
-      p_bqr_event->nak_count_, p_bqr_event->last_tx_ack_timestamp_,
-      p_bqr_event->flow_off_count_, p_bqr_event->last_flow_on_timestamp_,
-      p_bqr_event->buffer_overflow_bytes_,
-      p_bqr_event->buffer_underflow_bytes_);
-  if (ret < 0) {
-    LOG(WARNING) << __func__ << ": failed to log BQR event to statsd, error "
-                 << ret;
-  }
-  kpBqrEventQueue->Enqueue(p_bqr_event.release());
-}
-
-void ConfigureBqrCmpl(uint32_t current_evt_mask) {
-  LOG(INFO) << __func__ << ": current_evt_mask: " << loghex(current_evt_mask);
-  // (Un)Register for VSE of Bluetooth Quality Report sub event
-  tBTM_STATUS btm_status = BTM_BT_Quality_Report_VSE_Register(
-      current_evt_mask > kQualityEventMaskAllOff, AddBqrEventToQueue);
-
-  if (btm_status != BTM_SUCCESS) {
-    LOG(ERROR) << __func__ << ": Fail to (un)register VSE of BQR sub event."
-               << " status: " << btm_status;
-  }
-}
-
 void EnableBtQualityReport(bool is_enable) {
   LOG(INFO) << __func__ << ": is_enable: " << logbool(is_enable);
 
@@ -241,6 +256,31 @@
   ConfigureBqr(bqr_config);
 }
 
+void ConfigureBqr(const BqrConfiguration& bqr_config) {
+  if (bqr_config.report_action > REPORT_ACTION_CLEAR ||
+      bqr_config.quality_event_mask > kQualityEventMaskAll ||
+      bqr_config.minimum_report_interval_ms > kMinReportIntervalMaxMs) {
+    LOG(FATAL) << __func__ << ": Invalid Parameter"
+               << ", Action: " << bqr_config.report_action
+               << ", Mask: " << loghex(bqr_config.quality_event_mask)
+               << ", Interval: " << bqr_config.minimum_report_interval_ms;
+    return;
+  }
+
+  LOG(INFO) << __func__ << ": Action: " << bqr_config.report_action
+            << ", Mask: " << loghex(bqr_config.quality_event_mask)
+            << ", Interval: " << bqr_config.minimum_report_interval_ms;
+
+  uint8_t param[sizeof(BqrConfiguration)];
+  uint8_t* p_param = param;
+  UINT8_TO_STREAM(p_param, bqr_config.report_action);
+  UINT32_TO_STREAM(p_param, bqr_config.quality_event_mask);
+  UINT16_TO_STREAM(p_param, bqr_config.minimum_report_interval_ms);
+
+  BTM_VendorSpecificCommand(HCI_CONTROLLER_BQR, p_param - param, param,
+                            BqrVscCompleteCallback);
+}
+
 void BqrVscCompleteCallback(tBTM_VSC_CMPL* p_vsc_cmpl_params) {
   if (p_vsc_cmpl_params->param_len < 1) {
     LOG(ERROR) << __func__ << ": The length of returned parameters is less than 1";
@@ -273,29 +313,172 @@
   ConfigureBqrCmpl(current_quality_event_mask);
 }
 
-void ConfigureBqr(const BqrConfiguration& bqr_config) {
-  if (bqr_config.report_action > REPORT_ACTION_CLEAR ||
-      bqr_config.quality_event_mask > kQualityEventMaskAll ||
-      bqr_config.minimum_report_interval_ms > kMinReportIntervalMaxMs) {
-    LOG(FATAL) << __func__ << ": Invalid Parameter"
-               << ", Action: " << bqr_config.report_action
-               << ", Mask: " << loghex(bqr_config.quality_event_mask)
-               << ", Interval: " << bqr_config.minimum_report_interval_ms;
+void ConfigureBqrCmpl(uint32_t current_evt_mask) {
+  LOG(INFO) << __func__ << ": current_evt_mask: " << loghex(current_evt_mask);
+  // (Un)Register for VSE of Bluetooth Quality Report sub event
+  tBTM_STATUS btm_status = BTM_BT_Quality_Report_VSE_Register(
+      current_evt_mask > kQualityEventMaskAllOff, CategorizeBqrEvent);
+
+  if (btm_status != BTM_SUCCESS) {
+    LOG(ERROR) << __func__ << ": Fail to (un)register VSE of BQR sub event."
+               << " status: " << btm_status;
     return;
   }
 
-  LOG(INFO) << __func__ << ": Action: " << bqr_config.report_action
-            << ", Mask: " << loghex(bqr_config.quality_event_mask)
-            << ", Interval: " << bqr_config.minimum_report_interval_ms;
+  if (LmpLlMessageTraceLogFd != INVALID_FD &&
+      (current_evt_mask & kQualityEventMaskLmpMessageTrace) == 0) {
+    LOG(INFO) << __func__ << ": Closing LMP/LL log file.";
+    close(LmpLlMessageTraceLogFd);
+    LmpLlMessageTraceLogFd = INVALID_FD;
+  }
+  if (BtSchedulingTraceLogFd != INVALID_FD &&
+      (current_evt_mask & kQualityEventMaskBtSchedulingTrace) == 0) {
+    LOG(INFO) << __func__ << ": Closing Scheduling log file.";
+    close(BtSchedulingTraceLogFd);
+    BtSchedulingTraceLogFd = INVALID_FD;
+  }
+}
 
-  uint8_t param[sizeof(BqrConfiguration)];
-  uint8_t* p_param = param;
-  UINT8_TO_STREAM(p_param, bqr_config.report_action);
-  UINT32_TO_STREAM(p_param, bqr_config.quality_event_mask);
-  UINT16_TO_STREAM(p_param, bqr_config.minimum_report_interval_ms);
+void CategorizeBqrEvent(uint8_t length, uint8_t* p_bqr_event) {
+  if (length == 0) {
+    LOG(WARNING) << __func__ << ": Lengths of all of the parameters are zero.";
+    return;
+  }
 
-  BTM_VendorSpecificCommand(HCI_CONTROLLER_BQR_OPCODE_OCF, p_param - param,
-                            param, BqrVscCompleteCallback);
+  uint8_t quality_report_id = p_bqr_event[0];
+  switch (quality_report_id) {
+    case QUALITY_REPORT_ID_MONITOR_MODE:
+    case QUALITY_REPORT_ID_APPROACH_LSTO:
+    case QUALITY_REPORT_ID_A2DP_AUDIO_CHOPPY:
+    case QUALITY_REPORT_ID_SCO_VOICE_CHOPPY:
+      if (length < kLinkQualityParamTotalLen) {
+        LOG(FATAL) << __func__
+                   << ": Parameter total length: " << std::to_string(length)
+                   << " is abnormal. It shall be not shorter than: "
+                   << std::to_string(kLinkQualityParamTotalLen);
+        return;
+      }
+
+      AddLinkQualityEventToQueue(length, p_bqr_event);
+      break;
+
+    // The Root Inflammation and Log Dump related event should be handled and
+    // intercepted already.
+    case QUALITY_REPORT_ID_ROOT_INFLAMMATION:
+    case QUALITY_REPORT_ID_LMP_LL_MESSAGE_TRACE:
+    case QUALITY_REPORT_ID_BT_SCHEDULING_TRACE:
+    case QUALITY_REPORT_ID_CONTROLLER_DBG_INFO:
+      LOG(WARNING) << __func__
+                   << ": Unexpected ID: " << loghex(quality_report_id);
+      break;
+
+    default:
+      LOG(WARNING) << __func__ << ": Unknown ID: " << loghex(quality_report_id);
+      break;
+  }
+}
+
+void AddLinkQualityEventToQueue(uint8_t length, uint8_t* p_link_quality_event) {
+  std::unique_ptr<BqrVseSubEvt> p_bqr_event = std::make_unique<BqrVseSubEvt>();
+  p_bqr_event->ParseBqrLinkQualityEvt(length, p_link_quality_event);
+
+  LOG(WARNING) << *p_bqr_event;
+  int ret = android::util::stats_write(
+      android::util::BLUETOOTH_QUALITY_REPORT_REPORTED,
+      p_bqr_event->bqr_link_quality_event_.quality_report_id,
+      p_bqr_event->bqr_link_quality_event_.packet_types,
+      p_bqr_event->bqr_link_quality_event_.connection_handle,
+      p_bqr_event->bqr_link_quality_event_.connection_role,
+      p_bqr_event->bqr_link_quality_event_.tx_power_level,
+      p_bqr_event->bqr_link_quality_event_.rssi,
+      p_bqr_event->bqr_link_quality_event_.snr,
+      p_bqr_event->bqr_link_quality_event_.unused_afh_channel_count,
+      p_bqr_event->bqr_link_quality_event_.afh_select_unideal_channel_count,
+      p_bqr_event->bqr_link_quality_event_.lsto,
+      p_bqr_event->bqr_link_quality_event_.connection_piconet_clock,
+      p_bqr_event->bqr_link_quality_event_.retransmission_count,
+      p_bqr_event->bqr_link_quality_event_.no_rx_count,
+      p_bqr_event->bqr_link_quality_event_.nak_count,
+      p_bqr_event->bqr_link_quality_event_.last_tx_ack_timestamp,
+      p_bqr_event->bqr_link_quality_event_.flow_off_count,
+      p_bqr_event->bqr_link_quality_event_.last_flow_on_timestamp,
+      p_bqr_event->bqr_link_quality_event_.buffer_overflow_bytes,
+      p_bqr_event->bqr_link_quality_event_.buffer_underflow_bytes);
+  if (ret < 0) {
+    LOG(WARNING) << __func__ << ": failed to log BQR event to statsd, error "
+                 << ret;
+  }
+  kpBqrEventQueue->Enqueue(p_bqr_event.release());
+}
+
+void DumpLmpLlMessage(uint8_t length, uint8_t* p_lmp_ll_message_event) {
+  std::unique_ptr<BqrVseSubEvt> p_bqr_event = std::make_unique<BqrVseSubEvt>();
+
+  if (LmpLlMessageTraceLogFd == INVALID_FD ||
+      LmpLlMessageTraceCounter >= kLogDumpEventPerFile) {
+    LmpLlMessageTraceLogFd = OpenLmpLlTraceLogFile();
+  }
+  if (LmpLlMessageTraceLogFd != INVALID_FD) {
+    p_bqr_event->WriteLmpLlTraceLogFile(LmpLlMessageTraceLogFd, length,
+                                        p_lmp_ll_message_event);
+  }
+}
+
+int OpenLmpLlTraceLogFile() {
+  if (rename(kpLmpLlMessageTraceLogPath, kpLmpLlMessageTraceLastLogPath) != 0 &&
+      errno != ENOENT) {
+    LOG(ERROR) << __func__ << ": Unable to rename '"
+               << kpLmpLlMessageTraceLogPath << "' to '"
+               << kpLmpLlMessageTraceLastLogPath << "' : " << strerror(errno);
+  }
+
+  mode_t prevmask = umask(0);
+  int logfile_fd =
+      open(kpLmpLlMessageTraceLogPath, O_WRONLY | O_CREAT | O_TRUNC,
+           S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
+  umask(prevmask);
+  if (logfile_fd == INVALID_FD) {
+    LOG(ERROR) << __func__ << ": Unable to open '" << kpLmpLlMessageTraceLogPath
+               << "' : " << strerror(errno);
+  } else {
+    LmpLlMessageTraceCounter = 0;
+  }
+  return logfile_fd;
+}
+
+void DumpBtScheduling(uint8_t length, uint8_t* p_bt_scheduling_event) {
+  std::unique_ptr<BqrVseSubEvt> p_bqr_event = std::make_unique<BqrVseSubEvt>();
+
+  if (BtSchedulingTraceLogFd == INVALID_FD ||
+      BtSchedulingTraceCounter == kLogDumpEventPerFile) {
+    BtSchedulingTraceLogFd = OpenBtSchedulingTraceLogFile();
+  }
+  if (BtSchedulingTraceLogFd != INVALID_FD) {
+    p_bqr_event->WriteBtSchedulingTraceLogFile(BtSchedulingTraceLogFd, length,
+                                               p_bt_scheduling_event);
+  }
+}
+
+int OpenBtSchedulingTraceLogFile() {
+  if (rename(kpBtSchedulingTraceLogPath, kpBtSchedulingTraceLastLogPath) != 0 &&
+      errno != ENOENT) {
+    LOG(ERROR) << __func__ << ": Unable to rename '"
+               << kpBtSchedulingTraceLogPath << "' to '"
+               << kpBtSchedulingTraceLastLogPath << "' : " << strerror(errno);
+  }
+
+  mode_t prevmask = umask(0);
+  int logfile_fd =
+      open(kpBtSchedulingTraceLogPath, O_WRONLY | O_CREAT | O_TRUNC,
+           S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
+  umask(prevmask);
+  if (logfile_fd == INVALID_FD) {
+    LOG(ERROR) << __func__ << ": Unable to open '" << kpBtSchedulingTraceLogPath
+               << "' : " << strerror(errno);
+  } else {
+    BtSchedulingTraceCounter = 0;
+  }
+  return logfile_fd;
 }
 
 void DebugDump(int fd) {
@@ -309,8 +492,9 @@
   while (!kpBqrEventQueue->Empty()) {
     std::unique_ptr<BqrVseSubEvt> p_event(kpBqrEventQueue->Dequeue());
 
-    bool warning = (p_event->rssi_ < kCriWarnRssi ||
-                    p_event->unused_afh_channel_count_ > kCriWarnUnusedCh);
+    bool warning = (p_event->bqr_link_quality_event_.rssi < kCriWarnRssi ||
+                    p_event->bqr_link_quality_event_.unused_afh_channel_count >
+                        kCriWarnUnusedCh);
 
     std::stringstream ss_timestamp;
     ss_timestamp << std::put_time(&p_event->tm_timestamp_, "%m-%d %H:%M:%S");
diff --git a/btif/src/btif_config.cc b/btif/src/btif_config.cc
index 277671c..49cb19f 100644
--- a/btif/src/btif_config.cc
+++ b/btif/src/btif_config.cc
@@ -20,7 +20,6 @@
 
 #include "btif_config.h"
 
-#include <base/base64.h>
 #include <base/logging.h>
 #include <ctype.h>
 #include <openssl/rand.h>
@@ -30,18 +29,24 @@
 #include <string.h>
 #include <time.h>
 #include <unistd.h>
+#include <functional>
 #include <mutex>
 #include <sstream>
 #include <string>
+#include <unordered_map>
 
+#include <btif_keystore.h>
 #include "bt_types.h"
 #include "btcore/include/module.h"
 #include "btif_api.h"
 #include "btif_common.h"
+#include "btif_config_cache.h"
 #include "btif_config_transcode.h"
-#include "btif_keystore.h"
 #include "btif_util.h"
 #include "common/address_obfuscator.h"
+#include "common/metric_id_allocator.h"
+#include "main/shim/config.h"
+#include "main/shim/shim.h"
 #include "osi/include/alarm.h"
 #include "osi/include/allocator.h"
 #include "osi/include/compat.h"
@@ -49,8 +54,10 @@
 #include "osi/include/log.h"
 #include "osi/include/osi.h"
 #include "osi/include/properties.h"
+#include "raw_address.h"
 
 #define BT_CONFIG_SOURCE_TAG_NUM 1010001
+#define TEMPORARY_SECTION_CAPACITY 10000
 
 #define INFO_SECTION "Info"
 #define FILE_TIMESTAMP "TimeCreated"
@@ -59,16 +66,13 @@
 #define DISABLED "disabled"
 static const char* TIME_STRING_FORMAT = "%Y-%m-%d %H:%M:%S";
 
-constexpr int kBufferSize = 400 * 10;  // initial file is ~400B
-
-static bool btif_is_niap_mode() {
-  return getuid() == AID_BLUETOOTH && is_niap_mode();
-}
-
 #define BT_CONFIG_METRICS_SECTION "Metrics"
 #define BT_CONFIG_METRICS_SALT_256BIT "Salt256Bit"
-using bluetooth::BtifKeystore;
+#define BT_CONFIG_METRICS_ID_KEY "MetricsId"
+
+using bluetooth::bluetooth_keystore::BluetoothKeystoreInterface;
 using bluetooth::common::AddressObfuscator;
+using bluetooth::common::MetricIdAllocator;
 
 // TODO(armansito): Find a better way than searching by a hardcoded path.
 #if defined(OS_GENERIC)
@@ -78,8 +82,6 @@
 #else   // !defined(OS_GENERIC)
 static const char* CONFIG_FILE_PATH = "/data/misc/bluedroid/bt_config.conf";
 static const char* CONFIG_BACKUP_PATH = "/data/misc/bluedroid/bt_config.bak";
-static const char* CONFIG_FILE_CHECKSUM_PATH = "/data/misc/bluedroid/bt_config.conf.encrypted-checksum";
-static const char* CONFIG_BACKUP_CHECKSUM_PATH = "/data/misc/bluedroid/bt_config.bak.encrypted-checksum";
 static const char* CONFIG_LEGACY_FILE_PATH =
     "/data/misc/bluedroid/bt_config.xml";
 #endif  // defined(OS_GENERIC)
@@ -89,22 +91,22 @@
 static void btif_config_write(uint16_t event, char* p_param);
 static bool is_factory_reset(void);
 static void delete_config_files(void);
-static void btif_config_remove_unpaired(config_t* config);
-static void btif_config_remove_restricted(config_t* config);
-static std::unique_ptr<config_t> btif_config_open(const char* filename, const char* checksum_filename);
+static std::unique_ptr<config_t> btif_config_open(const char* filename);
 
 // Key attestation
-static std::string btif_convert_to_encrypt_key(
-    const std::string& unencrypt_str);
-static std::string btif_convert_to_unencrypt_key(
-    const std::string& encrypt_str);
+static bool config_checksum_pass(int check_bit) {
+  return ((get_niap_config_compare_result() & check_bit) == check_bit);
+}
+static bool btif_is_niap_mode() {
+  return getuid() == AID_BLUETOOTH && is_niap_mode();
+}
 static bool btif_in_encrypt_key_name_list(std::string key);
-static bool btif_is_key_encrypted(int key_from_config_size,
-                                  std::string key_type_string);
-static std::string hash_file(const char* filename);
-static std::string read_checksum_file(const char* filename);
-static void write_checksum_file(const char* filename, const std::string& hash);
 
+static const int CONFIG_FILE_COMPARE_PASS = 1;
+static const int CONFIG_BACKUP_COMPARE_PASS = 2;
+static const std::string ENCRYPTED_STR = "encrypted";
+static const std::string CONFIG_FILE_PREFIX = "bt_config-origin";
+static const std::string CONFIG_FILE_HASH = "hash";
 static const int ENCRYPT_KEY_NAME_LIST_SIZE = 7;
 static const std::string encrypt_key_name_list[] = {
     "LinkKey",      "LE_KEY_PENC", "LE_KEY_PID",  "LE_KEY_LID",
@@ -119,9 +121,29 @@
   RESET
 } btif_config_source = NOT_LOADED;
 
-static int btif_config_devices_loaded = -1;
 static char btif_config_time_created[TIME_STRING_LENGTH];
 
+static const storage_config_t interface = {
+    checksum_read,         checksum_save,      config_get_bool,
+    config_get_int,        config_get_string,  config_get_uint64,
+    config_has_key,        config_has_section, config_new,
+    config_new_clone,      config_new_empty,   config_remove_key,
+    config_remove_section, config_save,        config_set_bool,
+    config_set_int,        config_set_string,  config_set_uint64,
+};
+
+static const storage_config_t* storage_config_get_interface() {
+  if (bluetooth::shim::is_gd_stack_started_up()) {
+    return bluetooth::shim::storage_config_get_interface();
+  } else {
+    return &interface;
+  }
+}
+
+static BluetoothKeystoreInterface* get_bluetooth_keystore_interface() {
+  return bluetooth::bluetooth_keystore::getBluetoothKeystoreInterface();
+}
+
 // TODO(zachoverflow): Move these two functions out, because they are too
 // specific for this file
 // {grumpy-cat/no, monty-python/you-make-me-sad}
@@ -185,36 +207,96 @@
   AddressObfuscator::GetInstance()->Initialize(metrics_salt);
 }
 
+/**
+ * Initialize metric id allocator by reading metric_id from config by mac
+ * address. If there is no metric id for a mac address, then allocate it a new
+ * metric id.
+ */
+static void init_metric_id_allocator() {
+  std::unordered_map<RawAddress, int> paired_device_map;
+
+  // When user update the system, there will be devices paired with older
+  // version of android without a metric id.
+  std::vector<RawAddress> addresses_without_id;
+
+  for (auto& section : btif_config_sections()) {
+    auto& section_name = section.name;
+    RawAddress mac_address;
+    if (!RawAddress::FromString(section_name, mac_address)) {
+      continue;
+    }
+    // if the section name is a mac address
+    bool is_valid_id_found = false;
+    if (btif_config_exist(section_name, BT_CONFIG_METRICS_ID_KEY)) {
+      // there is one metric id under this mac_address
+      int id = 0;
+      btif_config_get_int(section_name, BT_CONFIG_METRICS_ID_KEY, &id);
+      if (MetricIdAllocator::IsValidId(id)) {
+        paired_device_map[mac_address] = id;
+        is_valid_id_found = true;
+      }
+    }
+    if (!is_valid_id_found) {
+      addresses_without_id.push_back(mac_address);
+    }
+  }
+
+  // Initialize MetricIdAllocator
+  MetricIdAllocator::Callback save_device_callback =
+      [](const RawAddress& address, const int id) {
+        return btif_config_set_int(address.ToString(), BT_CONFIG_METRICS_ID_KEY,
+                                   id);
+      };
+  MetricIdAllocator::Callback forget_device_callback =
+      [](const RawAddress& address, const int id) {
+        return btif_config_remove(address.ToString(), BT_CONFIG_METRICS_ID_KEY);
+      };
+  if (!MetricIdAllocator::GetInstance().Init(
+          paired_device_map, std::move(save_device_callback),
+          std::move(forget_device_callback))) {
+    LOG(FATAL) << __func__ << "Failed to initialize MetricIdAllocator";
+  }
+
+  // Add device_without_id
+  for (auto& address : addresses_without_id) {
+    MetricIdAllocator::GetInstance().AllocateId(address);
+    MetricIdAllocator::GetInstance().SaveDevice(address);
+  }
+}
+
 static std::recursive_mutex config_lock;  // protects operations on |config|.
-static std::unique_ptr<config_t> config;
 static alarm_t* config_timer;
 
-static BtifKeystore btif_keystore(new keystore::KeystoreClientImpl);
+// limited btif config cache capacity
+static BtifConfigCache btif_config_cache(TEMPORARY_SECTION_CAPACITY);
 
 // Module lifecycle functions
 
 static future_t* init(void) {
   std::unique_lock<std::recursive_mutex> lock(config_lock);
+  std::unique_ptr<config_t> config;
 
   if (is_factory_reset()) delete_config_files();
 
   std::string file_source;
 
-  config = btif_config_open(CONFIG_FILE_PATH, CONFIG_FILE_CHECKSUM_PATH);
-  btif_config_source = ORIGINAL;
+  if (config_checksum_pass(CONFIG_FILE_COMPARE_PASS)) {
+    config = btif_config_open(CONFIG_FILE_PATH);
+    btif_config_source = ORIGINAL;
+  }
   if (!config) {
     LOG_WARN(LOG_TAG, "%s unable to load config file: %s; using backup.",
              __func__, CONFIG_FILE_PATH);
-    remove(CONFIG_FILE_CHECKSUM_PATH);
-    config = btif_config_open(CONFIG_BACKUP_PATH, CONFIG_BACKUP_CHECKSUM_PATH);
-    btif_config_source = BACKUP;
-    file_source = "Backup";
+    if (config_checksum_pass(CONFIG_BACKUP_COMPARE_PASS)) {
+      config = btif_config_open(CONFIG_BACKUP_PATH);
+      btif_config_source = BACKUP;
+      file_source = "Backup";
+    }
   }
   if (!config) {
     LOG_WARN(LOG_TAG,
              "%s unable to load backup; attempting to transcode legacy file.",
              __func__);
-    remove(CONFIG_BACKUP_CHECKSUM_PATH);
     config = btif_config_transcode(CONFIG_LEGACY_FILE_PATH);
     btif_config_source = LEGACY;
     file_source = "Legacy";
@@ -223,36 +305,42 @@
     LOG_ERROR(LOG_TAG,
               "%s unable to transcode legacy file; creating empty config.",
               __func__);
-    config = config_new_empty();
+    config = storage_config_get_interface()->config_new_empty();
     btif_config_source = NEW_FILE;
     file_source = "Empty";
   }
 
-  if (!file_source.empty())
-    config_set_string(config.get(), INFO_SECTION, FILE_SOURCE, file_source);
+  // move persistent config data from btif_config file to btif config cache
+  btif_config_cache.Init(std::move(config));
 
-  btif_config_remove_unpaired(config.get());
+  if (!file_source.empty()) {
+    btif_config_cache.SetString(INFO_SECTION, FILE_SOURCE, file_source);
+  }
 
   // Cleanup temporary pairings if we have left guest mode
-  if (!is_restricted_mode()) btif_config_remove_restricted(config.get());
+  if (!is_restricted_mode()) {
+    btif_config_cache.RemovePersistentSectionsWithKey("Restricted");
+  }
 
   // Read or set config file creation timestamp
-  const std::string* time_str;
-  time_str = config_get_string(*config, INFO_SECTION, FILE_TIMESTAMP, NULL);
-  if (time_str != NULL) {
-    strlcpy(btif_config_time_created, time_str->c_str(), TIME_STRING_LENGTH);
-  } else {
+  auto time_str = btif_config_cache.GetString(INFO_SECTION, FILE_TIMESTAMP);
+  if (!time_str) {
     time_t current_time = time(NULL);
     struct tm* time_created = localtime(&current_time);
     strftime(btif_config_time_created, TIME_STRING_LENGTH, TIME_STRING_FORMAT,
              time_created);
-    config_set_string(config.get(), INFO_SECTION, FILE_TIMESTAMP,
-                      btif_config_time_created);
+    btif_config_cache.SetString(INFO_SECTION, FILE_TIMESTAMP,
+                                btif_config_time_created);
+  } else {
+    strlcpy(btif_config_time_created, time_str->c_str(), TIME_STRING_LENGTH);
   }
 
   // Read or set metrics 256 bit hashing salt
   read_or_set_metrics_salt();
 
+  // Initialize MetricIdAllocator
+  init_metric_id_allocator();
+
   // TODO(sharvil): use a non-wake alarm for this once we have
   // API support for it. There's no need to wake the system to
   // write back to disk.
@@ -269,32 +357,15 @@
 error:
   alarm_free(config_timer);
   config.reset();
+  btif_config_cache.Clear();
   config_timer = NULL;
   btif_config_source = NOT_LOADED;
   return future_new_immediate(FUTURE_FAIL);
 }
 
-static std::unique_ptr<config_t> btif_config_open(const char* filename, const char* checksum_filename) {
-  // START KEY ATTESTATION
-  // Get hash of current file
-  std::string current_hash = hash_file(filename);
-  // Get stored hash
-  std::string stored_hash = read_checksum_file(checksum_filename);
-  if (stored_hash.empty()) {
-    LOG(ERROR) << __func__ << ": stored_hash=<empty>";
-    // Will encrypt once since the bt_config never encrypt.
-    if (!btif_keystore.DoesKeyExist() && !current_hash.empty()) {
-      write_checksum_file(checksum_filename, current_hash);
-      stored_hash = read_checksum_file(checksum_filename);
-    }
-  }
-  // Compare hashes
-  if (current_hash != stored_hash) {
-    return nullptr;
-  }
-  // END KEY ATTESTATION
-
-  std::unique_ptr<config_t> config = config_new(filename);
+static std::unique_ptr<config_t> btif_config_open(const char* filename) {
+  std::unique_ptr<config_t> config =
+      storage_config_get_interface()->config_new(filename);
   if (!config) return nullptr;
 
   if (!config_has_section(*config, "Adapter")) {
@@ -317,7 +388,9 @@
   config_timer = NULL;
 
   std::unique_lock<std::recursive_mutex> lock(config_lock);
-  config.reset();
+  get_bluetooth_keystore_interface()->clear_map();
+  MetricIdAllocator::GetInstance().Close();
+  btif_config_cache.Clear();
   return future_new_immediate(FUTURE_SUCCESS);
 }
 
@@ -328,101 +401,91 @@
                                              .clean_up = clean_up};
 
 bool btif_config_has_section(const char* section) {
-  CHECK(config != NULL);
   CHECK(section != NULL);
 
   std::unique_lock<std::recursive_mutex> lock(config_lock);
-  return config_has_section(*config, section);
+  return btif_config_cache.HasSection(section);
 }
 
 bool btif_config_exist(const std::string& section, const std::string& key) {
-  CHECK(config != NULL);
-
   std::unique_lock<std::recursive_mutex> lock(config_lock);
-  return config_has_key(*config, section, key);
+  return btif_config_cache.HasKey(section, key);
 }
 
 bool btif_config_get_int(const std::string& section, const std::string& key,
                          int* value) {
-  CHECK(config != NULL);
   CHECK(value != NULL);
-
   std::unique_lock<std::recursive_mutex> lock(config_lock);
-  bool ret = config_has_key(*config, section, key);
-  if (ret) *value = config_get_int(*config, section, key, *value);
-
-  return ret;
+  auto ret = btif_config_cache.GetInt(section, key);
+  if (!ret) {
+    return false;
+  }
+  *value = *ret;
+  return true;
 }
 
 bool btif_config_set_int(const std::string& section, const std::string& key,
                          int value) {
-  CHECK(config != NULL);
-
   std::unique_lock<std::recursive_mutex> lock(config_lock);
-  config_set_int(config.get(), section, key, value);
-
+  btif_config_cache.SetInt(section, key, value);
   return true;
 }
 
 bool btif_config_get_uint64(const std::string& section, const std::string& key,
                             uint64_t* value) {
-  CHECK(config != NULL);
   CHECK(value != NULL);
-
   std::unique_lock<std::recursive_mutex> lock(config_lock);
-  bool ret = config_has_key(*config, section, key);
-  if (ret) *value = config_get_uint64(*config, section, key, *value);
-
-  return ret;
+  auto ret = btif_config_cache.GetUint64(section, key);
+  if (!ret) {
+    return false;
+  }
+  *value = *ret;
+  return true;
 }
 
 bool btif_config_set_uint64(const std::string& section, const std::string& key,
                             uint64_t value) {
-  CHECK(config != NULL);
-
   std::unique_lock<std::recursive_mutex> lock(config_lock);
-  config_set_uint64(config.get(), section, key, value);
-
+  btif_config_cache.SetUint64(section, key, value);
   return true;
 }
 
 bool btif_config_get_str(const std::string& section, const std::string& key,
                          char* value, int* size_bytes) {
-  CHECK(config != NULL);
   CHECK(value != NULL);
   CHECK(size_bytes != NULL);
 
   {
     std::unique_lock<std::recursive_mutex> lock(config_lock);
-    const std::string* stored_value =
-        config_get_string(*config, section, key, NULL);
+    auto stored_value = btif_config_cache.GetString(section, key);
     if (!stored_value) return false;
     strlcpy(value, stored_value->c_str(), *size_bytes);
   }
-
   *size_bytes = strlen(value) + 1;
   return true;
 }
 
 bool btif_config_set_str(const std::string& section, const std::string& key,
                          const std::string& value) {
-  CHECK(config != NULL);
-
   std::unique_lock<std::recursive_mutex> lock(config_lock);
-  config_set_string(config.get(), section, key, value);
+  btif_config_cache.SetString(section, key, value);
   return true;
 }
 
+static bool btif_in_encrypt_key_name_list(std::string key) {
+  return std::find(encrypt_key_name_list,
+                   encrypt_key_name_list + ENCRYPT_KEY_NAME_LIST_SIZE,
+                   key) != (encrypt_key_name_list + ENCRYPT_KEY_NAME_LIST_SIZE);
+}
+
 bool btif_config_get_bin(const std::string& section, const std::string& key,
                          uint8_t* value, size_t* length) {
-  CHECK(config != NULL);
   CHECK(value != NULL);
   CHECK(length != NULL);
 
   std::unique_lock<std::recursive_mutex> lock(config_lock);
   const std::string* value_str;
-  const std::string* value_str_from_config =
-      config_get_string(*config, section, key, NULL);
+  auto value_str_from_config = btif_config_cache.GetString(section, key);
 
   if (!value_str_from_config) {
     VLOG(1) << __func__ << ": cannot find string for section " << section
@@ -431,16 +494,15 @@
   }
 
   bool in_encrypt_key_name_list = btif_in_encrypt_key_name_list(key);
-  bool is_key_encrypted =
-      btif_is_key_encrypted(value_str_from_config->size(), key);
+  bool is_key_encrypted = *value_str_from_config == ENCRYPTED_STR;
+  std::string string;
 
-  if (in_encrypt_key_name_list && is_key_encrypted) {
-    VLOG(2) << __func__ << " decrypt section: " << section << " key:" << key;
-    std::string tmp_value_str =
-        btif_convert_to_unencrypt_key(*value_str_from_config);
-    value_str = &tmp_value_str;
+  if (!value_str_from_config->empty() && in_encrypt_key_name_list &&
+      is_key_encrypted) {
+    string = get_bluetooth_keystore_interface()->get_key(section + "-" + key);
+    value_str = &string;
   } else {
-    value_str = value_str_from_config;
+    value_str = &value_str_from_config.value();
   }
 
   size_t value_len = value_str->length();
@@ -461,75 +523,26 @@
   }
 
   if (btif_is_niap_mode()) {
-    if (in_encrypt_key_name_list && !is_key_encrypted) {
-      VLOG(2) << __func__ << " encrypt section: " << section << " key:" << key;
-      std::string encrypt_str =
-          btif_convert_to_encrypt_key(*value_str_from_config);
-      config_set_string(config.get(), section, key, encrypt_str);
+    if (!value_str_from_config->empty() && in_encrypt_key_name_list &&
+        !is_key_encrypted) {
+      get_bluetooth_keystore_interface()->set_encrypt_key_or_remove_key(
+          section + "-" + key, *value_str_from_config);
+      btif_config_cache.SetString(section, key, ENCRYPTED_STR);
     }
   } else {
     if (in_encrypt_key_name_list && is_key_encrypted) {
-      config_set_string(config.get(), section, key, value_str->c_str());
+      btif_config_cache.SetString(section, key, *value_str);
     }
   }
 
   return true;
 }
 
-static bool btif_in_encrypt_key_name_list(std::string key) {
-  return std::find(encrypt_key_name_list,
-                   encrypt_key_name_list + ENCRYPT_KEY_NAME_LIST_SIZE,
-                   key) != (encrypt_key_name_list + ENCRYPT_KEY_NAME_LIST_SIZE);
-}
-
-static bool btif_is_key_encrypted(int key_from_config_size,
-                                  std::string key_type_string) {
-  if (key_type_string.compare("LinkKey") == 0) {
-    return sizeof(LinkKey) * 2 != key_from_config_size;
-  } else if (key_type_string.compare("LE_KEY_PENC") == 0) {
-    return sizeof(tBTM_LE_PENC_KEYS) * 2 != key_from_config_size;
-  } else if (key_type_string.compare("LE_KEY_PID") == 0) {
-    return sizeof(tBTM_LE_PID_KEYS) * 2 != key_from_config_size;
-  } else if (key_type_string.compare("LE_KEY_LID") == 0) {
-    return sizeof(tBTM_LE_PID_KEYS) * 2 != key_from_config_size;
-  } else if (key_type_string.compare("LE_KEY_PCSRK") == 0) {
-    return sizeof(tBTM_LE_PCSRK_KEYS) * 2 != key_from_config_size;
-  } else if (key_type_string.compare("LE_KEY_LENC") == 0) {
-    return sizeof(tBTM_LE_LENC_KEYS) * 2 != key_from_config_size;
-  } else if (key_type_string.compare("LE_KEY_LCSRK") == 0) {
-    return sizeof(tBTM_LE_LCSRK_KEYS) * 2 != key_from_config_size;
-  } else {
-    VLOG(2) << __func__ << ": " << key_type_string
-            << " Key type is unknown, return false first";
-    return false;
-  }
-}
-
-static std::string btif_convert_to_unencrypt_key(
-    const std::string& encrypt_str) {
-  if (!encrypt_str.empty()) {
-    std::string tmp_encrypt_str("");
-    if (base::Base64Decode(encrypt_str, &tmp_encrypt_str)) {
-      std::string unencrypt_str = btif_keystore.Decrypt(tmp_encrypt_str);
-      if (!unencrypt_str.empty()) {
-        return unencrypt_str;
-      }
-    } else {
-      LOG(WARNING) << __func__
-                   << ": base64string decode fail, will return empty string";
-    }
-  }
-  return "";
-}
-
 size_t btif_config_get_bin_length(const std::string& section,
                                   const std::string& key) {
-  CHECK(config != NULL);
-
   std::unique_lock<std::recursive_mutex> lock(config_lock);
-  const std::string* value_str = config_get_string(*config, section, key, NULL);
+  auto value_str = btif_config_cache.GetString(section, key);
   if (!value_str) return 0;
-
   size_t value_len = value_str->length();
   return ((value_len % 2) != 0) ? 0 : (value_len / 2);
 }
@@ -537,9 +550,6 @@
 bool btif_config_set_bin(const std::string& section, const std::string& key,
                          const uint8_t* value, size_t length) {
   const char* lookup = "0123456789abcdef";
-
-  CHECK(config != NULL);
-
   if (length > 0) CHECK(value != NULL);
 
   size_t max_value = ((size_t)-1);
@@ -556,54 +566,44 @@
   }
 
   std::string value_str;
-  if (btif_is_niap_mode() && btif_in_encrypt_key_name_list(key)) {
-    VLOG(2) << __func__ << " encrypt section: " << section << " key:" << key;
-    value_str = btif_convert_to_encrypt_key(str);
+  if ((length > 0) && btif_is_niap_mode() &&
+      btif_in_encrypt_key_name_list(key)) {
+    get_bluetooth_keystore_interface()->set_encrypt_key_or_remove_key(
+        section + "-" + key, str);
+    value_str = ENCRYPTED_STR;
   } else {
     value_str = str;
   }
 
   {
     std::unique_lock<std::recursive_mutex> lock(config_lock);
-    config_set_string(config.get(), section, key, value_str);
+    btif_config_cache.SetString(section, key, value_str);
   }
 
   osi_free(str);
   return true;
 }
 
-static std::string btif_convert_to_encrypt_key(
-    const std::string& unencrypt_str) {
-  if (!unencrypt_str.empty()) {
-    std::string encrypt_str = btif_keystore.Encrypt(unencrypt_str, 0);
-    if (!encrypt_str.empty()) {
-      base::Base64Encode(encrypt_str, &encrypt_str);
-      return encrypt_str;
-    } else {
-      LOG(ERROR) << __func__ << ": Encrypt fail, will return empty str.";
-    }
-  }
-  return "";
+const std::list<section_t>& btif_config_sections() {
+  return btif_config_cache.GetPersistentSections();
 }
 
-std::list<section_t>& btif_config_sections() { return config->sections; }
-
 bool btif_config_remove(const std::string& section, const std::string& key) {
-  CHECK(config != NULL);
-
+  if (is_niap_mode() && btif_in_encrypt_key_name_list(key)) {
+    get_bluetooth_keystore_interface()->set_encrypt_key_or_remove_key(
+        section + "-" + key, "");
+  }
   std::unique_lock<std::recursive_mutex> lock(config_lock);
-  return config_remove_key(config.get(), section, key);
+  return btif_config_cache.RemoveKey(section, key);
 }
 
 void btif_config_save(void) {
-  CHECK(config != NULL);
   CHECK(config_timer != NULL);
 
   alarm_set(config_timer, CONFIG_SETTLE_PERIOD_MS, timer_config_save_cb, NULL);
 }
 
 void btif_config_flush(void) {
-  CHECK(config != NULL);
   CHECK(config_timer != NULL);
 
   alarm_cancel(config_timer);
@@ -611,24 +611,17 @@
 }
 
 bool btif_config_clear(void) {
-  CHECK(config != NULL);
   CHECK(config_timer != NULL);
 
   alarm_cancel(config_timer);
 
   std::unique_lock<std::recursive_mutex> lock(config_lock);
 
-  config = config_new_empty();
-
-  bool ret = config_save(*config, CONFIG_FILE_PATH);
+  btif_config_cache.Clear();
+  bool ret = storage_config_get_interface()->config_save(
+      btif_config_cache.PersistentSectionCopy(), CONFIG_FILE_PATH);
   btif_config_source = RESET;
 
-  // Save encrypted hash
-  std::string current_hash = hash_file(CONFIG_FILE_PATH);
-  if (!current_hash.empty()) {
-    write_checksum_file(CONFIG_FILE_CHECKSUM_PATH, current_hash);
-  }
-
   return ret;
 }
 
@@ -641,53 +634,18 @@
 
 static void btif_config_write(UNUSED_ATTR uint16_t event,
                               UNUSED_ATTR char* p_param) {
-  CHECK(config != NULL);
   CHECK(config_timer != NULL);
 
   std::unique_lock<std::recursive_mutex> lock(config_lock);
   rename(CONFIG_FILE_PATH, CONFIG_BACKUP_PATH);
-  rename(CONFIG_FILE_CHECKSUM_PATH, CONFIG_BACKUP_CHECKSUM_PATH);
-  std::unique_ptr<config_t> config_paired = config_new_clone(*config);
-  btif_config_remove_unpaired(config_paired.get());
-  config_save(*config_paired, CONFIG_FILE_PATH);
-  // Save hash
-  std::string current_hash = hash_file(CONFIG_FILE_PATH);
-  if (!current_hash.empty()) {
-    write_checksum_file(CONFIG_FILE_CHECKSUM_PATH, current_hash);
+  storage_config_get_interface()->config_save(
+      btif_config_cache.PersistentSectionCopy(), CONFIG_FILE_PATH);
+  if (btif_is_niap_mode()) {
+    get_bluetooth_keystore_interface()->set_encrypt_key_or_remove_key(
+        CONFIG_FILE_PREFIX, CONFIG_FILE_HASH);
   }
 }
 
-static void btif_config_remove_unpaired(config_t* conf) {
-  CHECK(conf != NULL);
-  int paired_devices = 0;
-
-  // The paired config used to carry information about
-  // discovered devices during regular inquiry scans.
-  // We remove these now and cache them in memory instead.
-  for (auto it = conf->sections.begin(); it != conf->sections.end();) {
-    std::string& section = it->name;
-    if (RawAddress::IsValidAddress(section)) {
-      // TODO: config_has_key loop thorugh all data, maybe just make it so we
-      // loop just once ?
-      if (!config_has_key(*conf, section, "LinkKey") &&
-          !config_has_key(*conf, section, "LE_KEY_PENC") &&
-          !config_has_key(*conf, section, "LE_KEY_PID") &&
-          !config_has_key(*conf, section, "LE_KEY_PCSRK") &&
-          !config_has_key(*conf, section, "LE_KEY_LENC") &&
-          !config_has_key(*conf, section, "LE_KEY_LCSRK")) {
-        it = conf->sections.erase(it);
-        continue;
-      }
-      paired_devices++;
-    }
-    it++;
-  }
-
-  // should only happen once, at initial load time
-  if (btif_config_devices_loaded == -1)
-    btif_config_devices_loaded = paired_devices;
-}
-
 void btif_debug_config_dump(int fd) {
   dprintf(fd, "\nBluetooth Config:\n");
 
@@ -713,28 +671,15 @@
       break;
   }
 
-  std::string original = "Original";
-  dprintf(fd, "  Devices loaded: %d\n", btif_config_devices_loaded);
-  dprintf(fd, "  File created/tagged: %s\n", btif_config_time_created);
-  dprintf(fd, "  File source: %s\n",
-          config_get_string(*config, INFO_SECTION, FILE_SOURCE, &original)
-              ->c_str());
-}
-
-static void btif_config_remove_restricted(config_t* config) {
-  CHECK(config != NULL);
-
-  for (auto it = config->sections.begin(); it != config->sections.end();) {
-    const std::string& section = it->name;
-    if (RawAddress::IsValidAddress(section) &&
-        config_has_key(*config, section, "Restricted")) {
-      BTIF_TRACE_DEBUG("%s: Removing restricted device %s", __func__,
-                       section.c_str());
-      it = config->sections.erase(it);
-      continue;
-    }
-    it++;
+  auto file_source = btif_config_cache.GetString(INFO_SECTION, FILE_SOURCE);
+  if (!file_source) {
+    file_source.emplace("Original");
   }
+
+  dprintf(fd, "  Devices loaded: %zu\n",
+          btif_config_cache.GetPersistentSections().size());
+  dprintf(fd, "  File created/tagged: %s\n", btif_config_time_created);
+  dprintf(fd, "  File source: %s\n", file_source->c_str());
 }
 
 static bool is_factory_reset(void) {
@@ -746,65 +691,5 @@
 static void delete_config_files(void) {
   remove(CONFIG_FILE_PATH);
   remove(CONFIG_BACKUP_PATH);
-  remove(CONFIG_FILE_CHECKSUM_PATH);
-  remove(CONFIG_BACKUP_CHECKSUM_PATH);
   osi_property_set("persist.bluetooth.factoryreset", "false");
 }
-
-static std::string hash_file(const char* filename) {
-  if (!btif_is_niap_mode()) {
-    LOG(INFO) << __func__ << ": Disabled for multi-user";
-    return DISABLED;
-  }
-  FILE* fp = fopen(filename, "rb");
-  if (!fp) {
-    LOG(ERROR) << __func__ << ": unable to open config file: '" << filename
-               << "': " << strerror(errno);
-    return "";
-  }
-  uint8_t hash[SHA256_DIGEST_LENGTH];
-  SHA256_CTX sha256;
-  SHA256_Init(&sha256);
-  std::array<std::byte, kBufferSize> buffer;
-  int bytes_read = 0;
-  while ((bytes_read = fread(buffer.data(), 1, buffer.size(), fp))) {
-    SHA256_Update(&sha256, buffer.data(), bytes_read);
-  }
-  SHA256_Final(hash, &sha256);
-  std::stringstream ss;
-  for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) {
-    ss << std::hex << std::setw(2) << std::setfill('0') << (int)hash[i];
-  }
-  fclose(fp);
-  return ss.str();
-}
-
-static std::string read_checksum_file(const char* checksum_filename) {
-  if (!btif_is_niap_mode()) {
-    LOG(INFO) << __func__ << ": Disabled for multi-user";
-    return DISABLED;
-  }
-  std::string encrypted_hash = checksum_read(checksum_filename);
-  if (encrypted_hash.empty()) {
-    LOG(INFO) << __func__ << ": read empty hash.";
-    return "";
-  }
-  return btif_keystore.Decrypt(encrypted_hash);
-}
-
-static void write_checksum_file(const char* checksum_filename,
-                                const std::string& hash) {
-  if (!btif_is_niap_mode()) {
-    LOG(INFO) << __func__
-              << ": Disabled for multi-user, since config changed removing "
-                 "checksums.";
-    remove(CONFIG_FILE_CHECKSUM_PATH);
-    remove(CONFIG_BACKUP_CHECKSUM_PATH);
-    return;
-  }
-  std::string encrypted_checksum = btif_keystore.Encrypt(hash, 0);
-  CHECK(!encrypted_checksum.empty())
-      << __func__ << ": Failed encrypting checksum";
-  CHECK(checksum_save(encrypted_checksum, checksum_filename))
-      << __func__ << ": Failed to save checksum!";
-}
diff --git a/btif/src/btif_config_cache.cc b/btif/src/btif_config_cache.cc
new file mode 100644
index 0000000..c5562aa
--- /dev/null
+++ b/btif/src/btif_config_cache.cc
@@ -0,0 +1,301 @@
+/*
+ *  Copyright 2020 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#include <limits>
+
+#include "btif_config_cache.h"
+
+namespace {
+
+const std::unordered_set<std::string> kLinkKeyTypes = {
+    "LinkKey",      "LE_KEY_PENC", "LE_KEY_PID",
+    "LE_KEY_PCSRK", "LE_KEY_LENC", "LE_KEY_LCSRK"};
+
+const std::unordered_set<std::string> kLocalSectionNames = {"Info", "Metrics",
+                                                            "Adapter"};
+
+bool is_link_key(const std::string& key) {
+  return kLinkKeyTypes.find(key) != kLinkKeyTypes.end();
+}
+
+bool has_link_key_in_section(const section_t& section) {
+  for (const auto& entry : section.entries) {
+    if (is_link_key(entry.key)) {
+      return true;
+    }
+  }
+  return false;
+}
+
+bool is_local_section_info(const std::string& section) {
+  return kLocalSectionNames.find(section) != kLocalSectionNames.end();
+}
+
+// trim new line in place, return true if newline was found
+bool trim_new_line(std::string& value) {
+  size_t newline_position = value.find_first_of('\n');
+  if (newline_position != std::string::npos) {
+    value.erase(newline_position);
+    return true;
+  }
+  return false;
+}
+
+}  // namespace
+
+BtifConfigCache::BtifConfigCache(size_t capacity)
+    : unpaired_devices_cache_(capacity, "bt_config_cache") {
+  LOG(INFO) << __func__ << ", capacity: " << capacity;
+}
+
+BtifConfigCache::~BtifConfigCache() { Clear(); }
+
+void BtifConfigCache::Clear() {
+  unpaired_devices_cache_.Clear();
+  paired_devices_list_.sections.clear();
+}
+
+void BtifConfigCache::Init(std::unique_ptr<config_t> source) {
+  // get the config persistent data from btif_config file
+  paired_devices_list_ = std::move(*source);
+  source.reset();
+}
+
+bool BtifConfigCache::HasPersistentSection(const std::string& section_name) {
+  return paired_devices_list_.Find(section_name) !=
+         paired_devices_list_.sections.end();
+}
+
+bool BtifConfigCache::HasUnpairedSection(const std::string& section_name) {
+  return unpaired_devices_cache_.HasKey(section_name);
+}
+
+bool BtifConfigCache::HasSection(const std::string& section_name) {
+  return HasUnpairedSection(section_name) || HasPersistentSection(section_name);
+}
+
+bool BtifConfigCache::HasKey(const std::string& section_name,
+                             const std::string& key) {
+  auto section_iter = paired_devices_list_.Find(section_name);
+  if (section_iter != paired_devices_list_.sections.end()) {
+    return section_iter->Has(key);
+  }
+  section_t* section = unpaired_devices_cache_.Find(section_name);
+  if (section == nullptr) {
+    return false;
+  }
+  return section->Has(key);
+}
+
+// remove sections with the restricted key
+void BtifConfigCache::RemovePersistentSectionsWithKey(const std::string& key) {
+  for (auto it = paired_devices_list_.sections.begin();
+       it != paired_devices_list_.sections.end();) {
+    if (it->Has(key)) {
+      it = paired_devices_list_.sections.erase(it);
+      continue;
+    }
+    it++;
+  }
+}
+
+/* remove a key from section, section itself is removed when empty */
+bool BtifConfigCache::RemoveKey(const std::string& section_name,
+                                const std::string& key) {
+  section_t* section = unpaired_devices_cache_.Find(section_name);
+  if (section != nullptr) {
+    auto entry_iter = section->Find(key);
+    if (entry_iter == section->entries.end()) {
+      return false;
+    }
+    section->entries.erase(entry_iter);
+    if (section->entries.empty()) {
+      unpaired_devices_cache_.Remove(section_name);
+    }
+    return true;
+  } else {
+    auto section_iter = paired_devices_list_.Find(section_name);
+    if (section_iter == paired_devices_list_.sections.end()) {
+      return false;
+    }
+    auto entry_iter = section_iter->Find(key);
+    if (entry_iter == section_iter->entries.end()) {
+      return false;
+    }
+    section_iter->entries.erase(entry_iter);
+    if (section_iter->entries.empty()) {
+      paired_devices_list_.sections.erase(section_iter);
+    } else if (!has_link_key_in_section(*section_iter)) {
+      // if no link key in section after removal, move it to unpaired section
+      auto moved_section = std::move(*section_iter);
+      paired_devices_list_.sections.erase(section_iter);
+      unpaired_devices_cache_.Put(section_name, std::move(moved_section));
+    }
+    return true;
+  }
+}
+
+/* clone persistent sections (Local Adapter sections, remote paired devices
+ * section,..) */
+config_t BtifConfigCache::PersistentSectionCopy() {
+  return paired_devices_list_;
+}
+
+const std::list<section_t>& BtifConfigCache::GetPersistentSections() {
+  return paired_devices_list_.sections;
+}
+
+void BtifConfigCache::SetString(std::string section_name, std::string key,
+                                std::string value) {
+  if (trim_new_line(section_name) || trim_new_line(key) ||
+      trim_new_line(value)) {
+    android_errorWriteLog(0x534e4554, "70808273");
+  }
+  if (section_name.empty()) {
+    LOG(FATAL) << "Empty section not allowed";
+    return;
+  }
+  if (key.empty()) {
+    LOG(FATAL) << "Empty key not allowed";
+    return;
+  }
+  if (!paired_devices_list_.Has(section_name)) {
+    // section is not in paired_device_list, handle it in unpaired devices cache
+    section_t section = {};
+    bool in_unpaired_cache = true;
+    if (!unpaired_devices_cache_.Get(section_name, &section)) {
+      // it's a new unpaired section, add it to unpaired devices cache
+      section.name = section_name;
+      in_unpaired_cache = false;
+    }
+    // set key to value and replace existing key if already exist
+    section.Set(key, value);
+
+    if (is_local_section_info(section_name) ||
+        (is_link_key(key) && RawAddress::IsValidAddress(section_name))) {
+      // remove this section that has the LinkKey from unpaired devices cache.
+      if (in_unpaired_cache) {
+        unpaired_devices_cache_.Remove(section_name);
+      }
+      // when a unpaired section got the LinkKey, move this section to the
+      // paired devices list
+      paired_devices_list_.sections.emplace_back(std::move(section));
+    } else {
+      // update to the unpaired devices cache
+      unpaired_devices_cache_.Put(section_name, section);
+    }
+  } else {
+    // already have section in paired device list, add key-value entry.
+    auto section_found = paired_devices_list_.Find(section_name);
+    if (section_found == paired_devices_list_.sections.end()) {
+      LOG(WARNING) << __func__ << " , section_found not found!";
+      return;
+    }
+    section_found->Set(key, value);
+  }
+}
+
+std::optional<std::string> BtifConfigCache::GetString(
+    const std::string& section_name, const std::string& key) {
+  // Check paired sections first
+  auto section_iter = paired_devices_list_.Find(section_name);
+  if (section_iter != paired_devices_list_.sections.end()) {
+    auto entry_iter = section_iter->Find(key);
+    if (entry_iter == section_iter->entries.end()) {
+      return std::nullopt;
+    }
+    return entry_iter->value;
+  }
+  // Check unpaired sections later
+  section_t section = {};
+  if (!unpaired_devices_cache_.Get(section_name, &section)) {
+    return std::nullopt;
+  }
+  auto entry_iter = section.Find(key);
+  if (entry_iter == section.entries.end()) {
+    return std::nullopt;
+  }
+  return entry_iter->value;
+}
+
+void BtifConfigCache::SetInt(std::string section_name, std::string key,
+                             int value) {
+  SetString(std::move(section_name), std::move(key), std::to_string(value));
+}
+
+std::optional<int> BtifConfigCache::GetInt(const std::string& section_name,
+                                           const std::string& key) {
+  auto value = GetString(section_name, key);
+  if (!value) {
+    return std::nullopt;
+  }
+  char* endptr;
+  long ret_long = strtol(value->c_str(), &endptr, 0);
+  if (*endptr != '\0') {
+    LOG(WARNING) << "Failed to parse value to long for section " << section_name
+                 << ", key " << key;
+    return std::nullopt;
+  }
+  if (ret_long >= std::numeric_limits<int>::max()) {
+    LOG(WARNING) << "Integer overflow when parsing value to int for section "
+                 << section_name << ", key " << key;
+    return std::nullopt;
+  }
+  return static_cast<int>(ret_long);
+}
+
+void BtifConfigCache::SetUint64(std::string section_name, std::string key,
+                                uint64_t value) {
+  SetString(std::move(section_name), std::move(key), std::to_string(value));
+}
+
+std::optional<uint64_t> BtifConfigCache::GetUint64(
+    const std::string& section_name, const std::string& key) {
+  auto value = GetString(section_name, key);
+  if (!value) {
+    return std::nullopt;
+  }
+  char* endptr;
+  uint64_t ret = strtoull(value->c_str(), &endptr, 0);
+  if (*endptr != '\0') {
+    LOG(WARNING) << "Failed to parse value to uint64 for section "
+                 << section_name << ", key " << key;
+    return std::nullopt;
+  }
+  return ret;
+}
+
+void BtifConfigCache::SetBool(std::string section_name, std::string key,
+                              bool value) {
+  SetString(std::move(section_name), std::move(key), value ? "true" : "false");
+}
+
+std::optional<bool> BtifConfigCache::GetBool(const std::string& section_name,
+                                             const std::string& key) {
+  auto value = GetString(section_name, key);
+  if (!value) {
+    return std::nullopt;
+  }
+  if (*value == "true") {
+    return true;
+  }
+  if (*value == "false") {
+    return false;
+  }
+  LOG(WARNING) << "Failed to parse value to boolean for section "
+               << section_name << ", key " << key;
+  return std::nullopt;
+}
diff --git a/btif/src/btif_dm.cc b/btif/src/btif_dm.cc
index 503f823..48d3290 100644
--- a/btif/src/btif_dm.cc
+++ b/btif/src/btif_dm.cc
@@ -60,6 +60,7 @@
 #include "btif_storage.h"
 #include "btif_util.h"
 #include "btu.h"
+#include "common/metric_id_allocator.h"
 #include "common/metrics.h"
 #include "device/include/controller.h"
 #include "device/include/interop.h"
@@ -72,6 +73,7 @@
 #include "stack_config.h"
 
 using bluetooth::Uuid;
+using bluetooth::common::MetricIdAllocator;
 /******************************************************************************
  *  Constants & Macros
  *****************************************************************************/
@@ -505,6 +507,15 @@
   BTIF_TRACE_DEBUG("%s: state=%d, prev_state=%d, sdp_attempts = %d", __func__,
                    state, pairing_cb.state, pairing_cb.sdp_attempts);
 
+  if (state == BT_BOND_STATE_NONE) {
+    MetricIdAllocator::GetInstance().ForgetDevice(bd_addr);
+  } else if (state == BT_BOND_STATE_BONDED) {
+    MetricIdAllocator::GetInstance().AllocateId(bd_addr);
+    if (!MetricIdAllocator::GetInstance().SaveDevice(bd_addr)) {
+      LOG(FATAL) << __func__ << ": Fail to save metric id for device "
+                 << bd_addr;
+    }
+  }
   auto tmp = bd_addr;
   HAL_CBACK(bt_hal_cbacks, bond_state_changed_cb, status, &tmp, state);
 
@@ -663,7 +674,7 @@
   bool is_hid = check_cod(&bd_addr, COD_HID_POINTING);
   bond_state_changed(BT_STATUS_SUCCESS, bd_addr, BT_BOND_STATE_BONDING);
 
-  int device_type;
+  int device_type = 0;
   int addr_type;
   std::string addrstr = bd_addr.ToString();
   const char* bdstr = addrstr.c_str();
@@ -697,7 +708,7 @@
     if (status != BT_STATUS_SUCCESS)
       bond_state_changed(status, bd_addr, BT_BOND_STATE_NONE);
   } else {
-    BTA_DmBondByTransport(bd_addr, transport);
+    BTA_DmBond(bd_addr, addr_type, transport);
   }
   /*  Track  originator of bond creation  */
   pairing_cb.is_local_initiated = true;
@@ -925,8 +936,8 @@
  ******************************************************************************/
 static void btif_dm_ssp_cfm_req_evt(tBTA_DM_SP_CFM_REQ* p_ssp_cfm_req) {
   bt_bdname_t bd_name;
-  uint32_t cod;
   bool is_incoming = !(pairing_cb.state == BT_BOND_STATE_BONDING);
+  uint32_t cod;
   int dev_type;
 
   BTIF_TRACE_DEBUG("%s", __func__);
@@ -1108,7 +1119,6 @@
 
       LOG_WARN(LOG_TAG, "%s: Incoming HID Connection", __func__);
       bt_property_t prop;
-      RawAddress bd_addr;
       Uuid uuid = Uuid::From16Bit(UUID_SERVCLASS_HUMAN_INTERFACE);
 
       prop.type = BT_PROPERTY_UUIDS;
@@ -1153,6 +1163,7 @@
     // Do not call bond_state_changed_cb yet. Wait until remote service
     // discovery is complete
   } else {
+    bool is_bonded_device_removed = false;
     // Map the HCI fail reason  to  bt status
     switch (p_auth_cmpl->fail_reason) {
       case HCI_ERR_PAGE_TIMEOUT:
@@ -1171,14 +1182,17 @@
         break;
 
       case HCI_ERR_PAIRING_NOT_ALLOWED:
-        btif_storage_remove_bonded_device(&bd_addr);
+        is_bonded_device_removed =
+            (btif_storage_remove_bonded_device(&bd_addr) == BT_STATUS_SUCCESS);
         status = BT_STATUS_AUTH_REJECTED;
         break;
 
       /* map the auth failure codes, so we can retry pairing if necessary */
       case HCI_ERR_AUTH_FAILURE:
       case HCI_ERR_KEY_MISSING:
-        btif_storage_remove_bonded_device(&bd_addr);
+        is_bonded_device_removed =
+            (btif_storage_remove_bonded_device(&bd_addr) == BT_STATUS_SUCCESS);
+        [[fallthrough]];
       case HCI_ERR_HOST_REJECT_SECURITY:
       case HCI_ERR_ENCRY_MODE_NOT_ACCEPTABLE:
       case HCI_ERR_UNIT_KEY_USED:
@@ -1208,9 +1222,14 @@
       /* Remove Device as bonded in nvram as authentication failed */
       BTIF_TRACE_DEBUG("%s(): removing hid pointing device from nvram",
                        __func__);
-      btif_storage_remove_bonded_device(&bd_addr);
+      is_bonded_device_removed =
+          (btif_storage_remove_bonded_device(&bd_addr) == BT_STATUS_SUCCESS);
     }
-    bond_state_changed(status, bd_addr, state);
+    // Report bond state change to java only if we are bonding to a device or
+    // a device is removed from the pairing list.
+    if (pairing_cb.state == BT_BOND_STATE_BONDING || is_bonded_device_removed) {
+      bond_state_changed(status, bd_addr, state);
+    }
   }
 }
 
diff --git a/btif/src/btif_hearing_aid.cc b/btif/src/btif_hearing_aid.cc
index 943017e..52cce27 100644
--- a/btif/src/btif_hearing_aid.cc
+++ b/btif/src/btif_hearing_aid.cc
@@ -53,7 +53,7 @@
 class HearingAidInterfaceImpl
     : public bluetooth::hearing_aid::HearingAidInterface,
       public HearingAidCallbacks {
-  ~HearingAidInterfaceImpl() = default;
+  ~HearingAidInterfaceImpl() override = default;
 
   void Init(HearingAidCallbacks* callbacks) override {
     DVLOG(2) << __func__;
diff --git a/btif/src/btif_hf.cc b/btif/src/btif_hf.cc
index 04ad011..ec4c13f 100644
--- a/btif/src/btif_hf.cc
+++ b/btif/src/btif_hf.cc
@@ -319,12 +319,48 @@
     case BTA_AG_OPEN_EVT:
       // Check if an outoging connection is pending
       if (btif_hf_cb[idx].is_initiator) {
+        if ((p_data->open.status != BTA_AG_SUCCESS) &&
+            btif_hf_cb[idx].state != BTHF_CONNECTION_STATE_CONNECTING) {
+          if (p_data->open.bd_addr == btif_hf_cb[idx].connected_bda) {
+            LOG(WARNING) << __func__ << ": btif_hf_cb state["
+                         << p_data->open.status
+                         << "] is not expected, possible connection collision, "
+                            "ignoring AG open "
+                            "failure event for the same device "
+                         << p_data->open.bd_addr;
+          } else {
+            LOG(WARNING) << __func__ << ": btif_hf_cb state["
+                         << p_data->open.status
+                         << "] is not expected, possible connection collision, "
+                            "ignoring AG open failure "
+                            "event for the different devices btif_hf_cb bda: "
+                         << btif_hf_cb[idx].connected_bda
+                         << ", p_data bda: " << p_data->open.bd_addr
+                         << ", report disconnect state for p_data bda.";
+            bt_hf_callbacks->ConnectionStateCallback(
+                BTHF_CONNECTION_STATE_DISCONNECTED, &(p_data->open.bd_addr));
+          }
+          break;
+        }
+
         CHECK_EQ(btif_hf_cb[idx].state, BTHF_CONNECTION_STATE_CONNECTING)
             << "Control block must be in connecting state when initiating";
         CHECK(!btif_hf_cb[idx].connected_bda.IsEmpty())
             << "Remote device address must not be empty when initiating";
-        CHECK_EQ(btif_hf_cb[idx].connected_bda, p_data->open.bd_addr)
-            << "Incoming message's address must match expected one";
+        if (btif_hf_cb[idx].connected_bda != p_data->open.bd_addr) {
+          LOG(WARNING) << __func__
+                       << ": possible connection collision, ignore the "
+                          "outgoing connection for the "
+                          "different devices btif_hf_cb bda: "
+                       << btif_hf_cb[idx].connected_bda
+                       << ", p_data bda: " << p_data->open.bd_addr
+                       << ", report disconnect state for btif_hf_cb bda.";
+          bt_hf_callbacks->ConnectionStateCallback(
+              BTHF_CONNECTION_STATE_DISCONNECTED,
+              &(btif_hf_cb[idx].connected_bda));
+          reset_control_block(&btif_hf_cb[idx]);
+          btif_queue_advance();
+        }
       }
       if (p_data->open.status == BTA_AG_SUCCESS) {
         // In case this is an incoming connection
@@ -1324,14 +1360,18 @@
   BTIF_TRACE_EVENT("%s", __func__);
 
   btif_queue_cleanup(UUID_SERVCLASS_AG_HANDSFREE);
-  if (bt_hf_callbacks) {
+
+  tBTA_SERVICE_MASK mask = btif_get_enabled_services_mask();
 #if (defined(BTIF_HF_SERVICES) && (BTIF_HF_SERVICES & BTA_HFP_SERVICE_MASK))
+  if ((mask & (1 << BTA_HFP_SERVICE_ID)) != 0) {
     btif_disable_service(BTA_HFP_SERVICE_ID);
-#else
-    btif_disable_service(BTA_HSP_SERVICE_ID);
-#endif
-    bt_hf_callbacks = nullptr;
   }
+#else
+  if ((mask & (1 << BTA_HSP_SERVICE_ID)) != 0) {
+    btif_disable_service(BTA_HSP_SERVICE_ID);
+  }
+#endif
+  do_in_jni_thread(FROM_HERE, base::Bind([]() { bt_hf_callbacks = nullptr; }));
 }
 
 bt_status_t HeadsetInterface::SetScoAllowed(bool value) {
diff --git a/btif/src/btif_hf_client.cc b/btif/src/btif_hf_client.cc
old mode 100644
new mode 100755
index e84291d..06fe5e4
--- a/btif/src/btif_hf_client.cc
+++ b/btif/src/btif_hf_client.cc
@@ -747,7 +747,7 @@
 }
 
 static const bthf_client_interface_t bthfClientInterface = {
-    sizeof(bthf_client_interface_t),
+    .size = sizeof(bthf_client_interface_t),
     .init = init,
     .connect = connect,
     .disconnect = disconnect,
@@ -999,6 +999,10 @@
     case BTA_HF_CLIENT_RING_INDICATION:
       HAL_CBACK(bt_hf_client_callbacks, ring_indication_cb, &cb->peer_bda);
       break;
+    case BTA_HF_CLIENT_UNKNOWN_EVT:
+      HAL_CBACK(bt_hf_client_callbacks, unknown_event_cb, &cb->peer_bda,
+                p_data->unknown.event_string);
+      break;
     default:
       BTIF_TRACE_WARNING("%s: Unhandled event: %d", __func__, event);
       break;
@@ -1040,12 +1044,16 @@
 bt_status_t btif_hf_client_execute_service(bool b_enable) {
   BTIF_TRACE_EVENT("%s: enable: %d", __func__, b_enable);
 
+  tBTA_HF_CLIENT_FEAT features = BTIF_HF_CLIENT_FEATURES;
+  if (osi_property_get_bool("persist.bluetooth.hfpclient.sco_s4_supported",
+                            false))
+    features |= BTA_HF_CLIENT_FEAT_S4;
+
   if (b_enable) {
     /* Enable and register with BTA-HFClient */
-    BTIF_TRACE_EVENT("%s: support codec negotiation %d ", __func__,
-                     BTIF_HF_CLIENT_FEATURES);
-    BTA_HfClientEnable(bta_hf_client_evt, BTIF_HF_CLIENT_SECURITY,
-                       BTIF_HF_CLIENT_FEATURES, BTIF_HF_CLIENT_SERVICE_NAME);
+    BTIF_TRACE_EVENT("%s: support codec negotiation %d ", __func__, features);
+    BTA_HfClientEnable(bta_hf_client_evt, BTIF_HF_CLIENT_SECURITY, features,
+                       BTIF_HF_CLIENT_SERVICE_NAME);
   } else {
     BTA_HfClientDisable();
   }
diff --git a/btif/src/btif_hh.cc b/btif/src/btif_hh.cc
index 650923a..5380fa5 100644
--- a/btif/src/btif_hh.cc
+++ b/btif/src/btif_hh.cc
@@ -79,10 +79,6 @@
 #define BTUI_HH_SECURITY (BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT)
 #endif
 
-#ifndef BTUI_HH_MOUSE_SECURITY
-#define BTUI_HH_MOUSE_SECURITY (BTA_SEC_NONE)
-#endif
-
 /* HH request events */
 typedef enum {
   BTIF_HH_CONNECT_REQ_EVT = 0,
@@ -537,7 +533,8 @@
   p_dev = btif_hh_find_dev_by_bda(*bd_addr);
   if ((p_dev != NULL) && (p_dev->dev_status == BTHH_CONN_STATE_CONNECTED) &&
       (p_dev->attr_mask & HID_VIRTUAL_CABLE)) {
-    BTIF_TRACE_DEBUG("%s Sending BTA_HH_CTRL_VIRTUAL_CABLE_UNPLUG", __func__);
+    BTIF_TRACE_DEBUG("%s: Sending BTA_HH_CTRL_VIRTUAL_CABLE_UNPLUG for: %s", __func__,
+                     bd_addr->ToString().c_str());
     /* start the timer */
     btif_hh_start_vup_timer(bd_addr);
     p_dev->local_vup = true;
@@ -545,7 +542,8 @@
     return BT_STATUS_SUCCESS;
   } else if ((p_dev != NULL) &&
              (p_dev->dev_status == BTHH_CONN_STATE_CONNECTED)) {
-    BTIF_TRACE_ERROR("%s: Virtual unplug not suported, disconnecting device");
+    BTIF_TRACE_ERROR("%s: Virtual unplug not suported, disconnecting device: %s",
+                     __func__, bd_addr->ToString().c_str());
     /* start the timer */
     btif_hh_start_vup_timer(bd_addr);
     p_dev->local_vup = true;
@@ -611,10 +609,9 @@
    request from host, for subsequent user initiated connection. If the remote is
    not in
    pagescan mode, we will do 2 retries to connect before giving up */
-  tBTA_SEC sec_mask = BTUI_HH_SECURITY;
   btif_hh_cb.status = BTIF_HH_DEV_CONNECTING;
   btif_hh_cb.pending_conn_address = *bd_addr;
-  BTA_HhOpen(*bd_addr, BTA_HH_PROTO_RPT_MODE, sec_mask);
+  BTA_HhOpen(*bd_addr, BTA_HH_PROTO_RPT_MODE, BTUI_HH_SECURITY);
 
   // TODO(jpawlowski); make cback accept const and remove tmp!
   auto tmp = *bd_addr;
diff --git a/btif/src/btif_hl.cc b/btif/src/btif_hl.cc
deleted file mode 100644
index 184dbf4..0000000
--- a/btif/src/btif_hl.cc
+++ /dev/null
@@ -1,4655 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2009-2012 Broadcom Corporation
- *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at:
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- *
- ******************************************************************************/
-
-/*******************************************************************************
- *
- *  Filename:      btif_hl.c
- *
- *  Description:   Health Device Profile Bluetooth Interface
- *
- *
- ******************************************************************************/
-#define LOG_TAG "bt_btif_hl"
-
-#include "btif_hl.h"
-
-#include <base/logging.h>
-#include <ctype.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <pthread.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/poll.h>
-#include <sys/prctl.h>
-#include <sys/select.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <sys/un.h>
-#include <time.h>
-#include <unistd.h>
-
-#include <hardware/bt_hl.h>
-
-#include "bta_api.h"
-#include "btif_common.h"
-#include "btif_storage.h"
-#include "btif_util.h"
-#include "btu.h"
-#include "mca_api.h"
-#include "osi/include/list.h"
-#include "osi/include/log.h"
-#include "osi/include/osi.h"
-
-#define MAX_DATATYPE_SUPPORTED 8
-
-extern int btif_hl_update_maxfd(int max_org_s);
-extern void btif_hl_select_monitor_callback(fd_set* p_cur_set,
-                                            UNUSED_ATTR fd_set* p_org_set);
-extern void btif_hl_select_wakeup_callback(fd_set* p_org_set,
-                                           int wakeup_signal);
-extern int btif_hl_update_maxfd(int max_org_s);
-extern void btif_hl_select_monitor_callback(fd_set* p_cur_set,
-                                            UNUSED_ATTR fd_set* p_org_set);
-extern void btif_hl_select_wakeup_callback(fd_set* p_org_set,
-                                           int wakeup_signal);
-extern void btif_hl_soc_thread_init(void);
-extern void btif_hl_release_mcl_sockets(uint8_t app_idx, uint8_t mcl_idx);
-extern bool btif_hl_create_socket(uint8_t app_idx, uint8_t mcl_idx,
-                                  uint8_t mdl_idx);
-extern void btif_hl_release_socket(uint8_t app_idx, uint8_t mcl_idx,
-                                   uint8_t mdl_idx);
-
-btif_hl_cb_t btif_hl_cb;
-btif_hl_cb_t* p_btif_hl_cb = &btif_hl_cb;
-
-/*******************************************************************************
- *  Static variables
- ******************************************************************************/
-static bthl_callbacks_t bt_hl_callbacks_cb;
-static bthl_callbacks_t* bt_hl_callbacks = NULL;
-
-/* signal socketpair to wake up select loop */
-
-const int btif_hl_signal_select_wakeup = 1;
-const int btif_hl_signal_select_exit = 2;
-const int btif_hl_signal_select_close_connected = 3;
-
-static int listen_s = -1;
-static int connected_s = -1;
-static pthread_t select_thread_id = -1;
-static int signal_fds[2] = {-1, -1};
-static list_t* soc_queue;
-static int reg_counter;
-
-static inline int btif_hl_select_wakeup(void);
-static inline int btif_hl_select_close_connected(void);
-static inline int btif_hl_close_select_thread(void);
-static uint8_t btif_hl_get_next_app_id(void);
-static int btif_hl_get_next_channel_id(uint8_t app_id);
-static void btif_hl_init_next_app_id(void);
-static void btif_hl_init_next_channel_id(void);
-static void btif_hl_ctrl_cback(tBTA_HL_CTRL_EVT event, tBTA_HL_CTRL* p_data);
-static void btif_hl_set_state(btif_hl_state_t state);
-static btif_hl_state_t btif_hl_get_state(void);
-static void btif_hl_cback(tBTA_HL_EVT event, tBTA_HL* p_data);
-static void btif_hl_proc_cb_evt(uint16_t event, char* p_param);
-
-#define CHECK_CALL_CBACK(P_CB, P_CBACK, ...) \
-  do {                                       \
-    if ((P_CB) && (P_CB)->P_CBACK) {         \
-      (P_CB)->P_CBACK(__VA_ARGS__);          \
-    } else {                                 \
-      ASSERTC(0, "Callback is NULL", 0);     \
-    }                                        \
-  } while (0)
-
-#define BTIF_HL_CALL_CBACK(P_CB, P_CBACK, ...)              \
-  do {                                                      \
-    if ((p_btif_hl_cb->state != BTIF_HL_STATE_DISABLING) && \
-        (p_btif_hl_cb->state != BTIF_HL_STATE_DISABLED)) {  \
-      if ((P_CB) && (P_CB)->P_CBACK) {                      \
-        (P_CB)->P_CBACK(__VA_ARGS__);                       \
-      } else {                                              \
-        ASSERTC(0, "Callback is NULL", 0);                  \
-      }                                                     \
-    }                                                       \
-  } while (0)
-
-#define CHECK_BTHL_INIT()                                             \
-  do {                                                                \
-    if (bt_hl_callbacks == NULL) {                                    \
-      BTIF_TRACE_WARNING("BTHL: %s: BTHL not initialized", __func__); \
-      return BT_STATUS_NOT_READY;                                     \
-    }                                                                 \
-  } while (0)
-
-static const btif_hl_data_type_cfg_t data_type_table[] = {
-    /* Data Specilization                   Ntx     Nrx (from Bluetooth SIG's
-       HDP whitepaper)*/
-    {BTIF_HL_DATA_TYPE_PULSE_OXIMETER, 9216, 256},
-    {BTIF_HL_DATA_TYPE_BLOOD_PRESSURE_MON, 896, 224},
-    {BTIF_HL_DATA_TYPE_BODY_THERMOMETER, 896, 224},
-    {BTIF_HL_DATA_TYPE_BODY_WEIGHT_SCALE, 896, 224},
-    {BTIF_HL_DATA_TYPE_GLUCOSE_METER, 896, 224},
-    {BTIF_HL_DATA_TYPE_STEP_COUNTER, 6624, 224},
-    {BTIF_HL_DATA_TYPE_BCA, 7730, 1230},
-    {BTIF_HL_DATA_TYPE_PEAK_FLOW, 2030, 224},
-    {BTIF_HL_DATA_TYPE_ACTIVITY_HUB, 5120, 224},
-    {BTIF_HL_DATA_TYPE_AMM, 1024, 64}};
-
-#define BTIF_HL_DATA_TABLE_SIZE \
-  (sizeof(data_type_table) / sizeof(btif_hl_data_type_cfg_t))
-#define BTIF_HL_DEFAULT_SRC_TX_APDU_SIZE         \
-  10240 /* use this size if the data type is not \
-           defined in the table; for future proof */
-#define BTIF_HL_DEFAULT_SRC_RX_APDU_SIZE       \
-  512 /* use this size if the data type is not \
-         defined in the table; for future proof */
-
-#define BTIF_HL_ECHO_MAX_TX_RX_APDU_SIZE 1024
-
-/*******************************************************************************
- *  Static utility functions
- ******************************************************************************/
-
-#define BTIF_IF_GET_NAME 16
-void btif_hl_display_calling_process_name(void) {
-  char name[16];
-  prctl(BTIF_IF_GET_NAME, name, 0, 0, 0);
-  BTIF_TRACE_DEBUG("Process name (%s)", name);
-}
-#define BTIF_TIMEOUT_CCH_NO_DCH_MS (30 * 1000)
-
-/*******************************************************************************
- *
- * Function      btif_hl_if_channel_setup_pending
- *
- * Description   check whether channel id is in setup pending state or not
- *
- * Returns      bool
- *
- ******************************************************************************/
-bool btif_hl_if_channel_setup_pending(int channel_id, uint8_t* p_app_idx,
-                                      uint8_t* p_mcl_idx) {
-  btif_hl_app_cb_t* p_acb;
-  btif_hl_mcl_cb_t* p_mcb;
-  uint8_t i, j;
-  bool found = false;
-
-  *p_app_idx = 0;
-  *p_mcl_idx = 0;
-  for (i = 0; i < BTA_HL_NUM_APPS; i++) {
-    p_acb = BTIF_HL_GET_APP_CB_PTR(i);
-    if (p_acb->in_use) {
-      for (j = 0; j < BTA_HL_NUM_MCLS; j++) {
-        p_mcb = BTIF_HL_GET_MCL_CB_PTR(i, j);
-        if (p_mcb->in_use && p_mcb->is_connected &&
-            p_mcb->pcb.channel_id == channel_id) {
-          found = true;
-          *p_app_idx = i;
-          *p_mcl_idx = j;
-          break;
-        }
-      }
-    }
-    if (found) break;
-  }
-  BTIF_TRACE_DEBUG("%s found=%d channel_id=0x%08x", __func__, found, channel_id,
-                   *p_app_idx, *p_mcl_idx);
-  return found;
-}
-/*******************************************************************************
- *
- * Function      btif_hl_num_dchs_in_use
- *
- * Description find number of DCHs in use
- *
- * Returns      uint8_t
- ******************************************************************************/
-uint8_t btif_hl_num_dchs_in_use(uint8_t mcl_handle) {
-  btif_hl_app_cb_t* p_acb;
-  btif_hl_mcl_cb_t* p_mcb;
-  uint8_t i, j, x;
-  uint8_t cnt = 0;
-
-  for (i = 0; i < BTA_HL_NUM_APPS; i++) {
-    BTIF_TRACE_DEBUG("btif_hl_num_dchs:i = %d", i);
-    p_acb = BTIF_HL_GET_APP_CB_PTR(i);
-    if (p_acb && p_acb->in_use) {
-      for (j = 0; j < BTA_HL_NUM_MCLS; j++) {
-        if (p_acb->mcb[j].in_use)
-          BTIF_TRACE_DEBUG(
-              "btif_hl_num_dchs:mcb in use j=%d, mcl_handle=%d,mcb handle=%d",
-              j, mcl_handle, p_acb->mcb[j].mcl_handle);
-        if (p_acb->mcb[j].in_use && (p_acb->mcb[j].mcl_handle == mcl_handle)) {
-          p_mcb = &p_acb->mcb[j];
-          BTIF_TRACE_DEBUG("btif_hl_num_dchs: mcl handle found j =%d", j);
-          for (x = 0; x < BTA_HL_NUM_MDLS_PER_MCL; x++) {
-            if (p_mcb->mdl[x].in_use) {
-              BTIF_TRACE_DEBUG("btif_hl_num_dchs_in_use:found x =%d", x);
-              cnt++;
-            }
-          }
-        }
-      }
-    }
-  }
-
-  BTIF_TRACE_DEBUG("%s dch in use count=%d", __func__, cnt);
-  return cnt;
-}
-/*******************************************************************************
- *
- * Function      btif_hl_timer_timeout
- *
- * Description   Process timer timeout
- *
- * Returns      void
- ******************************************************************************/
-void btif_hl_timer_timeout(void* data) {
-  btif_hl_mcl_cb_t* p_mcb = (btif_hl_mcl_cb_t*)data;
-
-  BTIF_TRACE_DEBUG("%s", __func__);
-  if (p_mcb->is_connected) {
-    BTIF_TRACE_DEBUG("Idle timeout Close CCH mcl_handle=%d", p_mcb->mcl_handle);
-    BTA_HlCchClose(p_mcb->mcl_handle);
-  } else {
-    BTIF_TRACE_DEBUG("CCH idle timeout But CCH not connected");
-  }
-}
-
-/*******************************************************************************
- *
- * Function      btif_hl_stop_cch_timer
- *
- * Description  stop CCH timer
- *
- * Returns      void
- ******************************************************************************/
-void btif_hl_stop_cch_timer(uint8_t app_idx, uint8_t mcl_idx) {
-  btif_hl_mcl_cb_t* p_mcb = BTIF_HL_GET_MCL_CB_PTR(app_idx, mcl_idx);
-
-  BTIF_TRACE_DEBUG("%s app_idx=%d, mcl_idx=%d", __func__, app_idx, mcl_idx);
-  alarm_cancel(p_mcb->cch_timer);
-}
-
-/*******************************************************************************
- *
- * Function      btif_hl_start_cch_timer
- *
- * Description  start CCH timer
- *
- * Returns      void
- ******************************************************************************/
-void btif_hl_start_cch_timer(uint8_t app_idx, uint8_t mcl_idx) {
-  btif_hl_mcl_cb_t* p_mcb = BTIF_HL_GET_MCL_CB_PTR(app_idx, mcl_idx);
-  BTIF_TRACE_DEBUG("%s app_idx=%d, mcl_idx=%d", __func__, app_idx, mcl_idx);
-
-  alarm_free(p_mcb->cch_timer);
-  p_mcb->cch_timer = alarm_new("btif_hl.mcl_cch_timer");
-  alarm_set_on_mloop(p_mcb->cch_timer, BTIF_TIMEOUT_CCH_NO_DCH_MS,
-                     btif_hl_timer_timeout, p_mcb);
-}
-
-/*******************************************************************************
- *
- * Function      btif_hl_find_mdl_idx
- *
- * Description  Find the MDL index using MDL ID
- *
- * Returns      bool
- *
- ******************************************************************************/
-static bool btif_hl_find_mdl_idx(uint8_t app_idx, uint8_t mcl_idx,
-                                 uint16_t mdl_id, uint8_t* p_mdl_idx) {
-  btif_hl_mcl_cb_t* p_mcb = BTIF_HL_GET_MCL_CB_PTR(app_idx, mcl_idx);
-  bool found = false;
-  uint8_t i;
-
-  for (i = 0; i < BTA_HL_NUM_MDLS_PER_MCL; i++) {
-    if (p_mcb->mdl[i].in_use && (mdl_id != 0) &&
-        (p_mcb->mdl[i].mdl_id == mdl_id)) {
-      found = true;
-      *p_mdl_idx = i;
-      break;
-    }
-  }
-
-  BTIF_TRACE_DEBUG("%s found=%d mdl_id=%d mdl_idx=%d ", __func__, found, mdl_id,
-                   i);
-
-  return found;
-}
-
-/*******************************************************************************
- *
- * Function      btif_hl_is_the_first_reliable_existed
- *
- * Description  This function checks whether the first reliable DCH channel
- *              has been setup on the MCL or not
- *
- * Returns      bool - true exist
- *                        false does not exist
- *
- ******************************************************************************/
-bool btif_hl_is_the_first_reliable_existed(uint8_t app_idx, uint8_t mcl_idx) {
-  btif_hl_mcl_cb_t* p_mcb = BTIF_HL_GET_MCL_CB_PTR(app_idx, mcl_idx);
-  bool is_existed = false;
-  uint8_t i;
-
-  for (i = 0; i < BTA_HL_NUM_MDLS_PER_MCL; i++) {
-    if (p_mcb->mdl[i].in_use && p_mcb->mdl[i].is_the_first_reliable) {
-      is_existed = true;
-      break;
-    }
-  }
-
-  BTIF_TRACE_DEBUG("bta_hl_is_the_first_reliable_existed is_existed=%d  ",
-                   is_existed);
-  return is_existed;
-}
-/*******************************************************************************
- *
- * Function      btif_hl_clean_delete_mdl
- *
- * Description   Cleanup the delete mdl control block
- *
- * Returns     Nothing
- *
- ******************************************************************************/
-static void btif_hl_clean_delete_mdl(btif_hl_delete_mdl_t* p_cb) {
-  BTIF_TRACE_DEBUG("%s", __func__);
-  memset(p_cb, 0, sizeof(btif_hl_delete_mdl_t));
-}
-
-/*******************************************************************************
- *
- * Function      btif_hl_clean_pcb
- *
- * Description   Cleanup the pending chan control block
- *
- * Returns     Nothing
- *
- ******************************************************************************/
-static void btif_hl_clean_pcb(btif_hl_pending_chan_cb_t* p_pcb) {
-  BTIF_TRACE_DEBUG("%s", __func__);
-  memset(p_pcb, 0, sizeof(btif_hl_pending_chan_cb_t));
-}
-
-/*******************************************************************************
- *
- * Function      btif_hl_clean_mdl_cb
- *
- * Description   Cleanup the MDL control block
- *
- * Returns     Nothing
- *
- ******************************************************************************/
-static void btif_hl_clean_mdl_cb(btif_hl_mdl_cb_t* p_dcb) {
-  BTIF_TRACE_DEBUG("%s", __func__);
-  osi_free_and_reset((void**)&p_dcb->p_rx_pkt);
-  osi_free_and_reset((void**)&p_dcb->p_tx_pkt);
-  memset(p_dcb, 0, sizeof(btif_hl_mdl_cb_t));
-}
-
-/*******************************************************************************
- *
- * Function      btif_hl_reset_mcb
- *
- * Description   Reset MCL control block
- *
- * Returns      bool
- *
- ******************************************************************************/
-static void btif_hl_clean_mcl_cb(uint8_t app_idx, uint8_t mcl_idx) {
-  btif_hl_mcl_cb_t* p_mcb;
-  BTIF_TRACE_DEBUG("%s app_idx=%d, mcl_idx=%d", __func__, app_idx, mcl_idx);
-  p_mcb = BTIF_HL_GET_MCL_CB_PTR(app_idx, mcl_idx);
-  alarm_free(p_mcb->cch_timer);
-  memset(p_mcb, 0, sizeof(btif_hl_mcl_cb_t));
-}
-
-/*******************************************************************************
- *
- * Function      btif_hl_find_sdp_idx_using_mdep_filter
- *
- * Description  This function finds the SDP record index using MDEP filter
- *              parameters
- *
- * Returns      bool
- *
- ******************************************************************************/
-static void btif_hl_reset_mdep_filter(uint8_t app_idx) {
-  btif_hl_app_cb_t* p_acb = BTIF_HL_GET_APP_CB_PTR(app_idx);
-  p_acb->filter.num_elems = 0;
-}
-
-/*******************************************************************************
- *
- * Function      btif_hl_find_sdp_idx_using_mdep_filter
- *
- * Description  This function finds the SDP record index using MDEP filter
- *              parameters
- *
- * Returns      bool
- *
- ******************************************************************************/
-static bool btif_hl_find_sdp_idx_using_mdep_filter(uint8_t app_idx,
-                                                   uint8_t mcl_idx,
-                                                   uint8_t* p_sdp_idx) {
-  btif_hl_app_cb_t* p_acb = BTIF_HL_GET_APP_CB_PTR(app_idx);
-  btif_hl_mcl_cb_t* p_mcb = BTIF_HL_GET_MCL_CB_PTR(app_idx, mcl_idx);
-  uint8_t i, j, num_recs, num_elems, num_mdeps, mdep_idx;
-  tBTA_HL_MDEP_ROLE peer_mdep_role;
-  uint16_t data_type;
-  tBTA_HL_SDP_MDEP_CFG* p_mdep;
-  bool found = false;
-  bool elem_found;
-
-  BTIF_TRACE_DEBUG("btif_hl_find_sdp_idx_using_mdep_filter");
-  num_recs = p_mcb->sdp.num_recs;
-  num_elems = p_acb->filter.num_elems;
-  if (!num_elems) {
-    BTIF_TRACE_DEBUG("btif_hl_find_sdp_idx_using_mdep_filter num_elem=0");
-    *p_sdp_idx = 0;
-    found = true;
-    return found;
-  }
-
-  for (i = 0; i < num_recs; i++) {
-    num_mdeps = p_mcb->sdp.sdp_rec[i].num_mdeps;
-    for (j = 0; j < num_elems; j++) {
-      data_type = p_acb->filter.elem[j].data_type;
-      peer_mdep_role = p_acb->filter.elem[j].peer_mdep_role;
-      elem_found = false;
-      mdep_idx = 0;
-      while (!elem_found && mdep_idx < num_mdeps) {
-        p_mdep = &(p_mcb->sdp.sdp_rec[i].mdep_cfg[mdep_idx]);
-        if ((p_mdep->data_type == data_type) &&
-            (p_mdep->mdep_role == peer_mdep_role)) {
-          elem_found = true;
-        } else {
-          mdep_idx++;
-        }
-      }
-
-      if (!elem_found) {
-        found = false;
-        break;
-      } else {
-        found = true;
-      }
-    }
-
-    if (found) {
-      BTIF_TRACE_DEBUG("btif_hl_find_sdp_idx_using_mdep_filter found idx=%d",
-                       i);
-      *p_sdp_idx = i;
-      break;
-    }
-  }
-
-  BTIF_TRACE_DEBUG("%s found=%d sdp_idx=%d", __func__, found, *p_sdp_idx);
-
-  btif_hl_reset_mdep_filter(app_idx);
-
-  return found;
-}
-/*******************************************************************************
- *
- * Function      btif_hl_is_reconnect_possible
- *
- * Description  check reconnect is possible or not
- *
- * Returns      bool
- *
- ******************************************************************************/
-bool btif_hl_is_reconnect_possible(uint8_t app_idx, uint8_t mcl_idx,
-                                   int mdep_cfg_idx,
-                                   tBTA_HL_DCH_OPEN_PARAM* p_dch_open_api,
-                                   tBTA_HL_MDL_ID* p_mdl_id) {
-  btif_hl_app_cb_t* p_acb = BTIF_HL_GET_APP_CB_PTR(app_idx);
-  btif_hl_mcl_cb_t* p_mcb = BTIF_HL_GET_MCL_CB_PTR(app_idx, mcl_idx);
-  tBTA_HL_DCH_CFG local_cfg = p_dch_open_api->local_cfg;
-  tBTA_HL_DCH_MODE dch_mode = BTA_HL_DCH_MODE_RELIABLE;
-  bool use_mdl_dch_mode = false;
-  btif_hl_mdl_cfg_t* p_mdl;
-  btif_hl_mdl_cfg_t* p_mdl1;
-  uint8_t i, j;
-  bool is_reconnect_ok = false;
-  bool stream_mode_avail = false;
-  uint16_t data_type =
-      p_acb->sup_feature.mdep[mdep_cfg_idx].mdep_cfg.data_cfg[0].data_type;
-  tBTA_HL_MDEP_ID peer_mdep_id = p_dch_open_api->peer_mdep_id;
-  uint8_t mdl_idx;
-
-  BTIF_TRACE_DEBUG("%s app_idx=%d mcl_idx=%d mdep_cfg_idx=%d", __func__,
-                   app_idx, mcl_idx, mdep_cfg_idx);
-  switch (local_cfg) {
-    case BTA_HL_DCH_CFG_NO_PREF:
-      if (!btif_hl_is_the_first_reliable_existed(app_idx, mcl_idx)) {
-        dch_mode = BTA_HL_DCH_MODE_RELIABLE;
-      } else {
-        use_mdl_dch_mode = true;
-      }
-      break;
-    case BTA_HL_DCH_CFG_RELIABLE:
-      dch_mode = BTA_HL_DCH_MODE_RELIABLE;
-      break;
-    case BTA_HL_DCH_CFG_STREAMING:
-      dch_mode = BTA_HL_DCH_MODE_STREAMING;
-      break;
-    default:
-      BTIF_TRACE_ERROR("Invalid local_cfg=%d", local_cfg);
-      return is_reconnect_ok;
-      break;
-  }
-
-  BTIF_TRACE_DEBUG("local_cfg=%d use_mdl_dch_mode=%d dch_mode=%d ", local_cfg,
-                   use_mdl_dch_mode, dch_mode);
-
-  for (i = 0, p_mdl = &p_acb->mdl_cfg[0]; i < BTA_HL_NUM_MDL_CFGS;
-       i++, p_mdl++) {
-    if (p_mdl->base.active && p_mdl->extra.data_type == data_type &&
-        (p_mdl->extra.peer_mdep_id != BTA_HL_INVALID_MDEP_ID &&
-         p_mdl->extra.peer_mdep_id == peer_mdep_id) &&
-        p_mdl->base.peer_bd_addr != p_mcb->bd_addr && p_mdl->base.mdl_id &&
-        !btif_hl_find_mdl_idx(app_idx, mcl_idx, p_mdl->base.mdl_id, &mdl_idx)) {
-      BTIF_TRACE_DEBUG("i=%d Matched active=%d   mdl_id =%d, mdl_dch_mode=%d",
-                       i, p_mdl->base.active, p_mdl->base.mdl_id,
-                       p_mdl->base.dch_mode);
-      if (!use_mdl_dch_mode) {
-        if (p_mdl->base.dch_mode == dch_mode) {
-          is_reconnect_ok = true;
-          *p_mdl_id = p_mdl->base.mdl_id;
-          BTIF_TRACE_DEBUG("reconnect is possible dch_mode=%d mdl_id=%d",
-                           dch_mode, p_mdl->base.mdl_id);
-          break;
-        }
-      } else {
-        is_reconnect_ok = true;
-        for (j = i, p_mdl1 = &p_acb->mdl_cfg[i]; j < BTA_HL_NUM_MDL_CFGS;
-             j++, p_mdl1++) {
-          if (p_mdl1->base.active && p_mdl1->extra.data_type == data_type &&
-              (p_mdl1->extra.peer_mdep_id != BTA_HL_INVALID_MDEP_ID &&
-               p_mdl1->extra.peer_mdep_id == peer_mdep_id) &&
-              p_mdl1->base.peer_bd_addr != p_mcb->bd_addr &&
-              p_mdl1->base.dch_mode == BTA_HL_DCH_MODE_STREAMING) {
-            stream_mode_avail = true;
-            BTIF_TRACE_DEBUG("found streaming mode mdl index=%d", j);
-            break;
-          }
-        }
-
-        if (stream_mode_avail) {
-          dch_mode = BTA_HL_DCH_MODE_STREAMING;
-          *p_mdl_id = p_mdl1->base.mdl_id;
-          BTIF_TRACE_DEBUG(
-              "reconnect is ok index=%d dch_mode=streaming  mdl_id=%d", j,
-              *p_mdl_id);
-          break;
-        } else {
-          dch_mode = p_mdl->base.dch_mode;
-          *p_mdl_id = p_mdl->base.mdl_id;
-          BTIF_TRACE_DEBUG("reconnect is ok index=%d  dch_mode=%d mdl_id=%d", i,
-                           p_mdl->base.dch_mode, *p_mdl_id);
-          break;
-        }
-      }
-    }
-  }
-
-  BTIF_TRACE_DEBUG("is_reconnect_ok  dch_mode=%d mdl_id=%d", is_reconnect_ok,
-                   dch_mode, *p_mdl_id);
-  return is_reconnect_ok;
-}
-
-/*******************************************************************************
- *
- * Function      btif_hl_dch_open
- *
- * Description   Process DCH open request using the DCH Open API parameters
- *
- * Returns      bool
- *
- ******************************************************************************/
-bool btif_hl_dch_open(uint8_t app_id, const RawAddress& bd_addr,
-                      tBTA_HL_DCH_OPEN_PARAM* p_dch_open_api, int mdep_cfg_idx,
-                      btif_hl_pend_dch_op_t op, int* channel_id) {
-  btif_hl_app_cb_t* p_acb;
-  btif_hl_mcl_cb_t* p_mcb;
-  btif_hl_pending_chan_cb_t* p_pcb;
-  uint8_t app_idx, mcl_idx;
-  bool status = false;
-  tBTA_HL_MDL_ID mdl_id;
-  tBTA_HL_DCH_RECONNECT_PARAM reconnect_param;
-
-  BTIF_TRACE_DEBUG("%s app_id=%d ", __func__, app_id);
-  VLOG(0) << "DB " << bd_addr;
-
-  if (btif_hl_find_app_idx(app_id, &app_idx)) {
-    if (btif_hl_find_mcl_idx(app_idx, bd_addr, &mcl_idx)) {
-      p_mcb = BTIF_HL_GET_MCL_CB_PTR(app_idx, mcl_idx);
-
-      p_pcb = BTIF_HL_GET_PCB_PTR(app_idx, mcl_idx);
-      if (!p_pcb->in_use) {
-        p_mcb->req_ctrl_psm = p_dch_open_api->ctrl_psm;
-
-        p_pcb->in_use = true;
-        *channel_id = p_pcb->channel_id =
-            (int)btif_hl_get_next_channel_id(app_id);
-        p_pcb->cb_state = BTIF_HL_CHAN_CB_STATE_CONNECTING_PENDING;
-        p_pcb->mdep_cfg_idx = mdep_cfg_idx;
-        p_pcb->bd_addr = bd_addr;
-        p_pcb->op = op;
-
-        if (p_mcb->sdp.num_recs) {
-          if (p_mcb->valid_sdp_idx) {
-            p_dch_open_api->ctrl_psm = p_mcb->ctrl_psm;
-          }
-
-          if (!btif_hl_is_reconnect_possible(app_idx, mcl_idx, mdep_cfg_idx,
-                                             p_dch_open_api, &mdl_id)) {
-            BTIF_TRACE_DEBUG("Issue DCH open");
-            BTA_HlDchOpen(p_mcb->mcl_handle, p_dch_open_api);
-          } else {
-            reconnect_param.ctrl_psm = p_mcb->ctrl_psm;
-            reconnect_param.mdl_id = mdl_id;
-            ;
-            BTIF_TRACE_DEBUG("Issue Reconnect ctrl_psm=0x%x mdl_id=0x%x",
-                             reconnect_param.ctrl_psm, reconnect_param.mdl_id);
-            BTA_HlDchReconnect(p_mcb->mcl_handle, &reconnect_param);
-          }
-
-          status = true;
-        } else {
-          p_acb = BTIF_HL_GET_APP_CB_PTR(app_idx);
-          p_mcb->cch_oper = BTIF_HL_CCH_OP_DCH_OPEN;
-          BTA_HlSdpQuery(app_id, p_acb->app_handle, bd_addr);
-          status = true;
-        }
-      }
-    }
-  }
-
-  BTIF_TRACE_DEBUG("status=%d ", status);
-  return status;
-}
-
-/*******************************************************************************
- *
- * Function         btif_hl_dch_abort
- *
- * Description      Process DCH abort request
- *
- * Returns          Nothing
- *
- ******************************************************************************/
-void btif_hl_dch_abort(uint8_t app_idx, uint8_t mcl_idx) {
-  btif_hl_mcl_cb_t* p_mcb;
-
-  BTIF_TRACE_DEBUG("%s app_idx=%d mcl_idx=%d", __func__, app_idx, mcl_idx);
-  p_mcb = BTIF_HL_GET_MCL_CB_PTR(app_idx, mcl_idx);
-  if (p_mcb->is_connected) {
-    BTA_HlDchAbort(p_mcb->mcl_handle);
-  } else {
-    p_mcb->pcb.abort_pending = true;
-  }
-}
-/*******************************************************************************
- *
- * Function      btif_hl_cch_open
- *
- * Description   Process CCH open request
- *
- * Returns     Nothing
- *
- ******************************************************************************/
-bool btif_hl_cch_open(uint8_t app_id, const RawAddress& bd_addr,
-                      uint16_t ctrl_psm, int mdep_cfg_idx,
-                      btif_hl_pend_dch_op_t op, int* channel_id) {
-  btif_hl_app_cb_t* p_acb;
-  btif_hl_mcl_cb_t* p_mcb;
-  btif_hl_pending_chan_cb_t* p_pcb;
-  uint8_t app_idx, mcl_idx;
-  bool status = true;
-
-  BTIF_TRACE_DEBUG("%s app_id=%d ctrl_psm=%d mdep_cfg_idx=%d op=%d", __func__,
-                   app_id, ctrl_psm, mdep_cfg_idx, op);
-  VLOG(1) << "DB " << bd_addr;
-
-  if (btif_hl_find_app_idx(app_id, &app_idx)) {
-    p_acb = BTIF_HL_GET_APP_CB_PTR(app_idx);
-
-    if (!btif_hl_find_mcl_idx(app_idx, bd_addr, &mcl_idx)) {
-      if (btif_hl_find_avail_mcl_idx(app_idx, &mcl_idx)) {
-        p_mcb = BTIF_HL_GET_MCL_CB_PTR(app_idx, mcl_idx);
-        alarm_free(p_mcb->cch_timer);
-        memset(p_mcb, 0, sizeof(btif_hl_mcl_cb_t));
-        p_mcb->in_use = true;
-        p_mcb->bd_addr = bd_addr;
-
-        if (!ctrl_psm) {
-          p_mcb->cch_oper = BTIF_HL_CCH_OP_MDEP_FILTERING;
-        } else {
-          p_mcb->cch_oper = BTIF_HL_CCH_OP_MATCHED_CTRL_PSM;
-          p_mcb->req_ctrl_psm = ctrl_psm;
-        }
-
-        p_pcb = BTIF_HL_GET_PCB_PTR(app_idx, mcl_idx);
-        p_pcb->in_use = true;
-        p_pcb->mdep_cfg_idx = mdep_cfg_idx;
-        p_pcb->bd_addr = bd_addr;
-        p_pcb->op = op;
-
-        switch (op) {
-          case BTIF_HL_PEND_DCH_OP_OPEN:
-            *channel_id = p_pcb->channel_id =
-                (int)btif_hl_get_next_channel_id(app_id);
-            p_pcb->cb_state = BTIF_HL_CHAN_CB_STATE_CONNECTING_PENDING;
-            break;
-          case BTIF_HL_PEND_DCH_OP_DELETE_MDL:
-            p_pcb->channel_id = p_acb->delete_mdl.channel_id;
-            p_pcb->cb_state = BTIF_HL_CHAN_CB_STATE_DESTROYED_PENDING;
-            break;
-          default:
-            break;
-        }
-        BTA_HlSdpQuery(app_id, p_acb->app_handle, bd_addr);
-      } else {
-        status = false;
-        BTIF_TRACE_ERROR("Open CCH request discarded- No mcl cb");
-      }
-    } else {
-      status = false;
-      BTIF_TRACE_ERROR("Open CCH request discarded- already in USE");
-    }
-  } else {
-    status = false;
-    BTIF_TRACE_ERROR("Invalid app_id=%d", app_id);
-  }
-
-  if (channel_id) {
-    BTIF_TRACE_DEBUG("status=%d channel_id=0x%08x", status, *channel_id);
-  } else {
-    BTIF_TRACE_DEBUG("status=%d ", status);
-  }
-  return status;
-}
-
-/*******************************************************************************
- *
- * Function      btif_hl_find_mdl_idx_using_handle
- *
- * Description  Find the MDL index using channel id
- *
- * Returns      bool
- *
- ******************************************************************************/
-bool btif_hl_find_mdl_cfg_idx_using_channel_id(int channel_id,
-                                               uint8_t* p_app_idx,
-                                               uint8_t* p_mdl_cfg_idx) {
-  btif_hl_app_cb_t* p_acb;
-  btif_hl_mdl_cfg_t* p_mdl;
-  bool found = false;
-  uint8_t i, j;
-  int mdl_cfg_channel_id;
-
-  *p_app_idx = 0;
-  *p_mdl_cfg_idx = 0;
-  for (i = 0; i < BTA_HL_NUM_APPS; i++) {
-    p_acb = BTIF_HL_GET_APP_CB_PTR(i);
-    for (j = 0; j < BTA_HL_NUM_MDL_CFGS; j++) {
-      p_mdl = BTIF_HL_GET_MDL_CFG_PTR(i, j);
-      mdl_cfg_channel_id = *(BTIF_HL_GET_MDL_CFG_CHANNEL_ID_PTR(i, j));
-      if (p_acb->in_use && p_mdl->base.active &&
-          (mdl_cfg_channel_id == channel_id)) {
-        found = true;
-        *p_app_idx = i;
-        *p_mdl_cfg_idx = j;
-        break;
-      }
-    }
-  }
-
-  BTIF_TRACE_EVENT("%s found=%d channel_id=0x%08x, app_idx=%d mdl_cfg_idx=%d  ",
-                   __func__, found, channel_id, i, j);
-  return found;
-}
-/*******************************************************************************
- *
- * Function      btif_hl_find_mdl_idx_using_handle
- *
- * Description  Find the MDL index using channel id
- *
- * Returns      bool
- *
- ******************************************************************************/
-bool btif_hl_find_mdl_idx_using_channel_id(int channel_id, uint8_t* p_app_idx,
-                                           uint8_t* p_mcl_idx,
-                                           uint8_t* p_mdl_idx) {
-  btif_hl_app_cb_t* p_acb;
-  btif_hl_mcl_cb_t* p_mcb;
-  btif_hl_mdl_cb_t* p_dcb;
-  bool found = false;
-  uint8_t i, j, k;
-
-  for (i = 0; i < BTA_HL_NUM_APPS; i++) {
-    p_acb = BTIF_HL_GET_APP_CB_PTR(i);
-    for (j = 0; j < BTA_HL_NUM_MCLS; j++) {
-      p_mcb = BTIF_HL_GET_MCL_CB_PTR(i, j);
-      for (k = 0; k < BTA_HL_NUM_MDLS_PER_MCL; k++) {
-        p_dcb = BTIF_HL_GET_MDL_CB_PTR(i, j, k);
-        if (p_acb->in_use && p_mcb->in_use && p_dcb->in_use &&
-            (p_dcb->channel_id == channel_id)) {
-          found = true;
-          *p_app_idx = i;
-          *p_mcl_idx = j;
-          *p_mdl_idx = k;
-          break;
-        }
-      }
-    }
-  }
-  BTIF_TRACE_DEBUG("%s found=%d app_idx=%d mcl_idx=%d mdl_idx=%d  ", __func__,
-                   found, i, j, k);
-  return found;
-}
-
-/*******************************************************************************
- *
- * Function      btif_hl_find_channel_id_using_mdl_id
- *
- * Description  Find channel id using mdl_id'
- *
- * Returns      bool
- ******************************************************************************/
-bool btif_hl_find_channel_id_using_mdl_id(uint8_t app_idx,
-                                          tBTA_HL_MDL_ID mdl_id,
-                                          int* p_channel_id) {
-  btif_hl_app_cb_t* p_acb;
-  btif_hl_mdl_cfg_t* p_mdl;
-  bool found = false;
-  uint8_t j = 0;
-  int mdl_cfg_channel_id;
-  p_acb = BTIF_HL_GET_APP_CB_PTR(app_idx);
-  if (p_acb && p_acb->in_use) {
-    for (j = 0; j < BTA_HL_NUM_MDL_CFGS; j++) {
-      p_mdl = BTIF_HL_GET_MDL_CFG_PTR(app_idx, j);
-      mdl_cfg_channel_id = *(BTIF_HL_GET_MDL_CFG_CHANNEL_ID_PTR(app_idx, j));
-      if (p_mdl->base.active && (p_mdl->base.mdl_id == mdl_id)) {
-        found = true;
-        *p_channel_id = mdl_cfg_channel_id;
-        break;
-      }
-    }
-  }
-  BTIF_TRACE_EVENT(
-      "%s found=%d channel_id=0x%08x, mdl_id=0x%x app_idx=%d mdl_cfg_idx=%d  ",
-      __func__, found, *p_channel_id, mdl_id, app_idx, j);
-  return found;
-}
-
-/*******************************************************************************
- *
- * Function      btif_hl_find_mdl_idx_using_handle
- *
- * Description  Find the MDL index using handle
- *
- * Returns      bool
- *
- ******************************************************************************/
-bool btif_hl_find_mdl_idx_using_handle(tBTA_HL_MDL_HANDLE mdl_handle,
-                                       uint8_t* p_app_idx, uint8_t* p_mcl_idx,
-                                       uint8_t* p_mdl_idx) {
-  btif_hl_app_cb_t* p_acb;
-  btif_hl_mcl_cb_t* p_mcb;
-  btif_hl_mdl_cb_t* p_dcb;
-  bool found = false;
-  uint8_t i, j, k;
-
-  *p_app_idx = 0;
-  *p_mcl_idx = 0;
-  *p_mdl_idx = 0;
-  for (i = 0; i < BTA_HL_NUM_APPS; i++) {
-    p_acb = BTIF_HL_GET_APP_CB_PTR(i);
-    for (j = 0; j < BTA_HL_NUM_MCLS; j++) {
-      p_mcb = BTIF_HL_GET_MCL_CB_PTR(i, j);
-      for (k = 0; k < BTA_HL_NUM_MDLS_PER_MCL; k++) {
-        p_dcb = BTIF_HL_GET_MDL_CB_PTR(i, j, k);
-        if (p_acb->in_use && p_mcb->in_use && p_dcb->in_use &&
-            (p_dcb->mdl_handle == mdl_handle)) {
-          found = true;
-          *p_app_idx = i;
-          *p_mcl_idx = j;
-          *p_mdl_idx = k;
-          break;
-        }
-      }
-    }
-  }
-
-  BTIF_TRACE_EVENT("%s found=%d app_idx=%d mcl_idx=%d mdl_idx=%d  ", __func__,
-                   found, i, j, k);
-  return found;
-}
-/*******************************************************************************
- *
- * Function        btif_hl_find_peer_mdep_id
- *
- * Description      Find the peer MDEP ID from the received SPD records
- *
- * Returns          bool
- *
- ******************************************************************************/
-static bool btif_hl_find_peer_mdep_id(uint8_t app_id, const RawAddress& bd_addr,
-                                      tBTA_HL_MDEP_ROLE local_mdep_role,
-                                      uint16_t data_type,
-                                      tBTA_HL_MDEP_ID* p_peer_mdep_id) {
-  uint8_t app_idx, mcl_idx;
-  btif_hl_mcl_cb_t* p_mcb;
-  tBTA_HL_SDP_REC* p_rec;
-  uint8_t i, num_mdeps;
-  bool found = false;
-  tBTA_HL_MDEP_ROLE peer_mdep_role;
-
-  BTIF_TRACE_DEBUG("%s app_id=%d local_mdep_role=%d, data_type=%d", __func__,
-                   app_id, local_mdep_role, data_type);
-  VLOG(1) << "DB " << bd_addr;
-
-  BTIF_TRACE_DEBUG("local_mdep_role=%d", local_mdep_role);
-  BTIF_TRACE_DEBUG("data_type=%d", data_type);
-
-  if (local_mdep_role == BTA_HL_MDEP_ROLE_SINK)
-    peer_mdep_role = BTA_HL_MDEP_ROLE_SOURCE;
-  else
-    peer_mdep_role = BTA_HL_MDEP_ROLE_SINK;
-
-  if (btif_hl_find_app_idx(app_id, &app_idx)) {
-    if (btif_hl_find_mcl_idx(app_idx, bd_addr, &mcl_idx)) {
-      p_mcb = BTIF_HL_GET_MCL_CB_PTR(app_idx, mcl_idx);
-
-      BTIF_TRACE_DEBUG("app_idx=%d mcl_idx=%d", app_idx, mcl_idx);
-      BTIF_TRACE_DEBUG("valid_spd_idx=%d sdp_idx=%d", p_mcb->valid_sdp_idx,
-                       p_mcb->sdp_idx);
-      if (p_mcb->valid_sdp_idx) {
-        p_rec = &p_mcb->sdp.sdp_rec[p_mcb->sdp_idx];
-        num_mdeps = p_rec->num_mdeps;
-        BTIF_TRACE_DEBUG("num_mdeps=%d", num_mdeps);
-
-        for (i = 0; i < num_mdeps; i++) {
-          BTIF_TRACE_DEBUG("p_rec->mdep_cfg[%d].mdep_role=%d", i,
-                           p_rec->mdep_cfg[i].mdep_role);
-          BTIF_TRACE_DEBUG("p_rec->mdep_cfg[%d].data_type =%d", i,
-                           p_rec->mdep_cfg[i].data_type);
-          if ((p_rec->mdep_cfg[i].mdep_role == peer_mdep_role) &&
-              (p_rec->mdep_cfg[i].data_type == data_type)) {
-            found = true;
-            *p_peer_mdep_id = p_rec->mdep_cfg[i].mdep_id;
-            break;
-          }
-        }
-      }
-    }
-  }
-
-  BTIF_TRACE_DEBUG("found =%d  *p_peer_mdep_id=%d", found, *p_peer_mdep_id);
-
-  return found;
-}
-
-/*******************************************************************************
- *
- * Function      btif_hl_find_mdep_cfg_idx
- *
- * Description  Find the MDEP configuration index using local MDEP_ID
- *
- * Returns      bool
- *
- ******************************************************************************/
-static bool btif_hl_find_mdep_cfg_idx(uint8_t app_idx,
-                                      tBTA_HL_MDEP_ID local_mdep_id,
-                                      uint8_t* p_mdep_cfg_idx) {
-  btif_hl_app_cb_t* p_acb = BTIF_HL_GET_APP_CB_PTR(app_idx);
-  tBTA_HL_SUP_FEATURE* p_sup_feature = &p_acb->sup_feature;
-  bool found = false;
-  uint8_t i;
-
-  for (i = 0; i < p_sup_feature->num_of_mdeps; i++) {
-    BTIF_TRACE_DEBUG("btif_hl_find_mdep_cfg_idx: mdep_id=%d app_idx = %d",
-                     p_sup_feature->mdep[i].mdep_id, app_idx);
-    if (p_sup_feature->mdep[i].mdep_id == local_mdep_id) {
-      found = true;
-      *p_mdep_cfg_idx = i;
-      break;
-    }
-  }
-
-  BTIF_TRACE_DEBUG("%s found=%d mdep_idx=%d local_mdep_id=%d app_idx=%d ",
-                   __func__, found, i, local_mdep_id, app_idx);
-  return found;
-}
-
-/*******************************************************************************
- *
- * Function      btif_hl_find_mcl_idx
- *
- * Description  Find the MCL index using BD address
- *
- * Returns      bool
- *
- ******************************************************************************/
-bool btif_hl_find_mcl_idx(uint8_t app_idx, const RawAddress& p_bd_addr,
-                          uint8_t* p_mcl_idx) {
-  bool found = false;
-  uint8_t i;
-  btif_hl_mcl_cb_t* p_mcb;
-
-  *p_mcl_idx = 0;
-  for (i = 0; i < BTA_HL_NUM_MCLS; i++) {
-    p_mcb = BTIF_HL_GET_MCL_CB_PTR(app_idx, i);
-    if (p_mcb->in_use && p_mcb->bd_addr == p_bd_addr) {
-      found = true;
-      *p_mcl_idx = i;
-      break;
-    }
-  }
-
-  BTIF_TRACE_DEBUG("%s found=%d idx=%d", __func__, found, i);
-  return found;
-}
-/*******************************************************************************
- *
- * Function         btif_hl_init
- *
- * Description      HL initialization function.
- *
- * Returns          void
- *
- ******************************************************************************/
-static void btif_hl_init(void) {
-  BTIF_TRACE_DEBUG("%s", __func__);
-  memset(p_btif_hl_cb, 0, sizeof(btif_hl_cb_t));
-  btif_hl_init_next_app_id();
-  btif_hl_init_next_channel_id();
-}
-/*******************************************************************************
- *
- * Function         btif_hl_disable
- *
- * Description      Disable initialization function.
- *
- * Returns          void
- *
- ******************************************************************************/
-static void btif_hl_disable(void) {
-  BTIF_TRACE_DEBUG("%s", __func__);
-
-  if ((p_btif_hl_cb->state != BTIF_HL_STATE_DISABLING) &&
-      (p_btif_hl_cb->state != BTIF_HL_STATE_DISABLED)) {
-    btif_hl_set_state(BTIF_HL_STATE_DISABLING);
-    BTA_HlDisable();
-  }
-}
-/*******************************************************************************
- *
- * Function      btif_hl_is_no_active_app
- *
- * Description  Find whether or not  any APP is still in use
- *
- * Returns      bool
- *
- ******************************************************************************/
-static bool btif_hl_is_no_active_app(void) {
-  bool no_active_app = true;
-  uint8_t i;
-
-  for (i = 0; i < BTA_HL_NUM_APPS; i++) {
-    if (btif_hl_cb.acb[i].in_use) {
-      no_active_app = false;
-      break;
-    }
-  }
-
-  BTIF_TRACE_DEBUG("%s no_active_app=%d  ", __func__, no_active_app);
-  return no_active_app;
-}
-
-/*******************************************************************************
- *
- * Function      btif_hl_free_app_idx
- *
- * Description free an application control block
- *
- * Returns      void
- *
- ******************************************************************************/
-static void btif_hl_free_app_idx(uint8_t app_idx) {
-  if ((app_idx < BTA_HL_NUM_APPS) && btif_hl_cb.acb[app_idx].in_use) {
-    btif_hl_cb.acb[app_idx].in_use = false;
-    for (size_t i = 0; i < BTA_HL_NUM_MCLS; i++)
-      alarm_free(btif_hl_cb.acb[app_idx].mcb[i].cch_timer);
-    memset(&btif_hl_cb.acb[app_idx], 0, sizeof(btif_hl_app_cb_t));
-  }
-}
-/*******************************************************************************
- *
- * Function      btif_hl_set_state
- *
- * Description set HL state
- *
- * Returns      void
- *
- ******************************************************************************/
-static void btif_hl_set_state(btif_hl_state_t state) {
-  BTIF_TRACE_DEBUG("btif_hl_set_state:  %d ---> %d ", p_btif_hl_cb->state,
-                   state);
-  p_btif_hl_cb->state = state;
-}
-
-/*******************************************************************************
- *
- * Function      btif_hl_set_state
- *
- * Description get HL state
- *
- * Returns      btif_hl_state_t
- *
- ******************************************************************************/
-
-static btif_hl_state_t btif_hl_get_state(void) {
-  BTIF_TRACE_DEBUG("btif_hl_get_state:  %d   ", p_btif_hl_cb->state);
-  return p_btif_hl_cb->state;
-}
-
-/*******************************************************************************
- *
- * Function      btif_hl_find_data_type_idx
- *
- * Description  Find the index in the data type table
- *
- * Returns      bool
- *
- ******************************************************************************/
-static bool btif_hl_find_data_type_idx(uint16_t data_type, uint8_t* p_idx) {
-  bool found = false;
-  uint8_t i;
-
-  for (i = 0; i < BTIF_HL_DATA_TABLE_SIZE; i++) {
-    if (data_type_table[i].data_type == data_type) {
-      found = true;
-      *p_idx = i;
-      break;
-    }
-  }
-
-  BTIF_TRACE_DEBUG("%s found=%d, data_type=0x%x idx=%d", __func__, found,
-                   data_type, i);
-  return found;
-}
-
-/*******************************************************************************
- *
- * Function      btif_hl_get_max_tx_apdu_size
- *
- * Description  Find the maximum TX APDU size for the specified data type and
- *              MDEP role
- *
- * Returns      uint16_t
- *
- ******************************************************************************/
-uint16_t btif_hl_get_max_tx_apdu_size(tBTA_HL_MDEP_ROLE mdep_role,
-                                      uint16_t data_type) {
-  uint8_t idx;
-  uint16_t max_tx_apdu_size = 0;
-
-  if (btif_hl_find_data_type_idx(data_type, &idx)) {
-    if (mdep_role == BTA_HL_MDEP_ROLE_SOURCE) {
-      max_tx_apdu_size = data_type_table[idx].max_tx_apdu_size;
-    } else {
-      max_tx_apdu_size = data_type_table[idx].max_rx_apdu_size;
-    }
-  } else {
-    if (mdep_role == BTA_HL_MDEP_ROLE_SOURCE) {
-      max_tx_apdu_size = BTIF_HL_DEFAULT_SRC_TX_APDU_SIZE;
-    } else {
-      max_tx_apdu_size = BTIF_HL_DEFAULT_SRC_RX_APDU_SIZE;
-    }
-  }
-
-  BTIF_TRACE_DEBUG("%s mdep_role=%d data_type=0x%4x size=%d", __func__,
-                   mdep_role, data_type, max_tx_apdu_size);
-  return max_tx_apdu_size;
-}
-
-/*******************************************************************************
- *
- * Function      btif_hl_get_max_rx_apdu_size
- *
- * Description  Find the maximum RX APDU size for the specified data type and
- *              MDEP role
- *
- * Returns      uint16_t
- *
- ******************************************************************************/
-uint16_t btif_hl_get_max_rx_apdu_size(tBTA_HL_MDEP_ROLE mdep_role,
-                                      uint16_t data_type) {
-  uint8_t idx;
-  uint16_t max_rx_apdu_size = 0;
-
-  if (btif_hl_find_data_type_idx(data_type, &idx)) {
-    if (mdep_role == BTA_HL_MDEP_ROLE_SOURCE) {
-      max_rx_apdu_size = data_type_table[idx].max_rx_apdu_size;
-    } else {
-      max_rx_apdu_size = data_type_table[idx].max_tx_apdu_size;
-    }
-  } else {
-    if (mdep_role == BTA_HL_MDEP_ROLE_SOURCE) {
-      max_rx_apdu_size = BTIF_HL_DEFAULT_SRC_RX_APDU_SIZE;
-    } else {
-      max_rx_apdu_size = BTIF_HL_DEFAULT_SRC_TX_APDU_SIZE;
-    }
-  }
-
-  BTIF_TRACE_DEBUG("%s mdep_role=%d data_type=0x%4x size=%d", __func__,
-                   mdep_role, data_type, max_rx_apdu_size);
-
-  return max_rx_apdu_size;
-}
-
-/*******************************************************************************
- *
- * Function      btif_hl_if_channel_setup_pending
- *
- * Description
- *
- * Returns      bool
- *
- ******************************************************************************/
-
-static bool btif_hl_get_bta_mdep_role(bthl_mdep_role_t mdep,
-                                      tBTA_HL_MDEP_ROLE* p) {
-  bool status = true;
-  switch (mdep) {
-    case BTHL_MDEP_ROLE_SOURCE:
-      *p = BTA_HL_MDEP_ROLE_SOURCE;
-      break;
-    case BTHL_MDEP_ROLE_SINK:
-      *p = BTA_HL_MDEP_ROLE_SINK;
-      break;
-    default:
-      *p = BTA_HL_MDEP_ROLE_SOURCE;
-      status = false;
-      break;
-  }
-
-  BTIF_TRACE_DEBUG("%s status=%d bta_mdep_role=%d (%d:btif)", __func__, status,
-                   *p, mdep);
-  return status;
-}
-/*******************************************************************************
- *
- * Function btif_hl_get_bta_channel_type
- *
- * Description convert bthl channel type to BTA DCH channel type
- *
- * Returns bool
- *
- ******************************************************************************/
-
-static bool btif_hl_get_bta_channel_type(bthl_channel_type_t channel_type,
-                                         tBTA_HL_DCH_CFG* p) {
-  bool status = true;
-  switch (channel_type) {
-    case BTHL_CHANNEL_TYPE_RELIABLE:
-      *p = BTA_HL_DCH_CFG_RELIABLE;
-      break;
-    case BTHL_CHANNEL_TYPE_STREAMING:
-      *p = BTA_HL_DCH_CFG_STREAMING;
-      break;
-    case BTHL_CHANNEL_TYPE_ANY:
-      *p = BTA_HL_DCH_CFG_NO_PREF;
-      break;
-    default:
-      status = false;
-      break;
-  }
-  BTIF_TRACE_DEBUG("%s status = %d BTA DCH CFG=%d (1-rel 2-strm", __func__,
-                   status, *p);
-  return status;
-}
-/*******************************************************************************
- *
- * Function btif_hl_get_next_app_id
- *
- * Description get next applcation id
- *
- * Returns uint8_t
- *
- ******************************************************************************/
-
-static uint8_t btif_hl_get_next_app_id() {
-  uint8_t next_app_id = btif_hl_cb.next_app_id;
-
-  btif_hl_cb.next_app_id++;
-  return next_app_id;
-}
-/*******************************************************************************
- *
- * Function btif_hl_get_next_channel_id
- *
- * Description get next channel id
- *
- * Returns int
- *
- ******************************************************************************/
-static int btif_hl_get_next_channel_id(uint8_t app_id) {
-  uint16_t next_channel_id = btif_hl_cb.next_channel_id;
-  int channel_id;
-  btif_hl_cb.next_channel_id++;
-  channel_id = (app_id << 16) + next_channel_id;
-  BTIF_TRACE_DEBUG("%s channel_id=0x%08x, app_id=0x%02x next_channel_id=0x%04x",
-                   __func__, channel_id, app_id, next_channel_id);
-  return channel_id;
-}
-/*******************************************************************************
- *
- * Function btif_hl_get_app_id
- *
- * Description get the applicaiton id associated with the channel id
- *
- * Returns uint8_t
- *
- ******************************************************************************/
-
-static uint8_t btif_hl_get_app_id(int channel_id) {
-  uint8_t app_id = (uint8_t)(channel_id >> 16);
-  BTIF_TRACE_DEBUG("%s channel_id=0x%08x, app_id=0x%02x ", __func__, channel_id,
-                   app_id);
-  return app_id;
-}
-/*******************************************************************************
- *
- * Function btif_hl_init_next_app_id
- *
- * Description initialize the application id
- *
- * Returns void
- *
- ******************************************************************************/
-static void btif_hl_init_next_app_id(void) { btif_hl_cb.next_app_id = 1; }
-/*******************************************************************************
- *
- * Function btif_hl_init_next_channel_id
- *
- * Description initialize the channel id
- *
- * Returns void
- *
- ******************************************************************************/
-static void btif_hl_init_next_channel_id(void) {
-  btif_hl_cb.next_channel_id = 1;
-}
-
-/*******************************************************************************
- *
- * Function      btif_hl_find_app_idx_using_handle
- *
- * Description  Find the applicaiton index using handle
- *
- * Returns      bool
- *
- ******************************************************************************/
-bool btif_hl_find_app_idx_using_handle(tBTA_HL_APP_HANDLE app_handle,
-                                       uint8_t* p_app_idx) {
-  bool found = false;
-  uint8_t i;
-
-  for (i = 0; i < BTA_HL_NUM_APPS; i++) {
-    if (btif_hl_cb.acb[i].in_use &&
-        (btif_hl_cb.acb[i].app_handle == app_handle)) {
-      found = true;
-      *p_app_idx = i;
-      break;
-    }
-  }
-
-  BTIF_TRACE_EVENT("%s status=%d handle=%d app_idx=%d ", __func__, found,
-                   app_handle, i);
-
-  return found;
-}
-
-/*******************************************************************************
- *
- * Function      btif_hl_find_app_idx_using_app_id
- *
- * Description  Find the applicaiton index using app_id
- *
- * Returns      bool
- *
- ******************************************************************************/
-bool btif_hl_find_app_idx_using_app_id(uint8_t app_id, uint8_t* p_app_idx) {
-  bool found = false;
-  uint8_t i;
-
-  *p_app_idx = 0;
-  for (i = 0; i < BTA_HL_NUM_APPS; i++) {
-    if (btif_hl_cb.acb[i].in_use && (btif_hl_cb.acb[i].app_id == app_id)) {
-      found = true;
-      *p_app_idx = i;
-      break;
-    }
-  }
-
-  BTIF_TRACE_EVENT("%s found=%d app_id=%d app_idx=%d ", __func__, found, app_id,
-                   i);
-
-  return found;
-}
-
-/*******************************************************************************
- *
- * Function      btif_hl_find_mcl_idx_using_handle
- *
- * Description  Find the MCL index using handle
- *
- * Returns      bool
- *
- ******************************************************************************/
-bool btif_hl_find_mcl_idx_using_handle(tBTA_HL_MCL_HANDLE mcl_handle,
-                                       uint8_t* p_app_idx, uint8_t* p_mcl_idx) {
-  btif_hl_app_cb_t* p_acb;
-  bool found = false;
-  uint8_t i, j;
-
-  for (i = 0; i < BTA_HL_NUM_APPS; i++) {
-    p_acb = BTIF_HL_GET_APP_CB_PTR(i);
-    for (j = 0; j < BTA_HL_NUM_MCLS; j++) {
-      if (p_acb->mcb[j].in_use)
-        BTIF_TRACE_DEBUG(
-            "btif_hl_find_mcl_idx_using_handle:app_idx=%d,"
-            "mcl_idx =%d mcl_handle=%d",
-            i, j, p_acb->mcb[j].mcl_handle);
-      if (p_acb->mcb[j].in_use && (p_acb->mcb[j].mcl_handle == mcl_handle)) {
-        found = true;
-        *p_app_idx = i;
-        *p_mcl_idx = j;
-        break;
-      }
-    }
-  }
-  BTIF_TRACE_DEBUG("%s found=%d app_idx=%d mcl_idx=%d", __func__, found, i, j);
-  return found;
-}
-
-/*******************************************************************************
- *
- * Function      btif_hl_find_mdl_idx_using_mdl_id
- *
- * Description  Find the mdl index using mdl_id
- *
- * Returns      bool
- *
- ******************************************************************************/
-bool btif_hl_find_mcl_idx_using_mdl_id(uint8_t mdl_id, uint8_t mcl_handle,
-                                       uint8_t* p_app_idx, uint8_t* p_mcl_idx) {
-  btif_hl_app_cb_t* p_acb;
-  btif_hl_mcl_cb_t* p_mcb;
-  bool found = false;
-  uint8_t i, j, x;
-
-  for (i = 0; i < BTA_HL_NUM_APPS; i++) {
-    p_acb = BTIF_HL_GET_APP_CB_PTR(i);
-    for (j = 0; j < BTA_HL_NUM_MCLS; j++) {
-      if (p_acb->mcb[j].in_use && (p_acb->mcb[j].mcl_handle == mcl_handle)) {
-        p_mcb = &p_acb->mcb[j];
-        BTIF_TRACE_DEBUG(
-            "btif_hl_find_mcl_idx_using_mdl_id: mcl handle found j =%d", j);
-        for (x = 0; x < BTA_HL_NUM_MDLS_PER_MCL; x++) {
-          if (p_mcb->mdl[x].in_use && p_mcb->mdl[x].mdl_id == mdl_id) {
-            BTIF_TRACE_DEBUG("btif_hl_find_mcl_idx_using_mdl_id:found x =%d",
-                             x);
-            found = true;
-            *p_app_idx = i;
-            *p_mcl_idx = j;
-            break;
-          }
-        }
-      }
-    }
-  }
-  BTIF_TRACE_DEBUG("%s found=%d app_idx=%d mcl_idx=%d", __func__, found, i, j);
-  return found;
-}
-
-/*******************************************************************************
- *
- * Function      btif_hl_find_mcl_idx_using_deleted_mdl_id
- *
- * Description  Find the app index deleted_mdl_id
- *
- * Returns      bool
- *
- ******************************************************************************/
-bool btif_hl_find_app_idx_using_deleted_mdl_id(uint8_t mdl_id,
-                                               uint8_t* p_app_idx) {
-  btif_hl_app_cb_t* p_acb;
-  bool found = false;
-  uint8_t i;
-
-  for (i = 0; i < BTA_HL_NUM_APPS; i++) {
-    p_acb = BTIF_HL_GET_APP_CB_PTR(i);
-    if (p_acb->delete_mdl.active) {
-      BTIF_TRACE_DEBUG("%s: app_idx=%d, mdl_id=%d", __func__, i, mdl_id);
-    }
-    if (p_acb->delete_mdl.active && (p_acb->delete_mdl.mdl_id == mdl_id)) {
-      found = true;
-      *p_app_idx = i;
-      break;
-    }
-  }
-  BTIF_TRACE_DEBUG("%s found=%d app_idx=%d", __func__, found, i);
-  return found;
-}
-
-/*******************************************************************************
- *
- * Function      btif_hl_stop_timer_using_handle
- *
- * Description  clean control channel cb using handle
- *
- * Returns      void
- *
- ******************************************************************************/
-static void btif_hl_stop_timer_using_handle(tBTA_HL_MCL_HANDLE mcl_handle) {
-  btif_hl_app_cb_t* p_acb;
-  uint8_t i, j;
-
-  for (i = 0; i < BTA_HL_NUM_APPS; i++) {
-    p_acb = BTIF_HL_GET_APP_CB_PTR(i);
-    for (j = 0; j < BTA_HL_NUM_MCLS; j++) {
-      if (p_acb->mcb[j].in_use && (p_acb->mcb[j].mcl_handle == mcl_handle)) {
-        btif_hl_stop_cch_timer(i, j);
-      }
-    }
-  }
-}
-
-/*******************************************************************************
- *
- * Function      btif_hl_find_mcl_idx_using_app_idx
- *
- * Description  Find the MCL index using handle
- *
- * Returns      bool
- *
- ******************************************************************************/
-bool btif_hl_find_mcl_idx_using_app_idx(tBTA_HL_MCL_HANDLE mcl_handle,
-                                        uint8_t p_app_idx, uint8_t* p_mcl_idx) {
-  btif_hl_app_cb_t* p_acb;
-  bool found = false;
-  uint8_t j;
-
-  p_acb = BTIF_HL_GET_APP_CB_PTR(p_app_idx);
-  for (j = 0; j < BTA_HL_NUM_MCLS; j++) {
-    if (p_acb->mcb[j].in_use && (p_acb->mcb[j].mcl_handle == mcl_handle)) {
-      found = true;
-      *p_mcl_idx = j;
-      break;
-    }
-  }
-  BTIF_TRACE_DEBUG("%s found=%dmcl_idx=%d", __func__, found, j);
-  return found;
-}
-
-/*******************************************************************************
- *
- * Function      btif_hl_clean_mdls_using_app_idx
- *
- * Description  clean dch cpntrol bloack using app_idx
- *
- * Returns      void
- *
- ******************************************************************************/
-void btif_hl_clean_mdls_using_app_idx(uint8_t app_idx) {
-  btif_hl_app_cb_t* p_acb;
-  btif_hl_mcl_cb_t* p_mcb;
-  btif_hl_mdl_cb_t* p_dcb;
-  uint8_t j, x;
-  RawAddress bd_addr;
-
-  p_acb = BTIF_HL_GET_APP_CB_PTR(app_idx);
-  for (j = 0; j < BTA_HL_NUM_MCLS; j++) {
-    if (p_acb->mcb[j].in_use) {
-      p_mcb = &p_acb->mcb[j];
-      BTIF_TRACE_DEBUG(
-          "btif_hl_find_mcl_idx_using_mdl_id: mcl handle found j =%d", j);
-      for (x = 0; x < BTA_HL_NUM_MDLS_PER_MCL; x++) {
-        if (p_mcb->mdl[x].in_use) {
-          p_dcb = BTIF_HL_GET_MDL_CB_PTR(app_idx, j, x);
-          btif_hl_release_socket(app_idx, j, x);
-          bd_addr = p_mcb->bd_addr;
-          BTIF_HL_CALL_CBACK(bt_hl_callbacks, channel_state_cb, p_acb->app_id,
-                             &bd_addr, p_dcb->local_mdep_cfg_idx,
-                             p_dcb->channel_id, BTHL_CONN_STATE_DISCONNECTED,
-                             0);
-          btif_hl_clean_mdl_cb(p_dcb);
-          if (!btif_hl_num_dchs_in_use(p_mcb->mcl_handle))
-            BTA_HlCchClose(p_mcb->mcl_handle);
-          BTIF_TRACE_DEBUG("remote DCH close success mdl_idx=%d", x);
-        }
-      }
-    }
-  }
-}
-
-/*******************************************************************************
- *
- * Function      btif_hl_find_app_idx
- *
- * Description  Find the application index using application ID
- *
- * Returns      bool
- *
- ******************************************************************************/
-bool btif_hl_find_app_idx(uint8_t app_id, uint8_t* p_app_idx) {
-  bool found = false;
-  uint8_t i;
-
-  for (i = 0; i < BTA_HL_NUM_APPS; i++) {
-    if (btif_hl_cb.acb[i].in_use && (btif_hl_cb.acb[i].app_id == app_id)) {
-      found = true;
-      *p_app_idx = i;
-      break;
-    }
-  }
-  BTIF_TRACE_DEBUG("%s found=%d app_idx=%d", __func__, found, i);
-
-  return found;
-}
-
-/*******************************************************************************
- *
- * Function      btif_hl_find_app_idx
- *
- * Description  Find the application index using application ID
- *
- * Returns      bool
- *
- ******************************************************************************/
-bool btif_hl_find_app_idx_using_mdepId(uint8_t mdep_id, uint8_t* p_app_idx) {
-  bool found = false;
-  uint8_t i;
-
-  *p_app_idx = 0;
-  for (i = 0; i < BTA_HL_NUM_APPS; i++) {
-    BTIF_TRACE_DEBUG("btif_hl_find_app_idx_using_mdepId: MDEP-ID = %d",
-                     btif_hl_cb.acb[i].sup_feature.mdep[0].mdep_id);
-    if (btif_hl_cb.acb[i].in_use &&
-        (btif_hl_cb.acb[i].sup_feature.mdep[0].mdep_id == mdep_id)) {
-      found = true;
-      *p_app_idx = i;
-      break;
-    }
-  }
-  BTIF_TRACE_DEBUG("%s found=%d app_idx=%d", __func__, found, i);
-
-  return found;
-}
-
-/*******************************************************************************
- *
- * Function      btif_hl_find_avail_mdl_idx
- *
- * Description  Find a not in-use MDL index
- *
- * Returns      bool
- *
- ******************************************************************************/
-bool btif_hl_find_avail_mdl_idx(uint8_t app_idx, uint8_t mcl_idx,
-                                uint8_t* p_mdl_idx) {
-  btif_hl_mcl_cb_t* p_mcb = BTIF_HL_GET_MCL_CB_PTR(app_idx, mcl_idx);
-  bool found = false;
-  uint8_t i;
-
-  for (i = 0; i < BTA_HL_NUM_MDLS_PER_MCL; i++) {
-    if (!p_mcb->mdl[i].in_use) {
-      btif_hl_clean_mdl_cb(&p_mcb->mdl[i]);
-      found = true;
-      *p_mdl_idx = i;
-      break;
-    }
-  }
-
-  BTIF_TRACE_DEBUG("%s found=%d idx=%d", __func__, found, i);
-  return found;
-}
-
-/*******************************************************************************
- *
- * Function      btif_hl_find_avail_mcl_idx
- *
- * Description  Find a not in-use MDL index
- *
- * Returns      bool
- *
- ******************************************************************************/
-bool btif_hl_find_avail_mcl_idx(uint8_t app_idx, uint8_t* p_mcl_idx) {
-  bool found = false;
-  uint8_t i;
-
-  for (i = 0; i < BTA_HL_NUM_MCLS; i++) {
-    if (!btif_hl_cb.acb[app_idx].mcb[i].in_use) {
-      found = true;
-      *p_mcl_idx = i;
-      break;
-    }
-  }
-  BTIF_TRACE_DEBUG("%s found=%d mcl_idx=%d", __func__, found, i);
-  return found;
-}
-
-/*******************************************************************************
- *
- * Function      btif_hl_find_avail_app_idx
- *
- * Description  Find a not in-use APP index
- *
- * Returns      bool
- *
- ******************************************************************************/
-static bool btif_hl_find_avail_app_idx(uint8_t* p_idx) {
-  bool found = false;
-  uint8_t i;
-
-  for (i = 0; i < BTA_HL_NUM_APPS; i++) {
-    if (!btif_hl_cb.acb[i].in_use) {
-      found = true;
-      *p_idx = i;
-      break;
-    }
-  }
-
-  BTIF_TRACE_DEBUG("%s found=%d app_idx=%d", __func__, found, i);
-  return found;
-}
-
-/*******************************************************************************
- *
- * Function         btif_hl_proc_dereg_cfm
- *
- * Description      Process the de-registration confirmation
- *
- * Returns          Nothing
- *
- ******************************************************************************/
-static void btif_hl_proc_dereg_cfm(tBTA_HL* p_data)
-
-{
-  btif_hl_app_cb_t* p_acb;
-  uint8_t app_idx;
-  int app_id = 0;
-  bthl_app_reg_state_t state = BTHL_APP_REG_STATE_DEREG_SUCCESS;
-
-  BTIF_TRACE_DEBUG("%s de-reg status=%d app_handle=%d", __func__,
-                   p_data->dereg_cfm.status, p_data->dereg_cfm.app_handle);
-
-  if (btif_hl_find_app_idx_using_app_id(p_data->dereg_cfm.app_id, &app_idx)) {
-    p_acb = BTIF_HL_GET_APP_CB_PTR(app_idx);
-    app_id = (int)p_acb->app_id;
-    if (p_data->dereg_cfm.status == BTA_HL_STATUS_OK) {
-      btif_hl_clean_mdls_using_app_idx(app_idx);
-      for (size_t i = 0; i < BTA_HL_NUM_MCLS; i++)
-        alarm_free(p_acb->mcb[i].cch_timer);
-      memset(p_acb, 0, sizeof(btif_hl_app_cb_t));
-    } else
-      state = BTHL_APP_REG_STATE_DEREG_FAILED;
-
-    BTIF_TRACE_DEBUG("call reg state callback app_id=%d state=%d", app_id,
-                     state);
-    BTIF_HL_CALL_CBACK(bt_hl_callbacks, app_reg_state_cb, app_id, state);
-
-    if (btif_hl_is_no_active_app()) {
-      btif_hl_disable();
-    }
-  }
-}
-
-/*******************************************************************************
- *
- * Function         btif_hl_proc_reg_cfm
- *
- * Description      Process the registration confirmation
- *
- * Returns          Nothing
- *
- ******************************************************************************/
-static void btif_hl_proc_reg_cfm(tBTA_HL* p_data) {
-  btif_hl_app_cb_t* p_acb;
-  uint8_t app_idx;
-  bthl_app_reg_state_t state = BTHL_APP_REG_STATE_REG_SUCCESS;
-
-  BTIF_TRACE_DEBUG("%s reg status=%d app_handle=%d", __func__,
-                   p_data->reg_cfm.status, p_data->reg_cfm.app_handle);
-
-  if (btif_hl_find_app_idx(p_data->reg_cfm.app_id, &app_idx)) {
-    p_acb = BTIF_HL_GET_APP_CB_PTR(app_idx);
-    if (p_data->reg_cfm.status == BTA_HL_STATUS_OK) {
-      p_acb->app_handle = p_data->reg_cfm.app_handle;
-    } else {
-      btif_hl_free_app_idx(app_idx);
-      state = BTHL_APP_REG_STATE_REG_FAILED;
-    }
-
-    BTIF_TRACE_DEBUG("%s call reg state callback app_id=%d reg state=%d",
-                     __func__, p_data->reg_cfm.app_id, state);
-    BTIF_HL_CALL_CBACK(bt_hl_callbacks, app_reg_state_cb,
-                       ((int)p_data->reg_cfm.app_id), state);
-  }
-}
-
-/*******************************************************************************
- *
- * Function btif_hl_set_chan_cb_state
- *
- * Description set the channel callback state
- *
- * Returns void
- *
- ******************************************************************************/
-void btif_hl_set_chan_cb_state(uint8_t app_idx, uint8_t mcl_idx,
-                               btif_hl_chan_cb_state_t state) {
-  btif_hl_pending_chan_cb_t* p_pcb = BTIF_HL_GET_PCB_PTR(app_idx, mcl_idx);
-  btif_hl_chan_cb_state_t cur_state = p_pcb->cb_state;
-
-  if (cur_state != state) {
-    p_pcb->cb_state = state;
-    BTIF_TRACE_DEBUG("%s state %d--->%d", __func__, cur_state, state);
-  }
-}
-/*******************************************************************************
- *
- * Function btif_hl_send_destroyed_cb
- *
- * Description send the channel destroyed callback
- *
- * Returns void
- *
- ******************************************************************************/
-void btif_hl_send_destroyed_cb(btif_hl_app_cb_t* p_acb) {
-  int app_id = (int)btif_hl_get_app_id(p_acb->delete_mdl.channel_id);
-
-  RawAddress bd_addr = p_acb->delete_mdl.bd_addr;
-  BTIF_TRACE_DEBUG("%s", __func__);
-  BTIF_TRACE_DEBUG(
-      "call channel state callback channel_id=0x%08x mdep_cfg_idx=%d, state=%d "
-      "fd=%d",
-      p_acb->delete_mdl.channel_id, p_acb->delete_mdl.mdep_cfg_idx,
-      BTHL_CONN_STATE_DESTROYED, 0);
-  VLOG(1) << "BD " << bd_addr;
-
-  BTIF_HL_CALL_CBACK(bt_hl_callbacks, channel_state_cb, app_id, &bd_addr,
-                     p_acb->delete_mdl.mdep_cfg_idx,
-                     p_acb->delete_mdl.channel_id, BTHL_CONN_STATE_DESTROYED,
-                     0);
-}
-/*******************************************************************************
- *
- * Function btif_hl_send_disconnecting_cb
- *
- * Description send a channel disconnecting callback
- *
- * Returns void
- *
- ******************************************************************************/
-void btif_hl_send_disconnecting_cb(uint8_t app_idx, uint8_t mcl_idx,
-                                   uint8_t mdl_idx) {
-  btif_hl_mdl_cb_t* p_dcb = BTIF_HL_GET_MDL_CB_PTR(app_idx, mcl_idx, mdl_idx);
-  btif_hl_soc_cb_t* p_scb = p_dcb->p_scb;
-  int app_id = (int)btif_hl_get_app_id(p_scb->channel_id);
-
-  RawAddress bd_addr = p_scb->bd_addr;
-
-  BTIF_TRACE_DEBUG("%s", __func__);
-  BTIF_TRACE_DEBUG(
-      "call channel state callback  channel_id=0x%08x mdep_cfg_idx=%d, "
-      "state=%d fd=%d",
-      p_scb->channel_id, p_scb->mdep_cfg_idx, BTHL_CONN_STATE_DISCONNECTING,
-      p_scb->socket_id[0]);
-  VLOG(1) << "BD " << bd_addr;
-  BTIF_HL_CALL_CBACK(bt_hl_callbacks, channel_state_cb, app_id, &bd_addr,
-                     p_scb->mdep_cfg_idx, p_scb->channel_id,
-                     BTHL_CONN_STATE_DISCONNECTING, p_scb->socket_id[0]);
-}
-/*******************************************************************************
- *
- * Function btif_hl_send_setup_connecting_cb
- *
- * Description send a channel connecting callback
- *
- * Returns void
- *
- ******************************************************************************/
-void btif_hl_send_setup_connecting_cb(uint8_t app_idx, uint8_t mcl_idx) {
-  btif_hl_pending_chan_cb_t* p_pcb = BTIF_HL_GET_PCB_PTR(app_idx, mcl_idx);
-  int app_id = (int)btif_hl_get_app_id(p_pcb->channel_id);
-
-  RawAddress bd_addr = p_pcb->bd_addr;
-
-  if (p_pcb->in_use &&
-      p_pcb->cb_state == BTIF_HL_CHAN_CB_STATE_CONNECTING_PENDING) {
-    BTIF_TRACE_DEBUG("%s", __func__);
-    BTIF_TRACE_DEBUG(
-        "call channel state callback  channel_id=0x%08x mdep_cfg_idx=%d "
-        "state=%d fd=%d",
-        p_pcb->channel_id, p_pcb->mdep_cfg_idx, BTHL_CONN_STATE_CONNECTING, 0);
-    VLOG(1) << "BD " << bd_addr;
-
-    BTIF_HL_CALL_CBACK(bt_hl_callbacks, channel_state_cb, app_id, &bd_addr,
-                       p_pcb->mdep_cfg_idx, p_pcb->channel_id,
-                       BTHL_CONN_STATE_CONNECTING, 0);
-    btif_hl_set_chan_cb_state(app_idx, mcl_idx,
-                              BTIF_HL_CHAN_CB_STATE_CONNECTED_PENDING);
-  }
-}
-/*******************************************************************************
- *
- * Function btif_hl_send_setup_disconnected_cb
- *
- * Description send a channel disconnected callback
- *
- * Returns void
- *
- ******************************************************************************/
-void btif_hl_send_setup_disconnected_cb(uint8_t app_idx, uint8_t mcl_idx) {
-  btif_hl_pending_chan_cb_t* p_pcb = BTIF_HL_GET_PCB_PTR(app_idx, mcl_idx);
-  int app_id = (int)btif_hl_get_app_id(p_pcb->channel_id);
-
-  RawAddress bd_addr = p_pcb->bd_addr;
-
-  BTIF_TRACE_DEBUG("%s p_pcb->in_use=%d", __func__, p_pcb->in_use);
-  if (p_pcb->in_use) {
-    BTIF_TRACE_DEBUG("%p_pcb->cb_state=%d", p_pcb->cb_state);
-    if (p_pcb->cb_state == BTIF_HL_CHAN_CB_STATE_CONNECTING_PENDING) {
-      BTIF_TRACE_DEBUG(
-          "call channel state callback  channel_id=0x%08x mdep_cfg_idx=%d "
-          "state=%d fd=%d",
-          p_pcb->channel_id, p_pcb->mdep_cfg_idx, BTHL_CONN_STATE_CONNECTING,
-          0);
-      VLOG(1) << "BD " << bd_addr;
-      BTIF_HL_CALL_CBACK(bt_hl_callbacks, channel_state_cb, app_id, &bd_addr,
-                         p_pcb->mdep_cfg_idx, p_pcb->channel_id,
-                         BTHL_CONN_STATE_CONNECTING, 0);
-
-      BTIF_TRACE_DEBUG(
-          "call channel state callback  channel_id=0x%08x mdep_cfg_idx=%d "
-          "state=%d fd=%d",
-          p_pcb->channel_id, p_pcb->mdep_cfg_idx, BTHL_CONN_STATE_DISCONNECTED,
-          0);
-      VLOG(1) << "BD " << bd_addr;
-      BTIF_HL_CALL_CBACK(bt_hl_callbacks, channel_state_cb, app_id, &bd_addr,
-                         p_pcb->mdep_cfg_idx, p_pcb->channel_id,
-                         BTHL_CONN_STATE_DISCONNECTED, 0);
-    } else if (p_pcb->cb_state == BTIF_HL_CHAN_CB_STATE_CONNECTED_PENDING) {
-      BTIF_TRACE_DEBUG(
-          "call channel state callback  channel_id=0x%08x mdep_cfg_idx=%d "
-          "state=%d fd=%d",
-          p_pcb->channel_id, p_pcb->mdep_cfg_idx, BTHL_CONN_STATE_DISCONNECTED,
-          0);
-      VLOG(1) << "BD " << bd_addr;
-      BTIF_HL_CALL_CBACK(bt_hl_callbacks, channel_state_cb, app_id, &bd_addr,
-                         p_pcb->mdep_cfg_idx, p_pcb->channel_id,
-                         BTHL_CONN_STATE_DISCONNECTED, 0);
-    }
-    btif_hl_clean_pcb(p_pcb);
-  }
-}
-/*******************************************************************************
- *
- * Function         btif_hl_proc_sdp_query_cfm
- *
- * Description      Process the SDP query confirmation
- *
- * Returns          Nothing
- *
- ******************************************************************************/
-static bool btif_hl_proc_sdp_query_cfm(tBTA_HL* p_data) {
-  btif_hl_app_cb_t* p_acb;
-  btif_hl_mcl_cb_t* p_mcb;
-  tBTA_HL_SDP* p_sdp;
-  tBTA_HL_CCH_OPEN_PARAM open_param;
-  uint8_t app_idx, mcl_idx, sdp_idx = 0;
-  uint8_t num_recs, i, num_mdeps, j;
-  btif_hl_cch_op_t old_cch_oper;
-  bool status = false;
-  btif_hl_pending_chan_cb_t* p_pcb;
-
-  BTIF_TRACE_DEBUG("%s", __func__);
-
-  p_sdp = p_data->sdp_query_cfm.p_sdp;
-  num_recs = p_sdp->num_recs;
-
-  BTIF_TRACE_DEBUG("num of SDP records=%d", num_recs);
-  for (i = 0; i < num_recs; i++) {
-    BTIF_TRACE_DEBUG("rec_idx=%d ctrl_psm=0x%x data_psm=0x%x", (i + 1),
-                     p_sdp->sdp_rec[i].ctrl_psm, p_sdp->sdp_rec[i].data_psm);
-    BTIF_TRACE_DEBUG("MCAP supported procedures=0x%x",
-                     p_sdp->sdp_rec[i].mcap_sup_proc);
-    num_mdeps = p_sdp->sdp_rec[i].num_mdeps;
-    BTIF_TRACE_DEBUG("num of mdeps =%d", num_mdeps);
-    for (j = 0; j < num_mdeps; j++) {
-      BTIF_TRACE_DEBUG("mdep_idx=%d mdep_id=0x%x data_type=0x%x mdep_role=0x%x",
-                       (j + 1), p_sdp->sdp_rec[i].mdep_cfg[j].mdep_id,
-                       p_sdp->sdp_rec[i].mdep_cfg[j].data_type,
-                       p_sdp->sdp_rec[i].mdep_cfg[j].mdep_role);
-    }
-  }
-
-  if (btif_hl_find_app_idx_using_app_id(p_data->sdp_query_cfm.app_id,
-                                        &app_idx)) {
-    p_acb = BTIF_HL_GET_APP_CB_PTR(app_idx);
-
-    if (btif_hl_find_mcl_idx(app_idx, p_data->sdp_query_cfm.bd_addr,
-                             &mcl_idx)) {
-      p_mcb = BTIF_HL_GET_MCL_CB_PTR(app_idx, mcl_idx);
-      if (p_mcb->cch_oper != BTIF_HL_CCH_OP_NONE) {
-        memcpy(&p_mcb->sdp, p_sdp, sizeof(tBTA_HL_SDP));
-        old_cch_oper = p_mcb->cch_oper;
-        p_mcb->cch_oper = BTIF_HL_CCH_OP_NONE;
-
-        switch (old_cch_oper) {
-          case BTIF_HL_CCH_OP_MDEP_FILTERING:
-            status = btif_hl_find_sdp_idx_using_mdep_filter(app_idx, mcl_idx,
-                                                            &sdp_idx);
-            break;
-          default:
-            break;
-        }
-
-        if (status) {
-          p_mcb->sdp_idx = sdp_idx;
-          p_mcb->valid_sdp_idx = true;
-          p_mcb->ctrl_psm = p_mcb->sdp.sdp_rec[sdp_idx].ctrl_psm;
-
-          switch (old_cch_oper) {
-            case BTIF_HL_CCH_OP_MDEP_FILTERING:
-              p_pcb = BTIF_HL_GET_PCB_PTR(app_idx, mcl_idx);
-              if (p_pcb->in_use) {
-                if (!p_pcb->abort_pending) {
-                  switch (p_pcb->op) {
-                    case BTIF_HL_PEND_DCH_OP_OPEN:
-                      btif_hl_send_setup_connecting_cb(app_idx, mcl_idx);
-                      break;
-                    case BTIF_HL_PEND_DCH_OP_DELETE_MDL:
-                    default:
-                      break;
-                  }
-                  open_param.ctrl_psm = p_mcb->ctrl_psm;
-                  open_param.bd_addr = p_mcb->bd_addr;
-                  open_param.sec_mask =
-                      (BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT);
-                  BTA_HlCchOpen(p_acb->app_id, p_acb->app_handle, &open_param);
-                } else {
-                  BTIF_TRACE_DEBUG("channel abort pending");
-                }
-              }
-              break;
-
-            case BTIF_HL_CCH_OP_DCH_OPEN:
-              status = btif_hl_proc_pending_op(app_idx, mcl_idx);
-              break;
-
-            default:
-              BTIF_TRACE_ERROR("Invalid CCH oper %d", old_cch_oper);
-              break;
-          }
-        } else {
-          BTIF_TRACE_ERROR("Can not find SDP idx discard CCH Open request");
-        }
-      }
-    }
-  }
-
-  // this was allocated in bta_hl_sdp_query_results
-  osi_free_and_reset((void**)&p_data->sdp_query_cfm.p_sdp);
-
-  return status;
-}
-
-/*******************************************************************************
- *
- * Function         btif_hl_proc_cch_open_ind
- *
- * Description      Process the CCH open indication
- *
- * Returns          Nothing
- *
- ******************************************************************************/
-static void btif_hl_proc_cch_open_ind(tBTA_HL* p_data)
-
-{
-  btif_hl_mcl_cb_t* p_mcb;
-  uint8_t mcl_idx;
-  int i;
-
-  BTIF_TRACE_DEBUG("%s", __func__);
-  for (i = 0; i < BTA_HL_NUM_APPS; i++) {
-    if (btif_hl_cb.acb[i].in_use) {
-      if (!btif_hl_find_mcl_idx(i, p_data->cch_open_ind.bd_addr, &mcl_idx)) {
-        if (btif_hl_find_avail_mcl_idx(i, &mcl_idx)) {
-          p_mcb = BTIF_HL_GET_MCL_CB_PTR(i, mcl_idx);
-          alarm_free(p_mcb->cch_timer);
-          memset(p_mcb, 0, sizeof(btif_hl_mcl_cb_t));
-          p_mcb->in_use = true;
-          p_mcb->is_connected = true;
-          p_mcb->mcl_handle = p_data->cch_open_ind.mcl_handle;
-          p_mcb->bd_addr = p_data->cch_open_ind.bd_addr;
-          btif_hl_start_cch_timer(i, mcl_idx);
-        }
-      } else {
-        BTIF_TRACE_ERROR("The MCL already exist for cch_open_ind");
-      }
-    }
-  }
-}
-
-/*******************************************************************************
- *
- * Function         btif_hl_proc_pending_op
- *
- * Description      Process the pending dch operation.
- *
- * Returns          Nothing
- *
- ******************************************************************************/
-bool btif_hl_proc_pending_op(uint8_t app_idx, uint8_t mcl_idx)
-
-{
-  btif_hl_app_cb_t* p_acb = BTIF_HL_GET_APP_CB_PTR(app_idx);
-  btif_hl_mcl_cb_t* p_mcb = BTIF_HL_GET_MCL_CB_PTR(app_idx, mcl_idx);
-  btif_hl_pending_chan_cb_t* p_pcb;
-  bool status = false;
-  tBTA_HL_DCH_OPEN_PARAM dch_open;
-  tBTA_HL_MDL_ID mdl_id;
-  tBTA_HL_DCH_RECONNECT_PARAM reconnect_param;
-
-  p_pcb = BTIF_HL_GET_PCB_PTR(app_idx, mcl_idx);
-  if (p_pcb->in_use) {
-    switch (p_pcb->op) {
-      case BTIF_HL_PEND_DCH_OP_OPEN:
-        if (!p_pcb->abort_pending) {
-          BTIF_TRACE_DEBUG("op BTIF_HL_PEND_DCH_OP_OPEN");
-          dch_open.ctrl_psm = p_mcb->ctrl_psm;
-          dch_open.local_mdep_id =
-              p_acb->sup_feature.mdep[p_pcb->mdep_cfg_idx].mdep_id;
-          if (btif_hl_find_peer_mdep_id(
-                  p_acb->app_id, p_mcb->bd_addr,
-                  p_acb->sup_feature.mdep[p_pcb->mdep_cfg_idx]
-                      .mdep_cfg.mdep_role,
-                  p_acb->sup_feature.mdep[p_pcb->mdep_cfg_idx]
-                      .mdep_cfg.data_cfg[0]
-                      .data_type,
-                  &dch_open.peer_mdep_id)) {
-            dch_open.local_cfg = p_acb->channel_type[p_pcb->mdep_cfg_idx];
-            if ((p_acb->sup_feature.mdep[p_pcb->mdep_cfg_idx]
-                     .mdep_cfg.mdep_role == BTA_HL_MDEP_ROLE_SOURCE) &&
-                !btif_hl_is_the_first_reliable_existed(app_idx, mcl_idx)) {
-              dch_open.local_cfg = BTA_HL_DCH_CFG_RELIABLE;
-            }
-            dch_open.sec_mask = (BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT);
-            BTIF_TRACE_DEBUG("dch_open.local_cfg=%d  ", dch_open.local_cfg);
-            btif_hl_send_setup_connecting_cb(app_idx, mcl_idx);
-
-            if (!btif_hl_is_reconnect_possible(app_idx, mcl_idx,
-                                               p_pcb->mdep_cfg_idx, &dch_open,
-                                               &mdl_id)) {
-              BTIF_TRACE_DEBUG("Issue DCH open, mcl_handle=%d",
-                               p_mcb->mcl_handle);
-              BTA_HlDchOpen(p_mcb->mcl_handle, &dch_open);
-            } else {
-              reconnect_param.ctrl_psm = p_mcb->ctrl_psm;
-              reconnect_param.mdl_id = mdl_id;
-              ;
-              BTIF_TRACE_DEBUG("Issue Reconnect ctrl_psm=0x%x mdl_id=0x%x",
-                               reconnect_param.ctrl_psm,
-                               reconnect_param.mdl_id);
-              BTA_HlDchReconnect(p_mcb->mcl_handle, &reconnect_param);
-            }
-            status = true;
-          }
-        } else {
-          btif_hl_send_setup_disconnected_cb(app_idx, mcl_idx);
-          status = true;
-        }
-        break;
-      case BTIF_HL_PEND_DCH_OP_DELETE_MDL:
-        BTA_HlDeleteMdl(p_mcb->mcl_handle, p_acb->delete_mdl.mdl_id);
-        status = true;
-        break;
-
-      default:
-        break;
-    }
-  }
-  return status;
-}
-
-/*******************************************************************************
- *
- * Function         btif_hl_proc_cch_open_cfm
- *
- * Description      Process the CCH open confirmation
- *
- * Returns          Nothing
- *
- ******************************************************************************/
-static bool btif_hl_proc_cch_open_cfm(tBTA_HL* p_data)
-
-{
-  btif_hl_mcl_cb_t* p_mcb;
-  uint8_t app_idx, mcl_idx;
-  bool status = false;
-
-  BTIF_TRACE_DEBUG("%s", __func__);
-
-  if (btif_hl_find_app_idx_using_app_id(p_data->cch_open_cfm.app_id,
-                                        &app_idx)) {
-    BTIF_TRACE_DEBUG("app_idx=%d", app_idx);
-    if (btif_hl_find_mcl_idx(app_idx, p_data->cch_open_cfm.bd_addr, &mcl_idx)) {
-      p_mcb = BTIF_HL_GET_MCL_CB_PTR(app_idx, mcl_idx);
-      BTIF_TRACE_DEBUG("mcl_idx=%d, mcl_handle=%d", mcl_idx,
-                       p_data->cch_open_cfm.mcl_handle);
-      p_mcb->mcl_handle = p_data->cch_open_cfm.mcl_handle;
-      p_mcb->is_connected = true;
-      status = btif_hl_proc_pending_op(app_idx, mcl_idx);
-      if (status) btif_hl_start_cch_timer(app_idx, mcl_idx);
-    }
-  }
-
-  return status;
-}
-
-/*******************************************************************************
- *
- * Function      btif_hl_clean_mcb_using_handle
- *
- * Description  clean control channel cb using handle
- *
- * Returns      void
- *
- ******************************************************************************/
-static void btif_hl_clean_mcb_using_handle(tBTA_HL_MCL_HANDLE mcl_handle) {
-  btif_hl_app_cb_t* p_acb;
-  uint8_t i, j;
-
-  for (i = 0; i < BTA_HL_NUM_APPS; i++) {
-    p_acb = BTIF_HL_GET_APP_CB_PTR(i);
-    for (j = 0; j < BTA_HL_NUM_MCLS; j++) {
-      if (p_acb->mcb[j].in_use)
-        BTIF_TRACE_DEBUG(
-            "btif_hl_find_mcl_idx_using_handle: app_idx=%d,"
-            "mcl_idx =%d mcl_handle=%d",
-            i, j, p_acb->mcb[j].mcl_handle);
-      if (p_acb->mcb[j].in_use && (p_acb->mcb[j].mcl_handle == mcl_handle)) {
-        btif_hl_stop_cch_timer(i, j);
-        btif_hl_release_mcl_sockets(i, j);
-        btif_hl_send_setup_disconnected_cb(i, j);
-        btif_hl_clean_mcl_cb(i, j);
-      }
-    }
-  }
-}
-
-/*******************************************************************************
- *
- * Function         btif_hl_proc_cch_close_ind
- *
- * Description      Process the CCH close indication
- *
- * Returns          Nothing
- *
- ******************************************************************************/
-static void btif_hl_proc_cch_close_ind(tBTA_HL* p_data)
-
-{
-  BTIF_TRACE_DEBUG("%s", __func__);
-
-  btif_hl_clean_mcb_using_handle(p_data->cch_close_ind.mcl_handle);
-}
-
-/*******************************************************************************
- *
- * Function         btif_hl_proc_cch_close_cfm
- *
- * Description      Process the CCH close confirmation
- *
- * Returns          Nothing
- *
- ******************************************************************************/
-static void btif_hl_proc_cch_close_cfm(tBTA_HL* p_data) {
-  BTIF_TRACE_DEBUG("%s", __func__);
-
-  btif_hl_clean_mcb_using_handle(p_data->cch_close_ind.mcl_handle);
-}
-
-/*******************************************************************************
- *
- * Function         btif_hl_proc_create_ind
- *
- * Description      Process the MDL create indication
- *
- * Returns          Nothing
- *
- ******************************************************************************/
-static void btif_hl_proc_create_ind(tBTA_HL* p_data) {
-  btif_hl_app_cb_t* p_acb;
-  btif_hl_mcl_cb_t* p_mcb;
-  tBTA_HL_MDEP* p_mdep;
-  uint8_t orig_app_idx, mcl_idx, mdep_cfg_idx;
-  bool first_reliable_exist;
-  bool success = true;
-  tBTA_HL_DCH_CFG rsp_cfg = BTA_HL_DCH_CFG_UNKNOWN;
-  tBTA_HL_DCH_CREATE_RSP rsp_code = BTA_HL_DCH_CREATE_RSP_CFG_REJ;
-  tBTA_HL_DCH_CREATE_RSP_PARAM create_rsp_param;
-
-  BTIF_TRACE_DEBUG("%s", __func__);
-
-  // Find the correct app_idx based on the mdep_id;
-  btif_hl_find_app_idx_using_mdepId(p_data->dch_create_ind.local_mdep_id,
-                                    &orig_app_idx);
-  if (btif_hl_find_mcl_idx(orig_app_idx, p_data->dch_create_ind.bd_addr,
-                           &mcl_idx)) {
-    p_acb = BTIF_HL_GET_APP_CB_PTR(orig_app_idx);
-    p_mcb = BTIF_HL_GET_MCL_CB_PTR(orig_app_idx, mcl_idx);
-
-    if (btif_hl_find_mdep_cfg_idx(orig_app_idx,
-                                  p_data->dch_create_ind.local_mdep_id,
-                                  &mdep_cfg_idx)) {
-      p_mdep = &(p_acb->sup_feature.mdep[mdep_cfg_idx]);
-      first_reliable_exist =
-          btif_hl_is_the_first_reliable_existed(orig_app_idx, mcl_idx);
-      switch (p_mdep->mdep_cfg.mdep_role) {
-        case BTA_HL_MDEP_ROLE_SOURCE:
-          if (p_data->dch_create_ind.cfg == BTA_HL_DCH_CFG_NO_PREF) {
-            if (first_reliable_exist) {
-              rsp_cfg = p_acb->channel_type[mdep_cfg_idx];
-            } else {
-              rsp_cfg = BTA_HL_DCH_CFG_RELIABLE;
-            }
-            rsp_code = BTA_HL_DCH_CREATE_RSP_SUCCESS;
-          }
-
-          break;
-        case BTA_HL_MDEP_ROLE_SINK:
-
-          BTIF_TRACE_DEBUG("btif_hl_proc_create_ind:BTA_HL_MDEP_ROLE_SINK");
-          if ((p_data->dch_create_ind.cfg == BTA_HL_DCH_CFG_RELIABLE) ||
-              (first_reliable_exist &&
-               (p_data->dch_create_ind.cfg == BTA_HL_DCH_CFG_STREAMING))) {
-            rsp_code = BTA_HL_DCH_CREATE_RSP_SUCCESS;
-            rsp_cfg = p_data->dch_create_ind.cfg;
-            BTIF_TRACE_DEBUG(
-                "btif_hl_proc_create_ind:BTA_HL_MDEP_ROLE_SINK cfg = %d",
-                rsp_cfg);
-          }
-          break;
-        default:
-          break;
-      }
-    }
-  } else {
-    success = false;
-  }
-
-  if (success) {
-    BTIF_TRACE_DEBUG("create response rsp_code=%d rsp_cfg=%d", rsp_code,
-                     rsp_cfg);
-    create_rsp_param.local_mdep_id = p_data->dch_create_ind.local_mdep_id;
-    create_rsp_param.mdl_id = p_data->dch_create_ind.mdl_id;
-    create_rsp_param.rsp_code = rsp_code;
-    create_rsp_param.cfg_rsp = rsp_cfg;
-    BTA_HlDchCreateRsp(p_mcb->mcl_handle, &create_rsp_param);
-  }
-}
-
-/*******************************************************************************
- *
- * Function         btif_hl_proc_dch_open_ind
- *
- * Description      Process the DCH open indication
- *
- * Returns          Nothing
- *
- ******************************************************************************/
-static void btif_hl_proc_dch_open_ind(tBTA_HL* p_data)
-
-{
-  btif_hl_mdl_cb_t* p_dcb;
-  uint8_t orig_app_idx, mcl_idx, mdl_idx, mdep_cfg_idx;
-  bool close_dch = false;
-
-  BTIF_TRACE_DEBUG("%s", __func__);
-
-  // Find the correct app_idx based on the mdep_id;
-  btif_hl_find_app_idx_using_mdepId(p_data->dch_open_ind.local_mdep_id,
-                                    &orig_app_idx);
-
-  if (btif_hl_find_mcl_idx_using_app_idx(p_data->dch_open_ind.mcl_handle,
-                                         orig_app_idx, &mcl_idx)) {
-    if (btif_hl_find_avail_mdl_idx(orig_app_idx, mcl_idx, &mdl_idx)) {
-      p_dcb = BTIF_HL_GET_MDL_CB_PTR(orig_app_idx, mcl_idx, mdl_idx);
-
-      if (btif_hl_find_mdep_cfg_idx(orig_app_idx,
-                                    p_data->dch_open_ind.local_mdep_id,
-                                    &mdep_cfg_idx)) {
-        p_dcb->in_use = true;
-        p_dcb->mdl_handle = p_data->dch_open_ind.mdl_handle;
-        p_dcb->local_mdep_cfg_idx = mdep_cfg_idx;
-        p_dcb->local_mdep_id = p_data->dch_open_ind.local_mdep_id;
-        p_dcb->mdl_id = p_data->dch_open_ind.mdl_id;
-        p_dcb->dch_mode = p_data->dch_open_ind.dch_mode;
-        p_dcb->dch_mode = p_data->dch_open_ind.dch_mode;
-        p_dcb->is_the_first_reliable = p_data->dch_open_ind.first_reliable;
-        p_dcb->mtu = p_data->dch_open_ind.mtu;
-
-        if (btif_hl_find_channel_id_using_mdl_id(orig_app_idx, p_dcb->mdl_id,
-                                                 &p_dcb->channel_id)) {
-          BTIF_TRACE_DEBUG(" app_idx=%d mcl_idx=%d mdl_idx=%d channel_id=%d",
-                           orig_app_idx, mcl_idx, mdl_idx, p_dcb->channel_id);
-          if (!btif_hl_create_socket(orig_app_idx, mcl_idx, mdl_idx)) {
-            BTIF_TRACE_ERROR("Unable to create socket");
-            close_dch = true;
-          }
-        } else {
-          BTIF_TRACE_ERROR("Unable find channel id for mdl_id=0x%x",
-                           p_dcb->mdl_id);
-          close_dch = true;
-        }
-      } else {
-        BTIF_TRACE_ERROR("INVALID_LOCAL_MDEP_ID mdep_id=%d",
-                         p_data->dch_open_cfm.local_mdep_id);
-        close_dch = true;
-      }
-
-      if (close_dch) btif_hl_clean_mdl_cb(p_dcb);
-    } else
-      close_dch = true;
-  } else
-    close_dch = true;
-
-  if (close_dch) BTA_HlDchClose(p_data->dch_open_cfm.mdl_handle);
-}
-
-/*******************************************************************************
- *
- * Function         btif_hl_proc_dch_open_cfm
- *
- * Description      Process the DCH close confirmation
- *
- * Returns          Nothing
- *
- ******************************************************************************/
-static bool btif_hl_proc_dch_open_cfm(tBTA_HL* p_data)
-
-{
-  btif_hl_mdl_cb_t* p_dcb;
-  btif_hl_pending_chan_cb_t* p_pcb;
-  uint8_t app_idx, mcl_idx, mdl_idx, mdep_cfg_idx;
-  bool status = false;
-  bool close_dch = false;
-
-  BTIF_TRACE_DEBUG("%s", __func__);
-
-  // Find the correct app_idx based on the mdep_id;
-  btif_hl_find_app_idx_using_mdepId(p_data->dch_open_cfm.local_mdep_id,
-                                    &app_idx);
-
-  if (btif_hl_find_mcl_idx_using_app_idx(p_data->dch_open_cfm.mcl_handle,
-                                         app_idx, &mcl_idx)) {
-    p_pcb = BTIF_HL_GET_PCB_PTR(app_idx, mcl_idx);
-
-    if (btif_hl_find_avail_mdl_idx(app_idx, mcl_idx, &mdl_idx)) {
-      p_dcb = BTIF_HL_GET_MDL_CB_PTR(app_idx, mcl_idx, mdl_idx);
-
-      if (btif_hl_find_mdep_cfg_idx(app_idx, p_data->dch_open_cfm.local_mdep_id,
-                                    &mdep_cfg_idx)) {
-        p_dcb->in_use = true;
-        p_dcb->mdl_handle = p_data->dch_open_cfm.mdl_handle;
-        p_dcb->local_mdep_cfg_idx = mdep_cfg_idx;
-        p_dcb->local_mdep_id = p_data->dch_open_cfm.local_mdep_id;
-        p_dcb->mdl_id = p_data->dch_open_cfm.mdl_id;
-        p_dcb->dch_mode = p_data->dch_open_cfm.dch_mode;
-        p_dcb->is_the_first_reliable = p_data->dch_open_cfm.first_reliable;
-        p_dcb->mtu = p_data->dch_open_cfm.mtu;
-        p_dcb->channel_id = p_pcb->channel_id;
-
-        BTIF_TRACE_DEBUG("app_idx=%d mcl_idx=%d mdl_idx=%d", app_idx, mcl_idx,
-                         mdl_idx);
-        btif_hl_send_setup_connecting_cb(app_idx, mcl_idx);
-        if (btif_hl_create_socket(app_idx, mcl_idx, mdl_idx)) {
-          status = true;
-          BTIF_TRACE_DEBUG(
-              "app_idx=%d mcl_idx=%d mdl_idx=%d p_dcb->channel_id=0x%08x",
-              app_idx, mcl_idx, mdl_idx, p_dcb->channel_id);
-          btif_hl_clean_pcb(p_pcb);
-        } else {
-          BTIF_TRACE_ERROR("Unable to create socket");
-          close_dch = true;
-        }
-      } else {
-        BTIF_TRACE_ERROR("INVALID_LOCAL_MDEP_ID mdep_id=%d",
-                         p_data->dch_open_cfm.local_mdep_id);
-        close_dch = true;
-      }
-
-      if (close_dch) {
-        btif_hl_clean_mdl_cb(p_dcb);
-        BTA_HlDchClose(p_data->dch_open_cfm.mdl_handle);
-      }
-    }
-  }
-
-  return status;
-}
-/*******************************************************************************
- *
- * Function         btif_hl_proc_dch_reconnect_cfm
- *
- * Description      Process the DCH reconnect indication
- *
- * Returns          Nothing
- *
- ******************************************************************************/
-static bool btif_hl_proc_dch_reconnect_cfm(tBTA_HL* p_data) {
-  btif_hl_mdl_cb_t* p_dcb;
-  btif_hl_pending_chan_cb_t* p_pcb;
-  uint8_t app_idx, mcl_idx, mdl_idx, mdep_cfg_idx;
-  bool status = false;
-  bool close_dch = false;
-
-  BTIF_TRACE_DEBUG("%s", __func__);
-
-  btif_hl_find_app_idx_using_mdepId(p_data->dch_reconnect_cfm.local_mdep_id,
-                                    &app_idx);
-
-  if (btif_hl_find_mcl_idx_using_app_idx(p_data->dch_reconnect_cfm.mcl_handle,
-                                         app_idx, &mcl_idx)) {
-    p_pcb = BTIF_HL_GET_PCB_PTR(app_idx, mcl_idx);
-
-    if (btif_hl_find_avail_mdl_idx(app_idx, mcl_idx, &mdl_idx)) {
-      p_dcb = BTIF_HL_GET_MDL_CB_PTR(app_idx, mcl_idx, mdl_idx);
-
-      if (btif_hl_find_mdep_cfg_idx(app_idx,
-                                    p_data->dch_reconnect_cfm.local_mdep_id,
-                                    &mdep_cfg_idx)) {
-        p_dcb->in_use = true;
-        p_dcb->mdl_handle = p_data->dch_reconnect_cfm.mdl_handle;
-        p_dcb->local_mdep_cfg_idx = mdep_cfg_idx;
-        p_dcb->local_mdep_id = p_data->dch_reconnect_cfm.local_mdep_id;
-        p_dcb->mdl_id = p_data->dch_reconnect_cfm.mdl_id;
-        p_dcb->dch_mode = p_data->dch_reconnect_cfm.dch_mode;
-        p_dcb->is_the_first_reliable = p_data->dch_reconnect_cfm.first_reliable;
-        p_dcb->mtu = p_data->dch_reconnect_cfm.mtu;
-        p_dcb->channel_id = p_pcb->channel_id;
-
-        BTIF_TRACE_DEBUG("app_idx=%d mcl_idx=%d mdl_idx=%d", app_idx, mcl_idx,
-                         mdl_idx);
-        btif_hl_send_setup_connecting_cb(app_idx, mcl_idx);
-        if (btif_hl_create_socket(app_idx, mcl_idx, mdl_idx)) {
-          status = true;
-          BTIF_TRACE_DEBUG(
-              "app_idx=%d mcl_idx=%d mdl_idx=%d p_dcb->channel_id=0x%08x",
-              app_idx, mcl_idx, mdl_idx, p_dcb->channel_id);
-          btif_hl_clean_pcb(p_pcb);
-        } else {
-          BTIF_TRACE_ERROR("Unable to create socket");
-          close_dch = true;
-        }
-      } else {
-        BTIF_TRACE_ERROR("INVALID_LOCAL_MDEP_ID mdep_id=%d",
-                         p_data->dch_open_cfm.local_mdep_id);
-        close_dch = true;
-      }
-
-      if (close_dch) {
-        btif_hl_clean_mdl_cb(p_dcb);
-        BTA_HlDchClose(p_data->dch_reconnect_cfm.mdl_handle);
-      }
-    }
-  }
-
-  return status;
-}
-/*******************************************************************************
- *
- * Function         btif_hl_proc_dch_reconnect_ind
- *
- * Description      Process the DCH reconnect indication
- *
- * Returns          Nothing
- *
- ******************************************************************************/
-static void btif_hl_proc_dch_reconnect_ind(tBTA_HL* p_data)
-
-{
-  btif_hl_app_cb_t* p_acb;
-  btif_hl_mdl_cb_t* p_dcb;
-  uint8_t app_idx, mcl_idx, mdl_idx, mdep_cfg_idx;
-  bool close_dch = false;
-
-  BTIF_TRACE_DEBUG("%s", __func__);
-
-  // Find the correct app_idx based on the mdep_id;
-  btif_hl_find_app_idx_using_mdepId(p_data->dch_reconnect_ind.local_mdep_id,
-                                    &app_idx);
-
-  if (btif_hl_find_mcl_idx_using_app_idx(p_data->dch_reconnect_ind.mcl_handle,
-                                         app_idx, &mcl_idx)) {
-    p_acb = BTIF_HL_GET_APP_CB_PTR(app_idx);
-    BTIF_TRACE_DEBUG(
-        "btif_hl_proc_dch_reconnect_ind: app_idx = %d, mcl_idx = %d", app_idx,
-        mcl_idx);
-
-    if (btif_hl_find_avail_mdl_idx(app_idx, mcl_idx, &mdl_idx)) {
-      p_dcb = BTIF_HL_GET_MDL_CB_PTR(app_idx, mcl_idx, mdl_idx);
-
-      if (btif_hl_find_mdep_cfg_idx(app_idx,
-                                    p_data->dch_reconnect_ind.local_mdep_id,
-                                    &mdep_cfg_idx)) {
-        p_dcb->in_use = true;
-        p_dcb->mdl_handle = p_data->dch_reconnect_ind.mdl_handle;
-        p_dcb->local_mdep_cfg_idx = mdep_cfg_idx;
-        p_dcb->local_mdep_id = p_data->dch_reconnect_ind.local_mdep_id;
-        p_dcb->mdl_id = p_data->dch_reconnect_ind.mdl_id;
-        p_dcb->dch_mode = p_data->dch_reconnect_ind.dch_mode;
-        p_dcb->dch_mode = p_data->dch_reconnect_ind.dch_mode;
-        p_dcb->is_the_first_reliable = p_data->dch_reconnect_ind.first_reliable;
-        p_dcb->mtu = p_data->dch_reconnect_ind.mtu;
-        p_dcb->channel_id = btif_hl_get_next_channel_id(p_acb->app_id);
-
-        BTIF_TRACE_DEBUG(" app_idx=%d mcl_idx=%d mdl_idx=%d channel_id=%d",
-                         app_idx, mcl_idx, mdl_idx, p_dcb->channel_id);
-        if (!btif_hl_create_socket(app_idx, mcl_idx, mdl_idx)) {
-          BTIF_TRACE_ERROR("Unable to create socket");
-          close_dch = true;
-        }
-      } else {
-        BTIF_TRACE_ERROR("INVALID_LOCAL_MDEP_ID mdep_id=%d",
-                         p_data->dch_open_cfm.local_mdep_id);
-        close_dch = true;
-      }
-
-      if (close_dch) btif_hl_clean_mdl_cb(p_dcb);
-    } else
-      close_dch = true;
-  } else
-    close_dch = true;
-
-  if (close_dch) BTA_HlDchClose(p_data->dch_reconnect_ind.mdl_handle);
-}
-
-/*******************************************************************************
- *
- * Function         btif_hl_proc_dch_close_ind
- *
- * Description      Process the DCH close indication
- *
- * Returns          Nothing
- *
- ******************************************************************************/
-static void btif_hl_proc_dch_close_ind(tBTA_HL* p_data)
-
-{
-  btif_hl_mdl_cb_t* p_dcb;
-  btif_hl_mcl_cb_t* p_mcb;
-  uint8_t app_idx, mcl_idx, mdl_idx;
-
-  BTIF_TRACE_DEBUG("%s", __func__);
-  if (btif_hl_find_mdl_idx_using_handle(p_data->dch_close_ind.mdl_handle,
-                                        &app_idx, &mcl_idx, &mdl_idx)) {
-    p_dcb = BTIF_HL_GET_MDL_CB_PTR(app_idx, mcl_idx, mdl_idx);
-    btif_hl_release_socket(app_idx, mcl_idx, mdl_idx);
-    btif_hl_send_setup_disconnected_cb(app_idx, mcl_idx);
-    p_mcb = BTIF_HL_GET_MCL_CB_PTR(app_idx, mcl_idx);
-    btif_hl_clean_mdl_cb(p_dcb);
-    if (!btif_hl_num_dchs_in_use(p_mcb->mcl_handle))
-      btif_hl_start_cch_timer(app_idx, mcl_idx);
-    BTIF_TRACE_DEBUG("remote DCH close success mdl_idx=%d", mdl_idx);
-  }
-}
-
-/*******************************************************************************
- *
- * Function         btif_hl_proc_dch_close_cfm
- *
- * Description      Process the DCH reconnect confirmation
- *
- * Returns          Nothing
- *
- ******************************************************************************/
-static void btif_hl_proc_dch_close_cfm(tBTA_HL* p_data)
-
-{
-  btif_hl_mdl_cb_t* p_dcb;
-  btif_hl_mcl_cb_t* p_mcb;
-  uint8_t app_idx, mcl_idx, mdl_idx;
-
-  BTIF_TRACE_DEBUG("%s", __func__);
-  if (btif_hl_find_mdl_idx_using_handle(p_data->dch_close_cfm.mdl_handle,
-                                        &app_idx, &mcl_idx, &mdl_idx)) {
-    p_dcb = BTIF_HL_GET_MDL_CB_PTR(app_idx, mcl_idx, mdl_idx);
-    btif_hl_release_socket(app_idx, mcl_idx, mdl_idx);
-    btif_hl_clean_mdl_cb(p_dcb);
-    p_mcb = BTIF_HL_GET_MCL_CB_PTR(app_idx, mcl_idx);
-    if (!btif_hl_num_dchs_in_use(p_mcb->mcl_handle))
-      btif_hl_start_cch_timer(app_idx, mcl_idx);
-    BTIF_TRACE_DEBUG(" local DCH close success mdl_idx=%d", mdl_idx);
-  }
-}
-
-/*******************************************************************************
- *
- * Function         btif_hl_proc_abort_ind
- *
- * Description      Process the abort indicaiton
- *
- * Returns          Nothing
- *
- ******************************************************************************/
-static void btif_hl_proc_abort_ind(tBTA_HL_MCL_HANDLE mcl_handle) {
-  BTIF_TRACE_DEBUG("%s", __func__);
-  btif_hl_app_cb_t* p_acb;
-  uint8_t i, j;
-
-  for (i = 0; i < BTA_HL_NUM_APPS; i++) {
-    p_acb = BTIF_HL_GET_APP_CB_PTR(i);
-    for (j = 0; j < BTA_HL_NUM_MCLS; j++) {
-      if (p_acb->mcb[j].in_use)
-        BTIF_TRACE_DEBUG(
-            "btif_hl_find_mcl_idx_using_handle: app_idx=%d,mcl_idx =%d "
-            "mcl_handle=%d",
-            i, j, p_acb->mcb[j].mcl_handle);
-      if (p_acb->mcb[j].in_use && (p_acb->mcb[j].mcl_handle == mcl_handle)) {
-        btif_hl_stop_cch_timer(i, j);
-        btif_hl_send_setup_disconnected_cb(i, j);
-        btif_hl_clean_mcl_cb(i, j);
-      }
-    }
-  }
-}
-
-/*******************************************************************************
- *
- * Function         btif_hl_proc_abort_cfm
- *
- * Description      Process the abort confirmation
- *
- * Returns          Nothing
- *
- ******************************************************************************/
-static void btif_hl_proc_abort_cfm(tBTA_HL_MCL_HANDLE mcl_handle) {
-  BTIF_TRACE_DEBUG("%s", __func__);
-  btif_hl_app_cb_t* p_acb;
-  uint8_t i, j;
-
-  for (i = 0; i < BTA_HL_NUM_APPS; i++) {
-    p_acb = BTIF_HL_GET_APP_CB_PTR(i);
-    for (j = 0; j < BTA_HL_NUM_MCLS; j++) {
-      if (p_acb->mcb[j].in_use)
-        BTIF_TRACE_DEBUG(
-            "btif_hl_find_mcl_idx_using_handle: app_idx=%d,mcl_idx =%d "
-            "mcl_handle=%d",
-            i, j, p_acb->mcb[j].mcl_handle);
-      if (p_acb->mcb[j].in_use && (p_acb->mcb[j].mcl_handle == mcl_handle)) {
-        btif_hl_stop_cch_timer(i, j);
-        btif_hl_send_setup_disconnected_cb(i, j);
-        btif_hl_clean_mcl_cb(i, j);
-      }
-    }
-  }
-}
-
-/*******************************************************************************
- *
- * Function         btif_hl_proc_send_data_cfm
- *
- * Description      Process the send data confirmation
- *
- * Returns          Nothing
- *
- ******************************************************************************/
-static void btif_hl_proc_send_data_cfm(tBTA_HL_MDL_HANDLE mdl_handle,
-                                       UNUSED_ATTR tBTA_HL_STATUS status) {
-  uint8_t app_idx, mcl_idx, mdl_idx;
-  btif_hl_mdl_cb_t* p_dcb;
-
-  BTIF_TRACE_DEBUG("%s", __func__);
-  if (btif_hl_find_mdl_idx_using_handle(mdl_handle, &app_idx, &mcl_idx,
-                                        &mdl_idx)) {
-    p_dcb = BTIF_HL_GET_MDL_CB_PTR(app_idx, mcl_idx, mdl_idx);
-    osi_free_and_reset((void**)&p_dcb->p_tx_pkt);
-    BTIF_TRACE_DEBUG("send success free p_tx_pkt tx_size=%d", p_dcb->tx_size);
-    p_dcb->tx_size = 0;
-  }
-}
-
-/*******************************************************************************
- *
- * Function         btif_hl_proc_dch_cong_ind
- *
- * Description      Process the DCH congestion change indication
- *
- * Returns          Nothing
- *
- ******************************************************************************/
-static void btif_hl_proc_dch_cong_ind(tBTA_HL* p_data)
-
-{
-  btif_hl_mdl_cb_t* p_dcb;
-  uint8_t app_idx, mcl_idx, mdl_idx;
-
-  BTIF_TRACE_DEBUG("btif_hl_proc_dch_cong_ind");
-
-  if (btif_hl_find_mdl_idx_using_handle(p_data->dch_cong_ind.mdl_handle,
-                                        &app_idx, &mcl_idx, &mdl_idx)) {
-    p_dcb = BTIF_HL_GET_MDL_CB_PTR(app_idx, mcl_idx, mdl_idx);
-    p_dcb->cong = p_data->dch_cong_ind.cong;
-  }
-}
-
-/*******************************************************************************
- *
- * Function         btif_hl_proc_reg_request
- *
- * Description      Process registration request
- *
- * Returns          void
- *
- ******************************************************************************/
-static void btif_hl_proc_reg_request(uint8_t app_idx, uint8_t app_id,
-                                     tBTA_HL_REG_PARAM* p_reg_param,
-                                     UNUSED_ATTR tBTA_HL_CBACK* p_cback) {
-  BTIF_TRACE_DEBUG("%s app_idx=%d app_id=%d", __func__, app_idx, app_id);
-
-  if (reg_counter > 1) {
-    BTIF_TRACE_DEBUG("btif_hl_proc_reg_request: calling uPDATE");
-    BTA_HlUpdate(app_id, p_reg_param, true, btif_hl_cback);
-  } else
-    BTA_HlRegister(app_id, p_reg_param, btif_hl_cback);
-}
-
-/*******************************************************************************
- *
- * Function         btif_hl_proc_cb_evt
- *
- * Description      Process HL callback events
- *
- * Returns          void
- *
- ******************************************************************************/
-static void btif_hl_proc_cb_evt(uint16_t event, char* p_param) {
-  btif_hl_evt_cb_t* p_data = (btif_hl_evt_cb_t*)p_param;
-  bthl_channel_state_t state = BTHL_CONN_STATE_DISCONNECTED;
-  bool send_chan_cb = true;
-  tBTA_HL_REG_PARAM reg_param;
-  btif_hl_app_cb_t* p_acb;
-
-  BTIF_TRACE_DEBUG("%s event %d", __func__, event);
-  btif_hl_display_calling_process_name();
-
-  switch (event) {
-    case BTIF_HL_SEND_CONNECTED_CB:
-    case BTIF_HL_SEND_DISCONNECTED_CB:
-      if (p_data->chan_cb.cb_state == BTIF_HL_CHAN_CB_STATE_CONNECTED_PENDING)
-        state = BTHL_CONN_STATE_CONNECTED;
-      else if (p_data->chan_cb.cb_state ==
-               BTIF_HL_CHAN_CB_STATE_DISCONNECTED_PENDING)
-        state = BTHL_CONN_STATE_DISCONNECTED;
-      else
-        send_chan_cb = false;
-
-      if (send_chan_cb) {
-        RawAddress bd_addr = p_data->chan_cb.bd_addr;
-        BTIF_TRACE_DEBUG(
-            "state callbk: ch_id=0x%08x cb_state=%d state=%d  fd=%d",
-            p_data->chan_cb.channel_id, p_data->chan_cb.cb_state, state,
-            p_data->chan_cb.fd);
-        VLOG(1) << "BD " << bd_addr;
-        BTIF_HL_CALL_CBACK(
-            bt_hl_callbacks, channel_state_cb, p_data->chan_cb.app_id, &bd_addr,
-            p_data->chan_cb.mdep_cfg_index, p_data->chan_cb.channel_id, state,
-            p_data->chan_cb.fd);
-      }
-
-      break;
-
-    case BTIF_HL_REG_APP:
-      reg_counter++;
-      p_acb = BTIF_HL_GET_APP_CB_PTR(p_data->reg.app_idx);
-      BTIF_TRACE_DEBUG("Rcv BTIF_HL_REG_APP app_idx=%d reg_pending=%d",
-                       p_data->reg.app_idx, p_acb->reg_pending);
-      if (btif_hl_get_state() == BTIF_HL_STATE_ENABLED && p_acb->reg_pending) {
-        BTIF_TRACE_DEBUG("Rcv BTIF_HL_REG_APP reg_counter=%d", reg_counter);
-        p_acb->reg_pending = false;
-        reg_param.dev_type = p_acb->dev_type;
-        reg_param.sec_mask = BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT;
-        reg_param.p_srv_name = p_acb->srv_name;
-        reg_param.p_srv_desp = p_acb->srv_desp;
-        reg_param.p_provider_name = p_acb->provider_name;
-        btif_hl_proc_reg_request(p_data->reg.app_idx, p_acb->app_id, &reg_param,
-                                 btif_hl_cback);
-      } else {
-        BTIF_TRACE_DEBUG("reg request is processed state=%d reg_pending=%d",
-                         btif_hl_get_state(), p_acb->reg_pending);
-      }
-      break;
-
-    case BTIF_HL_UNREG_APP:
-      reg_counter--;
-      BTIF_TRACE_DEBUG("Rcv BTIF_HL_UNREG_APP app_idx=%d",
-                       p_data->unreg.app_idx);
-      p_acb = BTIF_HL_GET_APP_CB_PTR(p_data->unreg.app_idx);
-      if (btif_hl_get_state() == BTIF_HL_STATE_ENABLED) {
-        if (reg_counter >= 1)
-          BTA_HlUpdate(p_acb->app_id, NULL, false, NULL);
-        else
-          BTA_HlDeregister(p_acb->app_id, p_acb->app_handle);
-      }
-      break;
-
-    case BTIF_HL_UPDATE_MDL:
-      BTIF_TRACE_DEBUG("Rcv BTIF_HL_UPDATE_MDL app_idx=%d",
-                       p_data->update_mdl.app_idx);
-      p_acb = BTIF_HL_GET_APP_CB_PTR(p_data->update_mdl.app_idx);
-      break;
-
-    default:
-      BTIF_TRACE_ERROR("Unknown event %d", event);
-      break;
-  }
-}
-
-/*******************************************************************************
- *
- * Function         btif_hl_upstreams_evt
- *
- * Description      Process HL events
- *
- * Returns          void
- *
- ******************************************************************************/
-static void btif_hl_upstreams_evt(uint16_t event, char* p_param) {
-  tBTA_HL* p_data = (tBTA_HL*)p_param;
-  uint8_t app_idx, mcl_idx;
-  btif_hl_app_cb_t* p_acb;
-  btif_hl_mcl_cb_t* p_mcb = NULL;
-  btif_hl_pend_dch_op_t pending_op;
-  bool status;
-
-  BTIF_TRACE_DEBUG("%s event %d", __func__, event);
-  btif_hl_display_calling_process_name();
-  switch (event) {
-    case BTA_HL_REGISTER_CFM_EVT:
-      BTIF_TRACE_DEBUG("Rcv BTA_HL_REGISTER_CFM_EVT");
-      BTIF_TRACE_DEBUG("app_id=%d app_handle=%d status=%d ",
-                       p_data->reg_cfm.app_id, p_data->reg_cfm.app_handle,
-                       p_data->reg_cfm.status);
-
-      btif_hl_proc_reg_cfm(p_data);
-      break;
-    case BTA_HL_SDP_INFO_IND_EVT:
-      BTIF_TRACE_DEBUG("Rcv BTA_HL_SDP_INFO_IND_EVT");
-      BTIF_TRACE_DEBUG(
-          "app_handle=%d ctrl_psm=0x%04x data_psm=0x%04x x_spec=%d "
-          "mcap_sup_procs=0x%02x",
-          p_data->sdp_info_ind.app_handle, p_data->sdp_info_ind.ctrl_psm,
-          p_data->sdp_info_ind.data_psm, p_data->sdp_info_ind.data_x_spec,
-          p_data->sdp_info_ind.mcap_sup_procs);
-      // btif_hl_proc_sdp_info_ind(p_data);
-      break;
-
-    case BTA_HL_DEREGISTER_CFM_EVT:
-      BTIF_TRACE_DEBUG("Rcv BTA_HL_DEREGISTER_CFM_EVT");
-      BTIF_TRACE_DEBUG("app_handle=%d status=%d ", p_data->dereg_cfm.app_handle,
-                       p_data->dereg_cfm.status);
-      btif_hl_proc_dereg_cfm(p_data);
-      break;
-
-    case BTA_HL_SDP_QUERY_CFM_EVT:
-      BTIF_TRACE_DEBUG("Rcv BTA_HL_SDP_QUERY_CFM_EVT");
-      BTIF_TRACE_DEBUG("app_handle=%d app_id =%d,status =%d",
-                       p_data->sdp_query_cfm.app_handle,
-                       p_data->sdp_query_cfm.app_id,
-                       p_data->sdp_query_cfm.status);
-      VLOG(0) << "DB " << p_data->sdp_query_cfm.bd_addr;
-
-      if (p_data->sdp_query_cfm.status == BTA_HL_STATUS_OK)
-        status = btif_hl_proc_sdp_query_cfm(p_data);
-      else
-        status = false;
-
-      if (!status) {
-        BTIF_TRACE_DEBUG("BTA_HL_SDP_QUERY_CFM_EVT Status = %d",
-                         p_data->sdp_query_cfm.status);
-        if (btif_hl_find_app_idx_using_app_id(p_data->sdp_query_cfm.app_id,
-                                              &app_idx)) {
-          p_acb = BTIF_HL_GET_APP_CB_PTR(app_idx);
-          if (btif_hl_find_mcl_idx(app_idx, p_data->sdp_query_cfm.bd_addr,
-                                   &mcl_idx)) {
-            p_mcb = BTIF_HL_GET_MCL_CB_PTR(app_idx, mcl_idx);
-            if ((p_mcb->cch_oper == BTIF_HL_CCH_OP_MDEP_FILTERING) ||
-                (p_mcb->cch_oper == BTIF_HL_CCH_OP_DCH_OPEN)) {
-              pending_op = p_mcb->pcb.op;
-              switch (pending_op) {
-                case BTIF_HL_PEND_DCH_OP_OPEN:
-                  btif_hl_send_setup_disconnected_cb(app_idx, mcl_idx);
-                  break;
-                case BTIF_HL_PEND_DCH_OP_RECONNECT:
-                case BTIF_HL_PEND_DCH_OP_DELETE_MDL:
-                default:
-                  break;
-              }
-              if (!p_mcb->is_connected) btif_hl_clean_mcl_cb(app_idx, mcl_idx);
-            }
-          }
-        }
-      }
-
-      break;
-
-    case BTA_HL_CCH_OPEN_CFM_EVT:
-      BTIF_TRACE_DEBUG("Rcv BTA_HL_CCH_OPEN_CFM_EVT");
-      BTIF_TRACE_DEBUG(
-          "app_id=%d,app_handle=%d mcl_handle=%d status =%d",
-          p_data->cch_open_cfm.app_id, p_data->cch_open_cfm.app_handle,
-          p_data->cch_open_cfm.mcl_handle, p_data->cch_open_cfm.status);
-      VLOG(1) << "BD " << p_data->cch_open_cfm.bd_addr;
-
-      if (p_data->cch_open_cfm.status == BTA_HL_STATUS_OK ||
-          p_data->cch_open_cfm.status == BTA_HL_STATUS_DUPLICATE_CCH_OPEN) {
-        status = btif_hl_proc_cch_open_cfm(p_data);
-      } else {
-        status = false;
-      }
-
-      if (!status) {
-        if (btif_hl_find_app_idx_using_app_id(p_data->cch_open_cfm.app_id,
-                                              &app_idx)) {
-          p_acb = BTIF_HL_GET_APP_CB_PTR(app_idx);
-          if (btif_hl_find_mcl_idx(app_idx, p_data->cch_open_cfm.bd_addr,
-                                   &mcl_idx)) {
-            p_mcb = BTIF_HL_GET_MCL_CB_PTR(app_idx, mcl_idx);
-            pending_op = p_mcb->pcb.op;
-            switch (pending_op) {
-              case BTIF_HL_PEND_DCH_OP_OPEN:
-                btif_hl_send_setup_disconnected_cb(app_idx, mcl_idx);
-                break;
-              case BTIF_HL_PEND_DCH_OP_RECONNECT:
-              case BTIF_HL_PEND_DCH_OP_DELETE_MDL:
-              default:
-                break;
-            }
-            btif_hl_clean_mcl_cb(app_idx, mcl_idx);
-          }
-        }
-      }
-      break;
-
-    case BTA_HL_DCH_OPEN_CFM_EVT:
-      BTIF_TRACE_DEBUG("Rcv BTA_HL_DCH_OPEN_CFM_EVT");
-      BTIF_TRACE_DEBUG("mcl_handle=%d mdl_handle=0x%x status=%d ",
-                       p_data->dch_open_cfm.mcl_handle,
-                       p_data->dch_open_cfm.mdl_handle,
-                       p_data->dch_open_cfm.status);
-      BTIF_TRACE_DEBUG(
-          "first_reliable =%d dch_mode=%d local_mdep_id=%d mdl_id=%d mtu=%d",
-          p_data->dch_open_cfm.first_reliable, p_data->dch_open_cfm.dch_mode,
-          p_data->dch_open_cfm.local_mdep_id, p_data->dch_open_cfm.mdl_id,
-          p_data->dch_open_cfm.mtu);
-      if (p_data->dch_open_cfm.status == BTA_HL_STATUS_OK) {
-        status = btif_hl_proc_dch_open_cfm(p_data);
-      } else {
-        status = false;
-      }
-
-      if (!status) {
-        if (btif_hl_find_mcl_idx_using_handle(p_data->dch_open_cfm.mcl_handle,
-                                              &app_idx, &mcl_idx)) {
-          p_mcb = BTIF_HL_GET_MCL_CB_PTR(app_idx, mcl_idx);
-          pending_op = p_mcb->pcb.op;
-          switch (pending_op) {
-            case BTIF_HL_PEND_DCH_OP_OPEN:
-              btif_hl_send_setup_disconnected_cb(app_idx, mcl_idx);
-              break;
-            case BTIF_HL_PEND_DCH_OP_RECONNECT:
-            case BTIF_HL_PEND_DCH_OP_DELETE_MDL:
-            default:
-              break;
-          }
-        }
-      }
-      break;
-
-    case BTA_HL_CCH_OPEN_IND_EVT:
-      BTIF_TRACE_DEBUG("Rcv BTA_HL_CCH_OPEN_IND_EVT");
-      BTIF_TRACE_DEBUG("app_handle=%d mcl_handle=%d",
-                       p_data->cch_open_ind.app_handle,
-                       p_data->cch_open_ind.mcl_handle);
-      VLOG(0) << "DB " << p_data->cch_open_ind.bd_addr;
-
-      btif_hl_proc_cch_open_ind(p_data);
-      break;
-
-    case BTA_HL_DCH_CREATE_IND_EVT:
-      BTIF_TRACE_DEBUG("Rcv BTA_HL_DCH_CREATE_IND_EVT");
-      BTIF_TRACE_DEBUG("mcl_handle=%d", p_data->dch_create_ind.mcl_handle);
-      BTIF_TRACE_DEBUG("local_mdep_id =%d mdl_id=%d cfg=%d",
-                       p_data->dch_create_ind.local_mdep_id,
-                       p_data->dch_create_ind.mdl_id,
-                       p_data->dch_create_ind.cfg);
-      btif_hl_proc_create_ind(p_data);
-      break;
-
-    case BTA_HL_DCH_OPEN_IND_EVT:
-      BTIF_TRACE_DEBUG("Rcv BTA_HL_DCH_OPEN_IND_EVT");
-      BTIF_TRACE_DEBUG("mcl_handle=%d mdl_handle=0x%x",
-                       p_data->dch_open_ind.mcl_handle,
-                       p_data->dch_open_ind.mdl_handle);
-      BTIF_TRACE_DEBUG(
-          "first_reliable =%d dch_mode=%d local_mdep_id=%d mdl_id=%d mtu=%d",
-          p_data->dch_open_ind.first_reliable, p_data->dch_open_ind.dch_mode,
-          p_data->dch_open_ind.local_mdep_id, p_data->dch_open_ind.mdl_id,
-          p_data->dch_open_ind.mtu);
-
-      btif_hl_proc_dch_open_ind(p_data);
-      break;
-
-    case BTA_HL_DELETE_MDL_IND_EVT:
-      BTIF_TRACE_DEBUG("Rcv BTA_HL_DELETE_MDL_IND_EVT");
-      BTIF_TRACE_DEBUG("mcl_handle=%d mdl_id=0x%x",
-                       p_data->delete_mdl_ind.mcl_handle,
-                       p_data->delete_mdl_ind.mdl_id);
-      break;
-
-    case BTA_HL_DELETE_MDL_CFM_EVT:
-      BTIF_TRACE_DEBUG("Rcv BTA_HL_DELETE_MDL_CFM_EVT");
-      BTIF_TRACE_DEBUG("mcl_handle=%d mdl_id=0x%x status=%d",
-                       p_data->delete_mdl_cfm.mcl_handle,
-                       p_data->delete_mdl_cfm.mdl_id,
-                       p_data->delete_mdl_cfm.status);
-
-      if (btif_hl_find_app_idx_using_deleted_mdl_id(
-              p_data->delete_mdl_cfm.mdl_id, &app_idx)) {
-        p_acb = BTIF_HL_GET_APP_CB_PTR(app_idx);
-        btif_hl_send_destroyed_cb(p_acb);
-        btif_hl_clean_delete_mdl(&p_acb->delete_mdl);
-      }
-      break;
-
-    case BTA_HL_DCH_RECONNECT_CFM_EVT:
-      BTIF_TRACE_DEBUG("Rcv BTA_HL_DCH_RECONNECT_CFM_EVT");
-      BTIF_TRACE_DEBUG("mcl_handle=%d mdl_handle=%d status=%d   ",
-                       p_data->dch_reconnect_cfm.mcl_handle,
-                       p_data->dch_reconnect_cfm.mdl_handle,
-                       p_data->dch_reconnect_cfm.status);
-      BTIF_TRACE_DEBUG("first_reliable =%d dch_mode=%d mdl_id=%d mtu=%d",
-                       p_data->dch_reconnect_cfm.first_reliable,
-                       p_data->dch_reconnect_cfm.dch_mode,
-                       p_data->dch_reconnect_cfm.mdl_id,
-                       p_data->dch_reconnect_cfm.mtu);
-
-      if (p_data->dch_reconnect_cfm.status == BTA_HL_STATUS_OK) {
-        status = btif_hl_proc_dch_reconnect_cfm(p_data);
-      } else {
-        status = false;
-      }
-
-      if (!status) {
-        if (btif_hl_find_mcl_idx_using_handle(p_data->dch_open_cfm.mcl_handle,
-                                              &app_idx, &mcl_idx)) {
-          p_mcb = BTIF_HL_GET_MCL_CB_PTR(app_idx, mcl_idx);
-          pending_op = p_mcb->pcb.op;
-          switch (pending_op) {
-            case BTIF_HL_PEND_DCH_OP_OPEN:
-              btif_hl_send_setup_disconnected_cb(app_idx, mcl_idx);
-              break;
-            case BTIF_HL_PEND_DCH_OP_RECONNECT:
-            case BTIF_HL_PEND_DCH_OP_DELETE_MDL:
-            default:
-              break;
-          }
-        }
-      }
-
-      break;
-
-    case BTA_HL_CCH_CLOSE_CFM_EVT:
-      BTIF_TRACE_DEBUG("Rcv BTA_HL_CCH_CLOSE_CFM_EVT");
-      BTIF_TRACE_DEBUG("mcl_handle=%d status =%d",
-                       p_data->cch_close_cfm.mcl_handle,
-                       p_data->cch_close_cfm.status);
-      if (p_data->cch_close_cfm.status == BTA_HL_STATUS_OK) {
-        btif_hl_proc_cch_close_cfm(p_data);
-      }
-      break;
-
-    case BTA_HL_CCH_CLOSE_IND_EVT:
-      BTIF_TRACE_DEBUG("Rcv BTA_HL_CCH_CLOSE_IND_EVT");
-      BTIF_TRACE_DEBUG("mcl_handle =%d intentional_close=%s",
-                       p_data->cch_close_ind.mcl_handle,
-                       (p_data->cch_close_ind.intentional ? "Yes" : "No"));
-
-      btif_hl_proc_cch_close_ind(p_data);
-      break;
-
-    case BTA_HL_DCH_CLOSE_IND_EVT:
-      BTIF_TRACE_DEBUG("Rcv BTA_HL_DCH_CLOSE_IND_EVT");
-      BTIF_TRACE_DEBUG("mdl_handle=%d intentional_close=%s",
-                       p_data->dch_close_ind.mdl_handle,
-                       (p_data->dch_close_ind.intentional ? "Yes" : "No"));
-
-      btif_hl_proc_dch_close_ind(p_data);
-      break;
-
-    case BTA_HL_DCH_CLOSE_CFM_EVT:
-      BTIF_TRACE_DEBUG("Rcv BTA_HL_DCH_CLOSE_CFM_EVT");
-      BTIF_TRACE_DEBUG("mdl_handle=%d status=%d ",
-                       p_data->dch_close_cfm.mdl_handle,
-                       p_data->dch_close_cfm.status);
-
-      if (p_data->dch_close_cfm.status == BTA_HL_STATUS_OK) {
-        btif_hl_proc_dch_close_cfm(p_data);
-      }
-      break;
-
-    case BTA_HL_DCH_ECHO_TEST_CFM_EVT:
-      BTIF_TRACE_DEBUG("Rcv BTA_HL_DCH_ECHO_TEST_CFM_EVT");
-      BTIF_TRACE_DEBUG("mcl_handle=%d    status=%d",
-                       p_data->echo_test_cfm.mcl_handle,
-                       p_data->echo_test_cfm.status);
-      /* not supported */
-      break;
-
-    case BTA_HL_DCH_RECONNECT_IND_EVT:
-      BTIF_TRACE_DEBUG("Rcv BTA_HL_DCH_RECONNECT_IND_EVT");
-
-      BTIF_TRACE_DEBUG("mcl_handle=%d mdl_handle=5d",
-                       p_data->dch_reconnect_ind.mcl_handle,
-                       p_data->dch_reconnect_ind.mdl_handle);
-      BTIF_TRACE_DEBUG("first_reliable =%d dch_mode=%d mdl_id=%d mtu=%d",
-                       p_data->dch_reconnect_ind.first_reliable,
-                       p_data->dch_reconnect_ind.dch_mode,
-                       p_data->dch_reconnect_ind.mdl_id,
-                       p_data->dch_reconnect_ind.mtu);
-
-      btif_hl_proc_dch_reconnect_ind(p_data);
-      break;
-
-    case BTA_HL_CONG_CHG_IND_EVT:
-      BTIF_TRACE_DEBUG("Rcv BTA_HL_CONG_CHG_IND_EVT");
-      BTIF_TRACE_DEBUG("mdl_handle=%d cong =%d",
-                       p_data->dch_cong_ind.mdl_handle,
-                       p_data->dch_cong_ind.cong);
-      btif_hl_proc_dch_cong_ind(p_data);
-      break;
-
-    case BTA_HL_DCH_ABORT_IND_EVT:
-      BTIF_TRACE_DEBUG("Rcv BTA_HL_DCH_ABORT_IND_EVT");
-      BTIF_TRACE_DEBUG("mcl_handle=%d", p_data->dch_abort_ind.mcl_handle);
-      btif_hl_proc_abort_ind(p_data->dch_abort_ind.mcl_handle);
-      break;
-    case BTA_HL_DCH_ABORT_CFM_EVT:
-      BTIF_TRACE_DEBUG("Rcv BTA_HL_DCH_ABORT_CFM_EVT");
-      BTIF_TRACE_DEBUG("mcl_handle=%d status =%d",
-                       p_data->dch_abort_cfm.mcl_handle,
-                       p_data->dch_abort_cfm.status);
-      if (p_data->dch_abort_cfm.status == BTA_HL_STATUS_OK) {
-        btif_hl_proc_abort_cfm(p_data->dch_abort_ind.mcl_handle);
-      }
-      break;
-
-    case BTA_HL_DCH_SEND_DATA_CFM_EVT:
-      BTIF_TRACE_DEBUG("Rcv BTA_HL_DCH_SEND_DATA_CFM_EVT");
-      BTIF_TRACE_DEBUG("mdl_handle=0x%x status =%d",
-                       p_data->dch_send_data_cfm.mdl_handle,
-                       p_data->dch_send_data_cfm.status);
-      btif_hl_proc_send_data_cfm(p_data->dch_send_data_cfm.mdl_handle,
-                                 p_data->dch_send_data_cfm.status);
-      break;
-
-    case BTA_HL_DCH_RCV_DATA_IND_EVT:
-      BTIF_TRACE_DEBUG("Rcv BTA_HL_DCH_RCV_DATA_IND_EVT");
-      BTIF_TRACE_DEBUG("mdl_handle=0x%x ", p_data->dch_rcv_data_ind.mdl_handle);
-      /* do nothing here */
-      break;
-
-    default:
-      BTIF_TRACE_DEBUG("Unknown Event (0x%02x)...", event);
-      break;
-  }
-}
-
-/*******************************************************************************
- *
- * Function         btif_hl_cback
- *
- * Description      Callback function for HL events
- *
- * Returns          void
- *
- ******************************************************************************/
-static void btif_hl_cback(tBTA_HL_EVT event, tBTA_HL* p_data) {
-  bt_status_t status;
-  int param_len = 0;
-  BTIF_TRACE_DEBUG("%s event %d", __func__, event);
-  btif_hl_display_calling_process_name();
-  switch (event) {
-    case BTA_HL_REGISTER_CFM_EVT:
-      param_len = sizeof(tBTA_HL_REGISTER_CFM);
-      break;
-    case BTA_HL_SDP_INFO_IND_EVT:
-      param_len = sizeof(tBTA_HL_SDP_INFO_IND);
-      break;
-    case BTA_HL_DEREGISTER_CFM_EVT:
-      param_len = sizeof(tBTA_HL_DEREGISTER_CFM);
-      break;
-    case BTA_HL_SDP_QUERY_CFM_EVT:
-      param_len = sizeof(tBTA_HL_SDP_QUERY_CFM);
-      break;
-    case BTA_HL_CCH_OPEN_CFM_EVT:
-      param_len = sizeof(tBTA_HL_CCH_OPEN_CFM);
-      break;
-    case BTA_HL_DCH_OPEN_CFM_EVT:
-      param_len = sizeof(tBTA_HL_DCH_OPEN_CFM);
-      break;
-    case BTA_HL_CCH_OPEN_IND_EVT:
-      param_len = sizeof(tBTA_HL_CCH_OPEN_IND);
-      break;
-    case BTA_HL_DCH_CREATE_IND_EVT:
-      param_len = sizeof(tBTA_HL_DCH_CREATE_IND);
-      break;
-    case BTA_HL_DCH_OPEN_IND_EVT:
-      param_len = sizeof(tBTA_HL_DCH_OPEN_IND);
-      break;
-    case BTA_HL_DELETE_MDL_IND_EVT:
-      param_len = sizeof(tBTA_HL_MDL_IND);
-      break;
-    case BTA_HL_DELETE_MDL_CFM_EVT:
-      param_len = sizeof(tBTA_HL_MDL_CFM);
-      break;
-    case BTA_HL_DCH_RECONNECT_CFM_EVT:
-      param_len = sizeof(tBTA_HL_DCH_OPEN_CFM);
-      break;
-    case BTA_HL_CCH_CLOSE_CFM_EVT:
-      param_len = sizeof(tBTA_HL_MCL_CFM);
-      break;
-    case BTA_HL_CCH_CLOSE_IND_EVT:
-      param_len = sizeof(tBTA_HL_CCH_CLOSE_IND);
-      break;
-    case BTA_HL_DCH_CLOSE_IND_EVT:
-      param_len = sizeof(tBTA_HL_DCH_CLOSE_IND);
-      break;
-    case BTA_HL_DCH_CLOSE_CFM_EVT:
-      param_len = sizeof(tBTA_HL_MDL_CFM);
-      break;
-    case BTA_HL_DCH_ECHO_TEST_CFM_EVT:
-      param_len = sizeof(tBTA_HL_MCL_CFM);
-      break;
-    case BTA_HL_DCH_RECONNECT_IND_EVT:
-      param_len = sizeof(tBTA_HL_DCH_OPEN_IND);
-      break;
-    case BTA_HL_CONG_CHG_IND_EVT:
-      param_len = sizeof(tBTA_HL_DCH_CONG_IND);
-      break;
-    case BTA_HL_DCH_ABORT_IND_EVT:
-      param_len = sizeof(tBTA_HL_MCL_IND);
-      break;
-    case BTA_HL_DCH_ABORT_CFM_EVT:
-      param_len = sizeof(tBTA_HL_MCL_CFM);
-      break;
-    case BTA_HL_DCH_SEND_DATA_CFM_EVT:
-      param_len = sizeof(tBTA_HL_MDL_CFM);
-      break;
-    case BTA_HL_DCH_RCV_DATA_IND_EVT:
-      param_len = sizeof(tBTA_HL_MDL_IND);
-      break;
-    default:
-      param_len = sizeof(tBTA_HL_MDL_IND);
-      break;
-  }
-  status = btif_transfer_context(btif_hl_upstreams_evt, (uint16_t)event,
-                                 (char*)p_data, param_len, NULL);
-
-  /* catch any failed context transfers */
-  ASSERTC(status == BT_STATUS_SUCCESS, "context transfer failed", status);
-}
-
-/*******************************************************************************
- *
- * Function         btif_hl_upstreams_ctrl_evt
- *
- * Description      Callback function for HL control events in the BTIF task
- *                  context
- *
- * Returns          void
- *
- ******************************************************************************/
-static void btif_hl_upstreams_ctrl_evt(uint16_t event, char* p_param) {
-  tBTA_HL_CTRL* p_data = (tBTA_HL_CTRL*)p_param;
-  uint8_t i;
-  tBTA_HL_REG_PARAM reg_param;
-  btif_hl_app_cb_t* p_acb;
-
-  BTIF_TRACE_DEBUG("%s event %d", __func__, event);
-  btif_hl_display_calling_process_name();
-
-  switch (event) {
-    case BTA_HL_CTRL_ENABLE_CFM_EVT:
-      BTIF_TRACE_DEBUG("Rcv BTA_HL_CTRL_ENABLE_CFM_EVT");
-      BTIF_TRACE_DEBUG("status=%d", p_data->enable_cfm.status);
-
-      if (p_data->enable_cfm.status == BTA_HL_STATUS_OK) {
-        btif_hl_set_state(BTIF_HL_STATE_ENABLED);
-
-        for (i = 0; i < BTA_HL_NUM_APPS; i++) {
-          p_acb = BTIF_HL_GET_APP_CB_PTR(i);
-          if (p_acb->in_use && p_acb->reg_pending) {
-            p_acb->reg_pending = false;
-            reg_param.dev_type = p_acb->dev_type;
-            reg_param.sec_mask = BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT;
-            reg_param.p_srv_name = p_acb->srv_name;
-            reg_param.p_srv_desp = p_acb->srv_desp;
-            reg_param.p_provider_name = p_acb->provider_name;
-
-            BTIF_TRACE_DEBUG("Register pending app_id=%d", p_acb->app_id);
-            btif_hl_proc_reg_request(i, p_acb->app_id, &reg_param,
-                                     btif_hl_cback);
-          }
-        }
-      }
-
-      break;
-    case BTA_HL_CTRL_DISABLE_CFM_EVT:
-      BTIF_TRACE_DEBUG("Rcv BTA_HL_CTRL_DISABLE_CFM_EVT");
-      BTIF_TRACE_DEBUG("status=%d", p_data->disable_cfm.status);
-
-      if (p_data->disable_cfm.status == BTA_HL_STATUS_OK) {
-        for (size_t i = 0; i < BTA_HL_NUM_APPS; i++) {
-          for (size_t j = 0; j < BTA_HL_NUM_MCLS; j++) {
-            alarm_free(p_btif_hl_cb->acb[i].mcb[j].cch_timer);
-          }
-        }
-        memset(p_btif_hl_cb, 0, sizeof(btif_hl_cb_t));
-        btif_hl_set_state(BTIF_HL_STATE_DISABLED);
-      }
-
-      break;
-    default:
-      break;
-  }
-}
-
-/*******************************************************************************
- *
- * Function         btif_hl_ctrl_cback
- *
- * Description      Callback function for HL control events
- *
- * Returns          void
- *
- ******************************************************************************/
-static void btif_hl_ctrl_cback(tBTA_HL_CTRL_EVT event, tBTA_HL_CTRL* p_data) {
-  bt_status_t status;
-  int param_len = 0;
-
-  BTIF_TRACE_DEBUG("%s event %d", __func__, event);
-  btif_hl_display_calling_process_name();
-
-  switch (event) {
-    case BTA_HL_CTRL_ENABLE_CFM_EVT:
-    case BTA_HL_CTRL_DISABLE_CFM_EVT:
-      param_len = sizeof(tBTA_HL_CTRL_ENABLE_DISABLE);
-      break;
-    default:
-      break;
-  }
-
-  status = btif_transfer_context(btif_hl_upstreams_ctrl_evt, (uint16_t)event,
-                                 (char*)p_data, param_len, NULL);
-  ASSERTC(status == BT_STATUS_SUCCESS, "context transfer failed", status);
-}
-/*******************************************************************************
- *
- * Function         connect_channel
- *
- * Description     connect a data channel
- *
- * Returns         bt_status_t
- *
- ******************************************************************************/
-static bt_status_t connect_channel(int app_id, RawAddress* bd_addr,
-                                   int mdep_cfg_index, int* channel_id) {
-  uint8_t app_idx, mcl_idx;
-  btif_hl_app_cb_t* p_acb = NULL;
-  btif_hl_pending_chan_cb_t* p_pcb = NULL;
-  btif_hl_mcl_cb_t* p_mcb = NULL;
-  bt_status_t status = BT_STATUS_SUCCESS;
-  tBTA_HL_DCH_OPEN_PARAM dch_open;
-
-  CHECK_BTHL_INIT();
-  BTIF_TRACE_EVENT("%s", __func__);
-  btif_hl_display_calling_process_name();
-
-  if (btif_hl_find_app_idx(((uint8_t)app_id), &app_idx)) {
-    p_acb = BTIF_HL_GET_APP_CB_PTR(app_idx);
-    if (btif_hl_find_mcl_idx(app_idx, *bd_addr, &mcl_idx)) {
-      p_mcb = BTIF_HL_GET_MCL_CB_PTR(app_idx, mcl_idx);
-      if (p_mcb->is_connected) {
-        dch_open.ctrl_psm = p_mcb->ctrl_psm;
-        dch_open.local_mdep_id =
-            p_acb->sup_feature.mdep[mdep_cfg_index].mdep_id;
-        BTIF_TRACE_DEBUG(
-            "connect_channel: app_idx =%d, mdep_cfg_indx =%d, mdep_id =%d "
-            "app_id= %d",
-            app_idx, mdep_cfg_index, dch_open.local_mdep_id, app_id);
-        if (btif_hl_find_peer_mdep_id(
-                p_acb->app_id, p_mcb->bd_addr,
-                p_acb->sup_feature.mdep[mdep_cfg_index].mdep_cfg.mdep_role,
-                p_acb->sup_feature.mdep[mdep_cfg_index]
-                    .mdep_cfg.data_cfg[0]
-                    .data_type,
-                &dch_open.peer_mdep_id)) {
-          dch_open.local_cfg = p_acb->channel_type[mdep_cfg_index];
-          if ((p_acb->sup_feature.mdep[mdep_cfg_index].mdep_cfg.mdep_role ==
-               BTA_HL_MDEP_ROLE_SOURCE) &&
-              !btif_hl_is_the_first_reliable_existed(app_idx, mcl_idx)) {
-            dch_open.local_cfg = BTA_HL_DCH_CFG_RELIABLE;
-          }
-          dch_open.sec_mask = (BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT);
-
-          if (!btif_hl_dch_open(p_acb->app_id, *bd_addr, &dch_open,
-                                mdep_cfg_index, BTIF_HL_PEND_DCH_OP_OPEN,
-                                channel_id)) {
-            status = BT_STATUS_FAIL;
-            BTIF_TRACE_EVENT("%s loc0 status = BT_STATUS_FAIL", __func__);
-          }
-        } else {
-          p_mcb->cch_oper = BTIF_HL_CCH_OP_MDEP_FILTERING;
-
-          p_pcb = BTIF_HL_GET_PCB_PTR(app_idx, mcl_idx);
-          p_pcb->in_use = true;
-          p_pcb->mdep_cfg_idx = mdep_cfg_index;
-          p_pcb->bd_addr = *bd_addr;
-          p_pcb->op = BTIF_HL_PEND_DCH_OP_OPEN;
-          BTA_HlSdpQuery(app_id, p_acb->app_handle, *bd_addr);
-        }
-      } else {
-        status = BT_STATUS_FAIL;
-      }
-    } else {
-      p_acb->filter.num_elems = 1;
-      p_acb->filter.elem[0].data_type = p_acb->sup_feature.mdep[mdep_cfg_index]
-                                            .mdep_cfg.data_cfg[mdep_cfg_index]
-                                            .data_type;
-      if (p_acb->sup_feature.mdep[mdep_cfg_index].mdep_cfg.mdep_role ==
-          BTA_HL_MDEP_ROLE_SINK)
-        p_acb->filter.elem[0].peer_mdep_role = BTA_HL_MDEP_ROLE_SOURCE;
-      else
-        p_acb->filter.elem[0].peer_mdep_role = BTA_HL_MDEP_ROLE_SINK;
-
-      if (!btif_hl_cch_open(p_acb->app_id, *bd_addr, 0, mdep_cfg_index,
-                            BTIF_HL_PEND_DCH_OP_OPEN, channel_id)) {
-        status = BT_STATUS_FAIL;
-      }
-    }
-  } else {
-    status = BT_STATUS_FAIL;
-  }
-
-  BTIF_TRACE_DEBUG("%s status=%d channel_id=0x%08x", __func__, status,
-                   *channel_id);
-
-  return status;
-}
-/*******************************************************************************
- *
- * Function         destroy_channel
- *
- * Description      destroy a data channel
- *
- * Returns         bt_status_t
- *
- ******************************************************************************/
-static bt_status_t destroy_channel(int channel_id) {
-  uint8_t app_idx, mcl_idx, mdl_cfg_idx, mdep_cfg_idx = 0;
-  bt_status_t status = BT_STATUS_SUCCESS;
-  btif_hl_mdl_cfg_t* p_mdl;
-  btif_hl_mcl_cb_t* p_mcb;
-  btif_hl_app_cb_t* p_acb;
-
-  CHECK_BTHL_INIT();
-  BTIF_TRACE_EVENT("%s channel_id=0x%08x", __func__, channel_id);
-  btif_hl_display_calling_process_name();
-
-  if (btif_hl_if_channel_setup_pending(channel_id, &app_idx, &mcl_idx)) {
-    btif_hl_dch_abort(app_idx, mcl_idx);
-  } else {
-    if (btif_hl_find_mdl_cfg_idx_using_channel_id(channel_id, &app_idx,
-                                                  &mdl_cfg_idx))
-    //       if(btif_hl_find_mdl_idx_using_channel_id(channel_id,
-    //       &app_idx,&mcl_idx, &mdl_idx))
-    {
-      p_acb = BTIF_HL_GET_APP_CB_PTR(app_idx);
-      if (!p_acb->delete_mdl.active) {
-        p_mdl = BTIF_HL_GET_MDL_CFG_PTR(app_idx, mdl_cfg_idx);
-        p_acb->delete_mdl.active = true;
-        p_acb->delete_mdl.mdl_id = p_mdl->base.mdl_id;
-        p_acb->delete_mdl.channel_id = channel_id;
-        p_acb->delete_mdl.mdep_cfg_idx = p_mdl->extra.mdep_cfg_idx;
-        p_acb->delete_mdl.bd_addr = p_mdl->base.peer_bd_addr;
-
-        if (btif_hl_find_mcl_idx(app_idx, p_mdl->base.peer_bd_addr, &mcl_idx)) {
-          p_mcb = BTIF_HL_GET_MCL_CB_PTR(app_idx, mcl_idx);
-          if (p_mcb->is_connected) {
-            BTIF_TRACE_DEBUG("calling BTA_HlDeleteMdl mdl_id=%d",
-                             p_acb->delete_mdl.mdl_id);
-            BTA_HlDeleteMdl(p_mcb->mcl_handle, p_acb->delete_mdl.mdl_id);
-          } else {
-            status = BT_STATUS_FAIL;
-          }
-        } else {
-          BTIF_TRACE_DEBUG("btif_hl_delete_mdl calling btif_hl_cch_open");
-          mdep_cfg_idx = p_mdl->extra.mdep_cfg_idx;
-          p_acb->filter.num_elems = 1;
-          p_acb->filter.elem[0].data_type =
-              p_acb->sup_feature.mdep[mdep_cfg_idx]
-                  .mdep_cfg.data_cfg[mdep_cfg_idx]
-                  .data_type;
-          if (p_acb->sup_feature.mdep[mdep_cfg_idx].mdep_cfg.mdep_role ==
-              BTA_HL_MDEP_ROLE_SINK)
-            p_acb->filter.elem[0].peer_mdep_role = BTA_HL_MDEP_ROLE_SOURCE;
-          else
-            p_acb->filter.elem[0].peer_mdep_role = BTA_HL_MDEP_ROLE_SINK;
-          if (btif_hl_cch_open(p_acb->app_id, p_acb->delete_mdl.bd_addr, 0,
-                               mdep_cfg_idx, BTIF_HL_PEND_DCH_OP_DELETE_MDL,
-                               NULL)) {
-            status = BT_STATUS_FAIL;
-          }
-        }
-
-        if (status == BT_STATUS_FAIL) {
-          /* fail for now  */
-          btif_hl_clean_delete_mdl(&p_acb->delete_mdl);
-        }
-      } else {
-        status = BT_STATUS_BUSY;
-      }
-    } else {
-      status = BT_STATUS_FAIL;
-    }
-  }
-  return status;
-}
-/*******************************************************************************
- *
- * Function         unregister_application
- *
- * Description     unregister an HDP application
- *
- * Returns         bt_status_t
- *
- ******************************************************************************/
-static bt_status_t unregister_application(int app_id) {
-  uint8_t app_idx;
-  int len;
-  bt_status_t status = BT_STATUS_SUCCESS;
-  btif_hl_evt_cb_t evt_param;
-
-  CHECK_BTHL_INIT();
-  BTIF_TRACE_EVENT("%s app_id=%d", __func__, app_id);
-  btif_hl_display_calling_process_name();
-
-  if (btif_hl_find_app_idx(((uint8_t)app_id), &app_idx)) {
-    evt_param.unreg.app_idx = app_idx;
-    reg_counter--;
-    len = sizeof(btif_hl_unreg_t);
-    status = btif_transfer_context(btif_hl_proc_cb_evt, BTIF_HL_UNREG_APP,
-                                   (char*)&evt_param, len, NULL);
-    ASSERTC(status == BT_STATUS_SUCCESS, "context transfer failed", status);
-  } else {
-    status = BT_STATUS_FAIL;
-  }
-
-  BTIF_TRACE_DEBUG("de-reg return status=%d", status);
-  return status;
-}
-/*******************************************************************************
- *
- * Function         register_application
- *
- * Description     register an HDP application
- *
- * Returns         bt_status_t
- *
- ******************************************************************************/
-static bt_status_t register_application(bthl_reg_param_t* p_reg_param,
-                                        int* app_id) {
-  btif_hl_app_cb_t* p_acb;
-  tBTA_HL_SUP_FEATURE* p_sup;
-  tBTA_HL_MDEP_CFG* p_cfg;
-  tBTA_HL_MDEP_DATA_TYPE_CFG* p_data;
-  uint8_t app_idx = 0, i = 0;
-  bthl_mdep_cfg_t* p_mdep_cfg;
-  bt_status_t status = BT_STATUS_SUCCESS;
-  btif_hl_evt_cb_t evt_param;
-  int len;
-
-  CHECK_BTHL_INIT();
-  BTIF_TRACE_EVENT("%s", __func__);
-  btif_hl_display_calling_process_name();
-
-  if (btif_hl_get_state() == BTIF_HL_STATE_DISABLED) {
-    btif_hl_init();
-    btif_hl_set_state(BTIF_HL_STATE_ENABLING);
-    BTA_HlEnable(btif_hl_ctrl_cback);
-  }
-
-  if (!btif_hl_find_avail_app_idx(&app_idx)) {
-    BTIF_TRACE_ERROR("Unable to allocate a new application control block");
-    return BT_STATUS_FAIL;
-  }
-
-  p_acb = BTIF_HL_GET_APP_CB_PTR(app_idx);
-  p_acb->in_use = true;
-
-  p_acb->app_id = btif_hl_get_next_app_id();
-
-  if (p_reg_param->application_name != NULL)
-    strncpy(p_acb->application_name, p_reg_param->application_name,
-            BTIF_HL_APPLICATION_NAME_LEN);
-
-  if (p_reg_param->provider_name != NULL)
-    strncpy(p_acb->provider_name, p_reg_param->provider_name,
-            BTA_PROVIDER_NAME_LEN);
-
-  if (p_reg_param->srv_name != NULL)
-    strncpy(p_acb->srv_name, p_reg_param->srv_name, BTA_SERVICE_NAME_LEN);
-
-  if (p_reg_param->srv_desp != NULL)
-    strncpy(p_acb->srv_desp, p_reg_param->srv_desp, BTA_SERVICE_DESP_LEN);
-
-  p_sup = &p_acb->sup_feature;
-  p_sup->advertize_source_sdp = true;
-  p_sup->echo_cfg.max_rx_apdu_size = BTIF_HL_ECHO_MAX_TX_RX_APDU_SIZE;
-  p_sup->echo_cfg.max_tx_apdu_size = BTIF_HL_ECHO_MAX_TX_RX_APDU_SIZE;
-  p_sup->num_of_mdeps = p_reg_param->number_of_mdeps;
-
-  for (i = 0, p_mdep_cfg = p_reg_param->mdep_cfg; i < p_sup->num_of_mdeps;
-       i++, p_mdep_cfg++) {
-    p_cfg = &p_sup->mdep[i].mdep_cfg;
-    p_cfg->num_of_mdep_data_types = 1;
-    p_data = &p_cfg->data_cfg[0];
-
-    if (!btif_hl_get_bta_mdep_role(p_mdep_cfg->mdep_role,
-                                   &(p_cfg->mdep_role))) {
-      BTIF_TRACE_ERROR("Invalid mdep_role=%d", p_mdep_cfg->mdep_role);
-      status = BT_STATUS_FAIL;
-      break;
-    } else {
-      if (p_cfg->mdep_role == BTA_HL_MDEP_ROLE_SINK)
-        p_sup->app_role_mask |= BTA_HL_MDEP_ROLE_MASK_SINK;
-      else
-        p_sup->app_role_mask |= BTA_HL_MDEP_ROLE_MASK_SOURCE;
-
-      if ((p_sup->app_role_mask & BTA_HL_MDEP_ROLE_MASK_SINK) &&
-          (p_sup->app_role_mask & BTA_HL_MDEP_ROLE_MASK_SOURCE)) {
-        p_acb->dev_type = BTA_HL_DEVICE_TYPE_DUAL;
-      } else if (p_sup->app_role_mask & BTA_HL_MDEP_ROLE_MASK_SINK) {
-        p_acb->dev_type = BTA_HL_DEVICE_TYPE_SINK;
-      } else {
-        p_acb->dev_type = BTA_HL_DEVICE_TYPE_SOURCE;
-      }
-
-      p_data->data_type = (uint16_t)p_mdep_cfg->data_type;
-      p_data->max_rx_apdu_size =
-          btif_hl_get_max_rx_apdu_size(p_cfg->mdep_role, p_data->data_type);
-      p_data->max_tx_apdu_size =
-          btif_hl_get_max_tx_apdu_size(p_cfg->mdep_role, p_data->data_type);
-
-      if (p_mdep_cfg->mdep_description != NULL)
-        strncpy(p_data->desp, p_mdep_cfg->mdep_description,
-                BTA_SERVICE_DESP_LEN);
-
-      if (!btif_hl_get_bta_channel_type(p_mdep_cfg->channel_type,
-                                        &(p_acb->channel_type[i]))) {
-        BTIF_TRACE_ERROR("Invalid channel_type=%d", p_mdep_cfg->channel_type);
-        status = BT_STATUS_FAIL;
-        break;
-      }
-    }
-  }
-
-  if (status == BT_STATUS_SUCCESS) {
-    *app_id = (int)p_acb->app_id;
-    evt_param.reg.app_idx = app_idx;
-    len = sizeof(btif_hl_reg_t);
-    p_acb->reg_pending = true;
-    BTIF_TRACE_DEBUG("calling btif_transfer_context status=%d app_id=%d",
-                     status, *app_id);
-    status = btif_transfer_context(btif_hl_proc_cb_evt, BTIF_HL_REG_APP,
-                                   (char*)&evt_param, len, NULL);
-    ASSERTC(status == BT_STATUS_SUCCESS, "context transfer failed", status);
-
-  } else {
-    btif_hl_free_app_idx(app_idx);
-  }
-
-  BTIF_TRACE_DEBUG("register_application status=%d app_id=%d", status, *app_id);
-  return status;
-}
-
-/*******************************************************************************
- *
- * Function      btif_hl_save_mdl_cfg
- *
- * Description  Save the MDL configuration
- *
- * Returns      bool
- *
- ******************************************************************************/
-bool btif_hl_save_mdl_cfg(uint8_t mdep_id, uint8_t item_idx,
-                          tBTA_HL_MDL_CFG* p_mdl_cfg) {
-  btif_hl_mdl_cfg_t* p_mdl = NULL;
-  bool success = false;
-  btif_hl_app_cb_t* p_acb;
-  btif_hl_mcl_cb_t* p_mcb;
-  uint8_t app_idx, mcl_idx, len;
-  bt_status_t bt_status;
-  btif_hl_evt_cb_t evt_param;
-  int* p_channel_id;
-
-  BTIF_TRACE_DEBUG(
-      "%s mdep_id=%d item_idx=%d, local_mdep_id=%d mdl_id=0x%x dch_mode=%d",
-      __func__, mdep_id, item_idx, p_mdl_cfg->local_mdep_id, p_mdl_cfg->mdl_id,
-      p_mdl_cfg->dch_mode);
-
-  if (btif_hl_find_app_idx_using_mdepId(mdep_id, &app_idx)) {
-    p_acb = BTIF_HL_GET_APP_CB_PTR(app_idx);
-    p_mdl = BTIF_HL_GET_MDL_CFG_PTR(app_idx, item_idx);
-    p_channel_id = BTIF_HL_GET_MDL_CFG_CHANNEL_ID_PTR(app_idx, item_idx);
-    if (p_mdl) {
-      memcpy(&p_mdl->base, p_mdl_cfg, sizeof(tBTA_HL_MDL_CFG));
-      if (btif_hl_find_mcl_idx(app_idx, p_mdl->base.peer_bd_addr, &mcl_idx)) {
-        p_mcb = BTIF_HL_GET_MCL_CB_PTR(app_idx, mcl_idx);
-        if (p_mcb->pcb.in_use)
-          *p_channel_id = p_mcb->pcb.channel_id;
-        else
-          *p_channel_id = btif_hl_get_next_channel_id(p_acb->app_id);
-        p_mdl->extra.mdep_cfg_idx = p_mcb->pcb.mdep_cfg_idx;
-        p_mdl->extra.data_type =
-            p_acb->sup_feature.mdep[p_mcb->pcb.mdep_cfg_idx]
-                .mdep_cfg.data_cfg[0]
-                .data_type;
-
-        if (!btif_hl_find_peer_mdep_id(
-                p_acb->app_id, p_mcb->bd_addr,
-                p_acb->sup_feature.mdep[p_mcb->pcb.mdep_cfg_idx]
-                    .mdep_cfg.mdep_role,
-                p_acb->sup_feature.mdep[p_mcb->pcb.mdep_cfg_idx]
-                    .mdep_cfg.data_cfg[0]
-                    .data_type,
-                &p_mdl->extra.peer_mdep_id)) {
-          p_mdl->extra.peer_mdep_id = BTA_HL_INVALID_MDEP_ID;
-        }
-        BTIF_TRACE_DEBUG("%s app_idx=%d item_idx=%d mld_id=0x%x", __func__,
-                         app_idx, item_idx, p_mdl->base.mdl_id);
-        evt_param.update_mdl.app_idx = app_idx;
-        len = sizeof(btif_hl_update_mdl_t);
-        BTIF_TRACE_DEBUG("send BTIF_HL_UPDATE_MDL event app_idx=%d  ", app_idx);
-        bt_status =
-            btif_transfer_context(btif_hl_proc_cb_evt, BTIF_HL_UPDATE_MDL,
-                                  (char*)&evt_param, len, NULL);
-        if (bt_status == BT_STATUS_SUCCESS) {
-          success = true;
-        }
-        ASSERTC(bt_status == BT_STATUS_SUCCESS, "context transfer failed",
-                bt_status);
-      }
-    }
-  }
-  BTIF_TRACE_DEBUG("%s success=%d  ", __func__, success);
-
-  return success;
-}
-
-/*******************************************************************************
- *
- * Function      btif_hl_delete_mdl_cfg
- *
- * Description  Delete the MDL configuration
- *
- * Returns      bool
- *
- ******************************************************************************/
-bool btif_hl_delete_mdl_cfg(uint8_t mdep_id, uint8_t item_idx) {
-  btif_hl_mdl_cfg_t* p_mdl = NULL;
-  bool success = false;
-  uint8_t app_idx, len;
-  bt_status_t bt_status;
-  btif_hl_evt_cb_t evt_param;
-
-  if (btif_hl_find_app_idx_using_mdepId(mdep_id, &app_idx)) {
-    p_mdl = BTIF_HL_GET_MDL_CFG_PTR(app_idx, item_idx);
-    if (p_mdl) {
-      memset(p_mdl, 0, sizeof(btif_hl_mdl_cfg_t));
-      evt_param.update_mdl.app_idx = app_idx;
-      len = sizeof(btif_hl_update_mdl_t);
-      BTIF_TRACE_DEBUG("send BTIF_HL_UPDATE_MDL event app_idx=%d  ", app_idx);
-      bt_status = btif_transfer_context(btif_hl_proc_cb_evt, BTIF_HL_UPDATE_MDL,
-                                        (char*)&evt_param, len, NULL);
-      if (bt_status == BT_STATUS_SUCCESS) {
-        success = true;
-      }
-      ASSERTC(bt_status == BT_STATUS_SUCCESS, "context transfer failed",
-              bt_status);
-    }
-  }
-
-  BTIF_TRACE_DEBUG("%s success=%d  ", __func__, success);
-  return success;
-}
-
-/*******************************************************************************
- *
- * Function         init
- *
- * Description     initializes the hl interface
- *
- * Returns         bt_status_t
- *
- ******************************************************************************/
-static bt_status_t init(bthl_callbacks_t* callbacks) {
-  bt_status_t status = BT_STATUS_SUCCESS;
-
-  BTIF_TRACE_EVENT("%s", __func__);
-  btif_hl_display_calling_process_name();
-  bt_hl_callbacks_cb = *callbacks;
-  bt_hl_callbacks = &bt_hl_callbacks_cb;
-  btif_hl_soc_thread_init();
-  reg_counter = 0;
-  return status;
-}
-/*******************************************************************************
- *
- * Function         cleanup
- *
- * Description      Closes the HL interface
- *
- * Returns          void
- *
- ******************************************************************************/
-static void cleanup(void) {
-  BTIF_TRACE_EVENT("%s", __func__);
-  btif_hl_display_calling_process_name();
-  if (bt_hl_callbacks) {
-    btif_disable_service(BTA_HDP_SERVICE_ID);
-    bt_hl_callbacks = NULL;
-    reg_counter = 0;
-  }
-
-  btif_hl_disable();
-  btif_hl_close_select_thread();
-}
-
-static const bthl_interface_t bthlInterface = {
-    sizeof(bthl_interface_t),
-    init,
-    register_application,
-    unregister_application,
-    connect_channel,
-    destroy_channel,
-    cleanup,
-};
-
-/*******************************************************************************
- *
- * Function         btif_hl_get_interface
- *
- * Description      Get the hl callback interface
- *
- * Returns          bthf_interface_t
- *
- ******************************************************************************/
-const bthl_interface_t* btif_hl_get_interface() {
-  BTIF_TRACE_EVENT("%s", __func__);
-  return &bthlInterface;
-}
-
-/*******************************************************************************
- *
- * Function btif_hl_update_maxfd
- *
- * Description Update the max fd if the input fd is greater than the current max
- * fd
- *
- * Returns int
- *
- ******************************************************************************/
-int btif_hl_update_maxfd(int max_org_s) {
-  int maxfd = max_org_s;
-
-  BTIF_TRACE_DEBUG("btif_hl_update_maxfd max_org_s= %d", max_org_s);
-  for (const list_node_t* node = list_begin(soc_queue);
-       node != list_end(soc_queue); node = list_next(node)) {
-    btif_hl_soc_cb_t* p_scb = (btif_hl_soc_cb_t*)list_node(node);
-    if (maxfd < p_scb->max_s) {
-      maxfd = p_scb->max_s;
-      BTIF_TRACE_DEBUG("btif_hl_update_maxfd maxfd=%d", maxfd);
-    }
-  }
-
-  BTIF_TRACE_DEBUG("btif_hl_update_maxfd final *p_max_s=%d", maxfd);
-  return maxfd;
-}
-/*******************************************************************************
- *
- * Function btif_hl_get_socket_state
- *
- * Description get socket state
- *
- * Returns btif_hl_soc_state_t
- *
- ******************************************************************************/
-btif_hl_soc_state_t btif_hl_get_socket_state(btif_hl_soc_cb_t* p_scb) {
-  BTIF_TRACE_DEBUG("btif_hl_get_socket_state state=%d", p_scb->state);
-  return p_scb->state;
-}
-/*******************************************************************************
- *
- * Function btif_hl_set_socket_state
- *
- * Description set socket state
- *
- * Returns void
- *
- ******************************************************************************/
-void btif_hl_set_socket_state(btif_hl_soc_cb_t* p_scb,
-                              btif_hl_soc_state_t new_state) {
-  BTIF_TRACE_DEBUG("btif_hl_set_socket_state %d---->%d", p_scb->state,
-                   new_state);
-  p_scb->state = new_state;
-}
-/*******************************************************************************
- *
- * Function btif_hl_release_mcl_sockets
- *
- * Description Release all sockets on the MCL
- *
- * Returns void
- *
- ******************************************************************************/
-void btif_hl_release_mcl_sockets(uint8_t app_idx, uint8_t mcl_idx) {
-  uint8_t i;
-  btif_hl_mdl_cb_t* p_dcb;
-  bool found = false;
-  BTIF_TRACE_DEBUG("%s", __func__);
-  for (i = 0; i < BTA_HL_NUM_MDLS_PER_MCL; i++) {
-    p_dcb = BTIF_HL_GET_MDL_CB_PTR(app_idx, mcl_idx, i);
-    if (p_dcb && p_dcb->in_use && p_dcb->p_scb) {
-      BTIF_TRACE_DEBUG("found socket for app_idx=%d mcl_id=%d, mdl_idx=%d",
-                       app_idx, mcl_idx, i);
-      btif_hl_set_socket_state(p_dcb->p_scb, BTIF_HL_SOC_STATE_W4_REL);
-      p_dcb->p_scb = NULL;
-      found = true;
-    }
-  }
-  if (found) btif_hl_select_close_connected();
-}
-/*******************************************************************************
- *
- * Function btif_hl_release_socket
- *
- * Description release a specified socket
- *
- * Returns void
- *
- ******************************************************************************/
-void btif_hl_release_socket(uint8_t app_idx, uint8_t mcl_idx, uint8_t mdl_idx) {
-  btif_hl_soc_cb_t* p_scb = NULL;
-  btif_hl_mdl_cb_t* p_dcb = BTIF_HL_GET_MDL_CB_PTR(app_idx, mcl_idx, mdl_idx);
-
-  BTIF_TRACE_DEBUG("%s", __func__);
-  BTIF_TRACE_DEBUG("app_idx=%d mcl_idx=%d mdl_idx=%d", app_idx, mcl_idx,
-                   mdl_idx);
-
-  if (p_dcb && p_dcb->p_scb) {
-    p_scb = p_dcb->p_scb;
-    btif_hl_set_socket_state(p_scb, BTIF_HL_SOC_STATE_W4_REL);
-    p_dcb->p_scb = NULL;
-    btif_hl_select_close_connected();
-  }
-}
-/*******************************************************************************
- *
- * Function btif_hl_create_socket
- *
- * Description create a socket
- *
- * Returns bool
- *
- ******************************************************************************/
-bool btif_hl_create_socket(uint8_t app_idx, uint8_t mcl_idx, uint8_t mdl_idx) {
-  btif_hl_mcl_cb_t* p_mcb = BTIF_HL_GET_MCL_CB_PTR(app_idx, mcl_idx);
-  btif_hl_mdl_cb_t* p_dcb = BTIF_HL_GET_MDL_CB_PTR(app_idx, mcl_idx, mdl_idx);
-  bool status = false;
-
-  BTIF_TRACE_DEBUG("%s", __func__);
-
-  if (p_dcb) {
-    btif_hl_soc_cb_t* p_scb =
-        (btif_hl_soc_cb_t*)osi_malloc(sizeof(btif_hl_soc_cb_t));
-    if (socketpair(AF_UNIX, SOCK_STREAM, 0, p_scb->socket_id) >= 0) {
-      BTIF_TRACE_DEBUG("socket id[0]=%d id[1]=%d", p_scb->socket_id[0],
-                       p_scb->socket_id[1]);
-      p_dcb->p_scb = p_scb;
-      p_scb->app_idx = app_idx;
-      p_scb->mcl_idx = mcl_idx;
-      p_scb->mdl_idx = mdl_idx;
-      p_scb->channel_id = p_dcb->channel_id;
-      p_scb->mdep_cfg_idx = p_dcb->local_mdep_cfg_idx;
-      p_scb->bd_addr = p_mcb->bd_addr;
-      btif_hl_set_socket_state(p_scb, BTIF_HL_SOC_STATE_W4_ADD);
-      p_scb->max_s = p_scb->socket_id[1];
-      list_append(soc_queue, (void*)p_scb);
-      btif_hl_select_wakeup();
-      status = true;
-    } else {
-      osi_free_and_reset((void**)&p_scb);
-    }
-  }
-
-  BTIF_TRACE_DEBUG("%s status=%d", __func__, status);
-  return status;
-}
-/*******************************************************************************
- *
- * Function btif_hl_add_socket_to_set
- *
- * Description Add a socket
- *
- * Returns void
- *
- ******************************************************************************/
-void btif_hl_add_socket_to_set(fd_set* p_org_set) {
-  btif_hl_mdl_cb_t* p_dcb = NULL;
-  btif_hl_mcl_cb_t* p_mcb = NULL;
-  btif_hl_app_cb_t* p_acb = NULL;
-  btif_hl_evt_cb_t evt_param;
-  bt_status_t status;
-  int len;
-
-  BTIF_TRACE_DEBUG("entering %s", __func__);
-
-  for (const list_node_t* node = list_begin(soc_queue);
-       node != list_end(soc_queue); node = list_next(node)) {
-    btif_hl_soc_cb_t* p_scb = (btif_hl_soc_cb_t*)list_node(node);
-
-    BTIF_TRACE_DEBUG("btif_hl_add_socket_to_set first p_scb=0x%x", p_scb);
-    if (btif_hl_get_socket_state(p_scb) == BTIF_HL_SOC_STATE_W4_ADD) {
-      btif_hl_set_socket_state(p_scb, BTIF_HL_SOC_STATE_W4_READ);
-      FD_SET(p_scb->socket_id[1], p_org_set);
-      BTIF_TRACE_DEBUG("found and set socket_id=%d is_set=%d",
-                       p_scb->socket_id[1],
-                       FD_ISSET(p_scb->socket_id[1], p_org_set));
-      p_mcb = BTIF_HL_GET_MCL_CB_PTR(p_scb->app_idx, p_scb->mcl_idx);
-      p_dcb = BTIF_HL_GET_MDL_CB_PTR(p_scb->app_idx, p_scb->mcl_idx,
-                                     p_scb->mdl_idx);
-      p_acb = BTIF_HL_GET_APP_CB_PTR(p_scb->app_idx);
-      if (p_mcb && p_dcb) {
-        btif_hl_stop_timer_using_handle(p_mcb->mcl_handle);
-        evt_param.chan_cb.app_id = p_acb->app_id;
-        evt_param.chan_cb.bd_addr = p_mcb->bd_addr;
-        evt_param.chan_cb.channel_id = p_dcb->channel_id;
-        evt_param.chan_cb.fd = p_scb->socket_id[0];
-        evt_param.chan_cb.mdep_cfg_index = (int)p_dcb->local_mdep_cfg_idx;
-        evt_param.chan_cb.cb_state = BTIF_HL_CHAN_CB_STATE_CONNECTED_PENDING;
-        len = sizeof(btif_hl_send_chan_state_cb_t);
-        status = btif_transfer_context(btif_hl_proc_cb_evt,
-                                       BTIF_HL_SEND_CONNECTED_CB,
-                                       (char*)&evt_param, len, NULL);
-        ASSERTC(status == BT_STATUS_SUCCESS, "context transfer failed", status);
-      }
-    }
-  }
-  BTIF_TRACE_DEBUG("leaving %s", __func__);
-}
-
-/*******************************************************************************
- *
- * Function btif_hl_close_socket
- *
- * Description close a socket
- *
- * Returns void
- *
- ******************************************************************************/
-void btif_hl_close_socket(fd_set* p_org_set) {
-  BTIF_TRACE_DEBUG("entering %s", __func__);
-  for (const list_node_t* node = list_begin(soc_queue);
-       node != list_end(soc_queue); node = list_next(node)) {
-    btif_hl_soc_cb_t* p_scb = (btif_hl_soc_cb_t*)list_node(node);
-    if (btif_hl_get_socket_state(p_scb) == BTIF_HL_SOC_STATE_W4_REL) {
-      BTIF_TRACE_DEBUG("app_idx=%d mcl_id=%d, mdl_idx=%d", p_scb->app_idx,
-                       p_scb->mcl_idx, p_scb->mdl_idx);
-      btif_hl_set_socket_state(p_scb, BTIF_HL_SOC_STATE_IDLE);
-      if (p_scb->socket_id[1] != -1) {
-        FD_CLR(p_scb->socket_id[1], p_org_set);
-        shutdown(p_scb->socket_id[1], SHUT_RDWR);
-        close(p_scb->socket_id[1]);
-
-        btif_hl_evt_cb_t evt_param;
-        evt_param.chan_cb.app_id = (int)btif_hl_get_app_id(p_scb->channel_id);
-        evt_param.chan_cb.bd_addr = p_scb->bd_addr;
-        evt_param.chan_cb.channel_id = p_scb->channel_id;
-        evt_param.chan_cb.fd = p_scb->socket_id[0];
-        evt_param.chan_cb.mdep_cfg_index = (int)p_scb->mdep_cfg_idx;
-        evt_param.chan_cb.cb_state = BTIF_HL_CHAN_CB_STATE_DISCONNECTED_PENDING;
-        int len = sizeof(btif_hl_send_chan_state_cb_t);
-        bt_status_t status = btif_transfer_context(
-            btif_hl_proc_cb_evt, BTIF_HL_SEND_DISCONNECTED_CB,
-            (char*)&evt_param, len, NULL);
-        ASSERTC(status == BT_STATUS_SUCCESS, "context transfer failed", status);
-      }
-    }
-  }
-
-  for (const list_node_t* node = list_begin(soc_queue);
-       node != list_end(soc_queue);) {
-    // We may mutate this list so we need keep track of
-    // the current node and only remove items behind.
-    btif_hl_soc_cb_t* p_scb = (btif_hl_soc_cb_t*)list_node(node);
-    BTIF_TRACE_DEBUG("p_scb=0x%x", p_scb);
-    node = list_next(node);
-    if (btif_hl_get_socket_state(p_scb) == BTIF_HL_SOC_STATE_IDLE) {
-      btif_hl_mdl_cb_t* p_dcb = BTIF_HL_GET_MDL_CB_PTR(
-          p_scb->app_idx, p_scb->mcl_idx, p_scb->mdl_idx);
-      BTIF_TRACE_DEBUG(
-          "idle socket app_idx=%d mcl_id=%d, mdl_idx=%d p_dcb->in_use=%d",
-          p_scb->app_idx, p_scb->mcl_idx, p_scb->mdl_idx, p_dcb->in_use);
-      list_remove(soc_queue, p_scb);
-      osi_free(p_scb);
-      p_dcb->p_scb = NULL;
-    }
-  }
-  BTIF_TRACE_DEBUG("leaving %s", __func__);
-}
-
-/*******************************************************************************
- *
- * Function btif_hl_select_wakeup_callback
- *
- * Description Select wakup callback to add or close a socket
- *
- * Returns void
- *
- ******************************************************************************/
-
-void btif_hl_select_wakeup_callback(fd_set* p_org_set, int wakeup_signal) {
-  BTIF_TRACE_DEBUG("entering %s wakeup_signal=0x%04x", __func__, wakeup_signal);
-
-  if (wakeup_signal == btif_hl_signal_select_wakeup) {
-    btif_hl_add_socket_to_set(p_org_set);
-  } else if (wakeup_signal == btif_hl_signal_select_close_connected) {
-    btif_hl_close_socket(p_org_set);
-  }
-  BTIF_TRACE_DEBUG("leaving %s", __func__);
-}
-
-/*******************************************************************************
- *
- * Function btif_hl_select_monitor_callback
- *
- * Description Select monitor callback to check pending socket actions
- *
- * Returns void
- *
- ******************************************************************************/
-void btif_hl_select_monitor_callback(fd_set* p_cur_set,
-                                     UNUSED_ATTR fd_set* p_org_set) {
-  BTIF_TRACE_DEBUG("entering %s", __func__);
-
-  for (const list_node_t* node = list_begin(soc_queue);
-       node != list_end(soc_queue); node = list_next(node)) {
-    btif_hl_soc_cb_t* p_scb = (btif_hl_soc_cb_t*)list_node(node);
-    if (btif_hl_get_socket_state(p_scb) == BTIF_HL_SOC_STATE_W4_READ) {
-      if (FD_ISSET(p_scb->socket_id[1], p_cur_set)) {
-        BTIF_TRACE_DEBUG("read data state= BTIF_HL_SOC_STATE_W4_READ");
-        btif_hl_mdl_cb_t* p_dcb = BTIF_HL_GET_MDL_CB_PTR(
-            p_scb->app_idx, p_scb->mcl_idx, p_scb->mdl_idx);
-        CHECK(p_dcb != NULL);
-        if (p_dcb->p_tx_pkt) {
-          BTIF_TRACE_ERROR(
-              "Rcv new pkt but the last pkt is still not been"
-              "  sent tx_size=%d",
-              p_dcb->tx_size);
-          osi_free_and_reset((void**)&p_dcb->p_tx_pkt);
-        }
-        p_dcb->p_tx_pkt = (uint8_t*)osi_malloc(p_dcb->mtu);
-        ssize_t r;
-        OSI_NO_INTR(r = recv(p_scb->socket_id[1], p_dcb->p_tx_pkt, p_dcb->mtu,
-                             MSG_DONTWAIT));
-        if (r > 0) {
-          BTIF_TRACE_DEBUG("btif_hl_select_monitor_callback send data r =%d",
-                           r);
-          p_dcb->tx_size = r;
-          BTIF_TRACE_DEBUG(
-              "btif_hl_select_monitor_callback send data tx_size=%d",
-              p_dcb->tx_size);
-          BTA_HlSendData(p_dcb->mdl_handle, p_dcb->tx_size);
-        } else {
-          BTIF_TRACE_DEBUG(
-              "btif_hl_select_monitor_callback receive failed r=%d", r);
-          BTA_HlDchClose(p_dcb->mdl_handle);
-        }
-      }
-    }
-  }
-
-  if (list_is_empty(soc_queue))
-    BTIF_TRACE_DEBUG("btif_hl_select_monitor_queue is empty");
-
-  BTIF_TRACE_DEBUG("leaving %s", __func__);
-}
-
-/*******************************************************************************
- *
- * Function btif_hl_select_wakeup_init
- *
- * Description select loop wakup init
- *
- * Returns int
- *
- ******************************************************************************/
-static inline int btif_hl_select_wakeup_init(fd_set* set) {
-  BTIF_TRACE_DEBUG("%s", __func__);
-  if (signal_fds[0] == -1 &&
-      socketpair(AF_UNIX, SOCK_STREAM, 0, signal_fds) < 0) {
-    BTIF_TRACE_ERROR("socketpair failed: %s", strerror(errno));
-    return -1;
-  }
-
-  BTIF_TRACE_DEBUG(
-      "btif_hl_select_wakeup_init signal_fds[0]=%d signal_fds[1]=%d",
-      signal_fds[0], signal_fds[1]);
-  FD_SET(signal_fds[0], set);
-
-  return signal_fds[0];
-}
-
-/*******************************************************************************
- *
- * Function btif_hl_select_wakeup
- *
- * Description send a signal to wakupo the select loop
- *
- * Returns int
- *
- ******************************************************************************/
-static inline int btif_hl_select_wakeup(void) {
-  char sig_on = btif_hl_signal_select_wakeup;
-
-  BTIF_TRACE_DEBUG("%s", __func__);
-
-  ssize_t ret;
-  OSI_NO_INTR(ret = send(signal_fds[1], &sig_on, sizeof(sig_on), 0));
-
-  return (int)ret;
-}
-
-/*******************************************************************************
- *
- * Function btif_hl_select_close_connected
- *
- * Description send a signal to close a socket
- *
- * Returns int
- *
- ******************************************************************************/
-static inline int btif_hl_select_close_connected(void) {
-  char sig_on = btif_hl_signal_select_close_connected;
-
-  BTIF_TRACE_DEBUG("%s", __func__);
-
-  ssize_t ret;
-  OSI_NO_INTR(ret = send(signal_fds[1], &sig_on, sizeof(sig_on), 0));
-
-  return (int)ret;
-}
-
-/*******************************************************************************
- *
- * Function btif_hl_close_select_thread
- *
- * Description send signal to close the thread and then close all signal FDs
- *
- * Returns int
- *
- ******************************************************************************/
-static inline int btif_hl_close_select_thread(void) {
-  ssize_t result = 0;
-  char sig_on = btif_hl_signal_select_exit;
-
-  BTIF_TRACE_DEBUG("%", __func__);
-
-  OSI_NO_INTR(result = send(signal_fds[1], &sig_on, sizeof(sig_on), 0));
-
-  if (btif_is_enabled()) {
-    /* Wait for the select_thread_id to exit if BT is still enabled
-    and only this profile getting  cleaned up*/
-    if (select_thread_id != -1) {
-      pthread_join(select_thread_id, NULL);
-      select_thread_id = -1;
-    }
-  }
-  list_free(soc_queue);
-  soc_queue = NULL;
-
-  return (int)result;
-}
-
-/*******************************************************************************
- *
- * Function btif_hl_select_wake_reset
- *
- * Description clear the received signal for the select loop
- *
- * Returns int
- *
- ******************************************************************************/
-static inline int btif_hl_select_wake_reset(void) {
-  char sig_recv = 0;
-
-  BTIF_TRACE_DEBUG("%s", __func__);
-
-  ssize_t r;
-  OSI_NO_INTR(
-      r = recv(signal_fds[0], &sig_recv, sizeof(sig_recv), MSG_WAITALL));
-
-  return (int)sig_recv;
-}
-/*******************************************************************************
- *
- * Function btif_hl_select_wake_signaled
- *
- * Description check whether a fd is set or not
- *
- * Returns int
- *
- ******************************************************************************/
-static inline int btif_hl_select_wake_signaled(fd_set* set) {
-  BTIF_TRACE_DEBUG("btif_hl_select_wake_signaled");
-  return FD_ISSET(signal_fds[0], set);
-}
-/*******************************************************************************
- *
- * Function btif_hl_thread_cleanup
- *
- * Description  shut down and clean up the select loop
- *
- * Returns void
- *
- ******************************************************************************/
-static void btif_hl_thread_cleanup() {
-  if (listen_s != -1) close(listen_s);
-  if (connected_s != -1) {
-    shutdown(connected_s, SHUT_RDWR);
-    close(connected_s);
-  }
-  listen_s = connected_s = -1;
-  BTIF_TRACE_DEBUG("hl thread cleanup");
-}
-/*******************************************************************************
- *
- * Function btif_hl_select_thread
- *
- * Description the select loop
- *
- * Returns void
- *
- ******************************************************************************/
-static void* btif_hl_select_thread(UNUSED_ATTR void* arg) {
-  fd_set org_set, curr_set;
-  int r, max_curr_s, max_org_s;
-
-  BTIF_TRACE_DEBUG("entered btif_hl_select_thread");
-  FD_ZERO(&org_set);
-  max_org_s = btif_hl_select_wakeup_init(&org_set);
-  BTIF_TRACE_DEBUG("max_s=%d ", max_org_s);
-
-  for (;;) {
-    r = 0;
-    BTIF_TRACE_DEBUG("set curr_set = org_set ");
-    curr_set = org_set;
-    max_curr_s = max_org_s;
-    int ret = select((max_curr_s + 1), &curr_set, NULL, NULL, NULL);
-    BTIF_TRACE_DEBUG("select unblocked ret=%d", ret);
-    if (ret == -1) {
-      if (errno == EINTR) continue;
-      BTIF_TRACE_DEBUG("select() ret -1, exit the thread");
-      btif_hl_thread_cleanup();
-      select_thread_id = -1;
-      return 0;
-    } else if (ret) {
-      BTIF_TRACE_DEBUG("btif_hl_select_wake_signaled, signal ret=%d", ret);
-      if (btif_hl_select_wake_signaled(&curr_set)) {
-        r = btif_hl_select_wake_reset();
-        BTIF_TRACE_DEBUG("btif_hl_select_wake_signaled, signal:%d", r);
-        if (r == btif_hl_signal_select_wakeup ||
-            r == btif_hl_signal_select_close_connected) {
-          btif_hl_select_wakeup_callback(&org_set, r);
-        } else if (r == btif_hl_signal_select_exit) {
-          btif_hl_thread_cleanup();
-          BTIF_TRACE_DEBUG(
-              "Exit hl_select_thread for btif_hl_signal_select_exit");
-          return 0;
-        }
-      }
-
-      btif_hl_select_monitor_callback(&curr_set, &org_set);
-      max_org_s = btif_hl_update_maxfd(max_org_s);
-    } else
-      BTIF_TRACE_DEBUG("no data, select ret: %d\n", ret);
-  }
-  BTIF_TRACE_DEBUG("leaving hl_select_thread");
-  return 0;
-}
-
-/*******************************************************************************
- *
- * Function create_thread
- *
- * Description creat a select loop
- *
- * Returns pthread_t
- *
- ******************************************************************************/
-static inline pthread_t create_thread(void* (*start_routine)(void*),
-                                      void* arg) {
-  BTIF_TRACE_DEBUG("create_thread: entered");
-  pthread_attr_t thread_attr;
-
-  pthread_attr_init(&thread_attr);
-  pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_JOINABLE);
-  pthread_t thread_id = -1;
-  if (pthread_create(&thread_id, &thread_attr, start_routine, arg) != 0) {
-    BTIF_TRACE_ERROR("pthread_create : %s", strerror(errno));
-    return -1;
-  }
-  BTIF_TRACE_DEBUG("create_thread: thread created successfully");
-  return thread_id;
-}
-
-/*******************************************************************************
- *
- * Function         btif_hl_soc_thread_init
- *
- * Description      HL select loop init function.
- *
- * Returns          void
- *
- ******************************************************************************/
-void btif_hl_soc_thread_init(void) {
-  BTIF_TRACE_DEBUG("%s", __func__);
-  soc_queue = list_new(NULL);
-  if (soc_queue == NULL)
-    LOG_ERROR(LOG_TAG, "%s unable to allocate resources for thread", __func__);
-  select_thread_id = create_thread(btif_hl_select_thread, NULL);
-}
-/*******************************************************************************
- *
- * Function btif_hl_load_mdl_config
- *
- * Description load the MDL configuation from the application control block
- *
- * Returns bool
- *
- ******************************************************************************/
-bool btif_hl_load_mdl_config(uint8_t app_id, uint8_t buffer_size,
-                             tBTA_HL_MDL_CFG* p_mdl_buf) {
-  uint8_t app_idx;
-  bool result = false;
-  btif_hl_app_cb_t* p_acb;
-  tBTA_HL_MDL_CFG* p;
-  int i;
-  BTIF_TRACE_DEBUG("%s", __func__);
-
-  if (btif_hl_find_app_idx(app_id, &app_idx)) {
-    p_acb = BTIF_HL_GET_APP_CB_PTR(app_idx);
-    for (i = 0, p = p_mdl_buf; i < buffer_size; i++, p++) {
-      memcpy(p, &p_acb->mdl_cfg[i].base, sizeof(tBTA_HL_MDL_CFG));
-    }
-    result = true;
-  }
-
-  BTIF_TRACE_DEBUG("result=%d", result);
-  return result;
-}
diff --git a/btif/src/btif_keystore.cc b/btif/src/btif_keystore.cc
index 1bf2c0f..86194b7 100644
--- a/btif/src/btif_keystore.cc
+++ b/btif/src/btif_keystore.cc
@@ -1,148 +1,110 @@
-/******************************************************************************
+/*
+ * Copyright 2020 The Android Open Source Project
  *
- *  Copyright 2019 Google, Inc.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
  *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at:
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- *
- ******************************************************************************/
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 
-#include "btif_keystore.h"
-#include "keystore_client.pb.h"
-#include "string.h"
+/* BluetoothKeystore Interface */
 
-#include <base/files/file_util.h>
+#include <btif_common.h>
+#include <btif_keystore.h>
+
+#include <base/bind.h>
+#include <base/location.h>
 #include <base/logging.h>
-#include <base/strings/string_number_conversions.h>
-#include <base/strings/string_split.h>
-#include <base/strings/string_util.h>
-#include <base/strings/utf_string_conversions.h>
-#include <sys/stat.h>
+#include <hardware/bluetooth.h>
+#include <map>
 
-using namespace keystore;
-using namespace bluetooth;
-
-const std::string kKeyStore = "bluetooth-key-encrypted";
-constexpr uint32_t kAESKeySize = 256;     // bits
-constexpr uint32_t kMACOutputSize = 128;  // bits
+using base::Bind;
+using base::Unretained;
+using bluetooth::bluetooth_keystore::BluetoothKeystoreCallbacks;
+using bluetooth::bluetooth_keystore::BluetoothKeystoreInterface;
 
 namespace bluetooth {
+namespace bluetooth_keystore {
+class BluetoothKeystoreInterfaceImpl;
+std::unique_ptr<BluetoothKeystoreInterface> bluetoothKeystoreInstance;
 
-BtifKeystore::BtifKeystore(keystore::KeystoreClient* keystore_client)
-    : keystore_client_(keystore_client) {}
+class BluetoothKeystoreInterfaceImpl
+    : public bluetooth::bluetooth_keystore::BluetoothKeystoreInterface,
+      public bluetooth::bluetooth_keystore::BluetoothKeystoreCallbacks {
+  ~BluetoothKeystoreInterfaceImpl() override = default;
 
-std::string BtifKeystore::Encrypt(const std::string& data, int32_t flags) {
-  std::lock_guard<std::mutex> lock(api_mutex_);
-  std::string output;
-  if (data.empty()) {
-    LOG(ERROR) << __func__ << ": empty data";
-    return output;
-  }
-  if (!GenerateKey(kKeyStore, flags)) {
-    return output;
+  void init(BluetoothKeystoreCallbacks* callbacks) override {
+    VLOG(2) << __func__;
+    this->callbacks = callbacks;
   }
 
-  AuthorizationSetBuilder encrypt_params;
-  encrypt_params.Authorization(TAG_BLOCK_MODE, BlockMode::GCM)
-      .Authorization(TAG_MAC_LENGTH, kMACOutputSize)
-      .Padding(PaddingMode::NONE);
-  AuthorizationSet output_params;
-  std::string raw_encrypted_data;
-  if (!keystore_client_->oneShotOperation(
-          KeyPurpose::ENCRYPT, kKeyStore, encrypt_params, data,
-          std::string() /* signature_to_verify */, &output_params,
-          &raw_encrypted_data)) {
-    LOG(ERROR) << __func__ << ": AES operation failed.";
-    return output;
-  }
-  auto init_vector_blob = output_params.GetTagValue(TAG_NONCE);
-  if (!init_vector_blob.isOk()) {
-    LOG(ERROR) << __func__ << ": Missing initialization vector.";
-    return output;
-  }
+  void set_encrypt_key_or_remove_key(std::string prefix,
+                                     std::string decryptedString) override {
+    VLOG(2) << __func__ << " prefix: " << prefix;
 
-  const hidl_vec<uint8_t>& value = init_vector_blob.value();
-  std::string init_vector =
-      std::string(reinterpret_cast<const std::string::value_type*>(&value[0]),
-                  value.size());
-
-  if (memcmp(&init_vector_blob, &init_vector, init_vector.length()) == 0) {
-    LOG(ERROR) << __func__
-               << ": Protobuf nonce data doesn't match the actual nonce.";
-  }
-
-  EncryptedData protobuf;
-  protobuf.set_init_vector(init_vector);
-  protobuf.set_encrypted_data(raw_encrypted_data);
-  if (!protobuf.SerializeToString(&output)) {
-    LOG(ERROR) << __func__ << ": Failed to serialize EncryptedData protobuf.";
-  }
-  return output;
-}
-
-std::string BtifKeystore::Decrypt(const std::string& input) {
-  std::lock_guard<std::mutex> lock(api_mutex_);
-  if (input.empty()) {
-    LOG(ERROR) << __func__ << ": empty input data";
-    return "";
-  }
-  std::string output;
-  EncryptedData protobuf;
-  if (!protobuf.ParseFromString(input)) {
-    LOG(ERROR) << __func__ << ": Failed to parse EncryptedData protobuf.";
-    return output;
-  }
-  AuthorizationSetBuilder encrypt_params;
-  encrypt_params.Authorization(TAG_BLOCK_MODE, BlockMode::GCM)
-      .Authorization(TAG_MAC_LENGTH, kMACOutputSize)
-      .Authorization(TAG_NONCE, protobuf.init_vector().data(),
-                     protobuf.init_vector().size())
-      .Padding(PaddingMode::NONE);
-  AuthorizationSet output_params;
-  if (!keystore_client_->oneShotOperation(
-          KeyPurpose::DECRYPT, kKeyStore, encrypt_params,
-          protobuf.encrypted_data(), std::string() /* signature_to_verify */,
-          &output_params, &output)) {
-    LOG(ERROR) << __func__ << ": AES operation failed.";
-  }
-  return output;
-}
-
-bool BtifKeystore::GenerateKey(const std::string& name, int32_t flags) {
-  if (!DoesKeyExist()) {
-    AuthorizationSetBuilder params;
-    params.AesEncryptionKey(kAESKeySize)
-        .Authorization(TAG_NO_AUTH_REQUIRED)
-        .Authorization(TAG_BLOCK_MODE, BlockMode::GCM)
-        .Authorization(TAG_PURPOSE, KeyPurpose::ENCRYPT)
-        .Authorization(TAG_PURPOSE, KeyPurpose::DECRYPT)
-        .Padding(PaddingMode::NONE)
-        .Authorization(TAG_MIN_MAC_LENGTH, kMACOutputSize);
-    AuthorizationSet hardware_enforced_characteristics;
-    AuthorizationSet software_enforced_characteristics;
-    auto result = keystore_client_->generateKey(
-        name, params, flags, &hardware_enforced_characteristics,
-        &software_enforced_characteristics);
-    if (!result.isOk()) {
-      LOG(FATAL) << __func__ << "Failed to generate key: name: " << name
-                 << ", error code: " << result.getErrorCode();
-      return false;
+    if (!callbacks) {
+      LOG(WARNING) << __func__ << " callback isn't ready. prefix: " << prefix;
+      return;
     }
+
+    // Save the value into a map.
+    key_map[prefix] = decryptedString;
+
+    do_in_jni_thread(
+        base::Bind(&bluetooth::bluetooth_keystore::BluetoothKeystoreCallbacks::
+                       set_encrypt_key_or_remove_key,
+                   base::Unretained(callbacks), prefix, decryptedString));
   }
-  return true;
+
+  std::string get_key(std::string prefix) override {
+    VLOG(2) << __func__ << " prefix: " << prefix;
+
+    if (!callbacks) {
+      LOG(WARNING) << __func__ << " callback isn't ready. prefix: " << prefix;
+      return "";
+    }
+
+    std::string decryptedString;
+    // try to find the key.
+    std::map<std::string, std::string>::iterator iter = key_map.find(prefix);
+    if (iter == key_map.end()) {
+      decryptedString = callbacks->get_key(prefix);
+      // Save the value into a map.
+      key_map[prefix] = decryptedString;
+      VLOG(2) << __func__ << ": get key from bluetoothkeystore.";
+    } else {
+      decryptedString = iter->second;
+    }
+    return decryptedString;
+  }
+
+  void clear_map() override {
+    VLOG(2) << __func__;
+
+    std::map<std::string, std::string> empty_map;
+    key_map.swap(empty_map);
+    key_map.clear();
+  }
+
+ private:
+  BluetoothKeystoreCallbacks* callbacks = nullptr;
+  std::map<std::string, std::string> key_map;
+};
+
+BluetoothKeystoreInterface* getBluetoothKeystoreInterface() {
+  if (!bluetoothKeystoreInstance) {
+    bluetoothKeystoreInstance.reset(new BluetoothKeystoreInterfaceImpl());
+  }
+
+  return bluetoothKeystoreInstance.get();
 }
 
-bool BtifKeystore::DoesKeyExist() {
-  return keystore_client_->doesKeyExist(kKeyStore);
-}
-
+}  // namespace bluetooth_keystore
 }  // namespace bluetooth
diff --git a/btif/src/btif_rc.cc b/btif/src/btif_rc.cc
old mode 100644
new mode 100755
index 575d83e..9fc221e
--- a/btif/src/btif_rc.cc
+++ b/btif/src/btif_rc.cc
@@ -174,6 +174,7 @@
   bool br_connected;  // Browsing channel.
   uint8_t rc_handle;
   tBTA_AV_FEAT rc_features;
+  uint16_t rc_cover_art_psm;  // AVRCP-BIP psm
   btrc_connection_state_t rc_state;
   RawAddress rc_addr;
   uint16_t rc_pending_play;
@@ -294,7 +295,7 @@
                                                  tAVRC_RSP* p_rsp);
 static void cleanup_btrc_folder_items(btrc_folder_items_t* btrc_items,
                                       uint8_t item_count);
-static void handle_get_elem_attr_response(tBTA_AV_META_MSG* pmeta_msg,
+static void handle_get_metadata_attr_response(tBTA_AV_META_MSG* pmeta_msg,
                                           tAVRC_GET_ATTRS_RSP* p_rsp);
 static void handle_set_app_attr_val_response(tBTA_AV_META_MSG* pmeta_msg,
                                              tAVRC_RSP* p_rsp);
@@ -306,9 +307,16 @@
 static bt_status_t register_notification_cmd(uint8_t label, uint8_t event_id,
                                              uint32_t event_value,
                                              btif_rc_device_cb_t* p_dev);
+static bt_status_t get_metadata_attribute_cmd(uint8_t num_attribute,
+                                              const uint32_t* p_attr_ids,
+                                              btif_rc_device_cb_t* p_dev);
 static bt_status_t get_element_attribute_cmd(uint8_t num_attribute,
-                                             uint32_t* p_attr_ids,
+                                             const uint32_t* p_attr_ids,
                                              btif_rc_device_cb_t* p_dev);
+static bt_status_t get_item_attribute_cmd(uint64_t uid, int scope,
+                                           uint8_t num_attribute,
+                                           const uint32_t* p_attr_ids,
+                                           btif_rc_device_cb_t* p_dev);
 static bt_status_t getcapabilities_cmd(uint8_t cap_id,
                                        btif_rc_device_cb_t* p_dev);
 static bt_status_t list_player_app_setting_attrib_cmd(
@@ -346,6 +354,26 @@
 static btrc_callbacks_t* bt_rc_callbacks = NULL;
 static btrc_ctrl_callbacks_t* bt_rc_ctrl_callbacks = NULL;
 
+// List of desired media attribute keys to request by default
+static const uint32_t media_attr_list[] = {
+      AVRC_MEDIA_ATTR_ID_TITLE,       AVRC_MEDIA_ATTR_ID_ARTIST,
+      AVRC_MEDIA_ATTR_ID_ALBUM,       AVRC_MEDIA_ATTR_ID_TRACK_NUM,
+      AVRC_MEDIA_ATTR_ID_NUM_TRACKS,  AVRC_MEDIA_ATTR_ID_GENRE,
+      AVRC_MEDIA_ATTR_ID_PLAYING_TIME,
+      AVRC_MEDIA_ATTR_ID_COVER_ARTWORK_HANDLE};
+static const uint8_t media_attr_list_size =
+    sizeof(media_attr_list)/sizeof(uint32_t);
+
+// List of desired media attribute keys to request if cover artwork is not a
+// supported feature
+static const uint32_t media_attr_list_no_cover_art[] = {
+      AVRC_MEDIA_ATTR_ID_TITLE,       AVRC_MEDIA_ATTR_ID_ARTIST,
+      AVRC_MEDIA_ATTR_ID_ALBUM,       AVRC_MEDIA_ATTR_ID_TRACK_NUM,
+      AVRC_MEDIA_ATTR_ID_NUM_TRACKS,  AVRC_MEDIA_ATTR_ID_GENRE,
+      AVRC_MEDIA_ATTR_ID_PLAYING_TIME};
+static const uint8_t media_attr_list_no_cover_art_size =
+    sizeof(media_attr_list_no_cover_art)/sizeof(uint32_t);
+
 /*****************************************************************************
  *  Static functions
  *****************************************************************************/
@@ -425,6 +453,18 @@
   return NULL;
 }
 
+const uint32_t* get_requested_attributes_list(btif_rc_device_cb_t* p_dev) {
+  return (p_dev->rc_features & BTA_AV_FEAT_COVER_ARTWORK
+      ? media_attr_list
+      : media_attr_list_no_cover_art);
+}
+
+uint8_t get_requested_attributes_list_size(btif_rc_device_cb_t* p_dev) {
+  return (p_dev->rc_features & BTA_AV_FEAT_COVER_ARTWORK
+      ? media_attr_list_size
+      : media_attr_list_no_cover_art_size);
+}
+
 void fill_pdu_queue(int index, uint8_t ctype, uint8_t label, bool pending,
                     btif_rc_device_cb_t* p_dev) {
   p_dev->rc_pdu_info[index].ctype = ctype;
@@ -485,11 +525,25 @@
     rc_features |= BTRC_FEAT_BROWSE;
   }
 
+  /* Add cover art feature capability */
+  if (p_dev->rc_features & BTA_AV_FEAT_COVER_ARTWORK) {
+    rc_features |= BTRC_FEAT_COVER_ARTWORK;
+  }
+
   BTIF_TRACE_DEBUG("%s: Update rc features to CTRL: %d", __func__, rc_features);
   do_in_jni_thread(FROM_HERE, base::Bind(bt_rc_ctrl_callbacks->getrcfeatures_cb,
                                          p_dev->rc_addr, rc_features));
 }
 
+void handle_rc_ctrl_psm(btif_rc_device_cb_t* p_dev) {
+  uint16_t cover_art_psm = p_dev->rc_cover_art_psm;
+  BTIF_TRACE_DEBUG("%s: Update rc cover art psm to CTRL: %d", __func__,
+      cover_art_psm);
+  do_in_jni_thread(FROM_HERE, base::Bind(
+      bt_rc_ctrl_callbacks->get_cover_art_psm_cb,
+      p_dev->rc_addr, cover_art_psm));
+}
+
 void handle_rc_features(btif_rc_device_cb_t* p_dev) {
 
   CHECK(bt_rc_callbacks);
@@ -631,6 +685,9 @@
   p_dev->rc_features = p_rc_open->peer_features;
   BTIF_TRACE_DEBUG("%s: handle_rc_connect in features: 0x%x out features 0x%x",
                    __func__, p_rc_open->peer_features, p_dev->rc_features);
+  p_dev->rc_cover_art_psm = p_rc_open->cover_art_psm;
+  BTIF_TRACE_DEBUG("%s: cover art psm: 0x%x",
+                   __func__, p_dev->rc_cover_art_psm);
   p_dev->rc_vol_label = MAX_LABEL;
   p_dev->rc_volume = MAX_VOLUME;
 
@@ -651,6 +708,9 @@
   }
   /* report connection state if remote device is AVRCP target */
   handle_rc_ctrl_features(p_dev);
+
+  /* report psm if remote device is AVRCP target */
+  handle_rc_ctrl_psm(p_dev);
 }
 
 /***************************************************************************
@@ -701,6 +761,7 @@
     memset(p_dev->rc_notif, 0, sizeof(p_dev->rc_notif));
 
     p_dev->rc_features = 0;
+    p_dev->rc_cover_art_psm = 0;
     p_dev->rc_vol_label = MAX_LABEL;
     p_dev->rc_volume = MAX_VOLUME;
 
@@ -974,8 +1035,9 @@
   btif_rc_device_cb_t* p_dev = NULL;
   switch (event) {
     case BTA_AV_RC_OPEN_EVT: {
-      BTIF_TRACE_DEBUG("%s: Peer_features: %x", __func__,
-                       p_data->rc_open.peer_features);
+      BTIF_TRACE_DEBUG("%s: Peer_features: 0x%x Cover Art PSM: 0x%x", __func__,
+                       p_data->rc_open.peer_features,
+                       p_data->rc_open.cover_art_psm);
       handle_rc_connect(&(p_data->rc_open));
     } break;
 
@@ -1036,6 +1098,22 @@
       }
     } break;
 
+    case BTA_AV_RC_PSM_EVT: {
+      BTIF_TRACE_DEBUG("%s: Peer cover art PSM: %x", __func__,
+                       p_data->rc_cover_art_psm.cover_art_psm);
+      p_dev = btif_rc_get_device_by_handle(p_data->rc_cover_art_psm.rc_handle);
+      if (p_dev == NULL) {
+        BTIF_TRACE_ERROR("%s: RC PSM event for Invalid rc handle",
+                         __func__);
+        break;
+      }
+
+      p_dev->rc_cover_art_psm = p_data->rc_cover_art_psm.cover_art_psm;
+      if ((p_dev->rc_connected) && (bt_rc_ctrl_callbacks != NULL)) {
+        handle_rc_ctrl_psm(p_dev);
+      }
+    } break;
+
     case BTA_AV_META_MSG_EVT: {
       if (bt_rc_callbacks != NULL) {
         BTIF_TRACE_DEBUG("%s: BTA_AV_META_MSG_EVT code: %d label: %d", __func__,
@@ -1750,6 +1828,7 @@
            sizeof(btif_rc_cb.rc_multi_cb[idx]));
     btif_rc_cb.rc_multi_cb[idx].rc_vol_label = MAX_LABEL;
     btif_rc_cb.rc_multi_cb[idx].rc_volume = MAX_VOLUME;
+    btif_rc_cb.rc_multi_cb[idx].rc_features_processed = FALSE;
   }
   lbl_init();
 
@@ -1766,12 +1845,9 @@
     return;
   }
   p_dev->rc_procedure_complete = true;
-  uint32_t attr_list[] = {
-      AVRC_MEDIA_ATTR_ID_TITLE,       AVRC_MEDIA_ATTR_ID_ARTIST,
-      AVRC_MEDIA_ATTR_ID_ALBUM,       AVRC_MEDIA_ATTR_ID_TRACK_NUM,
-      AVRC_MEDIA_ATTR_ID_NUM_TRACKS,  AVRC_MEDIA_ATTR_ID_GENRE,
-      AVRC_MEDIA_ATTR_ID_PLAYING_TIME};
-  get_element_attribute_cmd(AVRC_MAX_NUM_MEDIA_ATTR_ID, attr_list, p_dev);
+  const uint32_t* attr_list = get_requested_attributes_list(p_dev);
+  const uint8_t attr_list_size = get_requested_attributes_list_size(p_dev);
+  get_metadata_attribute_cmd(attr_list_size, attr_list, p_dev);
 }
 
 /***************************************************************************
@@ -2542,9 +2618,9 @@
     BTIF_TRACE_DEBUG("%s: Peer supports absolute volume. newVolume: %d",
                      __func__, volume);
 
-    tAVRC_COMMAND avrc_cmd = {.volume = {.opcode = AVRC_OP_VENDOR,
-                                         .pdu = AVRC_PDU_SET_ABSOLUTE_VOLUME,
+    tAVRC_COMMAND avrc_cmd = {.volume = {.pdu = AVRC_PDU_SET_ABSOLUTE_VOLUME,
                                          .status = AVRC_STS_NO_ERROR,
+                                         .opcode = AVRC_OP_VENDOR,
                                          .volume = volume}};
 
     BT_HDR* p_msg = NULL;
@@ -2851,7 +2927,7 @@
 
     case AVRC_PDU_GET_ELEMENT_ATTR:
       avrc_response.get_attrs.status = BTIF_RC_STS_TIMEOUT;
-      handle_get_elem_attr_response(&meta_msg, &avrc_response.get_attrs);
+      handle_get_metadata_attr_response(&meta_msg, &avrc_response.get_attrs);
       break;
 
     case AVRC_PDU_GET_PLAY_STATUS:
@@ -3029,6 +3105,44 @@
 
 /***************************************************************************
  *
+ * Function         send_browsing_command
+ *
+ * Description      Send a command to a device on the browsing channel
+ *
+ * Parameters       avrc_cmd: The command you're sending
+ *                  p_dev: Device control block
+ *
+ * Returns          BT_STATUS_SUCCESS if command is issued successfully
+ *                  otherwise BT_STATUS_FAIL
+ *
+ **************************************************************************/
+static bt_status_t build_and_send_browsing_cmd(tAVRC_COMMAND* avrc_cmd,
+                                         btif_rc_device_cb_t* p_dev) {
+  BT_HDR* p_msg = NULL;
+  tAVRC_STS status = AVRC_BldCommand(avrc_cmd, &p_msg);
+  if (status != AVRC_STS_NO_ERROR) {
+    BTIF_TRACE_ERROR("%s: failed to build command status %d", __func__, status);
+    return BT_STATUS_FAIL;
+  }
+
+  rc_transaction_t* p_transaction = NULL;
+  bt_status_t tran_status = get_transaction(&p_transaction);
+
+  if (tran_status != BT_STATUS_SUCCESS || p_transaction == NULL) {
+    osi_free(p_msg);
+    BTIF_TRACE_ERROR("%s: failed to obtain txn details. status: 0x%02x",
+                     __func__, tran_status);
+    return BT_STATUS_FAIL;
+  }
+
+  BTIF_TRACE_DEBUG("%s msgreq being sent out with label %d", __func__,
+                   p_transaction->lbl);
+  BTA_AvMetaCmd(p_dev->rc_handle, p_transaction->lbl, AVRC_CMD_CTRL, p_msg);
+  return BT_STATUS_SUCCESS;
+}
+
+/***************************************************************************
+ *
  * Function         handle_get_capability_response
  *
  * Description      Handles the get_cap_response to populate company id info
@@ -3063,7 +3177,8 @@
           (p_rsp->param.event_id[xx] == AVRC_EVT_APP_SETTING_CHANGE) ||
           (p_rsp->param.event_id[xx] == AVRC_EVT_NOW_PLAYING_CHANGE) ||
           (p_rsp->param.event_id[xx] == AVRC_EVT_ADDR_PLAYER_CHANGE) ||
-          (p_rsp->param.event_id[xx] == AVRC_EVT_UIDS_CHANGE)) {
+          (p_rsp->param.event_id[xx] == AVRC_EVT_UIDS_CHANGE) ||
+          (p_rsp->param.event_id[xx] == AVRC_EVT_AVAL_PLAYERS_CHANGE)) {
         p_event = (btif_rc_supported_event_t*)osi_malloc(
             sizeof(btif_rc_supported_event_t));
         p_event->event_id = p_rsp->param.event_id[xx];
@@ -3128,17 +3243,14 @@
                                          tAVRC_REG_NOTIF_RSP* p_rsp) {
   btif_rc_device_cb_t* p_dev =
       btif_rc_get_device_by_handle(pmeta_msg->rc_handle);
-  uint32_t attr_list[] = {
-      AVRC_MEDIA_ATTR_ID_TITLE,       AVRC_MEDIA_ATTR_ID_ARTIST,
-      AVRC_MEDIA_ATTR_ID_ALBUM,       AVRC_MEDIA_ATTR_ID_TRACK_NUM,
-      AVRC_MEDIA_ATTR_ID_NUM_TRACKS,  AVRC_MEDIA_ATTR_ID_GENRE,
-      AVRC_MEDIA_ATTR_ID_PLAYING_TIME};
 
   if (p_dev == NULL) {
     BTIF_TRACE_ERROR("%s: p_dev NULL", __func__);
     return;
   }
 
+  const uint32_t* attr_list = get_requested_attributes_list(p_dev);
+  const uint8_t attr_list_size = get_requested_attributes_list_size(p_dev);
 
   if (pmeta_msg->code == AVRC_RSP_INTERIM) {
     btif_rc_supported_event_t* p_event;
@@ -3162,7 +3274,7 @@
           uint8_t* p_data = p_rsp->param.track;
           BE_STREAM_TO_UINT64(p_dev->rc_playing_uid, p_data);
           get_play_status_cmd(p_dev);
-          get_element_attribute_cmd(AVRC_MAX_NUM_MEDIA_ATTR_ID, attr_list,
+          get_metadata_attribute_cmd(attr_list_size, attr_list,
                                     p_dev);
         }
         break;
@@ -3178,6 +3290,11 @@
         break;
 
       case AVRC_EVT_AVAL_PLAYERS_CHANGE:
+        BTIF_TRACE_DEBUG("%s: AVRC_EVT_AVAL_PLAYERS_CHANGE", __func__);
+        do_in_jni_thread(
+            FROM_HERE,
+            base::Bind(bt_rc_ctrl_callbacks->available_player_changed_cb,
+                       p_dev->rc_addr));
         break;
 
       case AVRC_EVT_ADDR_PLAYER_CHANGE:
@@ -3258,10 +3375,6 @@
         /* Start timer to get play status periodically
          * if the play state is playing.
          */
-        if (p_rsp->param.play_status == AVRC_PLAYSTATE_PLAYING) {
-          get_element_attribute_cmd(AVRC_MAX_NUM_MEDIA_ATTR_ID, attr_list,
-                                    p_dev);
-        }
         do_in_jni_thread(
             FROM_HERE,
             base::Bind(bt_rc_ctrl_callbacks->play_status_changed_cb,
@@ -3274,7 +3387,7 @@
         if (rc_is_track_id_valid(p_rsp->param.track) != true) {
           break;
         }
-        get_element_attribute_cmd(AVRC_MAX_NUM_MEDIA_ATTR_ID, attr_list, p_dev);
+        get_metadata_attribute_cmd(attr_list_size, attr_list, p_dev);
         break;
 
       case AVRC_EVT_APP_SETTING_CHANGE: {
@@ -3742,14 +3855,14 @@
 
 /***************************************************************************
  *
- * Function         handle_get_elem_attr_response
+ * Function         handle_get_metadata_attr_response
  *
  * Description      handles the the element attributes response, calls
  *                  HAL callback to update track change information.
  * Returns          None
  *
  **************************************************************************/
-static void handle_get_elem_attr_response(tBTA_AV_META_MSG* pmeta_msg,
+static void handle_get_metadata_attr_response(tBTA_AV_META_MSG* pmeta_msg,
                                           tAVRC_GET_ATTRS_RSP* p_rsp) {
   btif_rc_device_cb_t* p_dev =
       btif_rc_get_device_by_handle(pmeta_msg->rc_handle);
@@ -3782,12 +3895,9 @@
     /* Retry for timeout case, this covers error handling
      * for continuation failure also.
      */
-    uint32_t attr_list[] = {
-        AVRC_MEDIA_ATTR_ID_TITLE,       AVRC_MEDIA_ATTR_ID_ARTIST,
-        AVRC_MEDIA_ATTR_ID_ALBUM,       AVRC_MEDIA_ATTR_ID_TRACK_NUM,
-        AVRC_MEDIA_ATTR_ID_NUM_TRACKS,  AVRC_MEDIA_ATTR_ID_GENRE,
-        AVRC_MEDIA_ATTR_ID_PLAYING_TIME};
-    get_element_attribute_cmd(AVRC_MAX_NUM_MEDIA_ATTR_ID, attr_list, p_dev);
+    const uint32_t* attr_list = get_requested_attributes_list(p_dev);
+    const uint8_t attr_list_size = get_requested_attributes_list_size(p_dev);
+    get_metadata_attribute_cmd(attr_list_size, attr_list, p_dev);
   } else {
     BTIF_TRACE_ERROR("%s: Error in get element attr procedure: %d", __func__,
                      p_rsp->status);
@@ -4037,6 +4147,9 @@
       case AVRC_MEDIA_ATTR_ID_PLAYING_TIME:
         btrc_attr_pair->attr_id = BTRC_MEDIA_ATTR_ID_PLAYING_TIME;
         break;
+      case AVRC_MEDIA_ATTR_ID_COVER_ARTWORK_HANDLE:
+        btrc_attr_pair->attr_id = BTRC_MEDIA_ATTR_ID_COVER_ARTWORK_HANDLE;
+        break;
       default:
         BTIF_TRACE_ERROR("%s invalid media attr id: 0x%x", __func__,
                          avrc_attr_pair->attr_id);
@@ -4274,7 +4387,7 @@
         break;
 
       case AVRC_PDU_GET_ELEMENT_ATTR:
-        handle_get_elem_attr_response(pmeta_msg, &avrc_response.get_attrs);
+        handle_get_metadata_attr_response(pmeta_msg, &avrc_response.get_attrs);
         break;
 
       case AVRC_PDU_GET_PLAY_STATUS:
@@ -4299,6 +4412,9 @@
       case AVRC_PDU_SET_BROWSED_PLAYER:
         handle_set_browsed_player_response(pmeta_msg, &avrc_response.br_player);
         break;
+      case AVRC_PDU_GET_ITEM_ATTRIBUTES:
+        handle_get_metadata_attr_response(pmeta_msg, &avrc_response.get_attrs);
+        break;
       default:
         BTIF_TRACE_ERROR("%s cannot handle browse pdu %d", __func__,
                          pmeta_msg->p_msg->hdr.opcode);
@@ -4517,6 +4633,28 @@
 
 /***************************************************************************
  *
+ * Function         get_current_metadata_cmd
+ *
+ * Description      Fetch the current track metadata for the device
+ *
+ * Returns          BT_STATUS_SUCCESS if command issued successfully otherwise
+ *                  BT_STATUS_FAIL.
+ *
+ **************************************************************************/
+static bt_status_t get_current_metadata_cmd(const RawAddress& bd_addr) {
+  BTIF_TRACE_DEBUG("%s", __func__);
+  btif_rc_device_cb_t* p_dev = btif_rc_get_device_by_bda(bd_addr);
+  if (p_dev == NULL) {
+    BTIF_TRACE_ERROR("%s: p_dev NULL", __func__);
+    return BT_STATUS_FAIL;
+  }
+  const uint32_t* attr_list = get_requested_attributes_list(p_dev);
+  const uint8_t attr_list_size = get_requested_attributes_list_size(p_dev);
+  return get_metadata_attribute_cmd(attr_list_size, attr_list, p_dev);
+}
+
+/***************************************************************************
+ *
  * Function         get_playback_state_cmd
  *
  * Description      Fetch the current playback state for the device
@@ -4554,6 +4692,35 @@
 
 /***************************************************************************
  *
+ * Function         get_item_attribute_cmd
+ *
+ * Description      Fetch the item attributes for a given uid.
+ *
+ * Parameters       uid: Track UID you want attributes for
+ *                  scope: Constant representing which scope you're querying
+ *                         (i.e AVRC_SCOPE_FILE_SYSTEM)
+ *                  p_dev: Device control block
+ *
+ * Returns          BT_STATUS_SUCCESS if command is issued successfully
+ *                  otherwise BT_STATUS_FAIL
+ *
+ **************************************************************************/
+static bt_status_t get_item_attribute_cmd(uint64_t uid, int scope,
+                                           uint8_t num_attribute,
+                                           const uint32_t* p_attr_ids,
+                                           btif_rc_device_cb_t* p_dev) {
+  tAVRC_COMMAND avrc_cmd = {0};
+  avrc_cmd.pdu = AVRC_PDU_GET_ITEM_ATTRIBUTES;
+  avrc_cmd.get_attrs.scope = scope;
+  memcpy(avrc_cmd.get_attrs.uid, &uid, 8);
+  avrc_cmd.get_attrs.uid_counter = 0;
+  avrc_cmd.get_attrs.attr_count = 0;
+
+  return build_and_send_browsing_cmd(&avrc_cmd, p_dev);
+}
+
+/***************************************************************************
+ *
  * Function         get_folder_list_cmd
  *
  * Description      Fetch the currently selected folder list
@@ -4625,26 +4792,7 @@
   memset(avrc_cmd.chg_path.folder_uid, 0, AVRC_UID_SIZE * sizeof(uint8_t));
   memcpy(avrc_cmd.chg_path.folder_uid, uid, AVRC_UID_SIZE * sizeof(uint8_t));
 
-  BT_HDR* p_msg = NULL;
-  tAVRC_STS status = AVRC_BldCommand(&avrc_cmd, &p_msg);
-  if (status != AVRC_STS_NO_ERROR) {
-    BTIF_TRACE_ERROR("%s failed to build command status %d", __func__, status);
-    return BT_STATUS_FAIL;
-  }
-
-  rc_transaction_t* p_transaction = NULL;
-  bt_status_t tran_status = get_transaction(&p_transaction);
-  if (tran_status != BT_STATUS_SUCCESS || p_transaction == NULL) {
-    osi_free(p_msg);
-    BTIF_TRACE_ERROR("%s: failed to obtain transaction details. status: 0x%02x",
-                     __func__, tran_status);
-    return BT_STATUS_FAIL;
-  }
-
-  BTIF_TRACE_DEBUG("%s msgreq being sent out with label %d", __func__,
-                   p_transaction->lbl);
-  BTA_AvMetaCmd(p_dev->rc_handle, p_transaction->lbl, AVRC_CMD_CTRL, p_msg);
-  return BT_STATUS_SUCCESS;
+  return build_and_send_browsing_cmd(&avrc_cmd, p_dev);
 }
 
 /***************************************************************************
@@ -4666,33 +4814,13 @@
   CHECK_RC_CONNECTED(p_dev);
   CHECK_BR_CONNECTED(p_dev);
 
-  rc_transaction_t* p_transaction = NULL;
-
   tAVRC_COMMAND avrc_cmd = {0};
   avrc_cmd.br_player.pdu = AVRC_PDU_SET_BROWSED_PLAYER;
   avrc_cmd.br_player.status = AVRC_STS_NO_ERROR;
   // TODO(sanketa): Improve for database aware clients.
   avrc_cmd.br_player.player_id = id;
 
-  BT_HDR* p_msg = NULL;
-  tAVRC_STS status = AVRC_BldCommand(&avrc_cmd, &p_msg);
-  if (status != AVRC_STS_NO_ERROR) {
-    BTIF_TRACE_ERROR("%s failed to build command status %d", __func__, status);
-    return BT_STATUS_FAIL;
-  }
-
-  bt_status_t tran_status = get_transaction(&p_transaction);
-  if (tran_status != BT_STATUS_SUCCESS || p_transaction == NULL) {
-    osi_free(p_msg);
-    BTIF_TRACE_ERROR("%s: failed to obtain transaction details. status: 0x%02x",
-                     __func__, tran_status);
-    return BT_STATUS_FAIL;
-  }
-
-  BTIF_TRACE_DEBUG("%s msgreq being sent out with label %d", __func__,
-                   p_transaction->lbl);
-  BTA_AvMetaCmd(p_dev->rc_handle, p_transaction->lbl, AVRC_CMD_CTRL, p_msg);
-  return BT_STATUS_SUCCESS;
+  return build_and_send_browsing_cmd(&avrc_cmd, p_dev);
 }
 
 /***************************************************************************
@@ -4716,33 +4844,12 @@
   CHECK_BR_CONNECTED(p_dev);
 
   tAVRC_COMMAND avrc_cmd = {0};
-  BT_HDR* p_msg = NULL;
-
   avrc_cmd.addr_player.pdu = AVRC_PDU_SET_ADDRESSED_PLAYER;
   avrc_cmd.addr_player.status = AVRC_STS_NO_ERROR;
   // TODO(sanketa): Improve for database aware clients.
   avrc_cmd.addr_player.player_id = id;
 
-  tAVRC_STS status = AVRC_BldCommand(&avrc_cmd, &p_msg);
-  if (status != AVRC_STS_NO_ERROR) {
-    BTIF_TRACE_ERROR("%s: failed to build command status %d", __func__, status);
-    return BT_STATUS_FAIL;
-  }
-
-  rc_transaction_t* p_transaction = NULL;
-  bt_status_t tran_status = get_transaction(&p_transaction);
-
-  if (tran_status != BT_STATUS_SUCCESS || p_transaction == NULL) {
-    osi_free(p_msg);
-    BTIF_TRACE_ERROR("%s: failed to obtain txn details. status: 0x%02x",
-                     __func__, tran_status);
-    return BT_STATUS_FAIL;
-  }
-
-  BTIF_TRACE_DEBUG("%s msgreq being sent out with label %d", __func__,
-                   p_transaction->lbl);
-  BTA_AvMetaCmd(p_dev->rc_handle, p_transaction->lbl, AVRC_CMD_CTRL, p_msg);
-  return BT_STATUS_SUCCESS;
+  return build_and_send_browsing_cmd(&avrc_cmd, p_dev);
 }
 
 /***************************************************************************
@@ -4782,26 +4889,7 @@
   avrc_cmd.get_items.end_item = end_item;
   avrc_cmd.get_items.attr_count = 0; /* p_attr_list does not matter hence */
 
-  BT_HDR* p_msg = NULL;
-  tAVRC_STS status = AVRC_BldCommand(&avrc_cmd, &p_msg);
-  if (status != AVRC_STS_NO_ERROR) {
-    BTIF_TRACE_ERROR("%s failed to build command status %d", __func__, status);
-    return BT_STATUS_FAIL;
-  }
-
-  rc_transaction_t* p_transaction = NULL;
-  bt_status_t tran_status = get_transaction(&p_transaction);
-  if (tran_status != BT_STATUS_SUCCESS || p_transaction == NULL) {
-    osi_free(p_msg);
-    BTIF_TRACE_ERROR("%s: failed to obtain transaction details. status: 0x%02x",
-                     __func__, tran_status);
-    return BT_STATUS_FAIL;
-  }
-
-  BTIF_TRACE_DEBUG("%s msgreq being sent out with label %d", __func__,
-                   p_transaction->lbl);
-  BTA_AvMetaCmd(p_dev->rc_handle, p_transaction->lbl, AVRC_CMD_CTRL, p_msg);
-  return BT_STATUS_SUCCESS;
+  return build_and_send_browsing_cmd(&avrc_cmd, p_dev);
 }
 
 /***************************************************************************
@@ -4862,7 +4950,8 @@
   memcpy(avrc_cmd.play_item.uid, uid, AVRC_UID_SIZE);
   avrc_cmd.play_item.uid_counter = uid_counter;
 
-  return build_and_send_vendor_cmd(&avrc_cmd, AVRC_CMD_CTRL, p_dev);
+  return build_and_send_browsing_cmd(&avrc_cmd, p_dev);
+  // return build_and_send_vendor_cmd(&avrc_cmd, AVRC_CMD_CTRL, p_dev);
 }
 
 /***************************************************************************
@@ -4961,6 +5050,35 @@
 
 /***************************************************************************
  *
+ * Function         get_metadata_attribute_cmd
+ *
+ * Description      Get metadata attributes for attributeIds. This function
+ *                  will make the right determination of whether to use the
+ *                  control or browsing channel for the request
+ *
+ * Returns          BT_STATUS_SUCCESS if the command is successfully issued
+ *                  otherwise BT_STATUS_FAIL
+ *
+ **************************************************************************/
+static bt_status_t get_metadata_attribute_cmd(uint8_t num_attribute,
+                                              const uint32_t* p_attr_ids,
+                                              btif_rc_device_cb_t* p_dev) {
+  BTIF_TRACE_DEBUG("%s: num_attribute: %d attribute_id: %d", __func__,
+                   num_attribute, p_attr_ids[0]);
+
+  // If browsing is connected then send the command out that channel
+  if (p_dev->br_connected) {
+    return get_item_attribute_cmd(p_dev->rc_playing_uid,
+                                   AVRC_SCOPE_NOW_PLAYING, num_attribute,
+                                   p_attr_ids, p_dev);
+  }
+
+  // Otherwise, default to the control channel
+  return get_element_attribute_cmd(num_attribute, p_attr_ids, p_dev);
+}
+
+/***************************************************************************
+ *
  * Function         get_element_attribute_cmd
  *
  * Description      Get Element Attribute for  attributeIds
@@ -4969,12 +5087,11 @@
  *
  **************************************************************************/
 static bt_status_t get_element_attribute_cmd(uint8_t num_attribute,
-                                             uint32_t* p_attr_ids,
+                                             const uint32_t* p_attr_ids,
                                              btif_rc_device_cb_t* p_dev) {
   BTIF_TRACE_DEBUG("%s: num_attribute: %d attribute_id: %d", __func__,
                    num_attribute, p_attr_ids[0]);
   CHECK_RC_CONNECTED(p_dev);
-
   tAVRC_COMMAND avrc_cmd = {0};
   avrc_cmd.get_elem_attrs.opcode = AVRC_OP_VENDOR;
   avrc_cmd.get_elem_attrs.status = AVRC_STS_NO_ERROR;
@@ -5214,6 +5331,7 @@
     send_groupnavigation_cmd,
     change_player_app_setting,
     play_item_cmd,
+    get_current_metadata_cmd,
     get_playback_state_cmd,
     get_now_playing_list_cmd,
     get_folder_list_cmd,
diff --git a/btif/src/btif_sock.cc b/btif/src/btif_sock.cc
index 0c4f6f6..8c4c9c3 100644
--- a/btif/src/btif_sock.cc
+++ b/btif/src/btif_sock.cc
@@ -28,6 +28,7 @@
 
 #include "bta_api.h"
 #include "btif_common.h"
+#include "btif_config.h"
 #include "btif_sock_l2cap.h"
 #include "btif_sock_rfc.h"
 #include "btif_sock_sco.h"
@@ -215,12 +216,24 @@
       status = btsock_l2cap_connect(bd_addr, channel, sock_fd, flags, app_uid);
       break;
 
-    case BTSOCK_L2CAP_LE:
+    case BTSOCK_L2CAP_LE: {
       flags |= BTSOCK_FLAG_LE_COC;
+
+      // Ensure device is in inquiry database
+      int addr_type = 0;
+      int device_type = 0;
+
+      if (btif_get_address_type(*bd_addr, &addr_type) &&
+          btif_get_device_type(*bd_addr, &device_type) &&
+          device_type != BT_DEVICE_TYPE_BREDR) {
+        BTA_DmAddBleDevice(*bd_addr, addr_type, device_type);
+      }
+
       LOG_DEBUG(LOG_TAG, "%s: type=BTSOCK_L2CAP_LE, channel=0x%x, flags=0x%x",
                 __func__, channel, flags);
       status = btsock_l2cap_connect(bd_addr, channel, sock_fd, flags, app_uid);
       break;
+    }
 
     case BTSOCK_SCO:
       status = btsock_sco_connect(bd_addr, sock_fd, flags);
diff --git a/btif/src/btif_storage.cc b/btif/src/btif_storage.cc
index 2427493..95e4ef0 100644
--- a/btif/src/btif_storage.cc
+++ b/btif/src/btif_storage.cc
@@ -72,9 +72,6 @@
 #define BTIF_STORAGE_PATH_REMOTE_DEVCLASS "DevClass"
 #define BTIF_STORAGE_PATH_REMOTE_DEVTYPE "DevType"
 #define BTIF_STORAGE_PATH_REMOTE_NAME "Name"
-#define BTIF_STORAGE_PATH_REMOTE_VER_MFCT "Manufacturer"
-#define BTIF_STORAGE_PATH_REMOTE_VER_VER "LmpVer"
-#define BTIF_STORAGE_PATH_REMOTE_VER_SUBVER "LmpSubVer"
 
 //#define BTIF_STORAGE_PATH_REMOTE_LINKKEYS "remote_linkkeys"
 #define BTIF_STORAGE_PATH_REMOTE_ALIASE "Aliase"
@@ -260,11 +257,10 @@
 
       if (!info) return false;
 
-      btif_config_set_int(bdstr, BTIF_STORAGE_PATH_REMOTE_VER_MFCT,
+      btif_config_set_int(bdstr, BT_CONFIG_KEY_REMOTE_VER_MFCT,
                           info->manufacturer);
-      btif_config_set_int(bdstr, BTIF_STORAGE_PATH_REMOTE_VER_VER,
-                          info->version);
-      btif_config_set_int(bdstr, BTIF_STORAGE_PATH_REMOTE_VER_SUBVER,
+      btif_config_set_int(bdstr, BT_CONFIG_KEY_REMOTE_VER_VER, info->version);
+      btif_config_set_int(bdstr, BT_CONFIG_KEY_REMOTE_VER_SUBVER,
                           info->sub_ver);
     } break;
 
@@ -382,15 +378,15 @@
       bt_remote_version_t* info = (bt_remote_version_t*)prop->val;
 
       if (prop->len >= (int)sizeof(bt_remote_version_t)) {
-        ret = btif_config_get_int(bdstr, BTIF_STORAGE_PATH_REMOTE_VER_MFCT,
+        ret = btif_config_get_int(bdstr, BT_CONFIG_KEY_REMOTE_VER_MFCT,
                                   &info->manufacturer);
 
         if (ret)
-          ret = btif_config_get_int(bdstr, BTIF_STORAGE_PATH_REMOTE_VER_VER,
+          ret = btif_config_get_int(bdstr, BT_CONFIG_KEY_REMOTE_VER_VER,
                                     &info->version);
 
         if (ret)
-          ret = btif_config_get_int(bdstr, BTIF_STORAGE_PATH_REMOTE_VER_SUBVER,
+          ret = btif_config_get_int(bdstr, BT_CONFIG_KEY_REMOTE_VER_SUBVER,
                                     &info->sub_ver);
       }
     } break;
@@ -889,8 +885,9 @@
     tBTA_LE_KEY_VALUE key;
     memset(&key, 0, sizeof(key));
 
-    if (btif_storage_get_ble_bonding_key(&bd_addr, BTIF_DM_LE_KEY_PENC, (uint8_t*)&key, sizeof(tBTM_LE_PENC_KEYS)) ==
-        BT_STATUS_SUCCESS) {
+    if (btif_storage_get_ble_bonding_key(
+            &bd_addr, BTIF_DM_LE_KEY_PENC, (uint8_t*)&key,
+            sizeof(tBTM_LE_PENC_KEYS)) == BT_STATUS_SUCCESS) {
       if (is_sample_ltk(key.penc_key.ltk)) {
         bad_ltk.push_back(bd_addr);
       }
@@ -899,7 +896,8 @@
 
   for (RawAddress address : bad_ltk) {
     android_errorWriteLog(0x534e4554, "128437297");
-    LOG(ERROR) << __func__ << ": removing bond to device using test TLK: " << address;
+    LOG(ERROR) << __func__
+               << ": removing bond to device using test TLK: " << address;
 
     btif_storage_remove_bonded_device(&address);
   }
@@ -1119,6 +1117,7 @@
       break;
     case BTIF_DM_LE_KEY_LID:
       name = "LE_KEY_LID";
+      break;
     default:
       return BT_STATUS_FAIL;
   }
diff --git a/btif/src/btif_util.cc b/btif/src/btif_util.cc
index 26d84fd..0aa1142 100644
--- a/btif/src/btif_util.cc
+++ b/btif/src/btif_util.cc
@@ -357,6 +357,7 @@
     CASE_RETURN_STR(BTA_AV_VENDOR_RSP_EVT)
     CASE_RETURN_STR(BTA_AV_META_MSG_EVT)
     CASE_RETURN_STR(BTA_AV_RC_FEAT_EVT)
+    CASE_RETURN_STR(BTA_AV_RC_PSM_EVT)
     default:
       return "UNKNOWN_EVENT";
   }
diff --git a/btif/test/btif_config_cache_test.cc b/btif/test/btif_config_cache_test.cc
new file mode 100644
index 0000000..aaccf8c
--- /dev/null
+++ b/btif/test/btif_config_cache_test.cc
@@ -0,0 +1,527 @@
+/*
+ *  Copyright 2020 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#include "btif/include/btif_config_cache.h"
+
+#include <filesystem>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace {
+
+const int kCapacity = 3;
+const int kTestRepeatCount = 30;
+const std::string kBtAddr1 = "11:22:33:44:55:66";
+const std::string kBtAddr2 = "AA:BB:CC:DD:EE:FF";
+const std::string kBtAddr3 = "AB:CD:EF:12:34:56";
+const std::string kBtAddr4 = "11:AA:22:BB:33:CC";
+const std::string kBtAddr5 = "11:AA:22:BB:33:CD";
+const std::string kBtLocalAddr = "12:34:56:78:90:AB";
+const std::string kBtInfo = "Info";
+const std::string kBtMetrics = "Metrics";
+const std::string kBtAdapter = "Adapter";
+const std::string kBtAddrInvalid1 = "AB:CD:EF:12:34";
+const std::string kBtAddrInvalid2 = "AB:CD:EF:12:34:56:78";
+const std::string kBtAddrInvalid3 = "ABCDEF123456";
+const std::string kBtAddrInvalid4 = "AB-CD-EF-12-34-56";
+const std::string kBtSectionInvalid1 = "Invalid Section";
+const std::filesystem::path kTestConfigFile =
+    std::filesystem::temp_directory_path() / "config_cache_test.conf";
+const char* TEST_CONFIG_FILE = kTestConfigFile.c_str();
+
+}  // namespace
+
+namespace testing {
+
+/* Test to basic btif_config_cache set up
+ * 1. when received Local device sections information, the sections can be put
+ * into btif config cache
+ * 2. the device sections and key-value will be set to Btif config cache when
+ * receiving different device sections
+ * 3. limit the capacity of unpacire devices cache to 3, test the oldest device
+ * section will be ruled out when receiveing 4 different device sections.
+ */
+TEST(BtifConfigCacheTest, test_setup_btif_config_cache) {
+  BtifConfigCache test_btif_config_cache(kCapacity);
+  // Info section
+  test_btif_config_cache.SetString(kBtInfo, "FileSource", "");
+  test_btif_config_cache.SetString(kBtInfo, "TimeCreated",
+                                   "2020-06-05 12:12:12");
+  // Metrics section
+  test_btif_config_cache.SetString(kBtMetrics, "Salt256Bit",
+                                   "92a331174d20f2bb");
+  // Adapter Section
+  test_btif_config_cache.SetString(kBtAdapter, "Address", kBtLocalAddr);
+  EXPECT_TRUE(test_btif_config_cache.HasSection(kBtAdapter));
+
+  // bt_device_1
+  test_btif_config_cache.SetString(kBtAddr1, "Name", "Headset_1");
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr1, "Name"));
+
+  test_btif_config_cache.SetInt(kBtAddr1, "Property_Int", 1);
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr1, "Property_Int"));
+
+  // bt_device_2
+  test_btif_config_cache.SetString(kBtAddr2, "Name", "Headset_2");
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr2, "Name"));
+
+  // bt_device_3
+  test_btif_config_cache.SetString(kBtAddr3, "Name", "Headset_3");
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr3, "Name"));
+
+  // bt_device_4
+  test_btif_config_cache.SetString(kBtAddr4, "Name", "Headset_4");
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr4, "Name"));
+
+  // out out the capacty of unpair devices cache, the bt_device_1 be ruled out
+  EXPECT_FALSE(test_btif_config_cache.HasSection(kBtAddr1));
+  EXPECT_TRUE(test_btif_config_cache.HasSection(kBtAddr2));
+  EXPECT_TRUE(test_btif_config_cache.HasSection(kBtAddr3));
+  EXPECT_TRUE(test_btif_config_cache.HasSection(kBtAddr4));
+}
+
+/* Test to set up btif_config_cache with invalid bt address or section name
+ * when received Invalid bt address or section, it's not allowed to put invalid
+ * section to paired devices list section
+ */
+TEST(BtifConfigCacheTest, test_set_up_config_cache_with_invalid_section) {
+  BtifConfigCache test_btif_config_cache(kCapacity);
+
+  // kBtAddrInvalid1
+  test_btif_config_cache.SetString(kBtAddrInvalid1, "Name", "Headset_1");
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddrInvalid1, "Name"));
+  EXPECT_TRUE(test_btif_config_cache.HasUnpairedSection(kBtAddrInvalid1));
+  // get the LinkKey
+  test_btif_config_cache.SetString(kBtAddrInvalid1, "LinkKey",
+                                   "1122334455667788");
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddrInvalid1, "LinkKey"));
+  EXPECT_TRUE(test_btif_config_cache.HasUnpairedSection(kBtAddrInvalid1));
+  EXPECT_FALSE(test_btif_config_cache.HasPersistentSection(kBtAddrInvalid1));
+
+  // kBtAddrInvalid2
+  test_btif_config_cache.SetString(kBtAddrInvalid2, "Name", "Headset_1");
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddrInvalid2, "Name"));
+  EXPECT_TRUE(test_btif_config_cache.HasUnpairedSection(kBtAddrInvalid2));
+  // get the LinkKey
+  test_btif_config_cache.SetString(kBtAddrInvalid2, "LinkKey",
+                                   "1122334455667788");
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddrInvalid2, "LinkKey"));
+  EXPECT_TRUE(test_btif_config_cache.HasUnpairedSection(kBtAddrInvalid2));
+  EXPECT_FALSE(test_btif_config_cache.HasPersistentSection(kBtAddrInvalid2));
+
+  // kBtAddrInvalid3
+  test_btif_config_cache.SetString(kBtAddrInvalid3, "Name", "Headset_1");
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddrInvalid3, "Name"));
+  EXPECT_TRUE(test_btif_config_cache.HasUnpairedSection(kBtAddrInvalid3));
+  // get the LinkKey
+  test_btif_config_cache.SetString(kBtAddrInvalid3, "LinkKey",
+                                   "1122334455667788");
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddrInvalid3, "LinkKey"));
+  EXPECT_TRUE(test_btif_config_cache.HasUnpairedSection(kBtAddrInvalid3));
+  EXPECT_FALSE(test_btif_config_cache.HasPersistentSection(kBtAddrInvalid3));
+
+  // kBtAddrInvalid4
+  test_btif_config_cache.SetString(kBtAddrInvalid4, "Name", "Headset_1");
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddrInvalid4, "Name"));
+  EXPECT_TRUE(test_btif_config_cache.HasUnpairedSection(kBtAddrInvalid4));
+  // get the LinkKey
+  test_btif_config_cache.SetString(kBtAddrInvalid4, "LinkKey",
+                                   "1122334455667788");
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddrInvalid4, "LinkKey"));
+  EXPECT_TRUE(test_btif_config_cache.HasUnpairedSection(kBtAddrInvalid4));
+  EXPECT_FALSE(test_btif_config_cache.HasPersistentSection(kBtAddrInvalid4));
+
+  // kBtSectionInvalid1
+  test_btif_config_cache.SetString(kBtSectionInvalid1, "Name", "Headset_1");
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtSectionInvalid1, "Name"));
+  EXPECT_TRUE(test_btif_config_cache.HasUnpairedSection(kBtSectionInvalid1));
+  // get the LinkKey
+  test_btif_config_cache.SetString(kBtSectionInvalid1, "LinkKey",
+                                   "1122334455667788");
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtSectionInvalid1, "LinkKey"));
+  EXPECT_TRUE(test_btif_config_cache.HasUnpairedSection(kBtSectionInvalid1));
+  EXPECT_FALSE(test_btif_config_cache.HasPersistentSection(kBtSectionInvalid1));
+}
+
+/* Stress test to set and get key values
+ * 1. stress test to set different type key value to unpaired device cache and
+ * get the different type key values in the unpaired cache section to check if
+ * we get the key-values the same with we set in unpaired device cache.
+ * 2. stress test to set different type key value to paired device section and
+ * get the different type key values in the paired cache section to check if we
+ * get the key-values the same with we set in paired device cache.
+ */
+TEST(BtifConfigCacheTest, test_get_set_key_value_test) {
+  BtifConfigCache test_btif_config_cache(kCapacity);
+  // test in unpaired cache
+  test_btif_config_cache.SetString(kBtAddr1, "Name", "Headset_1");
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr1, "Name"));
+  EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr1, "Name"),
+              Optional(StrEq("Headset_1")));
+
+  test_btif_config_cache.SetInt(kBtAddr1, "Property_Int", 65536);
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr1, "Property_Int"));
+  EXPECT_THAT(test_btif_config_cache.GetInt(kBtAddr1, "Property_Int"),
+              Optional(Eq(65536)));
+
+  test_btif_config_cache.SetUint64(kBtAddr1, "Property_64", 4294967296);
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr1, "Property_64"));
+  EXPECT_THAT(test_btif_config_cache.GetUint64(kBtAddr1, "Property_64"),
+              Optional(Eq(uint64_t(4294967296))));
+
+  test_btif_config_cache.SetBool(kBtAddr1, "Property_Bool", true);
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr1, "Property_Bool"));
+  EXPECT_THAT(test_btif_config_cache.GetBool(kBtAddr1, "Property_Bool"),
+              Optional(IsTrue()));
+
+  // empty value
+  test_btif_config_cache.SetString(kBtAddr1, "Name", "");
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr1, "Name"));
+  EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr1, "Name"),
+              Optional(StrEq("")));
+
+  // get the LinkKey
+  test_btif_config_cache.SetString(kBtAddr1, "LinkKey", "1122334455667788");
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr1, "LinkKey"));
+  EXPECT_FALSE(test_btif_config_cache.HasUnpairedSection(kBtAddr1));
+  EXPECT_TRUE(test_btif_config_cache.HasPersistentSection(kBtAddr1));
+
+  // test in unpaired cache
+  test_btif_config_cache.SetString(kBtAddr1, "Name", "Headset_1");
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr1, "Name"));
+  EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr1, "Name"),
+              Optional(StrEq("Headset_1")));
+
+  test_btif_config_cache.SetInt(kBtAddr1, "Property_Int", 65536);
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr1, "Property_Int"));
+  EXPECT_THAT(test_btif_config_cache.GetInt(kBtAddr1, "Property_Int"),
+              Optional(Eq(65536)));
+
+  test_btif_config_cache.SetUint64(kBtAddr1, "Property_64", 4294967296);
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr1, "Property_64"));
+  EXPECT_THAT(test_btif_config_cache.GetUint64(kBtAddr1, "Property_64"),
+              Optional(Eq(uint64_t(4294967296))));
+
+  test_btif_config_cache.SetBool(kBtAddr1, "Property_Bool", true);
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr1, "Property_Bool"));
+  EXPECT_THAT(test_btif_config_cache.GetBool(kBtAddr1, "Property_Bool"),
+              Optional(IsTrue()));
+
+  // empty section is disallowed
+  EXPECT_DEATH({ test_btif_config_cache.SetString("", "name", "Headset_1"); },
+               "Empty section not allowed");
+  // empty key is disallowed
+  EXPECT_DEATH({ test_btif_config_cache.SetString(kBtAddr1, "", "Headset_1"); },
+               "Empty key not allowed");
+  EXPECT_FALSE(test_btif_config_cache.HasKey(kBtAddr1, ""));
+  // empty value is allowed
+  test_btif_config_cache.SetString(kBtAddr1, "Name", "");
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr1, "Name"));
+  EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr1, "Name"),
+              Optional(StrEq("")));
+}
+
+/* Test to set values in the same key
+ * Receiving the same key with different values in a section, the new incoming
+ * value will be updated but the key will not be added repeatedly. test this
+ * feature in both unpaired devic cache and paired device list cache
+ */
+TEST(BtifConfigCacheTest, test_set_values_in_the_same_key) {
+  BtifConfigCache test_btif_config_cache(kCapacity);
+  // add new a key "Name"
+  test_btif_config_cache.SetString(kBtAddr1, "Name", "Headset_1");
+  EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr1, "Name"),
+              Optional(StrEq("Headset_1")));
+  EXPECT_TRUE(test_btif_config_cache.HasUnpairedSection(kBtAddr1));
+
+  // add the same key "Name" with different value
+  test_btif_config_cache.SetString(kBtAddr1, "Name", "Headset_1A");
+  EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr1, "Name"),
+              Optional(StrEq("Headset_1A")));
+
+  // add the same key "Name" with different value
+  test_btif_config_cache.SetString(kBtAddr1, "Name", "Headset_2A");
+  EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr1, "Name"),
+              Optional(StrEq("Headset_2A")));
+  EXPECT_TRUE(test_btif_config_cache.HasUnpairedSection(kBtAddr1));
+
+  // add new a key "Property_Int"
+  test_btif_config_cache.SetInt(kBtAddr1, "Property_Int", 65536);
+  EXPECT_THAT(test_btif_config_cache.GetInt(kBtAddr1, "Property_Int"),
+              Optional(Eq(65536)));
+
+  // add the same key "Property_Int" with different value
+  test_btif_config_cache.SetInt(kBtAddr1, "Property_Int", 256);
+  EXPECT_THAT(test_btif_config_cache.GetInt(kBtAddr1, "Property_Int"),
+              Optional(Eq(256)));
+
+  test_btif_config_cache.SetUint64(kBtAddr1, "Property_64", 4294967296);
+  EXPECT_THAT(test_btif_config_cache.GetUint64(kBtAddr1, "Property_64"),
+              Optional(Eq(uint64_t(4294967296))));
+
+  // get the LinkKey and set values in the same key in paired device list
+  test_btif_config_cache.SetString(kBtAddr1, "LinkKey", "1122334455667788");
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr1, "LinkKey"));
+  EXPECT_FALSE(test_btif_config_cache.HasUnpairedSection(kBtAddr1));
+  EXPECT_TRUE(test_btif_config_cache.HasPersistentSection(kBtAddr1));
+
+  // add the same key "Name" with the different value
+  test_btif_config_cache.SetString(kBtAddr1, "Name", "Headset_1A");
+  EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr1, "Name"),
+              Optional(StrEq("Headset_1A")));
+
+  // add the same key "Name" with the value different
+  test_btif_config_cache.SetString(kBtAddr1, "Name", "Headset_2A");
+  EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr1, "Name"),
+              Optional(StrEq("Headset_2A")));
+
+  test_btif_config_cache.SetInt(kBtAddr1, "Property_Int", 64);
+  EXPECT_THAT(test_btif_config_cache.GetInt(kBtAddr1, "Property_Int"),
+              Optional(Eq(64)));
+
+  test_btif_config_cache.SetUint64(kBtAddr1, "Property_64", 65537);
+  EXPECT_THAT(test_btif_config_cache.GetUint64(kBtAddr1, "Property_64"),
+              Optional(Eq(uint64_t(65537))));
+
+  EXPECT_TRUE(test_btif_config_cache.HasPersistentSection(kBtAddr1));
+}
+
+/* Stress test to pair with device then unpair device
+ * 1. paired with device by adding a "LinKey" to device and check the device be
+ * moved into paired devices list
+ * 2. unpaired with the device by removing the "LinkKey" and check the device be
+ * moved back to unpaired devices cache
+ * 3. loop for 30 times
+ */
+TEST(BtifConfigCacheTest, test_pair_unpair_device_stress_test) {
+  BtifConfigCache test_btif_config_cache(kCapacity);
+
+  // pair with Headset_1 11:22:33:44:55:66
+  test_btif_config_cache.SetString(kBtAddr1, "Name", "Headset_1");
+  EXPECT_TRUE(test_btif_config_cache.HasUnpairedSection(kBtAddr1));
+  EXPECT_FALSE(test_btif_config_cache.HasPersistentSection(kBtAddr1));
+
+  for (int i = 0; i < kTestRepeatCount; ++i) {
+    // get the LinkKey, the device will be moved from the unpaired cache to
+    // paired cache
+    test_btif_config_cache.SetString(kBtAddr1, "LinkKey", "1122334455667788");
+    EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr1, "LinkKey"));
+    EXPECT_FALSE(test_btif_config_cache.HasUnpairedSection(kBtAddr1));
+    EXPECT_TRUE(test_btif_config_cache.HasPersistentSection(kBtAddr1));
+
+    // remove the LinkKey, the device will be moved from the paired cache to
+    // unpaired cache
+    test_btif_config_cache.RemoveKey(kBtAddr1, "LinkKey");
+    EXPECT_FALSE(test_btif_config_cache.HasKey(kBtAddr1, "LinkKey"));
+    EXPECT_TRUE(test_btif_config_cache.HasUnpairedSection(kBtAddr1));
+    EXPECT_FALSE(test_btif_config_cache.HasPersistentSection(kBtAddr1));
+  }
+}
+
+/* Stress test to pair with multi-devices and unpair with multi-devices
+ * 1. Pired with 4 devices with Link-Key type key in order, to check these 4
+ * devices are in the paired devices list cache
+ * 2. unpair with these 4 devices by removed Link-Key type key in order, to
+ * check the fisrt device was ruled-out from unpaired devices cache due to
+ * capacity limitation, and other 3 devices are be moved to unpaired device
+ * cache.
+ */
+TEST(BtifConfigCacheTest, test_multi_pair_unpair_with_devices) {
+  BtifConfigCache test_btif_config_cache(kCapacity);
+  // pair with 4 bt address devices by add different type linkkey.
+  test_btif_config_cache.SetString(kBtAddr1, "name", "kBtAddr1");
+  test_btif_config_cache.SetString(kBtAddr1, "LinkKey", "1122334455667788");
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr1, "LinkKey"));
+
+  test_btif_config_cache.SetString(kBtAddr2, "name", "kBtAddr2");
+  test_btif_config_cache.SetString(kBtAddr2, "LE_KEY_PENC", "aabbccddeeff9900");
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr2, "LE_KEY_PENC"));
+
+  test_btif_config_cache.SetString(kBtAddr3, "name", "kBtAddr3");
+  test_btif_config_cache.SetString(kBtAddr3, "LE_KEY_PID", "a1b2c3d4e5feeeee");
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr3, "LE_KEY_PID"));
+
+  test_btif_config_cache.SetString(kBtAddr4, "LE_KEY_PCSRK",
+                                   "aaaabbbbccccdddd");
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr4, "LE_KEY_PCSRK"));
+
+  test_btif_config_cache.SetString(kBtAddr5, "name", "kBtAddr5");
+  test_btif_config_cache.SetString(kBtAddr5, "LE_KEY_LENC", "jilkjlkjlkn");
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr5, "LE_KEY_LENC"));
+
+  // checking these 4 devices are in paired list cache and the content are
+  // correct.
+  EXPECT_TRUE(test_btif_config_cache.HasPersistentSection(kBtAddr1));
+  EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr1, "LinkKey"),
+              Optional(StrEq("1122334455667788")));
+  EXPECT_TRUE(test_btif_config_cache.HasPersistentSection(kBtAddr2));
+  EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr2, "LE_KEY_PENC"),
+              Optional(StrEq("aabbccddeeff9900")));
+  EXPECT_TRUE(test_btif_config_cache.HasPersistentSection(kBtAddr3));
+  EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr3, "LE_KEY_PID"),
+              Optional(StrEq("a1b2c3d4e5feeeee")));
+  EXPECT_TRUE(test_btif_config_cache.HasPersistentSection(kBtAddr4));
+  EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr4, "LE_KEY_PCSRK"),
+              Optional(StrEq("aaaabbbbccccdddd")));
+  EXPECT_TRUE(test_btif_config_cache.HasPersistentSection(kBtAddr5));
+  EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr5, "LE_KEY_LENC"),
+              Optional(StrEq("jilkjlkjlkn")));
+
+  // unpair with these 4 bt address devices by removed the linkkey.
+  // unpair kBtAddr1 11:22:33:44:55:66
+  test_btif_config_cache.RemoveKey(kBtAddr1, "LinkKey");
+  EXPECT_FALSE(test_btif_config_cache.HasKey(kBtAddr1, "LinkKey"));
+  // no empty section is moved to unpaired
+  EXPECT_FALSE(test_btif_config_cache.HasPersistentSection(kBtAddr1));
+  EXPECT_TRUE(test_btif_config_cache.HasUnpairedSection(kBtAddr1));
+  EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr1, "name"),
+              Optional(StrEq("kBtAddr1")));
+
+  // unpair with kBtAddr2 aa:bb:cc:dd:ee:ff
+  test_btif_config_cache.RemoveKey(kBtAddr2, "LE_KEY_PENC");
+  EXPECT_FALSE(test_btif_config_cache.HasKey(kBtAddr2, "LE_KEY_PENC"));
+  EXPECT_FALSE(test_btif_config_cache.HasPersistentSection(kBtAddr2));
+  EXPECT_TRUE(test_btif_config_cache.HasUnpairedSection(kBtAddr2));
+  EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr2, "name"),
+              Optional(StrEq("kBtAddr2")));
+
+  // unpair with kBtAddr3 AB:CD:EF:12:34:56
+  test_btif_config_cache.RemoveKey(kBtAddr3, "LE_KEY_PID");
+  EXPECT_FALSE(test_btif_config_cache.HasKey(kBtAddr3, "LE_KEY_PID"));
+  EXPECT_FALSE(test_btif_config_cache.HasPersistentSection(kBtAddr3));
+  // no empty section is moved to unpaired
+  EXPECT_TRUE(test_btif_config_cache.HasUnpairedSection(kBtAddr3));
+  EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr3, "name"),
+              Optional(StrEq("kBtAddr3")));
+
+  // unpair with kBtAddr4 11:AA:22:BB:33:CC
+  test_btif_config_cache.RemoveKey(kBtAddr4, "LE_KEY_PCSRK");
+  EXPECT_FALSE(test_btif_config_cache.HasKey(kBtAddr4, "LE_KEY_PCSRK"));
+  EXPECT_FALSE(test_btif_config_cache.HasPersistentSection(kBtAddr4));
+  // empty section is removed
+  EXPECT_FALSE(test_btif_config_cache.HasUnpairedSection(kBtAddr4));
+
+  // unpair with kBtAddr5 11:AA:22:BB:33:CD
+  test_btif_config_cache.RemoveKey(kBtAddr5, "LE_KEY_LENC");
+  EXPECT_FALSE(test_btif_config_cache.HasKey(kBtAddr5, "LE_KEY_LENC"));
+  EXPECT_FALSE(test_btif_config_cache.HasPersistentSection(kBtAddr5));
+  // no empty section is moved to unpaired
+  EXPECT_TRUE(test_btif_config_cache.HasUnpairedSection(kBtAddr5));
+  EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr5, "name"),
+              Optional(StrEq("kBtAddr5")));
+
+  // checking the oldest unpaired device kBtAddr1 was ruled out from cache due
+  // to capacity limitation (3) in unpaired cache.
+  EXPECT_FALSE(test_btif_config_cache.HasUnpairedSection(kBtAddr1));
+}
+
+/* Test to remove sections with the specific key
+ * paired with sections with the specific "Restricted" key and then removed the
+ * "Restricted" key, check if the sections with the specific "Restricted" key
+ * are removed.
+ */
+TEST(BtifConfigCacheTest, test_remove_sections_with_key) {
+  BtifConfigCache test_btif_config_cache(kCapacity);
+  // pair with Headset_1 (kBtAddr1), Headset_2 (kBtAddr1), Heasdet_3 (kBtAddr3)
+  // , and Headset_1 (kBtAddr1), Headset_3 (kBtAddr3) have sepcific "Restricted"
+  // key
+  test_btif_config_cache.SetString(kBtAddr1, "Name", "Headset_1");
+  test_btif_config_cache.SetString(kBtAddr1, "Restricted", "1");
+  test_btif_config_cache.SetString(kBtAddr1, "LinkKey", "1122334455667788");
+  test_btif_config_cache.SetString(kBtAddr2, "Name", "Headset_2");
+  test_btif_config_cache.SetString(kBtAddr2, "LinkKey", "aabbccddeeff9900");
+  test_btif_config_cache.SetString(kBtAddr3, "Name", "Headset_3");
+  test_btif_config_cache.SetString(kBtAddr3, "LinkKey", "a1b2c3d4e5feeeee");
+  test_btif_config_cache.SetString(kBtAddr3, "Restricted", "1");
+
+  // remove sections with "Restricted" key
+  test_btif_config_cache.RemovePersistentSectionsWithKey("Restricted");
+
+  // checking the kBtAddr1 and kBtAddr3 can not be found in config cache, only
+  // keep kBtAddr2 in config cache.
+  EXPECT_FALSE(test_btif_config_cache.HasSection(kBtAddr1));
+  EXPECT_TRUE(test_btif_config_cache.HasSection(kBtAddr2));
+  EXPECT_FALSE(test_btif_config_cache.HasSection(kBtAddr3));
+}
+
+/* Test PersistentSectionCopy and Init */
+TEST(BtifConfigCacheTest, test_PersistentSectionCopy_Init) {
+  BtifConfigCache test_btif_config_cache(kCapacity);
+  config_t config_paired = {};
+  // pair with 3 bt devices, kBtAddr1, kBtAddr2, kBtAddr3
+  test_btif_config_cache.SetString(kBtAddr1, "LinkKey", "1122334455667788");
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr1, "LinkKey"));
+  EXPECT_TRUE(test_btif_config_cache.HasPersistentSection(kBtAddr1));
+  EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr1, "LinkKey"),
+              Optional(StrEq("1122334455667788")));
+
+  test_btif_config_cache.SetString(kBtAddr2, "LE_KEY_PENC", "aabbccddeeff9900");
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr2, "LE_KEY_PENC"));
+  EXPECT_TRUE(test_btif_config_cache.HasPersistentSection(kBtAddr2));
+  EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr2, "LE_KEY_PENC"),
+              Optional(StrEq("aabbccddeeff9900")));
+
+  test_btif_config_cache.SetString(kBtAddr3, "LE_KEY_PID", "a1b2c3d4e5feeeee");
+  EXPECT_TRUE(test_btif_config_cache.HasKey(kBtAddr3, "LE_KEY_PID"));
+  EXPECT_TRUE(test_btif_config_cache.HasPersistentSection(kBtAddr3));
+  EXPECT_THAT(test_btif_config_cache.GetString(kBtAddr3, "LE_KEY_PID"),
+              Optional(StrEq("a1b2c3d4e5feeeee")));
+
+  // check GetPersistentSections
+  int num_of_paired_devices = 0;
+  for (const section_t& sec : test_btif_config_cache.GetPersistentSections()) {
+    EXPECT_TRUE(test_btif_config_cache.HasPersistentSection(sec.name));
+    num_of_paired_devices++;
+  }
+  EXPECT_EQ(num_of_paired_devices, 3);
+
+  // copy persistent sections
+  int num_of_copy_paired_devices = 0;
+  config_paired = test_btif_config_cache.PersistentSectionCopy();
+  for (const section_t& sec : config_paired.sections) {
+    EXPECT_TRUE(test_btif_config_cache.HasPersistentSection(sec.name));
+    num_of_copy_paired_devices++;
+  }
+  EXPECT_EQ(num_of_copy_paired_devices, 3);
+
+  // write persistent sections to temp test config file
+  EXPECT_TRUE(config_save(config_paired, TEST_CONFIG_FILE));
+  // get persistent sections from temp test config file
+  int num_of_save_paired_devices = 0;
+  std::unique_ptr<config_t> config_source = config_new(TEST_CONFIG_FILE);
+  for (const section_t& sec : config_paired.sections) {
+    EXPECT_TRUE(test_btif_config_cache.HasPersistentSection(sec.name));
+    num_of_save_paired_devices++;
+  }
+  EXPECT_EQ(num_of_save_paired_devices, 3);
+
+  // Clear all btif config cache sections
+  test_btif_config_cache.Clear();
+
+  // move the persistent sections to btif config paired list
+  int num_of_init_paired_devices = 0;
+  test_btif_config_cache.Init(std::move(config_source));
+  for (const section_t& sec : config_paired.sections) {
+    EXPECT_TRUE(test_btif_config_cache.HasPersistentSection(sec.name));
+    num_of_init_paired_devices++;
+  }
+  EXPECT_EQ(num_of_init_paired_devices, 3);
+
+  EXPECT_TRUE(std::filesystem::remove(kTestConfigFile));
+}
+
+}  // namespace testing
diff --git a/btif/test/btif_hf_client_service_test.cc b/btif/test/btif_hf_client_service_test.cc
new file mode 100755
index 0000000..6844e86
--- /dev/null
+++ b/btif/test/btif_hf_client_service_test.cc
@@ -0,0 +1,42 @@
+#include <base/logging.h>
+#include <gtest/gtest.h>
+
+#undef LOG_TAG
+#include "btif/src/btif_hf_client.cc"
+
+static tBTA_HF_CLIENT_FEAT gFeatures;
+
+uint8_t btif_trace_level = BT_TRACE_LEVEL_WARNING;
+void LogMsg(uint32_t trace_set_mask, const char* fmt_str, ...) {}
+tBTA_STATUS BTA_HfClientEnable(tBTA_HF_CLIENT_CBACK* p_cback, tBTA_SEC sec_mask,
+                               tBTA_HF_CLIENT_FEAT features,
+                               const char* p_service_name) {
+  gFeatures = features;
+  return BTA_SUCCESS;
+}
+void BTA_HfClientDisable(void) {}
+bt_status_t btif_transfer_context(tBTIF_CBACK* p_cback, uint16_t event,
+                                  char* p_params, int param_len,
+                                  tBTIF_COPY_CBACK* p_copy_cback) {
+  return BT_STATUS_SUCCESS;
+}
+void btif_queue_advance() {}
+const char* dump_hf_client_event(uint16_t event) { return "UNKNOWN MSG ID"; }
+
+class BtifHfClientTest : public ::testing::Test {
+ protected:
+  void SetUp() override { gFeatures = BTIF_HF_CLIENT_FEATURES; }
+
+  void TearDown() override {}
+};
+
+TEST_F(BtifHfClientTest, test_btif_hf_cleint_service) {
+  bool enable = true;
+
+  osi_property_set("persist.bluetooth.hfpclient.sco_s4_supported", "true");
+  btif_hf_client_execute_service(enable);
+  EXPECT_TRUE(gFeatures & BTA_HF_CLIENT_FEAT_S4);
+  osi_property_set("persist.bluetooth.hfpclient.sco_s4_supported", "false");
+  btif_hf_client_execute_service(enable);
+  EXPECT_FALSE(gFeatures & BTA_HF_CLIENT_FEAT_S4);
+}
diff --git a/build/Android.bp b/build/Android.bp
index cde9a36..32c0f31 100644
--- a/build/Android.bp
+++ b/build/Android.bp
@@ -29,9 +29,10 @@
     },
 }
 
+// Fuzzable defaults are the subset of defaults that are used in fuzzing, which
+// requires no shared libraries, and no explicit sanitization.
 fluoride_defaults {
-    name: "fluoride_types_defaults",
-    defaults: ["libchrome_support_defaults"],
+    name: "fluoride_types_defaults_fuzzable",
     cflags: [
         "-DEXPORT_SYMBOL=__attribute__((visibility(\"default\")))",
         "-fvisibility=hidden",
@@ -54,15 +55,22 @@
 }
 
 fluoride_defaults {
-    name: "fluoride_defaults",
+    name: "fluoride_types_defaults",
+    defaults: [
+        "fluoride_types_defaults_fuzzable",
+        "libchrome_support_defaults"
+    ],
+}
+
+fluoride_defaults {
+    name: "fluoride_defaults_fuzzable",
     target: {
         android: {
             test_config_template: ":BluetoothTestConfigTemplate",
-        }
+        },
     },
-    defaults: ["fluoride_types_defaults"],
+    defaults: ["fluoride_types_defaults_fuzzable"],
     header_libs: ["libbluetooth_headers"],
-    shared_libs: ["libstatslog"],
     static_libs: [
         "libbluetooth-types",
         "libbt-platform-protos-lite",
@@ -73,6 +81,15 @@
     },
 }
 
+fluoride_defaults {
+    name: "fluoride_defaults",
+    defaults: ["fluoride_defaults_fuzzable", "fluoride_types_defaults"],
+    shared_libs: ["libstatslog"],
+    sanitize: {
+        misc_undefined: ["bounds"],
+    },
+}
+
 // Enables code coverage for a set of source files. Must be combined with
 // "clang_coverage_bin" in order to work. See //test/gen_coverage.py for more information
 // on generating code coverage.
diff --git a/build/fluoride.go b/build/fluoride.go
index 6a511a5..4ace71b 100644
--- a/build/fluoride.go
+++ b/build/fluoride.go
@@ -43,7 +43,7 @@
 	ctx.AppendProperties(p)
 }
 
-func globalDefaults(ctx android.BaseContext) ([]string, []string) {
+func globalDefaults(ctx android.LoadHookContext) ([]string, []string) {
 	var cflags []string
 	var includeDirs []string
 
diff --git a/build/toolchain/clang/get_clang_suffix.py b/build/toolchain/clang/get_clang_suffix.py
index dedacdd..fbfe16c 100644
--- a/build/toolchain/clang/get_clang_suffix.py
+++ b/build/toolchain/clang/get_clang_suffix.py
@@ -3,41 +3,43 @@
 import re
 import sys
 
-def which(cmd):
-  for p in os.environ["PATH"].split(os.pathsep):
-    clang_path = os.path.join(p, cmd)
-    if os.path.exists(clang_path):
-      return clang_path
-  return None
 
-CLANG_VERSION_REGEX=".*version\s*([0-9]*\.[0-9]*)\.*"
+def which(cmd):
+    for p in os.environ["PATH"].split(os.pathsep):
+        clang_path = os.path.join(p, cmd)
+        if os.path.exists(clang_path):
+            return clang_path
+    return None
+
+
+CLANG_VERSION_REGEX = ".*version\s*([0-9]*\.[0-9]*)\.*"
 clang_path = which("clang++")
 clang_version_major = 0
 clang_version_minor = 0
 
 if clang_path:
-  clang_version_out = subprocess.Popen([clang_path, "--version"],
-    stdout=subprocess.PIPE).communicate()[0]
-  clang_version_match = re.search(CLANG_VERSION_REGEX, clang_version_out)
-  clang_version_str = clang_version_match.group(1)
-  clang_version_array = clang_version_str.split('.')
-  clang_version_major = int(clang_version_array[0])
-  clang_version_minor = int(clang_version_array[1])
+    clang_version_out = subprocess.Popen(
+        [clang_path, "--version"], stdout=subprocess.PIPE).communicate()[0]
+    clang_version_match = re.search(CLANG_VERSION_REGEX, clang_version_out)
+    clang_version_str = clang_version_match.group(1)
+    clang_version_array = clang_version_str.split('.')
+    clang_version_major = int(clang_version_array[0])
+    clang_version_minor = int(clang_version_array[1])
 
 if clang_version_major >= 3 and clang_version_minor >= 5:
-  print ""
+    print ""
 else:
-  # Loop in support clang version only
-  clang_version_major = 3
-  clang_version_minor = 9
-  while clang_version_major >= 3 and clang_version_minor >= 5:
-    clang_version_str = "%d.%d" % (clang_version_major, clang_version_minor)
-    clang_path = which("clang++-" + clang_version_str)
-    if clang_path:
-      print clang_version_str
-      sys.exit(0)
-    clang_version_minor -= 1
-    if clang_version_minor < 0:
-      clang_version_minor = 9
-      clang_version_major -= 1
-  print "None"
+    # Loop in support clang version only
+    clang_version_major = 3
+    clang_version_minor = 9
+    while clang_version_major >= 3 and clang_version_minor >= 5:
+        clang_version_str = "%d.%d" % (clang_version_major, clang_version_minor)
+        clang_path = which("clang++-" + clang_version_str)
+        if clang_path:
+            print clang_version_str
+            sys.exit(0)
+        clang_version_minor -= 1
+        if clang_version_minor < 0:
+            clang_version_minor = 9
+            clang_version_major -= 1
+    print "None"
diff --git a/common/Android.bp b/common/Android.bp
index 202559d..e05528c 100644
--- a/common/Android.bp
+++ b/common/Android.bp
@@ -12,6 +12,7 @@
     srcs: [
         "address_obfuscator.cc",
         "message_loop_thread.cc",
+        "metric_id_allocator.cc",
         "metrics.cc",
         "once_timer.cc",
         "repeating_timer.cc",
@@ -37,11 +38,13 @@
         "system/bt",
         "system/bt/stack/include",
     ],
-    srcs : [
+    srcs: [
         "address_obfuscator_unittest.cc",
         "leaky_bonded_queue_unittest.cc",
+        "lru_unittest.cc",
         "message_loop_thread_unittest.cc",
         "metrics_unittest.cc",
+        "metric_id_allocator_unittest.cc",
         "once_timer_unittest.cc",
         "repeating_timer_unittest.cc",
         "state_machine_unittest.cc",
@@ -52,7 +55,7 @@
         "libprotobuf-cpp-lite",
         "libcrypto",
     ],
-    static_libs : [
+    static_libs: [
         "libgmock",
         "libbt-common",
         "libbt-protos-lite",
@@ -77,7 +80,7 @@
     static_libs: [
         "libgmock",
         "libosi",
-        "libbt-common"
+        "libbt-common",
     ],
 }
 
@@ -97,7 +100,7 @@
     ],
     static_libs: [
         "libosi",
-        "libbt-common"
+        "libbt-common",
     ],
 }
 
diff --git a/common/leaky_bonded_queue_unittest.cc b/common/leaky_bonded_queue_unittest.cc
index fcd55cd..b2ff35b 100644
--- a/common/leaky_bonded_queue_unittest.cc
+++ b/common/leaky_bonded_queue_unittest.cc
@@ -42,7 +42,7 @@
 class MockItem : public Item {
  public:
   MockItem(int i) : Item(i) {}
-  ~MockItem() { Destruct(); }
+  ~MockItem() override { Destruct(); }
   MOCK_METHOD0(Destruct, void());
 };
 
diff --git a/common/lru.h b/common/lru.h
new file mode 100644
index 0000000..36f8707
--- /dev/null
+++ b/common/lru.h
@@ -0,0 +1,188 @@
+/******************************************************************************
+ *
+ *  Copyright 2020 Google, Inc.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#pragma once
+
+#include <functional>
+#include <iterator>
+#include <list>
+#include <mutex>
+#include <optional>
+#include <thread>
+#include <unordered_map>
+
+#include <base/logging.h>
+
+namespace bluetooth {
+
+namespace common {
+
+template <typename K, typename V>
+class LruCache {
+ public:
+  using Node = std::pair<K, V>;
+  /**
+   * Constructor of the cache
+   *
+   * @param capacity maximum size of the cache
+   * @param log_tag, keyword to put at the head of log.
+   */
+  LruCache(const size_t& capacity, const std::string& log_tag)
+      : capacity_(capacity) {
+    if (capacity_ == 0) {
+      // don't allow invalid capacity
+      LOG(FATAL) << log_tag << " unable to have 0 LRU Cache capacity";
+    }
+  }
+
+  // delete copy constructor
+  LruCache(LruCache const&) = delete;
+  LruCache& operator=(LruCache const&) = delete;
+
+  ~LruCache() { Clear(); }
+
+  /**
+   * Clear the cache
+   */
+  void Clear() {
+    std::lock_guard<std::recursive_mutex> lock(lru_mutex_);
+    lru_map_.clear();
+    node_list_.clear();
+  }
+
+  /**
+   * Same as Get, but return an iterator to the accessed element
+   *
+   * Modifying the returned iterator does not warm up the cache
+   *
+   * @param key
+   * @return pointer to the underlying value to allow in-place modification
+   * nullptr when not found, will be invalidated when the key is evicted
+   */
+  V* Find(const K& key) {
+    std::lock_guard<std::recursive_mutex> lock(lru_mutex_);
+    auto map_iterator = lru_map_.find(key);
+    if (map_iterator == lru_map_.end()) {
+      return nullptr;
+    }
+    node_list_.splice(node_list_.begin(), node_list_, map_iterator->second);
+    return &(map_iterator->second->second);
+  }
+
+  /**
+   * Get the value of a key, and move the key to the head of cache, if there is
+   * one
+   *
+   * @param key
+   * @param value, output parameter of value of the key
+   * @return true if the cache has the key
+   */
+  bool Get(const K& key, V* value) {
+    CHECK(value != nullptr);
+    std::lock_guard<std::recursive_mutex> lock(lru_mutex_);
+    auto value_ptr = Find(key);
+    if (value_ptr == nullptr) {
+      return false;
+    }
+    *value = *value_ptr;
+    return true;
+  }
+
+  /**
+   * Check if the cache has the input key, move the key to the head
+   * if there is one
+   *
+   * @param key
+   * @return true if the cache has the key
+   */
+  bool HasKey(const K& key) {
+    std::lock_guard<std::recursive_mutex> lock(lru_mutex_);
+    return Find(key) != nullptr;
+  }
+
+  /**
+   * Put a key-value pair to the head of cache
+   *
+   * @param key
+   * @param value
+   * @return evicted node if tail value is popped, std::nullopt if no value
+   * is popped. std::optional can be treated as a boolean as well
+   */
+  std::optional<Node> Put(const K& key, V value) {
+    std::lock_guard<std::recursive_mutex> lock(lru_mutex_);
+    auto value_ptr = Find(key);
+    if (value_ptr != nullptr) {
+      // hasKey() calls get(), therefore already move the node to the head
+      *value_ptr = std::move(value);
+      return std::nullopt;
+    }
+
+    // remove tail
+    std::optional<Node> ret = std::nullopt;
+    if (lru_map_.size() == capacity_) {
+      lru_map_.erase(node_list_.back().first);
+      ret = std::move(node_list_.back());
+      node_list_.pop_back();
+    }
+    // insert to dummy next;
+    node_list_.emplace_front(key, std::move(value));
+    lru_map_.emplace(key, node_list_.begin());
+    return ret;
+  }
+
+  /**
+   * Delete a key from cache
+   *
+   * @param key
+   * @return true if deleted successfully
+   */
+  bool Remove(const K& key) {
+    std::lock_guard<std::recursive_mutex> lock(lru_mutex_);
+    auto map_iterator = lru_map_.find(key);
+    if (map_iterator == lru_map_.end()) {
+      return false;
+    }
+
+    // remove from the list
+    node_list_.erase(map_iterator->second);
+
+    // delete key from map
+    lru_map_.erase(map_iterator);
+
+    return true;
+  }
+
+  /**
+   * Return size of the cache
+   *
+   * @return size of the cache
+   */
+  int Size() const {
+    std::lock_guard<std::recursive_mutex> lock(lru_mutex_);
+    return lru_map_.size();
+  }
+
+ private:
+  std::list<Node> node_list_;
+  size_t capacity_;
+  std::unordered_map<K, typename std::list<Node>::iterator> lru_map_;
+  mutable std::recursive_mutex lru_mutex_;
+};
+
+}  // namespace common
+}  // namespace bluetooth
diff --git a/common/lru_unittest.cc b/common/lru_unittest.cc
new file mode 100644
index 0000000..6e1d5e4
--- /dev/null
+++ b/common/lru_unittest.cc
@@ -0,0 +1,260 @@
+/******************************************************************************
+ *
+ *  Copyright 2020 Google, Inc.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#include <base/logging.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <chrono>
+#include <limits>
+
+#include "common/lru.h"
+
+namespace testing {
+
+using bluetooth::common::LruCache;
+
+TEST(BluetoothLruCacheTest, LruCacheMainTest1) {
+  int* value = new int(0);
+  LruCache<int, int> cache(3, "testing");  // capacity = 3;
+  cache.Put(1, 10);
+  EXPECT_EQ(cache.Size(), 1);
+  EXPECT_FALSE(cache.Put(2, 20));
+  EXPECT_FALSE(cache.Put(3, 30));
+  EXPECT_EQ(cache.Size(), 3);
+
+  // 1, 2, 3 should be in cache
+  EXPECT_TRUE(cache.Get(1, value));
+  EXPECT_EQ(*value, 10);
+  EXPECT_TRUE(cache.Get(2, value));
+  EXPECT_EQ(*value, 20);
+  EXPECT_TRUE(cache.Get(3, value));
+  EXPECT_EQ(*value, 30);
+  EXPECT_EQ(cache.Size(), 3);
+
+  EXPECT_THAT(cache.Put(4, 40), Optional(Pair(1, 10)));
+  // 2, 3, 4 should be in cache, 1 is evicted
+  EXPECT_FALSE(cache.Get(1, value));
+  EXPECT_TRUE(cache.Get(4, value));
+  EXPECT_EQ(*value, 40);
+  EXPECT_TRUE(cache.Get(2, value));
+  EXPECT_EQ(*value, 20);
+  EXPECT_TRUE(cache.Get(3, value));
+  EXPECT_EQ(*value, 30);
+
+  EXPECT_THAT(cache.Put(5, 50), Optional(Pair(4, 40)));
+  EXPECT_EQ(cache.Size(), 3);
+  // 2, 3, 5 should be in cache, 4 is evicted
+
+  EXPECT_TRUE(cache.Remove(3));
+  EXPECT_FALSE(cache.Put(6, 60));
+  // 2, 5, 6 should be in cache
+
+  EXPECT_FALSE(cache.Get(3, value));
+  EXPECT_FALSE(cache.Get(4, value));
+  EXPECT_TRUE(cache.Get(2, value));
+  EXPECT_EQ(*value, 20);
+  EXPECT_TRUE(cache.Get(5, value));
+  EXPECT_EQ(*value, 50);
+  EXPECT_TRUE(cache.Get(6, value));
+  EXPECT_EQ(*value, 60);
+}
+
+TEST(BluetoothLruCacheTest, LruCacheMainTest2) {
+  int* value = new int(0);
+  LruCache<int, int> cache(2, "testing");  // size = 2;
+  EXPECT_FALSE(cache.Put(1, 10));
+  EXPECT_FALSE(cache.Put(2, 20));
+  EXPECT_THAT(cache.Put(3, 30), Optional(Pair(1, 10)));
+  EXPECT_FALSE(cache.Put(2, 200));
+  EXPECT_EQ(cache.Size(), 2);
+  // 3, 2 should be in cache
+
+  EXPECT_FALSE(cache.HasKey(1));
+  EXPECT_TRUE(cache.Get(2, value));
+  EXPECT_EQ(*value, 200);
+  EXPECT_TRUE(cache.Get(3, value));
+  EXPECT_EQ(*value, 30);
+
+  EXPECT_THAT(cache.Put(4, 40), Optional(Pair(2, 200)));
+  // 3, 4 should be in cache
+
+  EXPECT_FALSE(cache.HasKey(2));
+  EXPECT_TRUE(cache.Get(3, value));
+  EXPECT_EQ(*value, 30);
+  EXPECT_TRUE(cache.Get(4, value));
+  EXPECT_EQ(*value, 40);
+
+  EXPECT_TRUE(cache.Remove(4));
+  EXPECT_EQ(cache.Size(), 1);
+  cache.Put(2, 2000);
+  // 3, 2 should be in cache
+
+  EXPECT_FALSE(cache.HasKey(4));
+  EXPECT_TRUE(cache.Get(3, value));
+  EXPECT_EQ(*value, 30);
+  EXPECT_TRUE(cache.Get(2, value));
+  EXPECT_EQ(*value, 2000);
+
+  EXPECT_TRUE(cache.Remove(2));
+  EXPECT_TRUE(cache.Remove(3));
+  cache.Put(5, 50);
+  cache.Put(1, 100);
+  cache.Put(1, 1000);
+  EXPECT_EQ(cache.Size(), 2);
+  // 1, 5 should be in cache
+
+  EXPECT_FALSE(cache.HasKey(2));
+  EXPECT_FALSE(cache.HasKey(3));
+  EXPECT_TRUE(cache.Get(1, value));
+  EXPECT_EQ(*value, 1000);
+  EXPECT_TRUE(cache.Get(5, value));
+  EXPECT_EQ(*value, 50);
+}
+
+TEST(BluetoothLruCacheTest, LruCacheFindTest) {
+  LruCache<int, int> cache(10, "testing");
+  cache.Put(1, 10);
+  cache.Put(2, 20);
+  int value = 0;
+  EXPECT_TRUE(cache.Get(1, &value));
+  EXPECT_EQ(value, 10);
+  auto value_ptr = cache.Find(1);
+  EXPECT_NE(value_ptr, nullptr);
+  *value_ptr = 20;
+  EXPECT_TRUE(cache.Get(1, &value));
+  EXPECT_EQ(value, 20);
+  cache.Put(1, 40);
+  EXPECT_EQ(*value_ptr, 40);
+  EXPECT_EQ(cache.Find(10), nullptr);
+}
+
+TEST(BluetoothLruCacheTest, LruCacheGetTest) {
+  LruCache<int, int> cache(10, "testing");
+  cache.Put(1, 10);
+  cache.Put(2, 20);
+  int value = 0;
+  EXPECT_TRUE(cache.Get(1, &value));
+  EXPECT_EQ(value, 10);
+  EXPECT_TRUE(cache.HasKey(1));
+  EXPECT_TRUE(cache.HasKey(2));
+  EXPECT_FALSE(cache.HasKey(3));
+  EXPECT_FALSE(cache.Get(3, &value));
+  EXPECT_EQ(value, 10);
+}
+
+TEST(BluetoothLruCacheTest, LruCacheRemoveTest) {
+  LruCache<int, int> cache(10, "testing");
+  for (int key = 0; key <= 30; key++) {
+    cache.Put(key, key * 100);
+  }
+  for (int key = 0; key <= 20; key++) {
+    EXPECT_FALSE(cache.HasKey(key));
+  }
+  for (int key = 21; key <= 30; key++) {
+    EXPECT_TRUE(cache.HasKey(key));
+  }
+  for (int key = 21; key <= 30; key++) {
+    EXPECT_TRUE(cache.Remove(key));
+  }
+  for (int key = 21; key <= 30; key++) {
+    EXPECT_FALSE(cache.HasKey(key));
+  }
+}
+
+TEST(BluetoothLruCacheTest, LruCacheClearTest) {
+  LruCache<int, int> cache(10, "testing");
+  for (int key = 0; key < 10; key++) {
+    cache.Put(key, key * 100);
+  }
+  for (int key = 0; key < 10; key++) {
+    EXPECT_TRUE(cache.HasKey(key));
+  }
+  cache.Clear();
+  for (int key = 0; key < 10; key++) {
+    EXPECT_FALSE(cache.HasKey(key));
+  }
+
+  for (int key = 0; key < 10; key++) {
+    cache.Put(key, key * 1000);
+  }
+  for (int key = 0; key < 10; key++) {
+    EXPECT_TRUE(cache.HasKey(key));
+  }
+}
+
+TEST(BluetoothLruCacheTest, LruCachePressureTest) {
+  auto started = std::chrono::high_resolution_clock::now();
+  int max_size = 0xFFFFF;  // 2^20 = 1M
+  LruCache<int, int> cache(static_cast<size_t>(max_size), "testing");
+
+  // fill the cache
+  for (int key = 0; key < max_size; key++) {
+    cache.Put(key, key);
+  }
+
+  // make sure the cache is full
+  for (int key = 0; key < max_size; key++) {
+    EXPECT_TRUE(cache.HasKey(key));
+  }
+
+  // refresh the entire cache
+  for (int key = 0; key < max_size; key++) {
+    int new_key = key + max_size;
+    cache.Put(new_key, new_key);
+    EXPECT_FALSE(cache.HasKey(key));
+    EXPECT_TRUE(cache.HasKey(new_key));
+  }
+
+  // clear the entire cache
+  int* value = new int(0);
+  for (int key = max_size; key < 2 * max_size; key++) {
+    EXPECT_TRUE(cache.Get(key, value));
+    EXPECT_EQ(*value, key);
+    EXPECT_TRUE(cache.Remove(key));
+  }
+  EXPECT_EQ(cache.Size(), 0);
+
+  // test execution time
+  auto done = std::chrono::high_resolution_clock::now();
+  int execution_time =
+      std::chrono::duration_cast<std::chrono::milliseconds>(done - started)
+          .count();
+  // always around 750ms on flame. 1400 ms on crosshatch, 6800 ms on presubmit
+  // Shouldn't be more than 10000ms
+  EXPECT_LT(execution_time, 10000);
+}
+
+TEST(BluetoothLruCacheTest, BluetoothLruMultiThreadPressureTest) {
+  LruCache<int, int> cache(100, "testing");
+  auto pointer = &cache;
+  // make sure no deadlock
+  std::vector<std::thread> workers;
+  for (int key = 0; key < 100; key++) {
+    workers.push_back(std::thread([key, pointer]() {
+      pointer->Put(key, key);
+      EXPECT_TRUE(pointer->HasKey(key));
+      EXPECT_TRUE(pointer->Remove(key));
+    }));
+  }
+  for (auto& worker : workers) {
+    worker.join();
+  }
+  EXPECT_EQ(cache.Size(), 0);
+}
+
+}  // namespace testing
diff --git a/common/metric_id_allocator.cc b/common/metric_id_allocator.cc
new file mode 100644
index 0000000..49b11cc
--- /dev/null
+++ b/common/metric_id_allocator.cc
@@ -0,0 +1,205 @@
+/******************************************************************************
+ *
+ *  Copyright 2020 Google, Inc.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#include <base/logging.h>
+#include <functional>
+#include <mutex>
+#include <thread>
+
+#include "metric_id_allocator.h"
+
+namespace bluetooth {
+
+namespace common {
+
+const std::string MetricIdAllocator::LOGGING_TAG = "BluetoothMetricIdAllocator";
+const size_t MetricIdAllocator::kMaxNumUnpairedDevicesInMemory = 200;
+const size_t MetricIdAllocator::kMaxNumPairedDevicesInMemory = 65000;
+const int MetricIdAllocator::kMinId = 1;
+const int MetricIdAllocator::kMaxId = 65534;  // 2^16 - 2
+
+// id space should always be larger than kMaxNumPairedDevicesInMemory +
+// kMaxNumUnpairedDevicesInMemory
+static_assert((MetricIdAllocator::kMaxNumUnpairedDevicesInMemory +
+               MetricIdAllocator::kMaxNumPairedDevicesInMemory) <
+                  (MetricIdAllocator::kMaxId - MetricIdAllocator::kMinId),
+              "id space should always be larger than "
+              "kMaxNumPairedDevicesInMemory + MaxNumUnpairedDevicesInMemory");
+
+MetricIdAllocator::MetricIdAllocator()
+    : paired_device_cache_(kMaxNumPairedDevicesInMemory, LOGGING_TAG),
+      temporary_device_cache_(kMaxNumUnpairedDevicesInMemory, LOGGING_TAG) {}
+
+bool MetricIdAllocator::Init(
+    const std::unordered_map<RawAddress, int>& paired_device_map,
+    Callback save_id_callback, Callback forget_device_callback) {
+  std::lock_guard<std::mutex> lock(id_allocator_mutex_);
+  if (initialized_) {
+    return false;
+  }
+
+  // init paired_devices_map
+  if (paired_device_map.size() > kMaxNumPairedDevicesInMemory) {
+    LOG(FATAL)
+        << LOGGING_TAG
+        << "Paired device map is bigger than kMaxNumPairedDevicesInMemory";
+    // fail loudly to let caller know
+    return false;
+  }
+
+  next_id_ = kMinId;
+  for (const auto& p : paired_device_map) {
+    if (p.second < kMinId || p.second > kMaxId) {
+      LOG(FATAL) << LOGGING_TAG << "Invalid Bluetooth Metric Id in config";
+    }
+    auto evicted = paired_device_cache_.Put(p.first, p.second);
+    if (evicted) {
+      ForgetDevicePostprocess(evicted->first, evicted->second);
+    }
+    id_set_.insert(p.second);
+    next_id_ = std::max(next_id_, p.second + 1);
+  }
+  if (next_id_ > kMaxId) {
+    next_id_ = kMinId;
+  }
+
+  // init callbacks
+  save_id_callback_ = save_id_callback;
+  forget_device_callback_ = forget_device_callback;
+
+  return initialized_ = true;
+}
+
+MetricIdAllocator::~MetricIdAllocator() { Close(); }
+
+bool MetricIdAllocator::Close() {
+  std::lock_guard<std::mutex> lock(id_allocator_mutex_);
+  if (!initialized_) {
+    return false;
+  }
+  paired_device_cache_.Clear();
+  temporary_device_cache_.Clear();
+  id_set_.clear();
+  initialized_ = false;
+  return true;
+}
+
+MetricIdAllocator& MetricIdAllocator::GetInstance() {
+  static MetricIdAllocator metric_id_allocator;
+  return metric_id_allocator;
+}
+
+bool MetricIdAllocator::IsEmpty() const {
+  std::lock_guard<std::mutex> lock(id_allocator_mutex_);
+  return paired_device_cache_.Size() == 0 &&
+         temporary_device_cache_.Size() == 0;
+}
+
+// call this function when a new device is scanned
+int MetricIdAllocator::AllocateId(const RawAddress& mac_address) {
+  std::lock_guard<std::mutex> lock(id_allocator_mutex_);
+  int id = 0;
+  // if already have an id, return it
+  if (paired_device_cache_.Get(mac_address, &id)) {
+    return id;
+  }
+  if (temporary_device_cache_.Get(mac_address, &id)) {
+    return id;
+  }
+
+  // find next available id
+  while (id_set_.count(next_id_) > 0) {
+    next_id_++;
+    if (next_id_ > kMaxId) {
+      next_id_ = kMinId;
+      LOG(WARNING) << LOGGING_TAG << "Bluetooth metric id overflow.";
+    }
+  }
+  id = next_id_++;
+  id_set_.insert(id);
+  auto evicted = temporary_device_cache_.Put(mac_address, id);
+  if (evicted) {
+    this->id_set_.erase(evicted->second);
+  }
+
+  if (next_id_ > kMaxId) {
+    next_id_ = kMinId;
+  }
+  return id;
+}
+
+// call this function when a device is paired
+bool MetricIdAllocator::SaveDevice(const RawAddress& mac_address) {
+  std::lock_guard<std::mutex> lock(id_allocator_mutex_);
+  int id = 0;
+  if (paired_device_cache_.Get(mac_address, &id)) {
+    return true;
+  }
+  if (!temporary_device_cache_.Get(mac_address, &id)) {
+    LOG(ERROR) << LOGGING_TAG
+               << "Failed to save device because device is not in "
+               << "temporary_device_cache_";
+    return false;
+  }
+  if (!temporary_device_cache_.Remove(mac_address)) {
+    LOG(ERROR) << LOGGING_TAG
+               << "Failed to remove device from temporary_device_cache_";
+    return false;
+  }
+  auto evicted = paired_device_cache_.Put(mac_address, id);
+  if (evicted) {
+    ForgetDevicePostprocess(evicted->first, evicted->second);
+  }
+  if (!save_id_callback_(mac_address, id)) {
+    LOG(ERROR) << LOGGING_TAG
+               << "Callback returned false after saving the device";
+    return false;
+  }
+  return true;
+}
+
+// call this function when a device is forgotten
+void MetricIdAllocator::ForgetDevice(const RawAddress& mac_address) {
+  std::lock_guard<std::mutex> lock(id_allocator_mutex_);
+  int id = 0;
+  if (!paired_device_cache_.Get(mac_address, &id)) {
+    LOG(ERROR) << LOGGING_TAG
+               << "Failed to forget device because device is not in "
+               << "paired_device_cache_";
+    return;
+  }
+  if (!paired_device_cache_.Remove(mac_address)) {
+    LOG(ERROR) << LOGGING_TAG
+               << "Failed to remove device from paired_device_cache_";
+    return;
+  }
+  ForgetDevicePostprocess(mac_address, id);
+}
+
+bool MetricIdAllocator::IsValidId(const int id) {
+  return id >= kMinId && id <= kMaxId;
+}
+
+void MetricIdAllocator::ForgetDevicePostprocess(const RawAddress& mac_address,
+                                                const int id) {
+  id_set_.erase(id);
+  forget_device_callback_(mac_address, id);
+}
+
+}  // namespace common
+}  // namespace bluetooth
diff --git a/common/metric_id_allocator.h b/common/metric_id_allocator.h
new file mode 100644
index 0000000..63236e4
--- /dev/null
+++ b/common/metric_id_allocator.h
@@ -0,0 +1,139 @@
+/******************************************************************************
+ *
+ *  Copyright 2020 Google, Inc.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#pragma once
+
+#include <mutex>
+#include <string>
+#include <thread>
+#include <unordered_set>
+#include "raw_address.h"
+
+#include "lru.h"
+
+namespace bluetooth {
+
+namespace common {
+
+class MetricIdAllocator {
+ public:
+  using Callback = std::function<bool(const RawAddress& address, const int id)>;
+
+  static const size_t kMaxNumUnpairedDevicesInMemory;
+  static const size_t kMaxNumPairedDevicesInMemory;
+
+  static const int kMinId;
+  static const int kMaxId;
+
+  ~MetricIdAllocator();
+
+  /**
+   * Get the instance of singleton
+   *
+   * @return MetricIdAllocator&
+   */
+  static MetricIdAllocator& GetInstance();
+
+  /**
+   * Initialize the allocator
+   *
+   * @param paired_device_map map from mac_address to id already saved
+   * in the disk before init
+   * @param save_id_callback a callback that will be called after successfully
+   * saving id for a paired device
+   * @param forget_device_callback a callback that will be called after
+   * successful id deletion for forgotten device,
+   * @return true if successfully initialized
+   */
+  bool Init(const std::unordered_map<RawAddress, int>& paired_device_map,
+            Callback save_id_callback, Callback forget_device_callback);
+
+  /**
+   * Close the allocator. should be called when Bluetooth process is killed
+   *
+   * @return true if successfully close
+   */
+  bool Close();
+
+  /**
+   * Check if no id saved in memory
+   *
+   * @return true if no id is saved
+   */
+  bool IsEmpty() const;
+
+  /**
+   * Allocate an id for a scanned device, or return the id if there is already
+   * one
+   *
+   * @param mac_address mac address of Bluetooth device
+   * @return the id of device
+   */
+  int AllocateId(const RawAddress& mac_address);
+
+  /**
+   * Save the id for a paired device
+   *
+   * @param mac_address mac address of Bluetooth device
+   * @return true if save successfully
+   */
+  bool SaveDevice(const RawAddress& mac_address);
+
+  /**
+   * Delete the id for a device to be forgotten
+   *
+   * @param mac_address mac address of Bluetooth device
+   */
+  void ForgetDevice(const RawAddress& mac_address);
+
+  /**
+   * Check if an id is valid.
+   * The id should be less than or equal to kMaxId and bigger than or equal to
+   * kMinId
+   *
+   * @param mac_address mac address of Bluetooth device
+   * @return true if delete successfully
+   */
+  static bool IsValidId(const int id);
+
+ protected:
+  // Singleton
+  MetricIdAllocator();
+
+ private:
+  static const std::string LOGGING_TAG;
+  mutable std::mutex id_allocator_mutex_;
+
+  LruCache<RawAddress, int> paired_device_cache_;
+  LruCache<RawAddress, int> temporary_device_cache_;
+  std::unordered_set<int> id_set_;
+
+  int next_id_{kMinId};
+  bool initialized_{false};
+  Callback save_id_callback_;
+  Callback forget_device_callback_;
+
+  void ForgetDevicePostprocess(const RawAddress& mac_address, const int id);
+
+  // delete copy constructor for singleton
+  MetricIdAllocator(MetricIdAllocator const&) = delete;
+  MetricIdAllocator& operator=(MetricIdAllocator const&) = delete;
+};
+
+}  // namespace common
+}  // namespace bluetooth
diff --git a/common/metric_id_allocator_unittest.cc b/common/metric_id_allocator_unittest.cc
new file mode 100644
index 0000000..ccc1576
--- /dev/null
+++ b/common/metric_id_allocator_unittest.cc
@@ -0,0 +1,473 @@
+/******************************************************************************
+ *
+ *  Copyright 2020 Google, Inc.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#include <base/logging.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <thread>
+
+#include "common/metric_id_allocator.h"
+
+namespace testing {
+
+using bluetooth::common::MetricIdAllocator;
+
+RawAddress kthAddress(uint32_t k) {
+  uint8_t array[6] = {0, 0, 0, 0, 0, 0};
+  for (int i = 5; i >= 2; i--) {
+    array[i] = k % 256;
+    k = k / 256;
+  }
+  RawAddress addr(array);
+  return addr;
+}
+
+std::unordered_map<RawAddress, int> generateAddresses(const uint32_t num) {
+  // generate first num of mac address -> id pairs
+  // input may is always valid 256^6 = 2^48 > 2^32
+  std::unordered_map<RawAddress, int> device_map;
+  for (size_t key = 0; key < num; key++) {
+    device_map[kthAddress(key)] = key + MetricIdAllocator::kMinId;
+  }
+  return device_map;
+}
+
+TEST(BluetoothMetricIdAllocatorTest, MetricIdAllocatorInitCloseTest) {
+  auto& allocator = MetricIdAllocator::GetInstance();
+  std::unordered_map<RawAddress, int> paired_device_map;
+  MetricIdAllocator::Callback callback = [](const RawAddress&, const int) {
+    return true;
+  };
+  EXPECT_TRUE(allocator.Init(paired_device_map, callback, callback));
+  EXPECT_FALSE(allocator.Init(paired_device_map, callback, callback));
+  EXPECT_TRUE(allocator.Close());
+}
+
+TEST(BluetoothMetricIdAllocatorTest, MetricIdAllocatorNotCloseTest) {
+  auto& allocator = MetricIdAllocator::GetInstance();
+  std::unordered_map<RawAddress, int> paired_device_map;
+  MetricIdAllocator::Callback callback = [](const RawAddress&, const int) {
+    return true;
+  };
+  EXPECT_TRUE(allocator.Init(paired_device_map, callback, callback));
+
+  // should fail because it isn't closed
+  EXPECT_FALSE(allocator.Init(paired_device_map, callback, callback));
+  EXPECT_TRUE(allocator.Close());
+}
+
+TEST(BluetoothMetricIdAllocatorTest, MetricIdAllocatorScanDeviceFromEmptyTest) {
+  auto& allocator = MetricIdAllocator::GetInstance();
+  std::unordered_map<RawAddress, int> paired_device_map;
+  MetricIdAllocator::Callback callback = [](const RawAddress&, const int) {
+    return true;
+  };
+  // test empty map, next id should be kMinId
+  EXPECT_TRUE(allocator.Init(paired_device_map, callback, callback));
+  EXPECT_EQ(allocator.AllocateId(kthAddress(0)), MetricIdAllocator::kMinId);
+  EXPECT_EQ(allocator.AllocateId(kthAddress(1)), MetricIdAllocator::kMinId + 1);
+  EXPECT_EQ(allocator.AllocateId(kthAddress(0)), MetricIdAllocator::kMinId);
+  EXPECT_EQ(allocator.AllocateId(kthAddress(2)), MetricIdAllocator::kMinId + 2);
+  EXPECT_TRUE(allocator.Close());
+}
+
+TEST(BluetoothMetricIdAllocatorTest,
+     MetricIdAllocatorScanDeviceFromFilledTest) {
+  auto& allocator = MetricIdAllocator::GetInstance();
+  std::unordered_map<RawAddress, int> paired_device_map;
+  MetricIdAllocator::Callback callback = [](const RawAddress&, const int) {
+    return true;
+  };
+  int id = static_cast<int>(MetricIdAllocator::kMaxNumPairedDevicesInMemory) +
+           MetricIdAllocator::kMinId;
+  // next id should be MetricIdAllocator::kMaxNumPairedDevicesInMemory
+  paired_device_map =
+      generateAddresses(MetricIdAllocator::kMaxNumPairedDevicesInMemory);
+  EXPECT_TRUE(allocator.Init(paired_device_map, callback, callback));
+  // try new values not in the map, should get new id.
+  EXPECT_EQ(allocator.AllocateId(kthAddress(INT_MAX)), id);
+  EXPECT_EQ(allocator.AllocateId(kthAddress(INT_MAX - 1)), id + 1);
+  EXPECT_EQ(allocator.AllocateId(kthAddress(INT_MAX)), id);
+  EXPECT_EQ(allocator.AllocateId(kthAddress(INT_MAX - 2)), id + 2);
+  EXPECT_TRUE(allocator.Close());
+}
+
+TEST(BluetoothMetricIdAllocatorTest, MetricIdAllocatorAllocateExistingTest) {
+  auto& allocator = MetricIdAllocator::GetInstance();
+  std::unordered_map<RawAddress, int> paired_device_map =
+      generateAddresses(MetricIdAllocator::kMaxNumPairedDevicesInMemory);
+
+  MetricIdAllocator::Callback callback = [](const RawAddress&, const int) {
+    return true;
+  };
+  int id = MetricIdAllocator::kMinId;
+  // next id should be MetricIdAllocator::kMaxNumPairedDevicesInMemory
+  EXPECT_TRUE(allocator.Init(paired_device_map, callback, callback));
+
+  // try values already in the map, should get new id.
+  EXPECT_EQ(allocator.AllocateId(RawAddress({0, 0, 0, 0, 0, 0})), id);
+  EXPECT_EQ(allocator.AllocateId(RawAddress({0, 0, 0, 0, 0, 1})), id + 1);
+  EXPECT_EQ(allocator.AllocateId(RawAddress({0, 0, 0, 0, 0, 0})), id);
+  EXPECT_EQ(allocator.AllocateId(RawAddress({0, 0, 0, 0, 0, 2})), id + 2);
+  EXPECT_TRUE(allocator.Close());
+}
+
+TEST(BluetoothMetricIdAllocatorTest, MetricIdAllocatorMainTest1) {
+  auto& allocator = MetricIdAllocator::GetInstance();
+  std::unordered_map<RawAddress, int> paired_device_map;
+  int dummy = 22;
+  int* pointer = &dummy;
+  MetricIdAllocator::Callback save_callback = [pointer](const RawAddress&,
+                                                        const int) {
+    *pointer = *pointer * 2;
+    return true;
+  };
+  MetricIdAllocator::Callback forget_callback = [pointer](const RawAddress&,
+                                                          const int) {
+    *pointer = *pointer / 2;
+    return true;
+  };
+
+  EXPECT_TRUE(
+      allocator.Init(paired_device_map, save_callback, forget_callback));
+  EXPECT_EQ(allocator.AllocateId(RawAddress({0, 0, 0, 0, 0, 0})),
+            MetricIdAllocator::kMinId);
+  // save it and make sure the callback is called
+  EXPECT_TRUE(allocator.SaveDevice(RawAddress({0, 0, 0, 0, 0, 0})));
+  EXPECT_EQ(dummy, 44);
+
+  // should fail, since id of device is not allocated
+  EXPECT_FALSE(allocator.SaveDevice(RawAddress({0, 0, 0, 0, 0, 1})));
+  EXPECT_EQ(dummy, 44);
+
+  // save it and make sure the callback is called
+  EXPECT_EQ(allocator.AllocateId(RawAddress({0, 0, 0, 0, 0, 2})),
+            MetricIdAllocator::kMinId + 1);
+  EXPECT_EQ(allocator.AllocateId(RawAddress({0, 0, 0, 0, 0, 3})),
+            MetricIdAllocator::kMinId + 2);
+  EXPECT_TRUE(allocator.SaveDevice(RawAddress({0, 0, 0, 0, 0, 2})));
+  EXPECT_EQ(dummy, 88);
+  EXPECT_TRUE(allocator.SaveDevice(RawAddress({0, 0, 0, 0, 0, 3})));
+  EXPECT_EQ(dummy, 176);
+
+  // should be true but callback won't be called, since id had been saved
+  EXPECT_TRUE(allocator.SaveDevice(RawAddress({0, 0, 0, 0, 0, 0})));
+  EXPECT_EQ(dummy, 176);
+
+  // forget
+  allocator.ForgetDevice(RawAddress({0, 0, 0, 0, 0, 1}));
+  EXPECT_EQ(dummy, 176);
+  allocator.ForgetDevice(RawAddress({0, 0, 0, 0, 0, 2}));
+  EXPECT_EQ(dummy, 88);
+
+  EXPECT_TRUE(allocator.Close());
+}
+
+TEST(BluetoothMetricIdAllocatorTest, MetricIdAllocatorFullPairedMap) {
+  auto& allocator = MetricIdAllocator::GetInstance();
+  // preset a full map
+  std::unordered_map<RawAddress, int> paired_device_map =
+      generateAddresses(MetricIdAllocator::kMaxNumPairedDevicesInMemory);
+  int dummy = 243;
+  int* pointer = &dummy;
+  MetricIdAllocator::Callback save_callback = [pointer](const RawAddress&,
+                                                        const int) {
+    *pointer = *pointer * 2;
+    return true;
+  };
+  MetricIdAllocator::Callback forget_callback = [pointer](const RawAddress&,
+                                                          const int) {
+    *pointer = *pointer / 3;
+    return true;
+  };
+
+  EXPECT_TRUE(
+      allocator.Init(paired_device_map, save_callback, forget_callback));
+
+  // check if all preset ids are there.
+  // comments based on kMaxNumPairedDevicesInMemory = 200. It can change.
+  int key = 0;
+  for (key = 0;
+       key < static_cast<int>(MetricIdAllocator::kMaxNumPairedDevicesInMemory);
+       key++) {
+    EXPECT_EQ(allocator.AllocateId(kthAddress(key)),
+              key + MetricIdAllocator::kMinId);
+  }
+  // paired: 0, 1, 2 ... 199,
+  // scanned:
+
+  int id = static_cast<int>(MetricIdAllocator::kMaxNumPairedDevicesInMemory +
+                            MetricIdAllocator::kMinId);
+  // next id should be MetricIdAllocator::kMaxNumPairedDevicesInMemory +
+  // MetricIdAllocator::kMinId
+
+  EXPECT_EQ(allocator.AllocateId(kthAddress(key)), id++);
+  // paired: 0, 1, 2 ... 199,
+  // scanned: 200
+
+  // save it and make sure the callback is called
+  EXPECT_TRUE(allocator.SaveDevice(kthAddress(key)));
+  EXPECT_EQ(dummy, 162);  // one key is evicted, another key is saved so *2/3
+
+  // paired: 1, 2 ... 199, 200,
+  // scanned:
+
+  EXPECT_EQ(allocator.AllocateId(kthAddress(0)), id++);
+  // paired: 1, 2 ... 199, 200
+  // scanned: 0
+
+  // key == 200
+  // should fail, since id of device is not allocated
+  EXPECT_FALSE(allocator.SaveDevice(kthAddress(key + 1)));
+  EXPECT_EQ(dummy, 162);
+  // paired: 1, 2 ... 199, 200,
+  // scanned: 0
+
+  EXPECT_EQ(allocator.AllocateId(kthAddress(key + 1)), id++);
+  EXPECT_TRUE(allocator.SaveDevice(kthAddress(key + 1)));
+  EXPECT_EQ(dummy, 108);  // one key is evicted, another key is saved so *2/3,
+  // paired: 2 ... 199, 200, 201
+  // scanned: 0
+
+  EXPECT_EQ(allocator.AllocateId(kthAddress(1)), id++);
+  // paired: 2 ... 199, 200, 201,
+  // scanned: 0, 1
+
+  // save it and make sure the callback is called
+  EXPECT_EQ(allocator.AllocateId(kthAddress(key + 2)), id++);
+  EXPECT_EQ(allocator.AllocateId(kthAddress(key + 3)), id++);
+  // paired: 2 ... 199, 200, 201,
+  // scanned: 0, 1, 202, 203
+
+  dummy = 9;
+  EXPECT_TRUE(allocator.SaveDevice(kthAddress(key + 2)));
+  EXPECT_EQ(dummy, 6);  // one key is evicted, another key is saved so *2/3,
+  EXPECT_TRUE(allocator.SaveDevice(kthAddress(key + 3)));
+  EXPECT_EQ(dummy, 4);  // one key is evicted, another key is saved so *2/3,
+  // paired: 4 ... 199, 200, 201, 202, 203
+  // scanned: 0, 1
+
+  // should be true but callback won't be called, since id had been saved
+  EXPECT_TRUE(allocator.SaveDevice(kthAddress(key + 2)));
+  EXPECT_EQ(dummy, 4);
+
+  dummy = 27;
+  // forget
+  allocator.ForgetDevice(kthAddress(key + 200));
+  EXPECT_EQ(dummy, 27);  // should fail, no such a key
+  allocator.ForgetDevice(kthAddress(key + 2));
+  EXPECT_EQ(dummy, 9);
+  // paired: 4 ... 199, 200, 201, 203
+  // scanned: 0, 1
+
+  // save it and make sure the callback is called
+  EXPECT_EQ(allocator.AllocateId(kthAddress(key + 2)), id++);
+  EXPECT_EQ(allocator.AllocateId(kthAddress(key + 4)), id++);
+  EXPECT_EQ(allocator.AllocateId(kthAddress(key + 5)), id++);
+  // paired: 4 ... 199, 200, 201, 203
+  // scanned: 0, 1, 202, 204, 205
+
+  EXPECT_TRUE(allocator.SaveDevice(kthAddress(key + 2)));
+  EXPECT_EQ(dummy, 18);  // no key is evicted, a key is saved so *2,
+
+  // should be true but callback won't be called, since id had been saved
+  EXPECT_TRUE(allocator.SaveDevice(kthAddress(key + 3)));
+  EXPECT_EQ(dummy, 18);  // no such a key in scanned
+  EXPECT_TRUE(allocator.SaveDevice(kthAddress(key + 4)));
+  EXPECT_EQ(dummy, 12);  // one key is evicted, another key is saved so *2/3,
+  // paired: 5 6 ... 199, 200, 201, 203, 202, 204
+  // scanned: 0, 1, 205
+
+  // verify paired:
+  for (key = 5; key <= 199; key++) {
+    dummy = 3;
+    allocator.ForgetDevice(kthAddress(key));
+    EXPECT_EQ(dummy, 1);
+  }
+  for (size_t k = MetricIdAllocator::kMaxNumPairedDevicesInMemory;
+       k <= MetricIdAllocator::kMaxNumPairedDevicesInMemory + 4; k++) {
+    dummy = 3;
+    allocator.ForgetDevice(kthAddress(k));
+    EXPECT_EQ(dummy, 1);
+  }
+
+  // verify scanned
+  dummy = 4;
+  EXPECT_TRUE(allocator.SaveDevice(kthAddress(0)));
+  EXPECT_TRUE(allocator.SaveDevice(kthAddress(1)));
+  EXPECT_TRUE(allocator.SaveDevice(
+      kthAddress(MetricIdAllocator::kMaxNumPairedDevicesInMemory + 5)));
+  EXPECT_EQ(dummy, 32);
+
+  EXPECT_TRUE(allocator.Close());
+}
+
+TEST(BluetoothMetricIdAllocatorTest, MetricIdAllocatorFullScannedMap) {
+  auto& allocator = MetricIdAllocator::GetInstance();
+  std::unordered_map<RawAddress, int> paired_device_map;
+  int dummy = 22;
+  int* pointer = &dummy;
+  MetricIdAllocator::Callback save_callback = [pointer](const RawAddress&,
+                                                        const int) {
+    *pointer = *pointer * 2;
+    return true;
+  };
+  MetricIdAllocator::Callback forget_callback = [pointer](const RawAddress&,
+                                                          const int) {
+    *pointer = *pointer / 2;
+    return true;
+  };
+
+  EXPECT_TRUE(
+      allocator.Init(paired_device_map, save_callback, forget_callback));
+
+  // allocate kMaxNumUnpairedDevicesInMemory ids
+  // comments based on kMaxNumUnpairedDevicesInMemory = 200
+  for (int key = 0;
+       key <
+       static_cast<int>(MetricIdAllocator::kMaxNumUnpairedDevicesInMemory);
+       key++) {
+    EXPECT_EQ(allocator.AllocateId(kthAddress(key)),
+              key + MetricIdAllocator::kMinId);
+  }
+  // scanned: 0, 1, 2 ... 199,
+  // paired:
+
+  int id = MetricIdAllocator::kMaxNumUnpairedDevicesInMemory +
+           MetricIdAllocator::kMinId;
+  RawAddress addr =
+      kthAddress(MetricIdAllocator::kMaxNumUnpairedDevicesInMemory);
+  EXPECT_EQ(allocator.AllocateId(addr), id);
+  // scanned: 1, 2 ... 199, 200
+
+  // save it and make sure the callback is called
+  EXPECT_TRUE(allocator.SaveDevice(addr));
+  EXPECT_EQ(allocator.AllocateId(addr), id);
+  EXPECT_EQ(dummy, 44);
+  // paired: 200,
+  // scanned: 1, 2 ... 199,
+  id++;
+
+  addr = kthAddress(MetricIdAllocator::kMaxNumUnpairedDevicesInMemory + 1);
+  EXPECT_EQ(allocator.AllocateId(addr), id++);
+  // paired: 200,
+  // scanned: 1, 2 ... 199, 201
+
+  // try to allocate for device 0, 1, 2, 3, 4....199
+  // we should have a new id every time,
+  // since the scanned map is full at this point
+  for (int key = 0;
+       key <
+       static_cast<int>(MetricIdAllocator::kMaxNumUnpairedDevicesInMemory);
+       key++) {
+    EXPECT_EQ(allocator.AllocateId(kthAddress(key)), id++);
+  }
+  EXPECT_TRUE(allocator.Close());
+}
+
+TEST(BluetoothMetricIdAllocatorTest, MetricIdAllocatorMultiThreadPressureTest) {
+  std::unordered_map<RawAddress, int> paired_device_map;
+  auto& allocator = MetricIdAllocator::GetInstance();
+  int dummy = 22;
+  int* pointer = &dummy;
+  MetricIdAllocator::Callback save_callback = [pointer](const RawAddress&,
+                                                        const int) {
+    *pointer = *pointer + 1;
+    return true;
+  };
+  MetricIdAllocator::Callback forget_callback = [pointer](const RawAddress&,
+                                                          const int) {
+    *pointer = *pointer - 1;
+    return true;
+  };
+  EXPECT_TRUE(
+      allocator.Init(paired_device_map, save_callback, forget_callback));
+
+  // make sure no deadlock
+  std::vector<std::thread> workers;
+  for (int key = 0;
+       key <
+       static_cast<int>(MetricIdAllocator::kMaxNumUnpairedDevicesInMemory);
+       key++) {
+    workers.push_back(std::thread([key]() {
+      auto& allocator = MetricIdAllocator::GetInstance();
+      RawAddress fake_mac_address = kthAddress(key);
+      allocator.AllocateId(fake_mac_address);
+      EXPECT_TRUE(allocator.SaveDevice(fake_mac_address));
+      allocator.ForgetDevice(fake_mac_address);
+    }));
+  }
+  for (auto& worker : workers) {
+    worker.join();
+  }
+  EXPECT_TRUE(allocator.IsEmpty());
+  EXPECT_TRUE(allocator.Close());
+}
+
+TEST(BluetoothMetricIdAllocatorTest, MetricIdAllocatorWrapAroundTest1) {
+  std::unordered_map<RawAddress, int> paired_device_map;
+  auto& allocator = MetricIdAllocator::GetInstance();
+  MetricIdAllocator::Callback callback = [](const RawAddress&, const int) {
+    return true;
+  };
+
+  // make a sparse paired_device_map
+  int min_id = MetricIdAllocator::kMinId;
+  paired_device_map[kthAddress(min_id)] = min_id;
+  paired_device_map[kthAddress(min_id + 1)] = min_id + 1;
+  paired_device_map[kthAddress(min_id + 3)] = min_id + 3;
+  paired_device_map[kthAddress(min_id + 4)] = min_id + 4;
+
+  int max_id = MetricIdAllocator::kMaxId;
+  paired_device_map[kthAddress(max_id - 3)] = max_id - 3;
+  paired_device_map[kthAddress(max_id - 4)] = max_id - 4;
+
+  EXPECT_TRUE(allocator.Init(paired_device_map, callback, callback));
+
+  // next id should be max_id - 2, max_id - 1, max_id, min_id + 2, min_id + 5
+  EXPECT_EQ(allocator.AllocateId(kthAddress(max_id - 2)), max_id - 2);
+  EXPECT_EQ(allocator.AllocateId(kthAddress(max_id - 1)), max_id - 1);
+  EXPECT_EQ(allocator.AllocateId(kthAddress(max_id)), max_id);
+  EXPECT_EQ(allocator.AllocateId(kthAddress(min_id + 2)), min_id + 2);
+  EXPECT_EQ(allocator.AllocateId(kthAddress(min_id + 5)), min_id + 5);
+
+  EXPECT_TRUE(allocator.Close());
+}
+
+TEST(BluetoothMetricIdAllocatorTest, MetricIdAllocatorWrapAroundTest2) {
+  std::unordered_map<RawAddress, int> paired_device_map;
+  auto& allocator = MetricIdAllocator::GetInstance();
+  MetricIdAllocator::Callback callback = [](const RawAddress&, const int) {
+    return true;
+  };
+
+  // make a sparse paired_device_map
+  int min_id = MetricIdAllocator::kMinId;
+  int max_id = MetricIdAllocator::kMaxId;
+  paired_device_map[kthAddress(max_id)] = max_id;
+
+  EXPECT_TRUE(allocator.Init(paired_device_map, callback, callback));
+
+  // next id should be min_id, min_id + 1
+  EXPECT_EQ(allocator.AllocateId(kthAddress(min_id)), min_id);
+  EXPECT_EQ(allocator.AllocateId(kthAddress(min_id + 1)), min_id + 1);
+
+  EXPECT_TRUE(allocator.Close());
+}
+
+}  // namespace testing
diff --git a/common/metrics.cc b/common/metrics.cc
index 4550eeb..5d2365f 100644
--- a/common/metrics.cc
+++ b/common/metrics.cc
@@ -37,6 +37,7 @@
 
 #include "address_obfuscator.h"
 #include "leaky_bonded_queue.h"
+#include "metric_id_allocator.h"
 #include "metrics.h"
 #include "time_util.h"
 
@@ -576,8 +577,10 @@
                                  uint16_t hci_event, uint16_t hci_ble_event,
                                  uint16_t cmd_status, uint16_t reason_code) {
   std::string obfuscated_id;
+  int metric_id = 0;
   if (address != nullptr) {
     obfuscated_id = AddressObfuscator::GetInstance()->Obfuscate(*address);
+    metric_id = MetricIdAllocator::GetInstance().AllocateId(*address);
   }
   // nullptr and size 0 represent missing value for obfuscated_id
   android::util::BytesField bytes_field(
@@ -586,7 +589,7 @@
   int ret = android::util::stats_write(
       android::util::BLUETOOTH_LINK_LAYER_CONNECTION_EVENT, bytes_field,
       connection_handle, direction, link_type, hci_cmd, hci_event,
-      hci_ble_event, cmd_status, reason_code);
+      hci_ble_event, cmd_status, reason_code, metric_id);
   if (ret < 0) {
     LOG(WARNING) << __func__ << ": failed to log status " << loghex(cmd_status)
                  << ", reason " << loghex(reason_code) << " from cmd "
@@ -624,8 +627,10 @@
                                uint64_t encoding_interval_millis,
                                int num_missing_pcm_bytes) {
   std::string obfuscated_id;
+  int metric_id = 0;
   if (!address.IsEmpty()) {
     obfuscated_id = AddressObfuscator::GetInstance()->Obfuscate(address);
+    metric_id = MetricIdAllocator::GetInstance().AllocateId(address);
   }
   // nullptr and size 0 represent missing value for obfuscated_id
   android::util::BytesField bytes_field(
@@ -634,7 +639,7 @@
   int64_t encoding_interval_nanos = encoding_interval_millis * 1000000;
   int ret = android::util::stats_write(
       android::util::BLUETOOTH_A2DP_AUDIO_UNDERRUN_REPORTED, bytes_field,
-      encoding_interval_nanos, num_missing_pcm_bytes);
+      encoding_interval_nanos, num_missing_pcm_bytes, metric_id);
   if (ret < 0) {
     LOG(WARNING) << __func__ << ": failed for " << address
                  << ", encoding_interval_nanos " << encoding_interval_nanos
@@ -649,8 +654,10 @@
                               int num_dropped_encoded_frames,
                               int num_dropped_encoded_bytes) {
   std::string obfuscated_id;
+  int metric_id = 0;
   if (!address.IsEmpty()) {
     obfuscated_id = AddressObfuscator::GetInstance()->Obfuscate(address);
+    metric_id = MetricIdAllocator::GetInstance().AllocateId(address);
   }
   // nullptr and size 0 represent missing value for obfuscated_id
   android::util::BytesField bytes_field(
@@ -660,7 +667,7 @@
   int ret = android::util::stats_write(
       android::util::BLUETOOTH_A2DP_AUDIO_OVERRUN_REPORTED, bytes_field,
       encoding_interval_nanos, num_dropped_buffers, num_dropped_encoded_frames,
-      num_dropped_encoded_bytes);
+      num_dropped_encoded_bytes, metric_id);
   if (ret < 0) {
     LOG(WARNING) << __func__ << ": failed to log for " << address
                  << ", encoding_interval_nanos " << encoding_interval_nanos
@@ -674,16 +681,18 @@
 void LogReadRssiResult(const RawAddress& address, uint16_t handle,
                        uint32_t cmd_status, int8_t rssi) {
   std::string obfuscated_id;
+  int metric_id = 0;
   if (!address.IsEmpty()) {
     obfuscated_id = AddressObfuscator::GetInstance()->Obfuscate(address);
+    metric_id = MetricIdAllocator::GetInstance().AllocateId(address);
   }
   // nullptr and size 0 represent missing value for obfuscated_id
   android::util::BytesField bytes_field(
       address.IsEmpty() ? nullptr : obfuscated_id.c_str(),
       address.IsEmpty() ? 0 : obfuscated_id.size());
-  int ret =
-      android::util::stats_write(android::util::BLUETOOTH_DEVICE_RSSI_REPORTED,
-                                 bytes_field, handle, cmd_status, rssi);
+  int ret = android::util::stats_write(
+      android::util::BLUETOOTH_DEVICE_RSSI_REPORTED, bytes_field, handle,
+      cmd_status, rssi, metric_id);
   if (ret < 0) {
     LOG(WARNING) << __func__ << ": failed for " << address << ", handle "
                  << handle << ", status " << loghex(cmd_status) << ", rssi "
@@ -695,8 +704,10 @@
                                        uint16_t handle, uint32_t cmd_status,
                                        int32_t failed_contact_counter) {
   std::string obfuscated_id;
+  int metric_id = 0;
   if (!address.IsEmpty()) {
     obfuscated_id = AddressObfuscator::GetInstance()->Obfuscate(address);
+    metric_id = MetricIdAllocator::GetInstance().AllocateId(address);
   }
   // nullptr and size 0 represent missing value for obfuscated_id
   android::util::BytesField bytes_field(
@@ -704,7 +715,7 @@
       address.IsEmpty() ? 0 : obfuscated_id.size());
   int ret = android::util::stats_write(
       android::util::BLUETOOTH_DEVICE_FAILED_CONTACT_COUNTER_REPORTED,
-      bytes_field, handle, cmd_status, failed_contact_counter);
+      bytes_field, handle, cmd_status, failed_contact_counter, metric_id);
   if (ret < 0) {
     LOG(WARNING) << __func__ << ": failed for " << address << ", handle "
                  << handle << ", status " << loghex(cmd_status)
@@ -717,8 +728,10 @@
                                uint32_t cmd_status,
                                int32_t transmit_power_level) {
   std::string obfuscated_id;
+  int metric_id = 0;
   if (!address.IsEmpty()) {
     obfuscated_id = AddressObfuscator::GetInstance()->Obfuscate(address);
+    metric_id = MetricIdAllocator::GetInstance().AllocateId(address);
   }
   // nullptr and size 0 represent missing value for obfuscated_id
   android::util::BytesField bytes_field(
@@ -726,7 +739,7 @@
       address.IsEmpty() ? 0 : obfuscated_id.size());
   int ret = android::util::stats_write(
       android::util::BLUETOOTH_DEVICE_TX_POWER_LEVEL_REPORTED, bytes_field,
-      handle, cmd_status, transmit_power_level);
+      handle, cmd_status, transmit_power_level, metric_id);
   if (ret < 0) {
     LOG(WARNING) << __func__ << ": failed for " << address << ", handle "
                  << handle << ", status " << loghex(cmd_status)
@@ -739,8 +752,10 @@
                         android::bluetooth::DirectionEnum direction,
                         uint8_t smp_fail_reason) {
   std::string obfuscated_id;
+  int metric_id = 0;
   if (!address.IsEmpty()) {
     obfuscated_id = AddressObfuscator::GetInstance()->Obfuscate(address);
+    metric_id = MetricIdAllocator::GetInstance().AllocateId(address);
   }
   // nullptr and size 0 represent missing value for obfuscated_id
   android::util::BytesField obfuscated_id_field(
@@ -748,7 +763,7 @@
       address.IsEmpty() ? 0 : obfuscated_id.size());
   int ret = android::util::stats_write(
       android::util::BLUETOOTH_SMP_PAIRING_EVENT_REPORTED, obfuscated_id_field,
-      smp_cmd, direction, smp_fail_reason);
+      smp_cmd, direction, smp_fail_reason, metric_id);
   if (ret < 0) {
     LOG(WARNING) << __func__ << ": failed for " << address << ", smp_cmd "
                  << loghex(smp_cmd) << ", direction " << direction
@@ -760,15 +775,19 @@
 void LogClassicPairingEvent(const RawAddress& address, uint16_t handle, uint32_t hci_cmd, uint16_t hci_event,
                             uint16_t cmd_status, uint16_t reason_code, int64_t event_value) {
   std::string obfuscated_id;
+  int metric_id = 0;
   if (!address.IsEmpty()) {
     obfuscated_id = AddressObfuscator::GetInstance()->Obfuscate(address);
+    metric_id = MetricIdAllocator::GetInstance().AllocateId(address);
   }
   // nullptr and size 0 represent missing value for obfuscated_id
   android::util::BytesField obfuscated_id_field(
       address.IsEmpty() ? nullptr : obfuscated_id.c_str(),
       address.IsEmpty() ? 0 : obfuscated_id.size());
-  int ret = android::util::stats_write(android::util::BLUETOOTH_CLASSIC_PAIRING_EVENT_REPORTED, obfuscated_id_field,
-                                       handle, hci_cmd, hci_event, cmd_status, reason_code, event_value);
+  int ret = android::util::stats_write(
+      android::util::BLUETOOTH_CLASSIC_PAIRING_EVENT_REPORTED,
+      obfuscated_id_field, handle, hci_cmd, hci_event, cmd_status, reason_code,
+      event_value, metric_id);
   if (ret < 0) {
     LOG(WARNING) << __func__ << ": failed for " << address << ", handle " << handle << ", hci_cmd " << loghex(hci_cmd)
                  << ", hci_event " << loghex(hci_event) << ", cmd_status " << loghex(cmd_status) << ", reason "
@@ -780,8 +799,10 @@
                      uint16_t attribute_id, size_t attribute_size,
                      const char* attribute_value) {
   std::string obfuscated_id;
+  int metric_id = 0;
   if (!address.IsEmpty()) {
     obfuscated_id = AddressObfuscator::GetInstance()->Obfuscate(address);
+    metric_id = MetricIdAllocator::GetInstance().AllocateId(address);
   }
   // nullptr and size 0 represent missing value for obfuscated_id
   android::util::BytesField obfuscated_id_field(
@@ -790,7 +811,7 @@
   android::util::BytesField attribute_field(attribute_value, attribute_size);
   int ret = android::util::stats_write(
       android::util::BLUETOOTH_SDP_ATTRIBUTE_REPORTED, obfuscated_id_field,
-      protocol_uuid, attribute_id, attribute_field);
+      protocol_uuid, attribute_id, attribute_field, metric_id);
   if (ret < 0) {
     LOG(WARNING) << __func__ << ": failed for " << address << ", protocol_uuid "
                  << loghex(protocol_uuid) << ", attribute_id "
@@ -804,8 +825,10 @@
     int64_t tx_bytes, int64_t rx_bytes, int uid, int server_port,
     android::bluetooth::SocketRoleEnum socket_role) {
   std::string obfuscated_id;
+  int metric_id = 0;
   if (!address.IsEmpty()) {
     obfuscated_id = AddressObfuscator::GetInstance()->Obfuscate(address);
+    metric_id = MetricIdAllocator::GetInstance().AllocateId(address);
   }
   // nullptr and size 0 represent missing value for obfuscated_id
   android::util::BytesField obfuscated_id_field(
@@ -814,7 +837,7 @@
   int ret = android::util::stats_write(
       android::util::BLUETOOTH_SOCKET_CONNECTION_STATE_CHANGED,
       obfuscated_id_field, port, type, connection_state, tx_bytes, rx_bytes,
-      uid, server_port, socket_role);
+      uid, server_port, socket_role, metric_id);
   if (ret < 0) {
     LOG(WARNING) << __func__ << ": failed for " << address << ", port " << port
                  << ", type " << type << ", state " << connection_state
@@ -832,8 +855,10 @@
                          const std::string& hardware_version,
                          const std::string& software_version) {
   std::string obfuscated_id;
+  int metric_id = 0;
   if (!address.IsEmpty()) {
     obfuscated_id = AddressObfuscator::GetInstance()->Obfuscate(address);
+    metric_id = MetricIdAllocator::GetInstance().AllocateId(address);
   }
   // nullptr and size 0 represent missing value for obfuscated_id
   android::util::BytesField obfuscated_id_field(
@@ -842,7 +867,7 @@
   int ret = android::util::stats_write(
       android::util::BLUETOOTH_DEVICE_INFO_REPORTED, obfuscated_id_field,
       source_type, source_name.c_str(), manufacturer.c_str(), model.c_str(),
-      hardware_version.c_str(), software_version.c_str());
+      hardware_version.c_str(), software_version.c_str(), metric_id);
   if (ret < 0) {
     LOG(WARNING) << __func__ << ": failed for " << address << ", source_type "
                  << source_type << ", source_name " << source_name
diff --git a/common/metrics_unittest.cc b/common/metrics_unittest.cc
index 03a427a..a46c896 100644
--- a/common/metrics_unittest.cc
+++ b/common/metrics_unittest.cc
@@ -400,12 +400,12 @@
     bt_log_->Clear();
   }
 
-  void SetUp() {
+  void SetUp() override {
     bt_log_ = new BluetoothLog();
     // Clear existing metrics entries, if any
     BluetoothMetricsLogger::GetInstance()->Reset();
   }
-  void TearDown() {
+  void TearDown() override {
     // Clear remaining metrics entries, if any
     BluetoothMetricsLogger::GetInstance()->Reset();
     ClearLog();
diff --git a/common/repeating_timer_unittest.cc b/common/repeating_timer_unittest.cc
index 20d8d67..50520cd 100644
--- a/common/repeating_timer_unittest.cc
+++ b/common/repeating_timer_unittest.cc
@@ -27,7 +27,7 @@
 using bluetooth::common::RepeatingTimer;
 
 // Allowed error between the expected and actual delay for DoInThreadDelayed().
-constexpr uint32_t delay_error_ms = 3;
+constexpr uint32_t delay_error_ms = 100;
 
 /**
  * Unit tests to verify Task Scheduler.
diff --git a/common/test/thread_performance_test.cc b/common/test/thread_performance_test.cc
index dc6a53f..4c38fed 100644
--- a/common/test/thread_performance_test.cc
+++ b/common/test/thread_performance_test.cc
@@ -265,6 +265,7 @@
   std::chrono::milliseconds duration =
       std::chrono::duration_cast<std::chrono::milliseconds>(end_time -
                                                             start_time);
+  fixed_queue_unregister_dequeue(bt_msg_queue_);
 
   LOG(INFO) << "ReactorPerformanceTest, " << duration.count() << " ms, "
             << NUM_MESSAGES_TO_SEND << " messages";
diff --git a/device/include/controller.h b/device/include/controller.h
index c1fe337..2127b27 100644
--- a/device/include/controller.h
+++ b/device/include/controller.h
@@ -22,9 +22,9 @@
 #include <stdint.h>
 
 #include "device_features.h"
-#include "hci_layer.h"
-#include "hci_packet_factory.h"
-#include "hci_packet_parser.h"
+#include "hci/include/hci_layer.h"
+#include "hci/include/hci_packet_factory.h"
+#include "hci/include/hci_packet_parser.h"
 
 static const char CONTROLLER_MODULE[] = "controller_module";
 
@@ -89,6 +89,12 @@
 
 } controller_t;
 
+namespace bluetooth {
+namespace legacy {
+const controller_t* controller_get_interface();
+}  // namespace legacy
+}  // namespace bluetooth
+
 const controller_t* controller_get_interface();
 
 const controller_t* controller_get_test_interface(
diff --git a/device/include/interop.h b/device/include/interop.h
index a9bf586..d7ca917 100644
--- a/device/include/interop.h
+++ b/device/include/interop.h
@@ -94,6 +94,12 @@
   // Set a very low initial sniff subrating for HID devices that do not
   // set their own sniff interval.
   INTEROP_HID_HOST_LIMIT_SNIFF_INTERVAL,
+
+  // Disable remote name requst for some devices.
+  // The public address of these devices are same as the Random address in ADV.
+  // Then will get name by LE_Create_connection, actually fails,
+  // but will block pairing.
+  INTEROP_DISABLE_NAME_REQUEST
 } interop_feature_t;
 
 // Check if a given |addr| matches a known interoperability workaround as
diff --git a/device/include/interop_database.h b/device/include/interop_database.h
index d7425c9..b4b1b07 100644
--- a/device/include/interop_database.h
+++ b/device/include/interop_database.h
@@ -143,6 +143,13 @@
 
     // AirPods 2 - unacceptably loud volume
     {{{0x94, 0x16, 0x25, 0, 0, 0}}, 3, INTEROP_DISABLE_ABSOLUTE_VOLUME},
+
+    // AirPods 2 - unacceptably loud volume
+    {{{0x9c, 0x64, 0x8b, 0, 0, 0}}, 3, INTEROP_DISABLE_ABSOLUTE_VOLUME},
+
+    // for skip name request,
+    // because BR/EDR address and ADV random address are the same
+    {{{0xd4, 0x7a, 0xe2, 0, 0, 0}}, 3, INTEROP_DISABLE_NAME_REQUEST},
 };
 
 typedef struct {
@@ -170,7 +177,8 @@
     // Kenwood KMM-BT518HD - no audio when A2DP codec sample rate is changed
     {"KMM-BT51*HD", 11, INTEROP_DISABLE_AVDTP_RECONFIGURE},
 
-    // Nintendo Switch Pro Controller - does not set sniff interval dynamically.
-    // Requires custom HID report command to change mode.
+    // Nintendo Switch Pro Controller and Joy Con - do not set sniff interval
+    // dynamically. They require custom HID report command to change mode.
     {"Pro Controller", 14, INTEROP_HID_HOST_LIMIT_SNIFF_INTERVAL},
+    {"Joy-Con", 7, INTEROP_HID_HOST_LIMIT_SNIFF_INTERVAL},
 };
diff --git a/device/src/controller.cc b/device/src/controller.cc
index 34c8afd..a260b98 100644
--- a/device/src/controller.cc
+++ b/device/src/controller.cc
@@ -27,6 +27,8 @@
 #include "btcore/include/module.h"
 #include "btcore/include/version.h"
 #include "hcimsgs.h"
+#include "main/shim/controller.h"
+#include "main/shim/shim.h"
 #include "osi/include/future.h"
 #include "stack/include/btm_ble_api.h"
 
@@ -51,7 +53,7 @@
 #define BLE_SUPPORTED_FEATURES_SIZE 8
 #define MAX_LOCAL_SUPPORTED_CODECS_SIZE 8
 
-static const hci_t* hci;
+static const hci_t* local_hci;
 static const hci_packet_factory_t* packet_factory;
 static const hci_packet_parser_t* packet_parser;
 
@@ -88,7 +90,8 @@
 static bool secure_connections_supported;
 
 #define AWAIT_COMMAND(command) \
-  static_cast<BT_HDR*>(future_await(hci->transmit_command_futured(command)))
+  static_cast<BT_HDR*>(        \
+      future_await(local_hci->transmit_command_futured(command)))
 
 // Module lifecycle functions
 
@@ -582,12 +585,12 @@
     get_local_supported_codecs,
     get_le_all_initiating_phys};
 
-const controller_t* controller_get_interface() {
+const controller_t* bluetooth::legacy::controller_get_interface() {
   static bool loaded = false;
   if (!loaded) {
     loaded = true;
 
-    hci = hci_layer_get_interface();
+    local_hci = hci_layer_get_interface();
     packet_factory = hci_packet_factory_get_interface();
     packet_parser = hci_packet_parser_get_interface();
   }
@@ -595,11 +598,19 @@
   return &interface;
 }
 
+const controller_t* controller_get_interface() {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::controller_get_interface();
+  } else {
+    return bluetooth::legacy::controller_get_interface();
+  }
+}
+
 const controller_t* controller_get_test_interface(
     const hci_t* hci_interface,
     const hci_packet_factory_t* packet_factory_interface,
     const hci_packet_parser_t* packet_parser_interface) {
-  hci = hci_interface;
+  local_hci = hci_interface;
   packet_factory = packet_factory_interface;
   packet_parser = packet_parser_interface;
   return &interface;
diff --git a/device/src/interop.cc b/device/src/interop.cc
index e4bce56..9a701b1 100644
--- a/device/src/interop.cc
+++ b/device/src/interop.cc
@@ -50,7 +50,7 @@
 
   if (interop_match_fixed_(feature, addr) ||
       interop_match_dynamic_(feature, addr)) {
-    LOG_WARN(LOG_TAG, "%s() Device %s is a match for interop workaround %s.",
+    LOG_INFO(LOG_TAG, "%s() Device %s is a match for interop workaround %s.",
              __func__, addr->ToString().c_str(),
              interop_feature_string_(feature));
     return true;
@@ -69,6 +69,8 @@
         strlen(name) >= interop_name_database[i].length &&
         strncmp(name, interop_name_database[i].name,
                 interop_name_database[i].length) == 0) {
+      LOG_INFO(LOG_TAG, "%s() Device %s is a match for interop workaround %s.",
+             __func__, name, interop_feature_string_(feature));
       return true;
     }
   }
@@ -129,6 +131,7 @@
     CASE_RETURN_STR(INTEROP_DYNAMIC_ROLE_SWITCH)
     CASE_RETURN_STR(INTEROP_DISABLE_ROLE_SWITCH)
     CASE_RETURN_STR(INTEROP_HID_HOST_LIMIT_SNIFF_INTERVAL)
+    CASE_RETURN_STR(INTEROP_DISABLE_NAME_REQUEST)
   }
 
   return "UNKNOWN";
diff --git a/embdrv/g722/Android.bp b/embdrv/g722/Android.bp
index cc635d8..fbac145 100644
--- a/embdrv/g722/Android.bp
+++ b/embdrv/g722/Android.bp
@@ -1,12 +1,11 @@
-
 cc_library_static {
     name: "libg722codec",
     defaults: ["fluoride_defaults"],
     cflags: [
-        "-DG722_SUPPORT_MALLOC"
+        "-DG722_SUPPORT_MALLOC",
     ],
     srcs: [
         "g722_decode.cc",
         "g722_encode.cc",
     ],
-}
\ No newline at end of file
+}
diff --git a/embdrv/sbc/decoder/Android.bp b/embdrv/sbc/decoder/Android.bp
index 274f2bf..65599d4 100644
--- a/embdrv/sbc/decoder/Android.bp
+++ b/embdrv/sbc/decoder/Android.bp
@@ -24,3 +24,18 @@
         "srce",
     ],
 }
+
+cc_fuzz {
+    name: "sbcdecoder_fuzzer",
+    srcs: [
+        "sbcdecoder_fuzzer.c",
+    ],
+    static_libs: [
+        "libbt-sbc-decoder",
+    ],
+    local_include_dirs: [
+        "include",
+    ],
+    host_supported: false,
+}
+
diff --git a/embdrv/sbc/decoder/include/oi_bitstream.h b/embdrv/sbc/decoder/include/oi_bitstream.h
index 0d2aff2..003cfa3 100644
--- a/embdrv/sbc/decoder/include/oi_bitstream.h
+++ b/embdrv/sbc/decoder/include/oi_bitstream.h
@@ -78,17 +78,18 @@
 #define OI_BITSTREAM_READUINT(result, bits, ptr, value, bitPtr) \
   do {                                                          \
     OI_ASSERT((bits) <= 16);                                    \
-    OI_ASSERT((bitPtr) < 16);                                   \
-    OI_ASSERT((bitPtr) >= 8);                                   \
+    OI_ASSERT((bitPtr) < 32);                                   \
+    OI_ASSERT((bitPtr) >= 0);                                   \
+                                                                \
+    while ((bitPtr + bits) > 32) {                              \
+      (value) = ((value) << 8) | *(ptr)++;                      \
+      (bitPtr) -= 8;                                            \
+    }                                                           \
                                                                 \
     (result) = (value) << (bitPtr);                             \
     (result) >>= 32 - (bits);                                   \
                                                                 \
     (bitPtr) += (bits);                                         \
-    while ((bitPtr) >= 16) {                                    \
-      (value) = ((value) << 8) | *(ptr)++;                      \
-      (bitPtr) -= 8;                                            \
-    }                                                           \
     OI_ASSERT(((bits) == 0) || ((result) < (1u << (bits))));    \
   } while (0)
 
diff --git a/embdrv/sbc/decoder/sbcdecoder_fuzzer.c b/embdrv/sbc/decoder/sbcdecoder_fuzzer.c
new file mode 100644
index 0000000..9b23ccc
--- /dev/null
+++ b/embdrv/sbc/decoder/sbcdecoder_fuzzer.c
@@ -0,0 +1,61 @@
+#include <stddef.h>
+#include <stdio.h>
+#include "oi_codec_sbc.h"
+
+#define CODEC_DATA_WORDS(numChannels, numBuffers)                              \
+  (((sizeof(int32_t) * SBC_MAX_BLOCKS * (numChannels)*SBC_MAX_BANDS) +         \
+    (sizeof(SBC_BUFFER_T) * SBC_MAX_CHANNELS * SBC_MAX_BANDS * (numBuffers)) + \
+    (sizeof(uint32_t) - 1)) /                                                  \
+   sizeof(uint32_t))
+
+#define SBC_CODEC_FAST_FILTER_BUFFERS 27
+
+#define SBC_MAX_CHANNELS 2
+#define SBC_MAX_BANDS 8
+#define SBC_MAX_BLOCKS 16
+/* Minimum size of the bit allocation pool used to encode the stream */
+#define SBC_MIN_BITPOOL 2
+/* Maximum size of the bit allocation pool used to encode the stream */
+#define SBC_MAX_BITPOOL 250
+#define SBC_MAX_ONE_CHANNEL_BPS 320000
+#define SBC_MAX_TWO_CHANNEL_BPS 512000
+
+#define SBC_WBS_BITRATE 62000
+#define SBC_WBS_BITPOOL 27
+#define SBC_WBS_NROF_BLOCKS 16
+#define SBC_WBS_FRAME_LEN 62
+#define SBC_WBS_SAMPLES_PER_FRAME 128
+
+#define SBC_HEADER_LEN 4
+#define SBC_MAX_SAMPLES_PER_FRAME (SBC_MAX_BANDS * SBC_MAX_BLOCKS)
+
+static OI_CODEC_SBC_DECODER_CONTEXT btif_a2dp_sink_context;
+static uint32_t btif_a2dp_sink_context_data[CODEC_DATA_WORDS(
+    2, SBC_CODEC_FAST_FILTER_BUFFERS)];
+
+static int16_t
+    btif_a2dp_sink_pcm_data[15 * SBC_MAX_SAMPLES_PER_FRAME * SBC_MAX_CHANNELS];
+
+int LLVMFuzzerInitialize(int argc, char** argv) {
+  (void)argc;
+  (void)argv;
+  OI_CODEC_SBC_DecoderReset(&btif_a2dp_sink_context,
+                            btif_a2dp_sink_context_data,
+                            sizeof(btif_a2dp_sink_context_data), 2, 2, 0);
+
+  return 0;
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t* buf, size_t len) {
+  uint32_t pcmBytes, availPcmBytes;
+  int16_t* pcmDataPointer =
+      btif_a2dp_sink_pcm_data; /* Will be overwritten on next packet receipt */
+  availPcmBytes = sizeof(btif_a2dp_sink_pcm_data);
+
+  pcmBytes = availPcmBytes;
+  OI_CODEC_SBC_DecodeFrame(&btif_a2dp_sink_context, (const OI_BYTE**)&buf,
+                           (uint32_t*)&len, (int16_t*)pcmDataPointer,
+                           (uint32_t*)&pcmBytes);
+
+  return 0;
+}
diff --git a/embdrv/sbc/decoder/srce/bitstream-decode.c b/embdrv/sbc/decoder/srce/bitstream-decode.c
index 913c064..251ecd2 100644
--- a/embdrv/sbc/decoder/srce/bitstream-decode.c
+++ b/embdrv/sbc/decoder/srce/bitstream-decode.c
@@ -39,10 +39,9 @@
 #include "oi_stddefs.h"
 
 PRIVATE void OI_BITSTREAM_ReadInit(OI_BITSTREAM* bs, const OI_BYTE* buffer) {
-  bs->value =
-      ((int32_t)buffer[0] << 16) | ((int32_t)buffer[1] << 8) | (buffer[2]);
-  bs->ptr.r = buffer + 3;
-  bs->bitPtr = 8;
+  bs->value = 0;
+  bs->ptr.r = buffer;
+  bs->bitPtr = 32;
 }
 
 PRIVATE uint32_t OI_BITSTREAM_ReadUINT(OI_BITSTREAM* bs, OI_UINT bits) {
diff --git a/embdrv/sbc/decoder/srce/decoder-private.c b/embdrv/sbc/decoder/srce/decoder-private.c
index 2b10cde..cba8e96 100644
--- a/embdrv/sbc/decoder/srce/decoder-private.c
+++ b/embdrv/sbc/decoder/srce/decoder-private.c
@@ -180,43 +180,25 @@
   OI_CODEC_SBC_COMMON_CONTEXT* common = &context->common;
   OI_UINT nrof_blocks = common->frameInfo.nrof_blocks;
   int32_t* RESTRICT s = common->subdata;
-  uint8_t* ptr = global_bs->ptr.w;
+  const uint8_t* ptr = global_bs->ptr.r;
   uint32_t value = global_bs->value;
   OI_UINT bitPtr = global_bs->bitPtr;
-
   const OI_UINT iter_count =
-      common->frameInfo.nrof_channels * common->frameInfo.nrof_subbands / 4;
+      common->frameInfo.nrof_channels * common->frameInfo.nrof_subbands;
   do {
-    OI_UINT i;
-    for (i = 0; i < iter_count; ++i) {
-      uint32_t sf_by4 = ((uint32_t*)common->scale_factor)[i];
-      uint32_t bits_by4 = common->bits.uint32[i];
-      OI_UINT n;
-      for (n = 0; n < 4; ++n) {
-        int32_t dequant;
-        OI_UINT bits;
-        OI_INT sf;
-
-        if (OI_CPU_BYTE_ORDER == OI_LITTLE_ENDIAN_BYTE_ORDER) {
-          bits = bits_by4 & 0xFF;
-          bits_by4 >>= 8;
-          sf = sf_by4 & 0xFF;
-          sf_by4 >>= 8;
-        } else {
-          bits = (bits_by4 >> 24) & 0xFF;
-          bits_by4 <<= 8;
-          sf = (sf_by4 >> 24) & 0xFF;
-          sf_by4 <<= 8;
-        }
-        if (bits) {
-          uint32_t raw;
-          OI_BITSTREAM_READUINT(raw, bits, ptr, value, bitPtr);
-          dequant = OI_SBC_Dequant(raw, sf, bits);
-        } else {
-          dequant = 0;
-        }
-        *s++ = dequant;
+    OI_UINT n;
+    for (n = 0; n < iter_count; ++n) {
+      int32_t dequant;
+      OI_UINT bits = common->bits.uint8[n];
+      OI_INT sf = common->scale_factor[n];
+      if (bits) {
+        uint32_t raw;
+        OI_BITSTREAM_READUINT(raw, bits, ptr, value, bitPtr);
+        dequant = OI_SBC_Dequant(raw, sf, bits);
+      } else {
+        dequant = 0;
       }
+      *s++ = dequant;
     }
   } while (--nrof_blocks);
 }
diff --git a/embdrv/sbc/decoder/srce/decoder-sbc.c b/embdrv/sbc/decoder/srce/decoder-sbc.c
index 674645e..d5c59d9 100644
--- a/embdrv/sbc/decoder/srce/decoder-sbc.c
+++ b/embdrv/sbc/decoder/srce/decoder-sbc.c
@@ -33,6 +33,12 @@
 
 #define SPECIALIZE_READ_SAMPLES_JOINT
 
+#if __has_attribute(fallthrough)
+#define __fallthrough __attribute__((__fallthrough__))
+#else
+#define __fallthrough
+#endif
+
 /**
  * Scans through a buffer looking for a codec syncword. If the decoder has been
  * set for enhanced operation using OI_CODEC_SBC_DecoderReset(), it will search
@@ -411,7 +417,7 @@
 
       case SBC_DUAL_CHANNEL:
         frameLen *= 2;
-      /* fall through */
+        __fallthrough;
 
       default:
         if (mode == SBC_MONO) {
diff --git a/embdrv/sbc/encoder/srce/sbc_packing.c b/embdrv/sbc/encoder/srce/sbc_packing.c
index b478a80..205b373 100644
--- a/embdrv/sbc/encoder/srce/sbc_packing.c
+++ b/embdrv/sbc/encoder/srce/sbc_packing.c
@@ -28,26 +28,22 @@
 #if (SBC_ARM_ASM_OPT == TRUE)
 #define Mult32(s32In1, s32In2, s32OutLow)    \
   {                                          \
-    __asm {                                                                                    \
+    __asm {                                  \
         MUL s32OutLow,s32In1,s32In2; } \
   }
 #define Mult64(s32In1, s32In2, s32OutLow, s32OutHi)     \
   {                                                     \
-    __asm {														    						\
+    __asm {                                             \
         SMULL s32OutLow,s32OutHi,s32In1,s32In2 } \
   }
 #else
 #define Mult32(s32In1, s32In2, s32OutLow) \
   s32OutLow = (int32_t)(s32In1) * (int32_t)(s32In2);
-#define Mult64(s32In1, s32In2, s32OutLow, s32OutHi)                   \
-  {                                                                   \
-    (s32OutLow) = ((int32_t)(uint16_t)(s32In1) * (uint16_t)(s32In2)); \
-    s32TempVal2 = (int32_t)(((s32In1) >> 16) * (uint16_t)(s32In2));   \
-    s32Carry = ((((uint32_t)(s32OutLow) >> 16) & 0xFFFF) +            \
-                +(s32TempVal2 & 0xFFFF)) >>                           \
-               16;                                                    \
-    (s32OutLow) += (s32TempVal2 << 16);                               \
-    (s32OutHi) = (s32TempVal2 >> 16) + s32Carry;                      \
+#define Mult64(s32In1, s32In2, s32OutLow, s32OutHi)                \
+  {                                                                \
+    __builtin_mul_overflow(s32In1, (uint16_t)s32In2, &s64OutTemp); \
+    s32OutLow = s64OutTemp & 0xFFFFFFFF;                           \
+    s32OutHi = (s64OutTemp >> 32) & 0xFFFFFFFF;                    \
   }
 #endif
 
@@ -77,7 +73,10 @@
   int32_t s32Temp1;   /*used in 64-bit multiplication*/
   int32_t s32Low;     /*used in 64-bit multiplication*/
 #if (SBC_IS_64_MULT_IN_QUANTIZER == TRUE)
-  int32_t s32Hi1, s32Low1, s32Carry, s32TempVal2, s32Hi, s32Temp2;
+  int32_t s32Hi1, s32Low1, s32Hi, s32Temp2;
+#if (SBC_ARM_ASM_OPT != TRUE)
+  int64_t s64OutTemp;
+#endif
 #endif
 
   pu8PacketPtr = output;           /*Initialize the ptr*/
@@ -141,7 +140,7 @@
         u16Levels = (uint16_t)(((uint32_t)1 << s32LoopCount) - 1);
 
         /* quantizer */
-        s32Temp1 = (*ps32SbPtr >> 2) + (u32SfRaisedToPow2 << 12);
+        s32Temp1 = (*ps32SbPtr >> 2) + (int32_t)(u32SfRaisedToPow2 << 12);
         s32Temp2 = u16Levels;
 
         Mult64(s32Temp1, s32Temp2, s32Low, s32Hi);
diff --git a/gd/.gitignore b/gd/.gitignore
new file mode 100644
index 0000000..894b4c5
--- /dev/null
+++ b/gd/.gitignore
@@ -0,0 +1,3 @@
+**/default.profraw
+**/__pycache__/
+gd_cert_venv
diff --git a/gd/Android.bp b/gd/Android.bp
index e0b8d5d..540001f 100644
--- a/gd/Android.bp
+++ b/gd/Android.bp
@@ -8,15 +8,18 @@
                 "-DOS_LINUX_GENERIC",
             ],
             shared_libs: [
-                "liblog"
-            ]
+                "liblog",
+            ],
         },
         host: {
             cflags: [
                 "-DOS_LINUX",
                 "-DOS_LINUX_GENERIC",
-            ]
-        }
+            ],
+        },
+        darwin: {
+            enabled: false,
+        },
     },
     cpp_std: "c++17",
     cflags: [
@@ -24,6 +27,8 @@
         "-fvisibility=hidden",
         "-DLOG_NDEBUG=1",
         "-DGOOGLE_PROTOBUF_NO_RTTI",
+        "-Wno-unused-parameter",
+        "-Wno-unused-result",
     ],
     conlyflags: [
         "-std=c99",
@@ -77,13 +82,96 @@
         linux: {
             srcs: [
                 ":BluetoothOsSources_linux_generic",
-            ]
-        }
+            ],
+        },
+        host: {
+            srcs: [
+                ":BluetoothHalSources_hci_rootcanal",
+            ],
+        },
+        android: {
+            srcs: [
+                ":BluetoothHalSources_hci_android_hidl",
+            ],
+            shared_libs: [
+                "android.hardware.bluetooth@1.0",
+                "libhidlbase",
+                "libutils",
+            ],
+        },
     },
     srcs: [
+        "stack_manager.cc",
+        "module.cc",
+        ":BluetoothAttSources",
         ":BluetoothCommonSources",
+        ":BluetoothCryptoToolboxSources",
+        ":BluetoothHalSources",
+        ":BluetoothHciSources",
+        ":BluetoothL2capSources",
+        ":BluetoothNeighborSources",
         ":BluetoothPacketSources",
-    ]
+        ":BluetoothShimSources",
+        ":BluetoothSecuritySources",
+        ":BluetoothStorageSources",
+    ],
+    generated_headers: [
+        "BluetoothGeneratedPackets_h",
+    ],
+    shared_libs: [
+        "libchrome",
+    ],
+}
+
+cc_binary {
+    name: "bluetooth_stack_with_facade",
+    defaults: [
+        "gd_defaults",
+    ],
+    host_supported: true,
+    srcs: [
+        "facade/facade_main.cc",
+        "facade/grpc_root_server.cc",
+        "facade/read_only_property_server.cc",
+        "grpc/grpc_module.cc",
+        ":BluetoothFacade_hci_hal",
+        ":BluetoothFacade_hci_layer",
+        ":BluetoothFacade_l2cap_layer",
+        ":BluetoothFacade_neighbor",
+        ":BluetoothFacade_security_layer",
+    ],
+    generated_headers: [
+        "BluetoothGeneratedPackets_h",
+        "BluetoothFacadeGeneratedStub_h",
+    ],
+    generated_sources: [
+        "BluetoothFacadeGeneratedStub_cc",
+    ],
+    static_libs: [
+        "libbluetooth_gd",
+    ],
+    shared_libs: [
+        "libchrome",
+        "libgrpc++_unsecure",
+        "libprotobuf-cpp-full",
+    ],
+    target: {
+        android: {
+            shared_libs: [
+                "android.hardware.bluetooth@1.0",
+                "libhidlbase",
+                "libutils",
+            ],
+        },
+        host: {
+            required: [
+                "root-canal",
+            ],
+        },
+    },
+    sanitize: {
+        address: true,
+    },
 }
 
 cc_test {
@@ -98,21 +186,103 @@
         linux: {
             srcs: [
                 ":BluetoothOsTestSources_linux_generic",
-            ]
-        }
+            ],
+        },
+        host: {
+            srcs: [
+                ":BluetoothHalTestSources_hci_rootcanal",
+            ],
+        },
+        android: {
+            srcs: [
+                ":BluetoothHalTestSources_hci_android_hidl",
+            ],
+            shared_libs: [
+                "android.hardware.bluetooth@1.0",
+                "libhidlbase",
+                "libutils",
+            ],
+        },
     },
     srcs: [
+        "module_unittest.cc",
+        "stack_manager_unittest.cc",
+        ":BluetoothAttTestSources",
         ":BluetoothCommonTestSources",
+        ":BluetoothCryptoToolboxTestSources",
+        ":BluetoothHciTestSources",
+        ":BluetoothL2capTestSources",
+        ":BluetoothNeighborTestSources",
         ":BluetoothPacketTestSources",
+        ":BluetoothSecurityTestSources",
+        ":BluetoothShimTestSources",
+        ":BluetoothStorageTestSources",
     ],
-    static_libs : [
+    generated_headers: [
+        "BluetoothGeneratedPackets_h",
+    ],
+    static_libs: [
         "libbluetooth_gd",
+        "libgmock",
+    ],
+    shared_libs: [
+        "libchrome",
     ],
     sanitize: {
-        cfi: false,
+        address: true,
     },
 }
 
+cc_test {
+    name: "bluetooth_packet_parser_test",
+    test_suites: ["device-tests"],
+    defaults: [
+        "gd_defaults",
+        "gd_clang_coverage_bin",
+    ],
+    host_supported: true,
+    srcs: [
+        ":BluetoothPacketSources",
+        ":BluetoothPacketParserTestPacketTestSources",
+    ],
+    generated_headers: [
+        "BluetoothPacketParserTestPacketPdlGen_h",
+    ],
+    sanitize: {
+        address: true,
+        cfi: true,
+    },
+}
+
+cc_fuzz {
+  name: "bluetooth_gd_fuzz_test",
+  defaults: ["gd_defaults"],
+  srcs: [
+    "fuzz_test.cc",
+    ":BluetoothHciFuzzTestSources",
+    ":BluetoothL2capFuzzTestSources",
+  ],
+  static_libs: [
+    "libbluetooth_gd",
+    "libchrome",
+    "libgmock",
+    "libgtest",
+  ],
+  host_supported: true,
+  generated_headers: [
+    "BluetoothGeneratedPackets_h",
+  ],
+  target: {
+    android: {
+        shared_libs: [
+            "android.hardware.bluetooth@1.0",
+            "libhidlbase",
+            "libutils",
+        ],
+    },
+  },
+}
+
 cc_benchmark {
     name: "bluetooth_benchmark_gd",
     defaults: ["gd_defaults"],
@@ -121,7 +291,278 @@
         "benchmark.cc",
         ":BluetoothOsBenchmarkSources",
     ],
-    static_libs : [
-            "libbluetooth_gd",
+    static_libs: [
+        "libbluetooth_gd",
     ],
+    shared_libs: [
+        "libchrome",
+    ],
+}
+
+filegroup {
+    name: "BluetoothHciClassSources",
+    srcs: [
+        "hci/address.cc",
+        "hci/class_of_device.cc",
+        ],
+}
+
+genrule {
+    name: "BluetoothGeneratedPackets_h",
+    tools: [
+        "bluetooth_packetgen",
+    ],
+    cmd: "$(location bluetooth_packetgen) --include=system/bt/gd --out=$(genDir) $(in)",
+    srcs: [
+        "hci/hci_packets.pdl",
+        "l2cap/l2cap_packets.pdl",
+        "security/smp_packets.pdl",
+    ],
+    out: [
+        "hci/hci_packets.h",
+        "l2cap/l2cap_packets.h",
+        "security/smp_packets.h",
+    ],
+}
+
+genrule {
+    name: "BluetoothGeneratedPackets_python3_cc",
+    tools: [
+        "bluetooth_packetgen",
+    ],
+    cmd: "$(location bluetooth_packetgen) --include=system/bt/gd --out=$(genDir) --num_shards=5 $(in)",
+    srcs: [
+        "hci/hci_packets.pdl",
+        "l2cap/l2cap_packets.pdl",
+        "security/smp_packets.pdl",
+    ],
+    out: [
+        "hci/hci_packets_python3.cc",
+        "hci/hci_packets_python3_shard_0.cc",
+        "hci/hci_packets_python3_shard_1.cc",
+        "hci/hci_packets_python3_shard_2.cc",
+        "hci/hci_packets_python3_shard_3.cc",
+        "hci/hci_packets_python3_shard_4.cc",
+        "l2cap/l2cap_packets_python3.cc",
+        "l2cap/l2cap_packets_python3_shard_0.cc",
+        "l2cap/l2cap_packets_python3_shard_1.cc",
+        "l2cap/l2cap_packets_python3_shard_2.cc",
+        "l2cap/l2cap_packets_python3_shard_3.cc",
+        "l2cap/l2cap_packets_python3_shard_4.cc",
+        "security/smp_packets_python3.cc",
+        "security/smp_packets_python3_shard_0.cc",
+        "security/smp_packets_python3_shard_1.cc",
+        "security/smp_packets_python3_shard_2.cc",
+        "security/smp_packets_python3_shard_3.cc",
+        "security/smp_packets_python3_shard_4.cc",
+    ],
+}
+
+filegroup {
+    name: "BluetoothFacadeProto",
+    srcs: [
+        "facade/common.proto",
+        "facade/rootservice.proto",
+        "hal/facade.proto",
+        "hci/facade/facade.proto",
+        "hci/facade/acl_manager_facade.proto",
+        "hci/facade/controller_facade.proto",
+        "hci/facade/le_acl_manager_facade.proto",
+        "hci/facade/le_advertising_manager_facade.proto",
+        "hci/facade/le_scanning_manager_facade.proto",
+        "neighbor/facade/facade.proto",
+        "l2cap/classic/facade.proto",
+        "security/facade.proto",
+    ],
+}
+
+genrule {
+    name: "BluetoothFacadeGeneratedStub_h",
+    tools: [
+        "aprotoc",
+        "protoc-gen-grpc-cpp-plugin",
+    ],
+    cmd: "$(location aprotoc) -Isystem/bt/gd -Iexternal/protobuf/src --plugin=protoc-gen-grpc=$(location protoc-gen-grpc-cpp-plugin) $(in) --grpc_out=$(genDir) --cpp_out=$(genDir)",
+    srcs: [
+        ":BluetoothFacadeProto",
+    ],
+    out: [
+        "facade/common.grpc.pb.h",
+        "facade/common.pb.h",
+        "facade/rootservice.grpc.pb.h",
+        "facade/rootservice.pb.h",
+        "hal/facade.grpc.pb.h",
+        "hal/facade.pb.h",
+        "hci/facade/facade.grpc.pb.h",
+        "hci/facade/facade.pb.h",
+        "hci/facade/acl_manager_facade.grpc.pb.h",
+        "hci/facade/acl_manager_facade.pb.h",
+        "hci/facade/controller_facade.grpc.pb.h",
+        "hci/facade/controller_facade.pb.h",
+        "hci/facade/le_acl_manager_facade.grpc.pb.h",
+        "hci/facade/le_acl_manager_facade.pb.h",
+        "hci/facade/le_advertising_manager_facade.grpc.pb.h",
+        "hci/facade/le_advertising_manager_facade.pb.h",
+        "hci/facade/le_scanning_manager_facade.grpc.pb.h",
+        "hci/facade/le_scanning_manager_facade.pb.h",
+        "l2cap/classic/facade.grpc.pb.h",
+        "l2cap/classic/facade.pb.h",
+        "neighbor/facade/facade.grpc.pb.h",
+        "neighbor/facade/facade.pb.h",
+        "security/facade.grpc.pb.h",
+        "security/facade.pb.h",
+    ],
+}
+
+genrule {
+    name: "BluetoothFacadeGeneratedStub_cc",
+    tools: [
+        "aprotoc",
+        "protoc-gen-grpc-cpp-plugin",
+    ],
+    cmd: "$(location aprotoc) -Isystem/bt/gd -Iexternal/protobuf/src --plugin=protoc-gen-grpc=$(location protoc-gen-grpc-cpp-plugin) $(in) --grpc_out=$(genDir) --cpp_out=$(genDir)",
+    srcs: [
+        ":BluetoothFacadeProto",
+    ],
+    out: [
+        "facade/common.grpc.pb.cc",
+        "facade/common.pb.cc",
+        "facade/rootservice.grpc.pb.cc",
+        "facade/rootservice.pb.cc",
+        "hal/facade.grpc.pb.cc",
+        "hal/facade.pb.cc",
+        "hci/facade/facade.grpc.pb.cc",
+        "hci/facade/facade.pb.cc",
+        "hci/facade/acl_manager_facade.grpc.pb.cc",
+        "hci/facade/acl_manager_facade.pb.cc",
+        "hci/facade/controller_facade.grpc.pb.cc",
+        "hci/facade/controller_facade.pb.cc",
+        "hci/facade/le_acl_manager_facade.grpc.pb.cc",
+        "hci/facade/le_acl_manager_facade.pb.cc",
+        "hci/facade/le_advertising_manager_facade.grpc.pb.cc",
+        "hci/facade/le_advertising_manager_facade.pb.cc",
+        "hci/facade/le_scanning_manager_facade.grpc.pb.cc",
+        "hci/facade/le_scanning_manager_facade.pb.cc",
+        "l2cap/classic/facade.grpc.pb.cc",
+        "l2cap/classic/facade.pb.cc",
+        "neighbor/facade/facade.grpc.pb.cc",
+        "neighbor/facade/facade.pb.cc",
+        "security/facade.grpc.pb.cc",
+        "security/facade.pb.cc",
+    ],
+}
+
+genrule {
+    name: "BluetoothFacadeAndCertGeneratedStub_py",
+    tools: [
+        "aprotoc",
+        "protoc-gen-grpc-python-plugin",
+        "soong_zip",
+    ],
+    cmd: "$(location aprotoc) -Isystem/bt/gd -Iexternal/protobuf/src --plugin=protoc-gen-grpc=$(location protoc-gen-grpc-python-plugin) $(in) --grpc_out=$(genDir) --python_out=$(genDir); " +
+        "touch $(genDir)/facade/__init__.py; " +
+        "touch $(genDir)/hal/__init__.py; " +
+        "touch $(genDir)/hci/__init__.py; " +
+        "touch $(genDir)/hci/facade/__init__.py; " +
+        "touch $(genDir)/l2cap/classic/__init__.py; " +
+        "touch $(genDir)/neighbor/facade/__init__.py; " +
+        "touch $(genDir)/security/__init__.py; " +
+        "$(location soong_zip) -C $(genDir) -D $(genDir) -o $(out)",
+    srcs: [
+        ":BluetoothFacadeProto",
+    ],
+    out: ["bluetooth_cert_generated_py.zip"],
+    dist: {
+        targets: ["bluetooth_stack_with_facade"],
+    },
+
+}
+
+cc_defaults {
+  name: "bluetooth_py3_native_extension_defaults",
+  include_dirs: [
+    "external/python/cpython3/Include",
+  ],
+  target: {
+      android: {
+          include_dirs: ["external/python/cpython3/android/bionic/pyconfig"],
+      },
+      android_arm: {
+          cflags: ["-DSOABI=\"cpython-38android-arm-android-bionic\""],
+          suffix: ".cpython-38android-arm-android-bionic",
+      },
+      android_arm64: {
+          cflags: ["-DSOABI=\"cpython-38android-arm64-android-bionic\""],
+          suffix: ".cpython-38android-arm64-android-bionic",
+      },
+      android_x86: {
+          cflags: ["-DSOABI=\"cpython-38android-x86-android-bionic\""],
+          suffix: ".cpython-38android-x86-android-bionic",
+      },
+      android_x86_64: {
+          cflags: ["-DSOABI=\"cpython-38android-x86_64-android-bionic\""],
+          suffix: ".cpython-38android-x86_64-android-bionic",
+      },
+      // Regenerate include dirs with android_regen.sh
+      darwin_x86_64: {
+          include_dirs: ["external/python/cpython3/android/darwin_x86_64/pyconfig"],
+          cflags: [
+              "-Wno-deprecated-declarations",
+              "-Wno-pointer-arith",
+              "-DSOABI=\"cpython-38android-x86_64-darwin\"",
+          ],
+          suffix: ".cpython-38android-x86_64-darwin",
+      },
+      linux_bionic: {
+          // NB linux_bionic is a 'host' architecture but it uses the bionic libc like 'android'
+          // targets so use the android pyconfig.
+          include_dirs: ["external/python/cpython3/android/bionic/pyconfig"],
+          cflags: ["-DSOABI=\"cpython-38android-x86_64-linux-bionic\""],
+          suffix: ".cpython-38android-x86_64-linux-bionic",
+      },
+      linux_glibc_x86: {
+          enabled: false,
+      },
+      linux_glibc_x86_64: {
+          include_dirs: ["external/python/cpython3/android/linux_x86_64/pyconfig"],
+          cflags: ["-DSOABI=\"cpython-38android-x86_64-linux-gnu\""],
+          // Commenting out the Linux suffix so that cpython-38-x86_64-linux-gnu
+          // Python 3.8 can also import the untagged .so library per PEP 3149
+          // Keep this change until Android py3-cmd can run ACTS, gRPC and can
+          // Export Python native symbols such as PyType_Type
+          // suffix: ".cpython-38android-x86_64-linux-gnu",
+      },
+      windows: {
+          enabled: false,
+      },
+  },
+  allow_undefined_symbols: true,
+}
+
+cc_library_host_shared {
+  name: "bluetooth_packets_python3",
+  defaults: [
+    "gd_defaults",
+    "bluetooth_py3_native_extension_defaults"
+  ],
+  srcs: [
+    "packet/python3_module.cc",
+    "l2cap/fcs.cc",
+    ":BluetoothPacketSources",
+    "hci/address.cc",
+    "hci/class_of_device.cc",
+  ],
+  generated_headers: [
+    "BluetoothGeneratedPackets_h",
+  ],
+  generated_sources: [
+    "BluetoothGeneratedPackets_python3_cc",
+  ],
+  header_libs: [
+    "pybind11_headers",
+  ],
+  cflags: [
+    "-fexceptions",
+  ],
+  rtti: true,
 }
diff --git a/gd/Android.mk b/gd/Android.mk
new file mode 100644
index 0000000..60bcbcf
--- /dev/null
+++ b/gd/Android.mk
@@ -0,0 +1,46 @@
+LOCAL_PATH := $(call my-dir)
+
+bluetooth_cert_test_file_list := \
+    $(call all-named-files-under,*.py,cert) \
+    $(call all-named-files-under,*.sh,cert) \
+    $(call all-named-files-under,*.proto,cert facade hal hci/cert hci/facade l2cap/classic \
+	    l2cap/classic/cert neighbor/facade security) \
+    cert/cert_testcases_facade_only \
+    cert/android_devices_config.json \
+    cert/host_only_config_facade_only.json \
+    hal/cert/simple_hal_test.py \
+    hci/cert/acl_manager_test.py \
+    hci/cert/controller_test.py \
+    hci/cert/direct_hci_test.py \
+    hci/cert/le_acl_manager_test.py \
+    hci/cert/le_advertising_manager_test.py \
+    hci/cert/le_scanning_manager_test.py \
+    l2cap/classic/cert/l2cap_test.py \
+    l2cap/classic/cert/pts_l2cap_test.py \
+    neighbor/cert/neighbor_test.py \
+    security/cert/simple_security_test.py \
+    shim/cert/stack_test.py
+
+
+bluetooth_cert_test_file_list := $(addprefix $(LOCAL_PATH)/,$(bluetooth_cert_test_file_list))
+
+bluetooth_cert_test_file_list += \
+    $(HOST_OUT_EXECUTABLES)/bluetooth_stack_with_facade \
+    $(HOST_OUT_SHARED_LIBRARIES)/bluetooth_packets_python3.so \
+    $(HOST_OUT_SHARED_LIBRARIES)/libbluetooth_gd.so \
+    $(HOST_OUT_SHARED_LIBRARIES)/libc++.so \
+    $(HOST_OUT_SHARED_LIBRARIES)/libgrpc++_unsecure.so \
+    $(TARGET_OUT_EXECUTABLES)/bluetooth_stack_with_facade \
+    $(TARGET_OUT_SHARED_LIBRARIES)/libbluetooth_gd.so \
+    $(TARGET_OUT_SHARED_LIBRARIES)/libgrpc++_unsecure.so \
+    $(HOST_OUT_NATIVE_TESTS)/root-canal/root-canal
+
+bluetooth_cert_zip_path := \
+    $(call intermediates-dir-for,PACKAGING,bluetooth_cert_test_package,HOST)/bluetooth_cert_test.zip
+
+$(bluetooth_cert_zip_path): PRIVATE_BLUETOOTH_CERT_TEST_FILE_LIST := $(bluetooth_cert_test_file_list)
+
+$(bluetooth_cert_zip_path) : $(SOONG_ZIP) $(bluetooth_cert_test_file_list)
+	$(hide) $(SOONG_ZIP) -d -o $@ $(addprefix -f ,$(PRIVATE_BLUETOOTH_CERT_TEST_FILE_LIST))
+
+$(call dist-for-goals,bluetooth_stack_with_facade,$(bluetooth_cert_zip_path):bluetooth_cert_test.zip)
diff --git a/gd/README.md b/gd/README.md
new file mode 100644
index 0000000..52b4e58
--- /dev/null
+++ b/gd/README.md
@@ -0,0 +1,3 @@
+### Why is gabeldorsche plural?
+
+Please see this [informative video we've prepared](https://www.youtube.com/watch?v=vLRyJ0dawjM).
diff --git a/gd/TEST_MAPPING b/gd/TEST_MAPPING
index 471210b..f0ddeaf 100644
--- a/gd/TEST_MAPPING
+++ b/gd/TEST_MAPPING
@@ -3,6 +3,10 @@
     {
       "name" : "bluetooth_test_gd",
       "host" : true
+    },
+    {
+      "name" : "bluetooth_packet_parser_test",
+      "host" : true
     }
   ]
 }
diff --git a/gd/att/Android.bp b/gd/att/Android.bp
new file mode 100644
index 0000000..b7af1ef
--- /dev/null
+++ b/gd/att/Android.bp
@@ -0,0 +1,12 @@
+filegroup {
+    name: "BluetoothAttSources",
+    srcs: [
+        "att_module.cc",
+    ],
+}
+
+filegroup {
+    name: "BluetoothAttTestSources",
+    srcs: [
+    ],
+}
diff --git a/gd/att/att_module.cc b/gd/att/att_module.cc
new file mode 100644
index 0000000..2d1c3b1
--- /dev/null
+++ b/gd/att/att_module.cc
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "att"
+
+#include <memory>
+#include "module.h"
+#include "os/handler.h"
+#include "os/log.h"
+
+#include "att/att_module.h"
+#include "l2cap/classic/l2cap_classic_module.h"
+#include "l2cap/le/l2cap_le_module.h"
+
+namespace bluetooth {
+namespace att {
+
+const ModuleFactory AttModule::Factory = ModuleFactory([]() { return new AttModule(); });
+
+namespace {
+void OnAttRegistrationCompleteLe(l2cap::le::FixedChannelManager::RegistrationResult result,
+                                 std::unique_ptr<l2cap::le::FixedChannelService> le_smp_service) {
+  LOG_INFO("ATT channel registration complete");
+}
+
+void OnAttConnectionOpenLe(std::unique_ptr<l2cap::le::FixedChannel> channel) {
+  LOG_INFO("ATT conneciton opened");
+}
+}  // namespace
+
+struct AttModule::impl {
+  impl(os::Handler* att_handler, l2cap::le::L2capLeModule* l2cap_le_module,
+       l2cap::classic::L2capClassicModule* l2cap_classic_module)
+      : att_handler_(att_handler), l2cap_le_module_(l2cap_le_module), l2cap_classic_module_(l2cap_classic_module) {
+    // TODO: move that into a ATT manager, or other proper place
+    std::unique_ptr<bluetooth::l2cap::le::FixedChannelManager> l2cap_manager_le_(
+        l2cap_le_module_->GetFixedChannelManager());
+    l2cap_manager_le_->RegisterService(bluetooth::l2cap::kLeAttributeCid, {},
+                                       common::BindOnce(&OnAttRegistrationCompleteLe),
+                                       common::Bind(&OnAttConnectionOpenLe), att_handler_);
+  }
+
+  os::Handler* att_handler_;
+  l2cap::le::L2capLeModule* l2cap_le_module_;
+  l2cap::classic::L2capClassicModule* l2cap_classic_module_;
+};
+
+void AttModule::ListDependencies(ModuleList* list) {
+  list->add<l2cap::le::L2capLeModule>();
+  list->add<l2cap::classic::L2capClassicModule>();
+}
+
+void AttModule::Start() {
+  pimpl_ = std::make_unique<impl>(GetHandler(), GetDependency<l2cap::le::L2capLeModule>(),
+                                  GetDependency<l2cap::classic::L2capClassicModule>());
+}
+
+void AttModule::Stop() {
+  pimpl_.reset();
+}
+
+std::string AttModule::ToString() const {
+  return "Att Module";
+}
+
+// std::unique_ptr<AttManager> AttModule::GetAttManager() {
+//   return std::unique_ptr<AttManager>(
+//       new AttManager(pimpl_->att_handler_, &pimpl_->att_manager_impl));
+// }
+
+}  // namespace att
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/att/att_module.h b/gd/att/att_module.h
new file mode 100644
index 0000000..3501d64
--- /dev/null
+++ b/gd/att/att_module.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <memory>
+
+#include "module.h"
+
+namespace bluetooth {
+namespace att {
+
+class AttModule : public bluetooth::Module {
+ public:
+  AttModule() = default;
+  ~AttModule() = default;
+
+  static const ModuleFactory Factory;
+
+ protected:
+  void ListDependencies(ModuleList* list) override;
+
+  void Start() override;
+
+  void Stop() override;
+
+  std::string ToString() const override;
+
+ private:
+  struct impl;
+  std::unique_ptr<impl> pimpl_;
+  DISALLOW_COPY_AND_ASSIGN(AttModule);
+};
+
+}  // namespace att
+}  // namespace bluetooth
diff --git a/gd/cert/android_devices_config.json b/gd/cert/android_devices_config.json
new file mode 100644
index 0000000..7123c65
--- /dev/null
+++ b/gd/cert/android_devices_config.json
@@ -0,0 +1,51 @@
+{   "_description": "Bluetooth cert testing",
+    "testbed":
+    [
+        {
+            "_description": "Two Android devices cert testbed",
+            "name": "AndroidDeviceCert",
+            "GdDevice":
+            [
+                {
+                    "grpc_port": "8898",
+                    "grpc_root_server_port": "8896",
+                    "signal_port": "8894",
+                    "label": "cert_stack",
+                    "serial_number": "CERT",
+                    "cmd":
+                    [
+                        "adb",
+                        "-s",
+                        "$(serial_number)",
+                        "shell",
+                        "/system/bin/bluetooth_stack_with_facade",
+                        "--grpc-port=$(grpc_port)",
+                        "--root-server-port=$(grpc_root_server_port)",
+                        "--btsnoop=data/misc/bluetooth/logs/btsnoop_hci.log",
+                        "--signal-port=$(signal_port)"
+                    ]
+                },
+                {
+                    "grpc_port": "8899",
+                    "grpc_root_server_port": "8897",
+                    "signal_port": "8895",
+                    "label": "stack_under_test",
+                    "serial_number": "DUT",
+                    "cmd":
+                    [
+                        "adb",
+                        "-s",
+                        "$(serial_number)",
+                        "shell",
+                        "/system/bin/bluetooth_stack_with_facade",
+                        "--grpc-port=$(grpc_port)",
+                        "--root-server-port=$(grpc_root_server_port)",
+                        "--btsnoop=data/misc/bluetooth/logs/btsnoop_hci.log",
+                        "--signal-port=$(signal_port)"
+                    ]
+                }
+            ]
+        }
+    ],
+    "logpath": "/tmp/logs"
+}
diff --git a/gd/cert/bluetooth_packets_python3_setup.py b/gd/cert/bluetooth_packets_python3_setup.py
new file mode 100644
index 0000000..c9e25c2
--- /dev/null
+++ b/gd/cert/bluetooth_packets_python3_setup.py
@@ -0,0 +1,76 @@
+#!/usr/bin/env python3
+#
+#   Copyright 2019 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+# Usage:
+# 1. Run envsetup and lunch first in an Android checkout
+# 2. Make target bluetooth_packets_python3 that will generate C++ sources for the
+#    Extension
+# 3. Build only:
+#       python3 bluetooth_packets_python3_setup.py build_ext
+#   Then Find the .so file in build/lib.linux-x86_64-3.X
+# 4. Install:
+#       python3 bluetooth_packets_python3_setup.py install --user
+
+import os
+import glob
+from setuptools import setup, Extension
+
+ANDROID_BUILD_TOP = os.getenv("ANDROID_BUILD_TOP")
+PYBIND11_INCLUDE_DIR = os.path.join(ANDROID_BUILD_TOP,
+                                    "external/python/pybind11/include")
+GD_DIR = os.path.join(ANDROID_BUILD_TOP, "system/bt/gd")
+BT_PACKETS_GEN_DIR = os.path.join(
+    ANDROID_BUILD_TOP,
+    "out/soong/.intermediates/system/bt/gd/BluetoothGeneratedPackets_h/gen")
+BT_PACKETS_PY3_GEN_DIR = os.path.join(
+    ANDROID_BUILD_TOP,
+    "out/soong/.intermediates/system/bt/gd/BluetoothGeneratedPackets_python3_cc/gen"
+)
+
+BT_PACKETS_BASE_SRCS = [
+    os.path.join(GD_DIR, "l2cap/fcs.cc"),
+    os.path.join(GD_DIR, "packet/bit_inserter.cc"),
+    os.path.join(GD_DIR, "packet/byte_inserter.cc"),
+    os.path.join(GD_DIR, "packet/byte_observer.cc"),
+    os.path.join(GD_DIR, "packet/iterator.cc"),
+    os.path.join(GD_DIR, "packet/fragmenting_inserter.cc"),
+    os.path.join(GD_DIR, "packet/packet_view.cc"),
+    os.path.join(GD_DIR, "packet/raw_builder.cc"),
+    os.path.join(GD_DIR, "packet/view.cc"),
+]
+
+BT_PACKETS_PY3_SRCs = \
+  [os.path.join(GD_DIR, "packet/python3_module.cc")] \
+  + glob.glob(os.path.join(BT_PACKETS_PY3_GEN_DIR, "hci", "*.cc")) \
+  + glob.glob(os.path.join(BT_PACKETS_PY3_GEN_DIR, "l2cap", "*.cc")) \
+  + glob.glob(os.path.join(BT_PACKETS_PY3_GEN_DIR, "security", "*.cc"))
+
+bluetooth_packets_python3_module = Extension(
+    'bluetooth_packets_python3',
+    sources=BT_PACKETS_BASE_SRCS + BT_PACKETS_PY3_SRCs,
+    include_dirs=[
+        GD_DIR, BT_PACKETS_GEN_DIR, BT_PACKETS_PY3_GEN_DIR, PYBIND11_INCLUDE_DIR
+    ],
+    extra_compile_args=['-std=c++17'])
+
+setup(
+    name='bluetooth_packets_python3',
+    version='1.0',
+    author="Android Open Source Project",
+    description="""Bluetooth Packet Library""",
+    ext_modules=[bluetooth_packets_python3_module],
+    py_modules=["bluetooth_packets_python3"],
+)
diff --git a/gd/cert/cert_self_test.py b/gd/cert/cert_self_test.py
new file mode 100644
index 0000000..86edb4c
--- /dev/null
+++ b/gd/cert/cert_self_test.py
@@ -0,0 +1,232 @@
+#!/usr/bin/env python3
+#
+#   Copyright 2019 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import logging
+import time
+
+from mobly import asserts
+from datetime import datetime, timedelta
+from cert.gd_base_test_facade_only import GdFacadeOnlyBaseTestClass
+from cert.event_callback_stream import EventCallbackStream
+from cert.event_asserts import EventAsserts
+
+# Test packet nesting
+from bluetooth_packets_python3 import hci_packets
+from bluetooth_packets_python3 import l2cap_packets
+
+
+class BogusProto:
+
+    class BogusType:
+
+        def __init__(self):
+            self.name = "BogusProto"
+            self.is_extension = False
+            self.cpp_type = False
+
+        def type(self):
+            return 'BogusRpc'
+
+        def label(self):
+            return "label"
+
+    class BogusDescriptor:
+
+        def __init__(self, name):
+            self.full_name = name
+
+    def __init__(self, value):
+        self.value_ = value
+        self.DESCRIPTOR = BogusProto.BogusDescriptor(str(value))
+
+    def __str__(self):
+        return "BogusRpc value = " + str(self.value_)
+
+    def ListFields(self):
+        for field in [BogusProto.BogusType()]:
+            yield [field, self.value_]
+
+
+class FetchEvents:
+
+    def __init__(self, events, delay_ms):
+        self.events_ = events
+        self.sleep_time_ = (delay_ms * 1.0) / 1000
+        self.index_ = 0
+        self.done_ = False
+        self.then_ = datetime.now()
+
+    def __iter__(self):
+        for event in self.events_:
+            time.sleep(self.sleep_time_)
+            if self.done_:
+                return
+            logging.debug("yielding %d" % event)
+            yield BogusProto(event)
+
+    def done(self):
+        return self.done_
+
+    def cancel(self):
+        logging.debug("cancel")
+        self.done_ = True
+        return None
+
+
+class CertSelfTest(GdFacadeOnlyBaseTestClass):
+
+    def setup_test(self):
+        return True
+
+    def teardown_test(self):
+        return True
+
+    def test_assert_none_passes(self):
+        with EventCallbackStream(FetchEvents(events=[],
+                                             delay_ms=50)) as event_stream:
+            event_asserts = EventAsserts(event_stream)
+            event_asserts.assert_none(timeout=timedelta(milliseconds=10))
+
+    def test_assert_none_passes_after_one_second(self):
+        with EventCallbackStream(FetchEvents([1],
+                                             delay_ms=1500)) as event_stream:
+            event_asserts = EventAsserts(event_stream)
+            event_asserts.assert_none(timeout=timedelta(seconds=1.0))
+
+    def test_assert_none_fails(self):
+        try:
+            with EventCallbackStream(FetchEvents(events=[17],
+                                                 delay_ms=50)) as event_stream:
+                event_asserts = EventAsserts(event_stream)
+                event_asserts.assert_none(timeout=timedelta(seconds=1))
+        except Exception as e:
+            logging.debug(e)
+            return True  # Failed as expected
+        return False
+
+    def test_assert_none_matching_passes(self):
+        with EventCallbackStream(FetchEvents(events=[1, 2, 3],
+                                             delay_ms=50)) as event_stream:
+            event_asserts = EventAsserts(event_stream)
+            event_asserts.assert_none_matching(
+                lambda data: data.value_ == 4, timeout=timedelta(seconds=0.15))
+
+    def test_assert_none_matching_passes_after_1_second(self):
+        with EventCallbackStream(
+                FetchEvents(events=[1, 2, 3, 4], delay_ms=400)) as event_stream:
+            event_asserts = EventAsserts(event_stream)
+            event_asserts.assert_none_matching(
+                lambda data: data.value_ == 4, timeout=timedelta(seconds=1))
+
+    def test_assert_none_matching_fails(self):
+        try:
+            with EventCallbackStream(
+                    FetchEvents(events=[1, 2, 3], delay_ms=50)) as event_stream:
+                event_asserts = EventAsserts(event_stream)
+                event_asserts.assert_none_matching(
+                    lambda data: data.value_ == 2, timeout=timedelta(seconds=1))
+        except Exception as e:
+            logging.debug(e)
+            return True  # Failed as expected
+        return False
+
+    def test_assert_occurs_at_least_passes(self):
+        with EventCallbackStream(
+                FetchEvents(events=[1, 2, 3, 1, 2, 3],
+                            delay_ms=40)) as event_stream:
+            event_asserts = EventAsserts(event_stream)
+            event_asserts.assert_event_occurs(
+                lambda data: data.value_ == 1,
+                timeout=timedelta(milliseconds=300),
+                at_least_times=2)
+
+    def test_assert_occurs_passes(self):
+        with EventCallbackStream(FetchEvents(events=[1, 2, 3],
+                                             delay_ms=50)) as event_stream:
+            event_asserts = EventAsserts(event_stream)
+            event_asserts.assert_event_occurs(
+                lambda data: data.value_ == 1, timeout=timedelta(seconds=1))
+
+    def test_assert_occurs_fails(self):
+        try:
+            with EventCallbackStream(
+                    FetchEvents(events=[1, 2, 3], delay_ms=50)) as event_stream:
+                event_asserts = EventAsserts(event_stream)
+                event_asserts.assert_event_occurs(
+                    lambda data: data.value_ == 4, timeout=timedelta(seconds=1))
+        except Exception as e:
+            logging.debug(e)
+            return True  # Failed as expected
+        return False
+
+    def test_assert_occurs_at_most_passes(self):
+        with EventCallbackStream(FetchEvents(events=[1, 2, 3, 4],
+                                             delay_ms=50)) as event_stream:
+            event_asserts = EventAsserts(event_stream)
+            event_asserts.assert_event_occurs_at_most(
+                lambda data: data.value_ < 4,
+                timeout=timedelta(seconds=1),
+                at_most_times=3)
+
+    def test_assert_occurs_at_most_fails(self):
+        try:
+            with EventCallbackStream(
+                    FetchEvents(events=[1, 2, 3, 4],
+                                delay_ms=50)) as event_stream:
+                event_asserts = EventAsserts(event_stream)
+                event_asserts.assert_event_occurs_at_most(
+                    lambda data: data.value_ > 1,
+                    timeout=timedelta(seconds=1),
+                    at_most_times=2)
+        except Exception as e:
+            logging.debug(e)
+            return True  # Failed as expected
+        return False
+
+    def test_skip_a_test(self):
+        asserts.skip("Skipping this test because it's blocked by b/xyz")
+        assert False
+
+    def test_nested_packets(self):
+        handle = 123
+        inside = hci_packets.ReadScanEnableBuilder()
+        logging.debug(inside.Serialize())
+        logging.debug("building outside")
+        outside = hci_packets.AclPacketBuilder(
+            handle,
+            hci_packets.PacketBoundaryFlag.FIRST_NON_AUTOMATICALLY_FLUSHABLE,
+            hci_packets.BroadcastFlag.POINT_TO_POINT, inside)
+        logging.debug(outside.Serialize())
+        logging.debug("Done!")
+
+    def test_l2cap_config_options(self):
+        mtu_opt = l2cap_packets.MtuConfigurationOption()
+        mtu_opt.mtu = 123
+        fcs_opt = l2cap_packets.FrameCheckSequenceOption()
+        fcs_opt.fcs_type = l2cap_packets.FcsType.DEFAULT
+        request = l2cap_packets.ConfigurationRequestBuilder(
+            0x1d,  # Command ID
+            0xc1d,  # Channel ID
+            l2cap_packets.Continuation.END,
+            [mtu_opt, fcs_opt])
+        request.Serialize()
+        handle = 123
+        wrapped = hci_packets.AclPacketBuilder(
+            handle,
+            hci_packets.PacketBoundaryFlag.FIRST_NON_AUTOMATICALLY_FLUSHABLE,
+            hci_packets.BroadcastFlag.POINT_TO_POINT, request)
+        asserts.assert_true(
+            len(wrapped.Serialize()) == 16, "Packet serialized incorrectly")
diff --git a/gd/cert/cert_testcases_facade_only b/gd/cert/cert_testcases_facade_only
new file mode 100644
index 0000000..a5bb929
--- /dev/null
+++ b/gd/cert/cert_testcases_facade_only
@@ -0,0 +1,12 @@
+CertSelfTest
+SimpleHalTest
+DirectHciTest
+LeAdvertisingManagerTest
+LeScanningManagerTest
+SimpleSecurityTest
+NeighborTest
+ControllerTest
+AclManagerTest
+LeAclManagerTest
+StackTest
+L2capTest
diff --git a/gd/cert/event_asserts.py b/gd/cert/event_asserts.py
new file mode 100644
index 0000000..ea2dedc
--- /dev/null
+++ b/gd/cert/event_asserts.py
@@ -0,0 +1,176 @@
+#!/usr/bin/env python3
+#
+#   Copyright 2019 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+from datetime import datetime, timedelta
+import logging
+from queue import SimpleQueue, Empty
+
+from mobly import asserts
+
+from google.protobuf import text_format
+
+
+class EventAsserts(object):
+    """
+    A class that handles various asserts with respect to a gRPC unary stream
+
+    This class must be created before an event happens as events in a
+    EventCallbackStream is not sticky and will be lost if you don't subscribe
+    to them before generating those events.
+
+    When asserting on sequential events, a single EventAsserts object is enough
+
+    When asserting on simultaneous events, you would need multiple EventAsserts
+    objects as each EventAsserts object owns a separate queue that is actively
+    being popped as asserted events happen
+    """
+    DEFAULT_TIMEOUT_SECONDS = 3
+
+    def __init__(self, event_callback_stream):
+        if event_callback_stream is None:
+            raise ValueError("event_callback_stream cannot be None")
+        self.event_callback_stream = event_callback_stream
+        self.event_queue = SimpleQueue()
+        self.callback = lambda event: self.event_queue.put(event)
+        self.event_callback_stream.register_callback(self.callback)
+
+    def __del__(self):
+        self.event_callback_stream.unregister_callback(self.callback)
+
+    def remaining_time_delta(self, end_time):
+        remaining = end_time - datetime.now()
+        if remaining < timedelta(milliseconds=0):
+            remaining = timedelta(milliseconds=0)
+        return remaining
+
+    def assert_none(self, timeout=timedelta(seconds=DEFAULT_TIMEOUT_SECONDS)):
+        """
+        Assert no event happens within timeout period
+
+        :param timeout: a timedelta object
+        :return:
+        """
+        logging.debug("assert_none %fs" % (timeout.total_seconds()))
+        try:
+            event = self.event_queue.get(timeout=timeout.total_seconds())
+            asserts.assert_true(
+                event is None,
+                msg=("Expected None, but got %s" % text_format.MessageToString(
+                    event, as_one_line=True)))
+        except Empty:
+            return
+
+    def assert_none_matching(
+            self, match_fn, timeout=timedelta(seconds=DEFAULT_TIMEOUT_SECONDS)):
+        """
+        Assert no events where match_fn(event) is True happen within timeout
+        period
+
+        :param match_fn: return True/False on match_fn(event)
+        :param timeout: a timedelta object
+        :return:
+        """
+        logging.debug("assert_none_matching %fs" % (timeout.total_seconds()))
+        event = None
+        end_time = datetime.now() + timeout
+        while event is None and datetime.now() < end_time:
+            remaining = self.remaining_time_delta(end_time)
+            logging.debug("Waiting for event (%fs remaining)" %
+                          (remaining.total_seconds()))
+            try:
+                current_event = self.event_queue.get(
+                    timeout=remaining.total_seconds())
+                if match_fn(current_event):
+                    event = current_event
+            except Empty:
+                continue
+        logging.debug("Done waiting for an event")
+        if event is None:
+            return  # Avoid an assert in MessageToString(None, ...)
+        asserts.assert_true(
+            event is None,
+            msg=("Expected None matching, but got %s" %
+                 text_format.MessageToString(event, as_one_line=True)))
+
+    def assert_event_occurs(self,
+                            match_fn,
+                            at_least_times=1,
+                            timeout=timedelta(seconds=DEFAULT_TIMEOUT_SECONDS)):
+        """
+        Assert at least |at_least_times| instances of events happen where
+        match_fn(event) returns True within timeout period
+
+        :param match_fn: returns True/False on match_fn(event)
+        :param timeout: a timedelta object
+        :param at_least_times: how many times at least a matching event should
+                               happen
+        :return:
+        """
+        logging.debug("assert_event_occurs %d %fs" % (at_least_times,
+                                                      timeout.total_seconds()))
+        event_list = []
+        end_time = datetime.now() + timeout
+        while len(event_list) < at_least_times and datetime.now() < end_time:
+            remaining = self.remaining_time_delta(end_time)
+            logging.debug("Waiting for event (%fs remaining)" %
+                          (remaining.total_seconds()))
+            try:
+                current_event = self.event_queue.get(
+                    timeout=remaining.total_seconds())
+                if match_fn(current_event):
+                    event_list.append(current_event)
+            except Empty:
+                continue
+        logging.debug("Done waiting for event")
+        asserts.assert_true(
+            len(event_list) >= at_least_times,
+            msg=("Expected at least %d events, but got %d" % (at_least_times,
+                                                              len(event_list))))
+
+    def assert_event_occurs_at_most(
+            self,
+            match_fn,
+            at_most_times,
+            timeout=timedelta(seconds=DEFAULT_TIMEOUT_SECONDS)):
+        """
+        Assert at most |at_most_times| instances of events happen where
+        match_fn(event) returns True within timeout period
+
+        :param match_fn: returns True/False on match_fn(event)
+        :param at_most_times: how many times at most a matching event should
+                               happen
+        :param timeout:a timedelta object
+        :return:
+        """
+        logging.debug("assert_event_occurs_at_most")
+        event_list = []
+        end_time = datetime.now() + timeout
+        while len(event_list) <= at_most_times and datetime.now() < end_time:
+            remaining = self.remaining_time_delta(end_time)
+            logging.debug("Waiting for event iteration (%fs remaining)" %
+                          (remaining.total_seconds()))
+            try:
+                current_event = self.event_queue.get(
+                    timeout=remaining.total_seconds())
+                if match_fn(current_event):
+                    event_list.append(current_event)
+            except Empty:
+                continue
+        logging.debug("Done waiting, got %d events" % len(event_list))
+        asserts.assert_true(
+            len(event_list) <= at_most_times,
+            msg=("Expected at most %d events, but got %d" % (at_most_times,
+                                                             len(event_list))))
diff --git a/gd/cert/event_callback_stream.py b/gd/cert/event_callback_stream.py
new file mode 100644
index 0000000..00ea9cc
--- /dev/null
+++ b/gd/cert/event_callback_stream.py
@@ -0,0 +1,147 @@
+#!/usr/bin/env python3
+#
+#   Copyright 2019 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+from concurrent.futures import ThreadPoolExecutor
+from grpc import RpcError
+import logging
+
+
+class EventCallbackStream(object):
+    """
+    A an object that translate a gRPC stream of events to a Python stream of
+    callbacks.
+
+    All callbacks are non-sticky. This means that user will only receive callback
+    generated after EventCallbackStream is registered and will not receive any
+    callback after EventCallbackStream is unregistered
+
+    You would need a new EventCallbackStream and anything that depends on this
+    object once shutdown() is called
+    """
+
+    def __init__(self, server_stream_call):
+        """
+        Construct this object, call the |grpc_lambda| and trigger event_callback on
+        the thread used to create this object until |destroy| is called when this
+        object can no longer be used
+        :param server_stream_call: A server stream call object returned from
+                                   calling a gRPC server stream RPC API. The
+                                   object must support iterator interface (i.e.
+                                   next() method) and the grpc.Call interface
+                                   so that we can cancel it
+        :param event_callback: callback to be invoked with the only argument as
+                               the generated event. The callback will be invoked
+                               on a separate thread created within this object
+        """
+        if server_stream_call is None:
+            raise ValueError("server_stream_call must not be None")
+        self.server_stream_call = server_stream_call
+        self.handlers = []
+        self.executor = ThreadPoolExecutor()
+        self.future = self.executor.submit(EventCallbackStream._event_loop,
+                                           self)
+
+    def __enter__(self):
+        return self
+
+    def __exit__(self, type, value, traceback):
+        self.shutdown()
+        if traceback is None:
+            return True
+        else:
+            return False
+
+    def __del__(self):
+        self.shutdown()
+
+    def register_callback(self, callback, matcher_fn=None):
+        """
+        Register a callback to handle events. Event will be handled by callback
+        if matcher_fn(event) returns True
+
+        callback and matcher are registered as a tuple. Hence the same callback
+        with different matcher are considered two different handler units. Same
+        matcher, but different callback are also considered different handling
+        unit
+
+        Callback will be invoked on a ThreadPoolExecutor owned by this
+        EventCallbackStream
+
+        :param callback: Will be called as callback(event)
+        :param matcher_fn: A boolean function that returns True or False when
+                           calling matcher_fn(event), if None, all event will
+                           be matched
+        """
+        if callback is None:
+            raise ValueError("callback must not be None")
+        self.handlers.append((callback, matcher_fn))
+
+    def unregister_callback(self, callback, matcher_fn=None):
+        """
+        Unregister callback and matcher_fn from the event stream. Both objects
+        must match exactly the ones when calling register_callback()
+
+        :param callback: callback used in register_callback()
+        :param matcher_fn: matcher_fn used in register_callback()
+        :raises ValueError when (callback, matcher_fn) tuple is not found
+        """
+        if callback is None:
+            raise ValueError("callback must not be None")
+        self.handlers.remove((callback, matcher_fn))
+
+    def shutdown(self):
+        """
+        Stop the gRPC lambda so that event_callback will not be invoked after th
+        method returns.
+
+        This object will be useless after this call as there is no way to restart
+        the gRPC callback. You would have to create a new EventCallbackStream
+
+        :return: None on success, exception object on failure
+        """
+        while not self.server_stream_call.done():
+            self.server_stream_call.cancel()
+        exception_for_return = None
+        try:
+            result = self.future.result()
+            if result:
+                logging.warning("Inner loop error %s" % result)
+                raise result
+        except Exception as exp:
+            logging.warning("Exception: %s" % (exp))
+            exception_for_return = exp
+        self.executor.shutdown()
+        return exception_for_return
+
+    def _event_loop(self):
+        """
+        Main loop for consuming the gRPC stream events.
+        Blocks until computation is cancelled
+        :return: None on success, exception object on failure
+        """
+        try:
+            for event in self.server_stream_call:
+                for (callback, matcher_fn) in self.handlers:
+                    if not matcher_fn or matcher_fn(event):
+                        callback(event)
+            return None
+        except RpcError as exp:
+            if self.server_stream_call.cancelled():
+                logging.debug("Cancelled")
+                return None
+            else:
+                logging.warning("Some RPC error not due to cancellation")
+            return exp
diff --git a/gd/cert/gd_base_test.py b/gd/cert/gd_base_test.py
new file mode 100644
index 0000000..ab19ba1
--- /dev/null
+++ b/gd/cert/gd_base_test.py
@@ -0,0 +1,77 @@
+#!/usr/bin/env python3
+#
+#   Copyright 2019 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+from acts.base_test import BaseTestClass
+
+import importlib
+import logging
+import os
+import signal
+import subprocess
+
+ANDROID_BUILD_TOP = os.environ.get('ANDROID_BUILD_TOP')
+
+
+class GdBaseTestClass(BaseTestClass):
+
+    def __init__(self, configs):
+        BaseTestClass.__init__(self, configs)
+
+        log_path_base = getattr(configs, "log_path", "/tmp/logs")
+        gd_devices = self.controller_configs.get("GdDevice")
+        gd_cert_devices = self.controller_configs.get("GdCertDevice")
+
+        self.rootcanal_running = False
+        if 'rootcanal' in self.controller_configs:
+            self.rootcanal_running = True
+            rootcanal_logpath = os.path.join(log_path_base,
+                                             'rootcanal_logs.txt')
+            self.rootcanal_logs = open(rootcanal_logpath, 'w')
+            rootcanal_config = self.controller_configs['rootcanal']
+            rootcanal_hci_port = str(rootcanal_config.get("hci_port", "6402"))
+            android_host_out = os.environ.get('ANDROID_HOST_OUT')
+            rootcanal = android_host_out + "/nativetest64/root-canal/root-canal"
+            self.rootcanal_process = subprocess.Popen(
+                [
+                    rootcanal,
+                    str(rootcanal_config.get("test_port", "6401")),
+                    rootcanal_hci_port,
+                    str(rootcanal_config.get("link_layer_port", "6403"))
+                ],
+                cwd=ANDROID_BUILD_TOP,
+                env=os.environ.copy(),
+                stdout=self.rootcanal_logs,
+                stderr=self.rootcanal_logs)
+            for gd_device in gd_devices:
+                gd_device["rootcanal_port"] = rootcanal_hci_port
+            for gd_cert_device in gd_cert_devices:
+                gd_cert_device["rootcanal_port"] = rootcanal_hci_port
+
+        self.register_controller(
+            importlib.import_module('cert.gd_device'), builtin=True)
+        self.register_controller(
+            importlib.import_module('cert.gd_cert_device'), builtin=True)
+
+    def teardown_class(self):
+        if self.rootcanal_running:
+            self.rootcanal_process.send_signal(signal.SIGINT)
+            rootcanal_return_code = self.rootcanal_process.wait()
+            self.rootcanal_logs.close()
+            if rootcanal_return_code != 0 and\
+                rootcanal_return_code != -signal.SIGINT:
+                logging.error(
+                    "rootcanal stopped with code: %d" % rootcanal_return_code)
+                return False
diff --git a/gd/cert/gd_base_test_facade_only.py b/gd/cert/gd_base_test_facade_only.py
new file mode 100644
index 0000000..d79e20b
--- /dev/null
+++ b/gd/cert/gd_base_test_facade_only.py
@@ -0,0 +1,75 @@
+#!/usr/bin/env python3
+#
+#   Copyright 2019 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+from acts.base_test import BaseTestClass
+from acts import context
+
+import importlib
+import logging
+import os
+import signal
+import subprocess
+
+ANDROID_BUILD_TOP = os.environ.get('ANDROID_BUILD_TOP')
+
+
+class GdFacadeOnlyBaseTestClass(BaseTestClass):
+
+    def setup_class(self):
+
+        log_path_base = context.get_current_context().get_full_output_path()
+        gd_devices = self.controller_configs.get("GdDevice")
+
+        self.rootcanal_running = False
+        if 'rootcanal' in self.controller_configs:
+            self.rootcanal_running = True
+            rootcanal_logpath = os.path.join(log_path_base,
+                                             'rootcanal_logs.txt')
+            self.rootcanal_logs = open(rootcanal_logpath, 'w')
+            rootcanal_config = self.controller_configs['rootcanal']
+            rootcanal_hci_port = str(rootcanal_config.get("hci_port", "6402"))
+            android_host_out = os.environ.get('ANDROID_HOST_OUT')
+            rootcanal = android_host_out + "/nativetest64/root-canal/root-canal"
+            self.rootcanal_process = subprocess.Popen(
+                [
+                    rootcanal,
+                    str(rootcanal_config.get("test_port", "6401")),
+                    rootcanal_hci_port,
+                    str(rootcanal_config.get("link_layer_port", "6403"))
+                ],
+                cwd=ANDROID_BUILD_TOP,
+                env=os.environ.copy(),
+                stdout=self.rootcanal_logs,
+                stderr=self.rootcanal_logs)
+            for gd_device in gd_devices:
+                gd_device["rootcanal_port"] = rootcanal_hci_port
+
+        self.register_controller(
+            importlib.import_module('cert.gd_device'), builtin=True)
+
+        self.device_under_test = self.gd_devices[1]
+        self.cert_device = self.gd_devices[0]
+
+    def teardown_class(self):
+        if self.rootcanal_running:
+            self.rootcanal_process.send_signal(signal.SIGINT)
+            rootcanal_return_code = self.rootcanal_process.wait()
+            self.rootcanal_logs.close()
+            if rootcanal_return_code != 0 and\
+                rootcanal_return_code != -signal.SIGINT:
+                logging.error(
+                    "rootcanal stopped with code: %d" % rootcanal_return_code)
+                return False
diff --git a/gd/cert/gd_device.py b/gd/cert/gd_device.py
new file mode 100644
index 0000000..a13298c
--- /dev/null
+++ b/gd/cert/gd_device.py
@@ -0,0 +1,100 @@
+#!/usr/bin/env python3
+#
+#   Copyright 2019 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import logging
+
+from facade import rootservice_pb2_grpc as facade_rootservice_pb2_grpc
+from cert.gd_device_base import GdDeviceBase, GdDeviceConfigError, replace_vars
+from hal import facade_pb2_grpc as hal_facade_pb2_grpc
+from hci.facade import facade_pb2_grpc as hci_facade_pb2_grpc
+from hci.facade import acl_manager_facade_pb2_grpc
+from hci.facade import controller_facade_pb2_grpc
+from hci.facade import le_acl_manager_facade_pb2_grpc
+from hci.facade import le_advertising_manager_facade_pb2_grpc
+from hci.facade import le_scanning_manager_facade_pb2_grpc
+from neighbor.facade import facade_pb2_grpc as neighbor_facade_pb2_grpc
+from l2cap.classic import facade_pb2_grpc as l2cap_facade_pb2_grpc
+from security import facade_pb2_grpc as security_facade_pb2_grpc
+
+ACTS_CONTROLLER_CONFIG_NAME = "GdDevice"
+ACTS_CONTROLLER_REFERENCE_NAME = "gd_devices"
+
+
+def create(configs):
+    if not configs:
+        raise GdDeviceConfigError("Configuration is empty")
+    elif not isinstance(configs, list):
+        raise GdDeviceConfigError("Configuration should be a list")
+    return get_instances_with_configs(configs)
+
+
+def destroy(devices):
+    for device in devices:
+        try:
+            device.clean_up()
+        except:
+            device.log.exception("Failed to clean up properly.")
+
+
+def get_info(devices):
+    return []
+
+
+def get_instances_with_configs(configs):
+    print(configs)
+    devices = []
+    for config in configs:
+        resolved_cmd = []
+        for entry in config["cmd"]:
+            logging.debug(entry)
+            resolved_cmd.append(replace_vars(entry, config))
+        devices.append(
+            GdDevice(config["grpc_port"], config["grpc_root_server_port"],
+                     config["signal_port"], resolved_cmd, config["label"],
+                     config.get("serial_number", "")))
+    return devices
+
+
+class GdDevice(GdDeviceBase):
+
+    def __init__(self, grpc_port, grpc_root_server_port, signal_port, cmd,
+                 label, serial_number):
+        super().__init__(grpc_port, grpc_root_server_port, signal_port, cmd,
+                         label, ACTS_CONTROLLER_CONFIG_NAME, serial_number)
+
+        # Facade stubs
+        self.rootservice = facade_rootservice_pb2_grpc.RootFacadeStub(
+            self.grpc_root_server_channel)
+        self.hal = hal_facade_pb2_grpc.HciHalFacadeStub(self.grpc_channel)
+        self.controller_read_only_property = facade_rootservice_pb2_grpc.ReadOnlyPropertyStub(
+            self.grpc_channel)
+        self.hci = hci_facade_pb2_grpc.HciLayerFacadeStub(self.grpc_channel)
+        self.l2cap = l2cap_facade_pb2_grpc.L2capClassicModuleFacadeStub(
+            self.grpc_channel)
+        self.hci_acl_manager = acl_manager_facade_pb2_grpc.AclManagerFacadeStub(
+            self.grpc_channel)
+        self.hci_le_acl_manager = le_acl_manager_facade_pb2_grpc.LeAclManagerFacadeStub(
+            self.grpc_channel)
+        self.hci_controller = controller_facade_pb2_grpc.ControllerFacadeStub(
+            self.grpc_channel)
+        self.hci_le_advertising_manager = le_advertising_manager_facade_pb2_grpc.LeAdvertisingManagerFacadeStub(
+            self.grpc_channel)
+        self.hci_le_scanning_manager = le_scanning_manager_facade_pb2_grpc.LeScanningManagerFacadeStub(
+            self.grpc_channel)
+        self.neighbor = neighbor_facade_pb2_grpc.NeighborFacadeStub(
+            self.grpc_channel)
+        self.security = security_facade_pb2_grpc.SecurityModuleFacadeStub(
+            self.grpc_channel)
diff --git a/gd/cert/gd_device_base.py b/gd/cert/gd_device_base.py
new file mode 100644
index 0000000..40301c4
--- /dev/null
+++ b/gd/cert/gd_device_base.py
@@ -0,0 +1,164 @@
+#!/usr/bin/env python3
+#
+#   Copyright 2019 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import logging
+import os
+from builtins import open
+import json
+import signal
+import socket
+import subprocess
+import time
+
+from acts import context, error, tracelogger
+from acts.controllers.adb import AdbProxy
+
+import grpc
+
+ANDROID_BUILD_TOP = os.environ.get('ANDROID_BUILD_TOP')
+ANDROID_HOST_OUT = os.environ.get('ANDROID_HOST_OUT')
+ANDROID_PRODUCT_OUT = os.environ.get('ANDROID_PRODUCT_OUT')
+WAIT_CHANNEL_READY_TIMEOUT = 10
+
+
+def replace_vars(string, config):
+    serial_number = config.get("serial_number")
+    if serial_number is None:
+        serial_number = ""
+    rootcanal_port = config.get("rootcanal_port")
+    if rootcanal_port is None:
+        rootcanal_port = ""
+    if serial_number == "DUT" or serial_number == "CERT":
+        raise Exception("Did you forget to configure the serial number?")
+    return string.replace("$ANDROID_HOST_OUT", ANDROID_HOST_OUT) \
+                 .replace("$(grpc_port)", config.get("grpc_port")) \
+                 .replace("$(grpc_root_server_port)", config.get("grpc_root_server_port")) \
+                 .replace("$(rootcanal_port)", rootcanal_port) \
+                 .replace("$(signal_port)", config.get("signal_port")) \
+                 .replace("$(serial_number)", serial_number)
+
+
+class GdDeviceBase:
+
+    def __init__(self, grpc_port, grpc_root_server_port, signal_port, cmd,
+                 label, type_identifier, serial_number):
+        self.label = label if label is not None else grpc_port
+        # logging.log_path only exists when this is used in an ACTS test run.
+        self.log_path_base = context.get_current_context().get_full_output_path(
+        )
+        self.log = tracelogger.TraceLogger(
+            GdDeviceBaseLoggerAdapter(logging.getLogger(), {
+                'device': label,
+                'type_identifier': type_identifier
+            }))
+
+        backing_process_logpath = os.path.join(
+            self.log_path_base,
+            '%s_%s_backing_logs.txt' % (type_identifier, label))
+        self.backing_process_logs = open(backing_process_logpath, 'w')
+
+        cmd_str = json.dumps(cmd)
+        if "--btsnoop=" not in cmd_str:
+            btsnoop_path = os.path.join(self.log_path_base,
+                                        '%s_btsnoop_hci.log' % label)
+            cmd.append("--btsnoop=" + btsnoop_path)
+
+        self.serial_number = serial_number
+        if self.serial_number:
+            self.ad = AdbProxy(serial_number)
+            self.ad.shell("date " + time.strftime("%m%d%H%M%Y.%S"))
+            self.ad.tcp_forward(int(grpc_port), int(grpc_port))
+            self.ad.tcp_forward(
+                int(grpc_root_server_port), int(grpc_root_server_port))
+            self.ad.reverse("tcp:%s tcp:%s" % (signal_port, signal_port))
+            self.ad.push(
+                os.path.join(ANDROID_PRODUCT_OUT,
+                             "system/bin/bluetooth_stack_with_facade"),
+                "system/bin")
+            self.ad.push(
+                os.path.join(ANDROID_PRODUCT_OUT,
+                             "system/lib64/libbluetooth_gd.so"), "system/lib64")
+            self.ad.push(
+                os.path.join(ANDROID_PRODUCT_OUT,
+                             "system/lib64/libgrpc++_unsecure.so"),
+                "system/lib64")
+            self.ad.shell("logcat -c")
+            self.ad.shell("rm /data/misc/bluetooth/logs/btsnoop_hci.log")
+            self.ad.shell("svc bluetooth disable")
+
+        tester_signal_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        tester_signal_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,
+                                        1)
+        socket_address = ('localhost', int(signal_port))
+        tester_signal_socket.bind(socket_address)
+        tester_signal_socket.listen(1)
+
+        self.backing_process = subprocess.Popen(
+            cmd,
+            cwd=ANDROID_BUILD_TOP,
+            env=os.environ.copy(),
+            stdout=self.backing_process_logs,
+            stderr=self.backing_process_logs)
+        tester_signal_socket.accept()
+        tester_signal_socket.close()
+
+        self.grpc_root_server_channel = grpc.insecure_channel(
+            "localhost:" + grpc_root_server_port)
+        self.grpc_port = int(grpc_port)
+        self.grpc_channel = grpc.insecure_channel("localhost:" + grpc_port)
+
+    def clean_up(self):
+        self.grpc_channel.close()
+        self.grpc_root_server_channel.close()
+        stop_signal = signal.SIGINT
+        self.backing_process.send_signal(stop_signal)
+        backing_process_return_code = self.backing_process.wait()
+        self.backing_process_logs.close()
+        if backing_process_return_code not in [-stop_signal, 0]:
+            logging.error("backing process %s stopped with code: %d" %
+                          (self.label, backing_process_return_code))
+
+        if self.serial_number:
+            self.ad.shell("logcat -d -f /data/misc/bluetooth/logs/system_log")
+            self.ad.pull(
+                "/data/misc/bluetooth/logs/btsnoop_hci.log %s" % os.path.join(
+                    self.log_path_base, "%s_btsnoop_hci.log" % self.label))
+            self.ad.pull(
+                "/data/misc/bluetooth/logs/system_log %s" % os.path.join(
+                    self.log_path_base, "%s_system_log" % self.label))
+
+    def wait_channel_ready(self):
+        future = grpc.channel_ready_future(self.grpc_channel)
+        try:
+            future.result(timeout=WAIT_CHANNEL_READY_TIMEOUT)
+        except grpc.FutureTimeoutError:
+            logging.error("wait channel ready timeout")
+
+
+class GdDeviceBaseLoggerAdapter(logging.LoggerAdapter):
+
+    def process(self, msg, kwargs):
+        msg = "[%s|%s] %s" % (self.extra["type_identifier"],
+                              self.extra["device"], msg)
+        return (msg, kwargs)
+
+
+class GdDeviceConfigError(Exception):
+    """Raised when GdDevice configs are malformatted."""
+
+
+class GdDeviceError(error.ActsError):
+    """Raised when there is an error in GdDevice."""
diff --git a/gd/cert/host_only_config_facade_only.json b/gd/cert/host_only_config_facade_only.json
new file mode 100644
index 0000000..9de9d70
--- /dev/null
+++ b/gd/cert/host_only_config_facade_only.json
@@ -0,0 +1,48 @@
+
+{   "_description": "Bluetooth cert testing",
+    "testbed":
+    [
+        {
+            "_description": "Host only cert testbed",
+            "name": "HostOnlyCert",
+            "rootcanal":
+            {
+                "test_port": 6401,
+                "hci_port": 6402,
+                "link_layer_port": 6403
+            },
+            "GdDevice":
+            [
+                {
+                    "grpc_port": "8998",
+                    "grpc_root_server_port": "8996",
+                    "signal_port": "8994",
+                    "label": "cert_stack",
+                    "cmd":
+                    [
+                        "$ANDROID_HOST_OUT/bin/bluetooth_stack_with_facade",
+                        "--grpc-port=$(grpc_port)",
+                        "--root-server-port=$(grpc_root_server_port)",
+                        "--rootcanal-port=$(rootcanal_port)",
+                        "--signal-port=$(signal_port)"
+                    ]
+                },
+                {
+                    "grpc_port": "8999",
+                    "grpc_root_server_port": "8997",
+                    "signal_port": "8995",
+                    "label": "stack_under_test",
+                    "cmd":
+                    [
+                        "$ANDROID_HOST_OUT/bin/bluetooth_stack_with_facade",
+                        "--grpc-port=$(grpc_port)",
+                        "--root-server-port=$(grpc_root_server_port)",
+                        "--rootcanal-port=$(rootcanal_port)",
+                        "--signal-port=$(signal_port)"
+                    ]
+                }
+            ]
+        }
+    ],
+    "logpath": "/tmp/logs"
+}
diff --git a/gd/cert/pts.json b/gd/cert/pts.json
new file mode 100644
index 0000000..76a27be
--- /dev/null
+++ b/gd/cert/pts.json
@@ -0,0 +1,34 @@
+
+{   "_description": "Bluetooth cert testing",
+    "testbed":
+    [
+        {
+            "_description": "PTS l2cap layer test",
+            "name": "PTSL2CAP",
+            "GdDevice":
+            [
+                {
+                    "grpc_port": "8899",
+                    "grpc_root_server_port": "8897",
+                    "signal_port": "8895",
+                    "label": "stack_under_test",
+                    "serial_number": "DUT",
+                    "cmd":
+                    [
+                        "adb",
+                        "-s",
+                        "$(serial_number)",
+                        "shell",
+                        "/system/bin/bluetooth_stack_with_facade",
+                        "--grpc-port=$(grpc_port)",
+                        "--root-server-port=$(grpc_root_server_port)",
+                        "--btsnoop=data/misc/bluetooth/logs/btsnoop_hci.log",
+                        "--signal-port=$(signal_port)"
+                    ]
+                }
+            ],
+            "pts_address": "PTS"
+        }
+    ],
+    "logpath": "/tmp/logs"
+}
diff --git a/gd/cert/pts_base_test.py b/gd/cert/pts_base_test.py
new file mode 100644
index 0000000..906bccf
--- /dev/null
+++ b/gd/cert/pts_base_test.py
@@ -0,0 +1,34 @@
+#!/usr/bin/env python3
+#
+#   Copyright 2019 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+from acts.base_test import BaseTestClass
+
+import importlib
+import logging
+import os
+import signal
+import subprocess
+
+
+class PTSBaseTestClass(BaseTestClass):
+
+    def __init__(self, configs):
+        BaseTestClass.__init__(self, configs)
+
+        gd_devices = self.controller_configs.get("GdDevice")
+
+        self.register_controller(
+            importlib.import_module('cert.gd_device'), builtin=True)
diff --git a/gd/cert/pts_l2cap_testcase b/gd/cert/pts_l2cap_testcase
new file mode 100644
index 0000000..a0ae40e
--- /dev/null
+++ b/gd/cert/pts_l2cap_testcase
@@ -0,0 +1 @@
+PTSL2capTest
diff --git a/gd/cert/python3.8-gd b/gd/cert/python3.8-gd
new file mode 100755
index 0000000..2d9640e
--- /dev/null
+++ b/gd/cert/python3.8-gd
@@ -0,0 +1,2 @@
+#! /bin/bash
+PYTHONPATH=$PYTHONPATH:$ANDROID_BUILD_TOP/out/host/linux-x86/lib64:$ANDROID_BUILD_TOP/system/bt/gd:$ANDROID_BUILD_TOP/out/dist/bluetooth_cert_generated_py python3.8 "$@"
diff --git a/gd/cert/run_cert_facade_only.sh b/gd/cert/run_cert_facade_only.sh
new file mode 100755
index 0000000..e7b0110
--- /dev/null
+++ b/gd/cert/run_cert_facade_only.sh
@@ -0,0 +1,13 @@
+#! /bin/bash
+
+if [[ -z "${ANDROID_BUILD_TOP}" ]]; then
+  echo "ANDROID_BUILD_TOP is not set"
+fi
+
+if [[ -z "${ANDROID_HOST_OUT}" ]]; then
+  echo "ANDROID_HOST_OUT is not set for host run"
+fi
+
+unzip -o -q $ANDROID_BUILD_TOP/out/dist/bluetooth_cert_generated_py.zip -d $ANDROID_BUILD_TOP/out/dist/bluetooth_cert_generated_py
+
+PYTHONPATH=$PYTHONPATH:$ANDROID_BUILD_TOP/out/host/linux-x86/lib64:$ANDROID_BUILD_TOP/system/bt/gd:$ANDROID_BUILD_TOP/out/dist/bluetooth_cert_generated_py python3.8 `which act.py` -c $ANDROID_BUILD_TOP/system/bt/gd/cert/host_only_config_facade_only.json -tf $ANDROID_BUILD_TOP/system/bt/gd/cert/cert_testcases_facade_only -tp $ANDROID_BUILD_TOP/system/bt/gd
diff --git a/gd/cert/run_device_cert.sh b/gd/cert/run_device_cert.sh
new file mode 100755
index 0000000..1da2bcc
--- /dev/null
+++ b/gd/cert/run_device_cert.sh
@@ -0,0 +1,6 @@
+#! /bin/bash
+
+unzip -q -o $ANDROID_BUILD_TOP/out/dist/bluetooth_cert_generated_py.zip -d $ANDROID_BUILD_TOP/out/dist/bluetooth_cert_generated_py
+
+# For bluetooth_packets_python3
+PYTHONPATH=$PYTHONPATH:$ANDROID_BUILD_TOP/out/host/linux-x86/lib64:$ANDROID_BUILD_TOP/system/bt/gd:$ANDROID_BUILD_TOP/out/dist/bluetooth_cert_generated_py python3.8 `which act.py` -c $ANDROID_BUILD_TOP/system/bt/gd/cert/android_devices_config.json -tf $ANDROID_BUILD_TOP/system/bt/gd/cert/cert_testcases_facade_only -tp $ANDROID_BUILD_TOP/system/bt/gd
diff --git a/gd/cert/run_pts_l2cap.sh b/gd/cert/run_pts_l2cap.sh
new file mode 100755
index 0000000..313cbb7
--- /dev/null
+++ b/gd/cert/run_pts_l2cap.sh
@@ -0,0 +1,6 @@
+#! /bin/bash
+
+unzip -u $ANDROID_BUILD_TOP/out/dist/bluetooth_cert_generated_py.zip -d $ANDROID_BUILD_TOP/out/dist/bluetooth_cert_generated_py
+
+# For bluetooth_packets_python3
+PYTHONPATH=$PYTHONPATH:$ANDROID_BUILD_TOP/out/host/linux-x86/lib64:$ANDROID_BUILD_TOP/system/bt/gd:$ANDROID_BUILD_TOP/out/dist/bluetooth_cert_generated_py python3.8 `which act.py` -c $ANDROID_BUILD_TOP/system/bt/gd/cert/pts.json -tf $ANDROID_BUILD_TOP/system/bt/gd/cert/pts_l2cap_testcase -tp $ANDROID_BUILD_TOP/system/bt/gd
diff --git a/gd/cert/set_up_acts.sh b/gd/cert/set_up_acts.sh
new file mode 100755
index 0000000..a902efc
--- /dev/null
+++ b/gd/cert/set_up_acts.sh
@@ -0,0 +1,109 @@
+#! /bin/bash
+#
+# Script to setup environment to execute bluetooth certification stack
+#
+# for more info, see go/acts
+
+## Android build main build setup script relative to top level android source root
+BUILD_SETUP=./build/envsetup.sh
+
+function UsageAndroidTree {
+    cat<<EOF
+Ensure invoked from within the android source tree
+EOF
+}
+
+function UsageSourcedNotExecuted {
+    cat<<EOF
+Ensure script is SOURCED and not executed to persist the build setup
+e.g.
+source $0
+EOF
+}
+
+function UpFind {
+    while [[ $PWD != / ]] ; do
+        rc=$(find "$PWD" -maxdepth 1 "$@")
+        if [ -n "$rc" ]; then
+            echo $(dirname "$rc")
+            return
+        fi
+        cd ..
+    done
+}
+
+function SetUpAndroidBuild {
+    pushd .
+    android_root=$(UpFind -name out -type d)
+    if [[ -z $android_root ]] ; then
+        UsageAndroidTree
+        return
+    fi
+    echo "Found android root $android_root"
+    cd $android_root && . $BUILD_SETUP
+    echo "Sourced build setup rules"
+    cd $android_root && lunch
+    popd
+}
+
+function SetupPython38 {
+    echo "Setting up python3.8"
+    sudo apt-get install python3.8-dev
+}
+
+function CompileBluetoothPacketsPython3 {
+    echo "bluetooth_packets_python3 is not found, compiling"
+    croot
+    make -j bluetooth_packets_python3
+}
+
+if [[ "${BASH_SOURCE[0]}" == "${0}" ]] ; then
+    UsageSourcedNotExecuted
+    exit 1
+fi
+
+if [[ -z "$ANDROID_BUILD_TOP" ]] ; then
+    SetUpAndroidBuild
+fi
+
+## Check python3.8 is installed properly
+## Need Python 3.8 because bluetooth_packets_python3 is compiled against
+## Python 3.8 headers
+dpkg -l python3.8-dev > /dev/null 2>&1
+if [[ $? -ne 0 ]] ; then
+    SetupPython38
+fi
+
+## Check bluetooth_packets_python3 is compiled succssfully
+PYTHONPATH=$PYTHONPATH:$ANDROID_BUILD_TOP/out/host/linux-x86/lib64 python3.8 -c "
+import bluetooth_packets_python3 as bp3
+bp3.BaseStruct
+"
+if [[ $? -ne 0 ]] ; then
+  pushd .
+  CompileBluetoothPacketsPython3
+  popd
+  python3.8 -c "
+import bluetooth_packets_python3 as bp3
+bp3.BaseStruct
+"
+  if [[ $? -ne 0 ]] ; then
+    echo "Setup failed as bluetooth_packets_python3 cannot be found"
+  else
+    echo "Found bluetooth_packets_python3 after compilation"
+  fi
+else
+  echo "Found bluetooth_packets_python3"
+fi
+
+## All is good now so go ahead with the acts setup
+pushd .
+cd $ANDROID_BUILD_TOP/tools/test/connectivity/acts/framework/
+sudo python3.8 setup.py develop
+if [[ $? -eq 0 ]] ; then
+    echo "cert setup complete"
+else
+    echo "cert setup failed"
+fi
+popd
+
diff --git a/gd/cert/set_up_and_run_device_cert.sh b/gd/cert/set_up_and_run_device_cert.sh
new file mode 100755
index 0000000..1a46708
--- /dev/null
+++ b/gd/cert/set_up_and_run_device_cert.sh
@@ -0,0 +1,157 @@
+#!/bin/bash
+
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
+
+function menu-adb() {
+    TMP=$(adb devices -l | grep -v "List of device" | awk '{ print $1 }')
+    # TODO(optedoblivion): If the device doesn't have a name (offline), it misnames them
+    NTMP=$(adb devices -l | grep -v "List of device" | awk '{ print $6 }' | cut -d ':' -f 2)
+    SERIALS=($TMP)
+    DEVICES=($NTMP)
+    LEN=${#SERIALS[@]}
+    result=0
+    if [ $LEN -lt 1 ]; then
+        echo "No devices connected!"
+        exit 1
+    fi
+
+    if [ "$LEN" == "" ]; then
+        LEN=0
+    fi
+
+    answer=0
+
+    DEVICE_NAME="$1 device"
+
+    if [ $LEN -gt 1 ]; then
+        echo "+-------------------------------------------------+" 1>&2
+        echo "| Choose a ${DEVICE_NAME}:                         " 1>&2
+        echo "+-------------------------------------------------+" 1>&2
+        echo "|                                                 |" 1>&2
+        let fixed_len=$LEN-1
+        for i in `seq 0 $fixed_len`;
+        do
+            serial=${SERIALS[i]}
+            device=${DEVICES[i]}
+            echo "| $i) $serial $device" 1>&2
+            ## TODO[MSB]: Find character count, fill with space and ending box wall
+        done
+        echo "|                                                 |" 1>&2
+        echo "+-------------------------------------------------+" 1>&2
+        echo 1>&2
+        echo -n "Index number: " 1>&2
+        read answer
+    fi
+
+    if [ $answer -ge $LEN ]; then
+        echo
+        echo "Please choose a correct index!" 1>&2
+        echo
+        exit 1
+    fi
+
+    SERIAL=${SERIALS[$answer]}
+    echo $SERIAL
+}
+
+function UpFind {
+    while [[ $PWD != / ]] ; do
+        rc=$(find "$PWD" -maxdepth 1 "$@")
+        if [ -n "$rc" ]; then
+            echo $(dirname "$rc")
+            return
+        fi
+        cd ..
+    done
+}
+
+
+function get-android-root() {
+    android_root=$(UpFind -name out -type d)
+    if [[ -z $android_root ]] ; then
+        echo
+        echo "Needs to be ran in the android tree"
+        echo
+        exit 1
+    fi
+    echo "${android_root}"
+}
+
+function banner() {
+    echo
+    echo "GD On Device Cert Test"
+    echo
+}
+
+## Main
+banner
+
+DRY_RUN=""
+DO_BUILD=0
+if [ $# -gt 0 ]; then
+    for var in "$@"
+    do
+        if [ "$var" == "-h" ]; then
+            echo
+            echo "Usage: $0 [-h|-d]"
+            echo
+            echo "Available Options:"
+            echo "=================="
+            echo " -h | Help(this) Menu"
+            echo " -d | Dry run; just prints commands"
+            echo
+            exit 0
+        elif [ "$var" == "-d" ]; then
+            DRY_RUN="echo"
+        elif [ "$var" == "-b" ]; then
+            DO_BUILD=1
+        fi
+    done
+fi
+
+## Verify devices connected and sane
+DUT_SERIAL="$(menu-adb DUT)"
+DUT_ADB="adb -s ${DUT_SERIAL}"
+DUT_NAME="$(adb devices -l | grep -v "List of device" | grep ${DUT_SERIAL} | awk '{ print $6 }' | cut -d ':' -f 2)"
+
+CERT_SERIAL="$(menu-adb CERT)"
+CERT_ADB="adb -s ${CERT_SERIAL}"
+CERT_NAME="$(adb devices -l | grep -v "List of device" | grep ${CERT_SERIAL} | awk '{ print $6 }' | cut -d ':' -f 2)"
+
+if [ "${CERT_SERIAL}" == "${DUT_SERIAL}" ]; then
+    echo
+    echo "ERROR: CERT and DUT cannot be the same device, or you only have one device connected!"
+    echo
+    exit 1
+fi
+
+## Start builds
+if [ $DO_BUILD == 1 ]; then
+    $DRY_RUN cd $(get-android-root)
+    $DRY_RUN . build/envsetup.sh
+    #DUT
+    $DRY_RUN lunch $DUT_NAME
+    $DRY_RUN cd $(get-android-root)/system/bt/gd
+    $DRY_RUN mma -j `cat /proc/cpuinfo | grep core | wc -l`
+    $DRY_RUN cd $(get-android-root)
+    # CERT
+    $DRY_RUN lunch $CERT_NAME
+    $DRY_RUN cd $(get-android-root)/system/bt/gd
+    $DRY_RUN mma -j `cat /proc/cpuinfo | grep core | wc -l`
+    $DRY_RUN cd $(get-android-root)
+fi
+
+## Set android devices in config
+pushd .
+cd "${DIR}"
+# Reset in case user chooses different item in menu
+git co android_devices_config.json
+popd
+$DRY_RUN sed -i "s/\"DUT\"/\"${DUT_SERIAL}\"/g" ${DIR}/android_devices_config.json
+$DRY_RUN sed -i "s/\"CERT\"/\"${CERT_SERIAL}\"/g" ${DIR}/android_devices_config.json
+
+## ACTS
+#$DRY_RUN source $(get-android-root)/system/bt/gd/cert/set_up_acts.sh
+
+## Start test
+$DRY_RUN $(get-android-root)/system/bt/gd/cert/run_device_cert.sh
diff --git a/gd/cert/set_up_virtualenv.sh b/gd/cert/set_up_virtualenv.sh
new file mode 100644
index 0000000..c730583
--- /dev/null
+++ b/gd/cert/set_up_virtualenv.sh
@@ -0,0 +1,220 @@
+#! /bin/bash
+#
+# Script to setup virtual environment to run GD cert tests and devlop using IDE
+#
+# Usage
+#  1. cd system/bt/gd
+#  2. source cert/set_up_virtualenv.sh
+#  3. source gd_cert_venv/bin/activate
+#  4. [run tests, do development, hack]
+#  5. deactivate (or just close the terminal window)
+
+## Android build main build setup script relative to top level android source root
+BUILD_SETUP=./build/envsetup.sh
+
+function UsageAndroidTree {
+    cat<<EOF
+Ensure invoked from within the android source tree
+EOF
+}
+
+function UsageSourcedNotExecuted {
+    cat<<EOF
+Ensure script is SOURCED and not executed to persist the build setup
+e.g.
+source $0
+EOF
+}
+
+function UpFind {
+    while [[ $PWD != / ]] ; do
+        rc=$(find "$PWD" -maxdepth 1 "$@")
+        if [ -n "$rc" ]; then
+            echo $(dirname "$rc")
+            return
+        fi
+        cd ..
+    done
+}
+
+function SetUpAndroidBuild {
+    pushd .
+    android_root=$(UpFind -name out -type d)
+    if [[ -z $android_root ]] ; then
+        UsageAndroidTree
+        return
+    fi
+    echo "Found android root $android_root"
+    cd $android_root && . $BUILD_SETUP
+    echo "Sourced build setup rules"
+    cd $android_root && lunch
+    popd
+}
+
+function SetupPython38 {
+    echo "Setting up python3.8"
+    sudo apt-get install python3.8-dev
+}
+
+function SetupPip3 {
+    echo "Setting up pip3"
+    sudo apt-get install python3-pip
+}
+
+function CompileBluetoothPacketsPython3 {
+    echo "bluetooth_packets_python3 is not found, compiling"
+    croot
+    make -j bluetooth_packets_python3
+}
+
+# Deactivate existing virtual environment, if any, ignore errors
+deactivate > /dev/null 2>&1
+
+if [[ "${BASH_SOURCE[0]}" == "${0}" ]] ; then
+    UsageSourcedNotExecuted
+    return 1
+fi
+
+## Check python3.8 is installed properly
+## Need Python 3.8 because bluetooth_packets_python3 is compiled against
+## Python 3.8 headers
+dpkg -l python3.8-dev > /dev/null 2>&1
+if [[ $? -ne 0 ]] ; then
+    SetupPython38
+fi
+
+## Check pip3 is installed properly
+## Need pip3 for Python 3 support
+dpkg -l python3-pip > /dev/null 2>&1
+if [[ $? -ne 0 ]] ; then
+    SetupPip3
+fi
+
+# Install and upgrade virtualenv to latest version
+pip3 install --user --upgrade virtualenv > /dev/null 2>&1
+if [[ $? -ne 0 ]] ; then
+    echo "Error install and upgrade virtualenv"
+    return 1
+fi
+
+# Set-up Android environment variables
+if [[ -z "$ANDROID_BUILD_TOP" ]] ; then
+    SetUpAndroidBuild
+fi
+
+## Check bluetooth_packets_python3 is compiled succssfully
+$ANDROID_BUILD_TOP/system/bt/gd/cert/python3.8-gd -c "
+import bluetooth_packets_python3 as bp3
+bp3.BaseStruct
+"
+if [[ $? -ne 0 ]] ; then
+  pushd .
+  CompileBluetoothPacketsPython3
+  popd
+  $ANDROID_BUILD_TOP/system/bt/gd/cert/python3.8-gd -c "
+import bluetooth_packets_python3 as bp3
+bp3.BaseStruct
+"
+  if [[ $? -ne 0 ]] ; then
+    echo "Setup failed as bluetooth_packets_python3 cannot be found"
+    return 1
+  else
+    echo "Found bluetooth_packets_python3 after compilation"
+  fi
+else
+  echo "Found bluetooth_packets_python3"
+fi
+
+## Compile and unzip test artifacts
+if [[ ! -f "$ANDROID_BUILD_TOP/out/dist/bluetooth_cert_generated_py.zip" || ! -f "$ANDROID_BUILD_TOP/out/dist/bluetooth_cert_test.zip" ]]; then
+    echo "bluetooth_cert_generated_py.zip OR bluetooth_cert_test.zip is not found, compiling"
+    m -j dist bluetooth_stack_with_facade
+    if [[ ! -f "$ANDROID_BUILD_TOP/out/dist/bluetooth_cert_generated_py.zip" || ! -f "$ANDROID_BUILD_TOP/out/dist/bluetooth_cert_test.zip" ]]; then
+        echo "Failed to compile bluetooth_stack_with_facade"
+        return 1
+    fi
+fi
+unzip -u $ANDROID_BUILD_TOP/out/dist/bluetooth_cert_generated_py.zip -d $ANDROID_BUILD_TOP/out/dist/bluetooth_cert_generated_py
+if [[ $? -ne 0 ]] ; then
+    echo "Failed to unzip bluetooth_cert_generated_py.zip"
+    return 1
+fi
+unzip -u $ANDROID_BUILD_TOP/out/dist/bluetooth_cert_test.zip -d $ANDROID_BUILD_TOP/out/dist/bluetooth_cert_test
+if [[ $? -ne 0 ]] ; then
+    echo "Failed to unzip bluetooth_cert_test.zip"
+    return 1
+fi
+
+# Set-up virtualenv
+pushd .
+cd $ANDROID_BUILD_TOP/system/bt/gd
+virtualenv -p python3.8 gd_cert_venv
+popd
+if [[ $? -ne 0 ]] ; then
+    echo "Error setting up virtualenv"
+    return 1
+fi
+
+# Set up artifacts
+pushd .
+cd $ANDROID_BUILD_TOP/system/bt/gd/gd_cert_venv/lib/python3.8/site-packages
+# Python generated code
+ln -sfT $ANDROID_BUILD_TOP/tools/test/connectivity/acts/framework/acts acts
+ln -sfT $ANDROID_BUILD_TOP/out/dist/bluetooth_cert_generated_py/cert cert
+ln -sfT $ANDROID_BUILD_TOP/out/dist/bluetooth_cert_generated_py/facade facade
+ln -sfT $ANDROID_BUILD_TOP/out/dist/bluetooth_cert_generated_py/hal hal
+ln -sfT $ANDROID_BUILD_TOP/out/dist/bluetooth_cert_generated_py/hci hci
+ln -sfT $ANDROID_BUILD_TOP/out/dist/bluetooth_cert_generated_py/l2cap l2cap
+ln -sfT $ANDROID_BUILD_TOP/out/dist/bluetooth_cert_generated_py/neighbor neighbor
+ln -sfT $ANDROID_BUILD_TOP/out/dist/bluetooth_cert_generated_py/security security
+# Native libraries
+ln -sfT $ANDROID_BUILD_TOP/out/dist/bluetooth_cert_test/out/host/linux-x86/lib64/bluetooth_packets_python3.so bluetooth_packets_python3.so
+# Per systrace, Python only load from python3.8/lib64 directory for plugin imported native libraries
+mkdir -p $ANDROID_BUILD_TOP/system/bt/gd/gd_cert_venv/lib/python3.8/lib64
+cd $ANDROID_BUILD_TOP/system/bt/gd/gd_cert_venv/lib/python3.8/lib64
+ln -sfT $ANDROID_BUILD_TOP/out/dist/bluetooth_cert_test/out/host/linux-x86/lib64/libc++.so libc++.so
+ln -sfT $ANDROID_BUILD_TOP/out/dist/bluetooth_cert_test/out/host/linux-x86/lib64/libbluetooth_gd.so libbluetooth_gd.so
+ln -sfT $ANDROID_BUILD_TOP/out/dist/bluetooth_cert_test/out/host/linux-x86/lib64/libgrpc++_unsecure.so libgrpc++_unsecure.so
+# Binaries
+cd $ANDROID_BUILD_TOP/system/bt/gd/gd_cert_venv/bin
+ln -sfT $ANDROID_BUILD_TOP/out/host/linux-x86/bin/bluetooth_stack_with_facade bluetooth_stack_with_facade
+ln -sfT $ANDROID_BUILD_TOP/out/host/linux-x86/nativetest64/root-canal/root-canal root-canal
+popd
+
+# Activate virtualenv
+pushd .
+source $ANDROID_BUILD_TOP/system/bt/gd/gd_cert_venv/bin/activate
+popd
+if [[ $? -ne 0 ]] ; then
+    echo "Failed to activate virtualenv"
+    deactivate
+    return 1
+fi
+if [[ -z "$ANDROID_BUILD_TOP" ]] ; then
+    echo "Failed to inherit Android build environment"
+    deactivate
+    return 1
+fi
+
+## Set up ACTS
+# sudo is no longer needed since we are in a virtual environment
+python3.8 $ANDROID_BUILD_TOP/tools/test/connectivity/acts/framework/setup.py develop
+if [[ $? -ne 0 ]] ; then
+    echo "ACTS setup failed"
+    deactivate
+    return 1
+fi
+
+pip3 install protobuf
+if [[ $? -ne 0 ]] ; then
+    echo "Failed to install protobuf"
+    deactivate
+    return 1
+fi
+
+deactivate
+
+echo ""
+echo "Please mark GD root directory as \"Project Sources and Headers\" in IDE"
+echo "If still seeing errors, invalidate cached and restart"
+echo "virtualenv setup complete"
diff --git a/gd/common/Android.bp b/gd/common/Android.bp
index 9d8c16d..c923427 100644
--- a/gd/common/Android.bp
+++ b/gd/common/Android.bp
@@ -1,15 +1,16 @@
 filegroup {
     name: "BluetoothCommonSources",
     srcs: [
-        "address.cc",
-        "class_of_device.cc",
-    ]
+        "link_key.cc",
+    ],
 }
 
 filegroup {
     name: "BluetoothCommonTestSources",
     srcs: [
-        "address_unittest.cc",
-        "class_of_device_unittest.cc",
-    ]
+        "blocking_queue_unittest.cc",
+        "bidi_queue_unittest.cc",
+        "observer_registry_test.cc",
+        "link_key_unittest.cc",
+    ],
 }
diff --git a/gd/common/address.cc b/gd/common/address.cc
deleted file mode 100644
index cd8101a..0000000
--- a/gd/common/address.cc
+++ /dev/null
@@ -1,92 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2019 The Android Open Source Project
- *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at:
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- *
- ******************************************************************************/
-
-#include "address.h"
-
-#include <stdint.h>
-#include <algorithm>
-#include <sstream>
-#include <vector>
-
-namespace bluetooth {
-namespace common {
-
-static_assert(sizeof(Address) == 6, "Address must be 6 bytes long!");
-
-const Address Address::kAny{{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}};
-const Address Address::kEmpty{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
-
-Address::Address(const uint8_t (&addr)[6]) {
-  std::copy(addr, addr + kLength, address);
-};
-
-std::string Address::ToString() const {
-  char buffer[] = "00:00:00:00:00:00";
-  std::snprintf(&buffer[0], sizeof(buffer),
-      "%02x:%02x:%02x:%02x:%02x:%02x", address[5], address[4], address[3], address[2], address[1], address[0]);
-  std::string str(buffer);
-  return str;
-}
-
-bool Address::FromString(const std::string& from, Address& to) {
-  Address new_addr;
-  if (from.length() != 17) {
-    return false;
-  }
-
-  std::istringstream stream(from);
-  std::string token;
-  int index = 0;
-  while (getline(stream, token, ':')) {
-    if (index >= 6) {
-      return false;
-    }
-
-    if (token.length() != 2) {
-      return false;
-    }
-
-    char* temp = nullptr;
-    new_addr.address[5 - index] = strtol(token.c_str(), &temp, 16);
-    if (*temp != '\0') {
-      return false;
-    }
-
-    index++;
-  }
-
-  if (index != 6) {
-    return false;
-  }
-
-  to = new_addr;
-  return true;
-}
-
-size_t Address::FromOctets(const uint8_t* from) {
-  std::copy(from, from + kLength, address);
-  return kLength;
-};
-
-bool Address::IsValidAddress(const std::string& address) {
-  Address tmp;
-  return Address::FromString(address, tmp);
-}
-
-}  // namespace common
-}  // namespace bluetooth
diff --git a/gd/common/address.h b/gd/common/address.h
deleted file mode 100644
index 0036a59..0000000
--- a/gd/common/address.h
+++ /dev/null
@@ -1,81 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2019 The Android Open Source Project
- *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at:
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- *
- ******************************************************************************/
-
-#pragma once
-
-#include <string>
-
-namespace bluetooth {
-namespace common {
-
-class Address final {
- public:
-  static constexpr unsigned int kLength = 6;
-
-  uint8_t address[kLength];
-
-  Address() = default;
-  Address(const uint8_t (&addr)[6]);
-
-  bool operator<(const Address& rhs) const {
-    return (std::memcmp(address, rhs.address, sizeof(address)) < 0);
-  }
-  bool operator==(const Address& rhs) const {
-    return (std::memcmp(address, rhs.address, sizeof(address)) == 0);
-  }
-  bool operator>(const Address& rhs) const {
-    return (rhs < *this);
-  }
-  bool operator<=(const Address& rhs) const {
-    return !(*this > rhs);
-  }
-  bool operator>=(const Address& rhs) const {
-    return !(*this < rhs);
-  }
-  bool operator!=(const Address& rhs) const {
-    return !(*this == rhs);
-  }
-
-  bool IsEmpty() const {
-    return *this == kEmpty;
-  }
-
-  std::string ToString() const;
-
-  // Converts |string| to Address and places it in |to|. If |from| does
-  // not represent a Bluetooth address, |to| is not modified and this function
-  // returns false. Otherwise, it returns true.
-  static bool FromString(const std::string& from, Address& to);
-
-  // Copies |from| raw Bluetooth address octets to the local object.
-  // Returns the number of copied octets - should be always Address::kLength
-  size_t FromOctets(const uint8_t* from);
-
-  static bool IsValidAddress(const std::string& address);
-
-  static const Address kEmpty;  // 00:00:00:00:00:00
-  static const Address kAny;    // FF:FF:FF:FF:FF:FF
-};
-
-inline std::ostream& operator<<(std::ostream& os, const Address& a) {
-  os << a.ToString();
-  return os;
-}
-
-}  // namespace common
-}  // namespace bluetooth
diff --git a/gd/common/address_unittest.cc b/gd/common/address_unittest.cc
deleted file mode 100644
index cdecce3..0000000
--- a/gd/common/address_unittest.cc
+++ /dev/null
@@ -1,199 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2019 The Android Open Source Project
- *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at:
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- *
- ******************************************************************************/
-
-#include <gtest/gtest.h>
-
-#include "common/address.h"
-
-using bluetooth::common::Address;
-
-static const char* test_addr = "bc:9a:78:56:34:12";
-static const char* test_addr2 = "21:43:65:87:a9:cb";
-
-TEST(AddressUnittest, test_constructor_array) {
-  Address bdaddr({0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc});
-
-  ASSERT_EQ(0x12, bdaddr.address[0]);
-  ASSERT_EQ(0x34, bdaddr.address[1]);
-  ASSERT_EQ(0x56, bdaddr.address[2]);
-  ASSERT_EQ(0x78, bdaddr.address[3]);
-  ASSERT_EQ(0x9A, bdaddr.address[4]);
-  ASSERT_EQ(0xBC, bdaddr.address[5]);
-
-  std::string ret = bdaddr.ToString();
-
-  ASSERT_STREQ(test_addr, ret.c_str());
-}
-
-TEST(AddressUnittest, test_is_empty) {
-  Address empty;
-  Address::FromString("00:00:00:00:00:00", empty);
-  ASSERT_TRUE(empty.IsEmpty());
-
-  Address not_empty;
-  Address::FromString("00:00:00:00:00:01", not_empty);
-  ASSERT_FALSE(not_empty.IsEmpty());
-}
-
-TEST(AddressUnittest, test_to_from_str) {
-  Address bdaddr;
-  Address::FromString(test_addr, bdaddr);
-
-  ASSERT_EQ(0x12, bdaddr.address[0]);
-  ASSERT_EQ(0x34, bdaddr.address[1]);
-  ASSERT_EQ(0x56, bdaddr.address[2]);
-  ASSERT_EQ(0x78, bdaddr.address[3]);
-  ASSERT_EQ(0x9A, bdaddr.address[4]);
-  ASSERT_EQ(0xBC, bdaddr.address[5]);
-
-  std::string ret = bdaddr.ToString();
-
-  ASSERT_STREQ(test_addr, ret.c_str());
-}
-
-TEST(AddressUnittest, test_from_octets) {
-  static const uint8_t test_addr_array[] = {0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc};
-
-  Address bdaddr;
-  size_t expected_result = Address::kLength;
-  ASSERT_EQ(expected_result, bdaddr.FromOctets(test_addr_array));
-
-  ASSERT_EQ(0x12, bdaddr.address[0]);
-  ASSERT_EQ(0x34, bdaddr.address[1]);
-  ASSERT_EQ(0x56, bdaddr.address[2]);
-  ASSERT_EQ(0x78, bdaddr.address[3]);
-  ASSERT_EQ(0x9A, bdaddr.address[4]);
-  ASSERT_EQ(0xBC, bdaddr.address[5]);
-
-  std::string ret = bdaddr.ToString();
-
-  ASSERT_STREQ(test_addr, ret.c_str());
-}
-
-TEST(AddressTest, test_equals) {
-  Address bdaddr1;
-  Address bdaddr2;
-  Address bdaddr3;
-  Address::FromString(test_addr, bdaddr1);
-  Address::FromString(test_addr, bdaddr2);
-  EXPECT_TRUE(bdaddr1 == bdaddr2);
-  EXPECT_FALSE(bdaddr1 != bdaddr2);
-  EXPECT_TRUE(bdaddr1 == bdaddr1);
-  EXPECT_FALSE(bdaddr1 != bdaddr1);
-
-  Address::FromString(test_addr2, bdaddr3);
-  EXPECT_FALSE(bdaddr2 == bdaddr3);
-  EXPECT_TRUE(bdaddr2 != bdaddr3);
-}
-
-TEST(AddressTest, test_less_than) {
-  Address bdaddr1;
-  Address bdaddr2;
-  Address bdaddr3;
-  Address::FromString(test_addr, bdaddr1);
-  Address::FromString(test_addr, bdaddr2);
-  EXPECT_FALSE(bdaddr1 < bdaddr2);
-  EXPECT_FALSE(bdaddr1 < bdaddr1);
-
-  Address::FromString(test_addr2, bdaddr3);
-  EXPECT_TRUE(bdaddr2 < bdaddr3);
-  EXPECT_FALSE(bdaddr3 < bdaddr2);
-}
-
-TEST(AddressTest, test_more_than) {
-  Address bdaddr1;
-  Address bdaddr2;
-  Address bdaddr3;
-  Address::FromString(test_addr, bdaddr1);
-  Address::FromString(test_addr, bdaddr2);
-  EXPECT_FALSE(bdaddr1 > bdaddr2);
-  EXPECT_FALSE(bdaddr1 > bdaddr1);
-
-  Address::FromString(test_addr2, bdaddr3);
-  EXPECT_FALSE(bdaddr2 > bdaddr3);
-  EXPECT_TRUE(bdaddr3 > bdaddr2);
-}
-
-TEST(AddressTest, test_less_than_or_equal) {
-  Address bdaddr1;
-  Address bdaddr2;
-  Address bdaddr3;
-  Address::FromString(test_addr, bdaddr1);
-  Address::FromString(test_addr, bdaddr2);
-  EXPECT_TRUE(bdaddr1 <= bdaddr2);
-  EXPECT_TRUE(bdaddr1 <= bdaddr1);
-
-  Address::FromString(test_addr2, bdaddr3);
-  EXPECT_TRUE(bdaddr2 <= bdaddr3);
-  EXPECT_FALSE(bdaddr3 <= bdaddr2);
-}
-
-TEST(AddressTest, test_more_than_or_equal) {
-  Address bdaddr1;
-  Address bdaddr2;
-  Address bdaddr3;
-  Address::FromString(test_addr, bdaddr1);
-  Address::FromString(test_addr, bdaddr2);
-  EXPECT_TRUE(bdaddr1 >= bdaddr2);
-  EXPECT_TRUE(bdaddr1 >= bdaddr1);
-
-  Address::FromString(test_addr2, bdaddr3);
-  EXPECT_FALSE(bdaddr2 >= bdaddr3);
-  EXPECT_TRUE(bdaddr3 >= bdaddr2);
-}
-
-TEST(AddressTest, test_copy) {
-  Address bdaddr1;
-  Address bdaddr2;
-  Address::FromString(test_addr, bdaddr1);
-  bdaddr2 = bdaddr1;
-
-  EXPECT_TRUE(bdaddr1 == bdaddr2);
-}
-
-TEST(AddressTest, IsValidAddress) {
-  EXPECT_FALSE(Address::IsValidAddress(""));
-  EXPECT_FALSE(Address::IsValidAddress("000000000000"));
-  EXPECT_FALSE(Address::IsValidAddress("00:00:00:00:0000"));
-  EXPECT_FALSE(Address::IsValidAddress("00:00:00:00:00:0"));
-  EXPECT_FALSE(Address::IsValidAddress("00:00:00:00:00:0;"));
-  EXPECT_TRUE(Address::IsValidAddress("00:00:00:00:00:00"));
-  EXPECT_TRUE(Address::IsValidAddress("AB:cd:00:00:00:00"));
-  EXPECT_FALSE(Address::IsValidAddress("aB:cD:eF:Gh:iJ:Kl"));
-}
-
-TEST(AddressTest, BdAddrFromString) {
-  Address addr;
-  memset(&addr, 0, sizeof(addr));
-
-  EXPECT_TRUE(Address::FromString("00:00:00:00:00:00", addr));
-  const Address result0 = {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
-  EXPECT_EQ(0, memcmp(&addr, &result0, sizeof(addr)));
-
-  EXPECT_TRUE(Address::FromString("ab:01:4C:d5:21:9f", addr));
-  const Address result1 = {{0x9f, 0x21, 0xd5, 0x4c, 0x01, 0xab}};
-  EXPECT_EQ(0, memcmp(&addr, &result1, sizeof(addr)));
-}
-
-TEST(AddressTest, BdAddrFromStringToStringEquivalent) {
-  std::string address = "c1:c2:c3:d1:d2:d3";
-  Address addr;
-
-  EXPECT_TRUE(Address::FromString(address, addr));
-  EXPECT_EQ(addr.ToString(), address);
-}
diff --git a/gd/common/bidi_queue.h b/gd/common/bidi_queue.h
new file mode 100644
index 0000000..c108588
--- /dev/null
+++ b/gd/common/bidi_queue.h
@@ -0,0 +1,90 @@
+/******************************************************************************
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#pragma once
+
+#include "common/callback.h"
+#include "os/queue.h"
+
+namespace bluetooth {
+namespace common {
+
+template <typename TENQUEUE, typename TDEQUEUE>
+class BidiQueueEnd
+    : public ::bluetooth::os::IQueueEnqueue<TENQUEUE>,
+      public ::bluetooth::os::IQueueDequeue<TDEQUEUE> {
+ public:
+  using EnqueueCallback = Callback<std::unique_ptr<TENQUEUE>()>;
+  using DequeueCallback = Callback<void()>;
+
+  BidiQueueEnd(::bluetooth::os::IQueueEnqueue<TENQUEUE>* tx, ::bluetooth::os::IQueueDequeue<TDEQUEUE>* rx)
+      : tx_(tx), rx_(rx) {
+  }
+
+  void RegisterEnqueue(::bluetooth::os::Handler* handler, EnqueueCallback callback) override {
+    tx_->RegisterEnqueue(handler, callback);
+  }
+
+  void UnregisterEnqueue() override {
+    tx_->UnregisterEnqueue();
+  }
+
+  void RegisterDequeue(::bluetooth::os::Handler* handler, DequeueCallback callback) override {
+    rx_->RegisterDequeue(handler, callback);
+  }
+
+  void UnregisterDequeue() override {
+    rx_->UnregisterDequeue();
+  }
+
+  std::unique_ptr<TDEQUEUE> TryDequeue() override {
+    return rx_->TryDequeue();
+  }
+
+ private:
+  ::bluetooth::os::IQueueEnqueue<TENQUEUE>* tx_;
+  ::bluetooth::os::IQueueDequeue<TDEQUEUE>* rx_;
+};
+
+template <typename TUP, typename TDOWN>
+class BidiQueue {
+ public:
+  explicit BidiQueue(size_t capacity)
+      : up_queue_(capacity),
+        down_queue_(capacity),
+        up_end_(&down_queue_, &up_queue_),
+        down_end_(&up_queue_, &down_queue_) {
+  }
+
+  BidiQueueEnd<TDOWN, TUP>* GetUpEnd() {
+    return &up_end_;
+  }
+
+  BidiQueueEnd<TUP, TDOWN>* GetDownEnd() {
+    return &down_end_;
+  }
+
+ private:
+  ::bluetooth::os::Queue<TUP> up_queue_;
+  ::bluetooth::os::Queue<TDOWN> down_queue_;
+  BidiQueueEnd<TDOWN, TUP> up_end_;
+  BidiQueueEnd<TUP, TDOWN> down_end_;
+};
+
+}  // namespace common
+}  // namespace bluetooth
diff --git a/gd/common/bidi_queue_unittest.cc b/gd/common/bidi_queue_unittest.cc
new file mode 100644
index 0000000..7a5503c
--- /dev/null
+++ b/gd/common/bidi_queue_unittest.cc
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "common/bidi_queue.h"
+
+#include <future>
+
+#include "common/bind.h"
+#include "gtest/gtest.h"
+#include "os/handler.h"
+#include "os/thread.h"
+
+using ::bluetooth::os::Thread;
+using ::bluetooth::os::Handler;
+
+namespace bluetooth {
+namespace common {
+namespace {
+
+class BidiQueueTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    up_thread_ = new Thread("up_thread", Thread::Priority::NORMAL);
+    up_handler_ = new Handler(up_thread_);
+    down_thread_ = new Thread("down_thread", Thread::Priority::NORMAL);
+    down_handler_ = new Handler(down_thread_);
+  }
+
+  void TearDown() override {
+    delete up_handler_;
+    delete up_thread_;
+    delete down_handler_;
+    delete down_thread_;
+  }
+
+  Thread* up_thread_;
+  Handler* up_handler_;
+  Thread* down_thread_;
+  Handler* down_handler_;
+};
+
+class A {
+};
+
+class B {
+};
+
+template <typename TA, typename TB>
+class TestBidiQueueEnd {
+ public:
+  explicit TestBidiQueueEnd(BidiQueueEnd<TA, TB>* end, Handler* handler)
+      : handler_(handler), end_(end) {}
+
+  ~TestBidiQueueEnd() {
+    handler_->Clear();
+  }
+
+  std::promise<void>* Send(TA* value) {
+    std::promise<void>* promise = new std::promise<void>();
+    handler_->Post(BindOnce(&TestBidiQueueEnd<TA, TB>::handle_send, common::Unretained(this), common::Unretained(value),
+                            common::Unretained(promise)));
+    return promise;
+  }
+
+  std::promise<TB*>* Receive() {
+    std::promise<TB*>* promise = new std::promise<TB*>();
+    handler_->Post(
+        BindOnce(&TestBidiQueueEnd<TA, TB>::handle_receive, common::Unretained(this), common::Unretained(promise)));
+
+    return promise;
+  }
+
+  void handle_send(TA* value, std::promise<void>* promise) {
+    end_->RegisterEnqueue(handler_, Bind(&TestBidiQueueEnd<TA, TB>::handle_register_enqueue, common::Unretained(this),
+                                         common::Unretained(value), common::Unretained(promise)));
+  }
+
+  std::unique_ptr<TA> handle_register_enqueue(TA* value, std::promise<void>* promise) {
+    end_->UnregisterEnqueue();
+    promise->set_value();
+    return std::unique_ptr<TA>(value);
+  }
+
+  void handle_receive(std::promise<TB*>* promise) {
+    end_->RegisterDequeue(handler_, Bind(&TestBidiQueueEnd<TA, TB>::handle_register_dequeue, common::Unretained(this),
+                                         common::Unretained(promise)));
+  }
+
+  void handle_register_dequeue(std::promise<TB*>* promise) {
+    end_->UnregisterDequeue();
+    promise->set_value(end_->TryDequeue().get());
+  }
+
+ private:
+  Handler* handler_;
+  BidiQueueEnd<TA, TB>* end_;
+};
+
+TEST_F(BidiQueueTest, simple_test) {
+  BidiQueue<A, B> queue(100);
+  TestBidiQueueEnd<B, A> test_up(queue.GetUpEnd(), up_handler_);
+  TestBidiQueueEnd<A, B> test_down(queue.GetDownEnd(), down_handler_);
+
+  auto sending_b = new B();
+  auto promise_sending_b = test_up.Send(sending_b);
+  promise_sending_b->get_future().wait();
+  auto promise_receive_b = test_down.Receive();
+  EXPECT_EQ(promise_receive_b->get_future().get(), sending_b);
+  delete promise_receive_b;
+  delete promise_sending_b;
+
+  auto sending_a = new A();
+  auto promise_sending_a = test_down.Send(sending_a);
+  promise_sending_a->get_future().wait();
+  auto promise_receive_a = test_up.Receive();
+  EXPECT_EQ(promise_receive_a->get_future().get(), sending_a);
+  delete promise_receive_a;
+  delete promise_sending_a;
+}
+
+}  // namespace
+}  // namespace os
+}  // namespace bluetooth
diff --git a/gd/common/bind.h b/gd/common/bind.h
new file mode 100644
index 0000000..1adf16c
--- /dev/null
+++ b/gd/common/bind.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "base/bind.h"
+
+namespace bluetooth {
+namespace common {
+
+using base::Bind;
+using base::BindOnce;
+using base::ConstRef;
+using base::IgnoreResult;
+using base::Owned;
+using base::Passed;
+using base::RetainedRef;
+using base::Unretained;
+
+}  // namespace common
+}  // namespace bluetooth
diff --git a/gd/common/blocking_queue.h b/gd/common/blocking_queue.h
new file mode 100644
index 0000000..622ca08
--- /dev/null
+++ b/gd/common/blocking_queue.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <chrono>
+#include <condition_variable>
+#include <mutex>
+#include <queue>
+
+namespace bluetooth {
+namespace common {
+
+template <typename T>
+class BlockingQueue {
+ public:
+  void push(T data) {
+    std::unique_lock<std::mutex> lock(mutex_);
+    queue_.push(std::move(data));
+    if (queue_.size() == 1) {
+      not_empty_.notify_all();
+    }
+  };
+
+  T take() {
+    std::unique_lock<std::mutex> lock(mutex_);
+    while (queue_.empty()) {
+      not_empty_.wait(lock);
+    }
+    T data = queue_.front();
+    queue_.pop();
+    return data;
+  };
+
+  // Returns true if take() will not block within a time period
+  bool wait_to_take(std::chrono::milliseconds time) {
+    std::unique_lock<std::mutex> lock(mutex_);
+    while (queue_.empty()) {
+      if (not_empty_.wait_for(lock, time) == std::cv_status::timeout) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  bool empty() const {
+    std::unique_lock<std::mutex> lock(mutex_);
+    return queue_.empty();
+  };
+
+  void clear() {
+    std::unique_lock<std::mutex> lock(mutex_);
+    std::queue<T> empty;
+    std::swap(queue_, empty);
+  };
+
+ private:
+  std::queue<T> queue_;
+  mutable std::mutex mutex_;
+  std::condition_variable not_empty_;
+};
+
+}  // namespace common
+}  // namespace bluetooth
diff --git a/gd/common/blocking_queue_unittest.cc b/gd/common/blocking_queue_unittest.cc
new file mode 100644
index 0000000..57a6245
--- /dev/null
+++ b/gd/common/blocking_queue_unittest.cc
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "common/blocking_queue.h"
+
+#include <thread>
+
+#include <gtest/gtest.h>
+
+namespace bluetooth {
+namespace common {
+namespace {
+class BlockingQueueTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    EXPECT_TRUE(queue_.empty());
+  }
+
+  // Postcondition for each test case: clear the blocking queue
+  void TearDown() override {
+    EXPECT_TRUE(queue_.empty());
+  }
+
+  BlockingQueue<int> queue_;
+};
+
+TEST_F(BlockingQueueTest, initial_empty) {
+  EXPECT_TRUE(queue_.empty());
+}
+
+TEST_F(BlockingQueueTest, same_thread_push_and_pop) {
+  int data = 1;
+  queue_.push(data);
+  EXPECT_FALSE(queue_.empty());
+  EXPECT_EQ(queue_.take(), data);
+  EXPECT_TRUE(queue_.empty());
+}
+
+TEST_F(BlockingQueueTest, same_thread_push_and_pop_sequential) {
+  for (int data = 0; data < 10; data++) {
+    queue_.push(data);
+    EXPECT_FALSE(queue_.empty());
+    EXPECT_EQ(queue_.take(), data);
+    EXPECT_TRUE(queue_.empty());
+  }
+}
+
+TEST_F(BlockingQueueTest, same_thread_push_and_pop_batch) {
+  for (int data = 0; data < 10; data++) {
+    queue_.push(data);
+  }
+  EXPECT_FALSE(queue_.empty());
+  for (int data = 0; data < 10; data++) {
+    EXPECT_EQ(queue_.take(), data);
+  }
+  EXPECT_TRUE(queue_.empty());
+}
+
+TEST_F(BlockingQueueTest, clear_queue) {
+  for (int data = 0; data < 10; data++) {
+    queue_.push(data);
+  }
+  EXPECT_FALSE(queue_.empty());
+  queue_.clear();
+  EXPECT_TRUE(queue_.empty());
+}
+
+TEST_F(BlockingQueueTest, wait_for_non_empty) {
+  int data = 1;
+  std::thread waiter_thread([this, data] { EXPECT_EQ(queue_.take(), data); });
+  queue_.push(data);
+  waiter_thread.join();
+  EXPECT_TRUE(queue_.empty());
+}
+
+TEST_F(BlockingQueueTest, wait_to_take_fail) {
+  EXPECT_FALSE(queue_.wait_to_take(std::chrono::milliseconds(3)));
+}
+
+TEST_F(BlockingQueueTest, wait_to_take_after_non_empty) {
+  int data = 1;
+  queue_.push(data);
+  EXPECT_TRUE(queue_.wait_to_take(std::chrono::milliseconds(3)));
+  queue_.clear();
+}
+
+TEST_F(BlockingQueueTest, wait_to_take_before_non_empty) {
+  int data = 1;
+  std::thread waiter_thread([this] { EXPECT_TRUE(queue_.wait_to_take(std::chrono::milliseconds(3))); });
+  queue_.push(data);
+  waiter_thread.join();
+  queue_.clear();
+}
+
+TEST_F(BlockingQueueTest, wait_for_non_empty_batch) {
+  std::thread waiter_thread([this] {
+    for (int data = 0; data < 10; data++) {
+      EXPECT_EQ(queue_.take(), data);
+    }
+  });
+  for (int data = 0; data < 10; data++) {
+    queue_.push(data);
+  }
+  waiter_thread.join();
+  EXPECT_TRUE(queue_.empty());
+}
+
+class VectorBlockingQueueTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    EXPECT_TRUE(queue_.empty());
+  }
+
+  // Postcondition for each test case: clear the blocking queue
+  void TearDown() override {
+    EXPECT_TRUE(queue_.empty());
+  }
+
+  BlockingQueue<std::vector<uint8_t>> queue_;
+};
+
+TEST_F(VectorBlockingQueueTest, same_thread_push_and_pop) {
+  std::vector<uint8_t> data = {1, 2, 3, 4, 5, 6};
+  queue_.push(data);
+  EXPECT_FALSE(queue_.empty());
+  EXPECT_EQ(queue_.take(), data);
+  EXPECT_TRUE(queue_.empty());
+}
+
+}  // namespace
+}  // namespace common
+}  // namespace bluetooth
diff --git a/gd/common/callback.h b/gd/common/callback.h
new file mode 100644
index 0000000..b8be642
--- /dev/null
+++ b/gd/common/callback.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "base/callback.h"
+
+namespace bluetooth {
+namespace common {
+
+using base::Callback;
+using base::Closure;
+using base::OnceCallback;
+using base::OnceClosure;
+
+}  // namespace common
+}  // namespace bluetooth
diff --git a/gd/common/callback_list.h b/gd/common/callback_list.h
new file mode 100644
index 0000000..857b0c7
--- /dev/null
+++ b/gd/common/callback_list.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <utility>
+#include "base/callback_list.h"
+#include "os/handler.h"
+
+/* This file contains CallbackList implementation that will execute callback on provided Handler thread
+
+Example usage inside your class:
+
+private:
+  common::CallbackList<void(int)> callbacks_list_;
+public:
+  std::unique_ptr<common::CallbackList<void(int)>::Subscription> RegisterCallback(
+      const base::RepeatingCallback<void(int)>& cb, os::Handler* handler) {
+    return callbacks_list_.Add({cb, handler});
+  }
+
+  void NotifyAllCallbacks(int value) {
+    callbacks_list_.Notify(value);
+  }
+*/
+
+namespace bluetooth {
+namespace common {
+
+namespace {
+template <typename CallbackType>
+struct CallbackWithHandler {
+  CallbackWithHandler(base::RepeatingCallback<CallbackType> callback, os::Handler* handler)
+      : callback(callback), handler(handler) {}
+
+  bool is_null() const {
+    return callback.is_null();
+  }
+
+  void Reset() {
+    callback.Reset();
+  }
+
+  base::RepeatingCallback<CallbackType> callback;
+  os::Handler* handler;
+};
+
+}  // namespace
+
+template <typename Sig>
+class CallbackList;
+template <typename... Args>
+class CallbackList<void(Args...)> : public base::internal::CallbackListBase<CallbackWithHandler<void(Args...)>> {
+ public:
+  using CallbackType = CallbackWithHandler<void(Args...)>;
+  CallbackList() = default;
+  template <typename... RunArgs>
+  void Notify(RunArgs&&... args) {
+    auto it = this->GetIterator();
+    CallbackType* cb;
+    while ((cb = it.GetNext()) != nullptr) {
+      cb->handler->Post(base::Bind(cb->callback, args...));
+    }
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CallbackList);
+};
+
+}  // namespace common
+}  // namespace bluetooth
diff --git a/gd/common/class_of_device.cc b/gd/common/class_of_device.cc
deleted file mode 100644
index 8d7ff68..0000000
--- a/gd/common/class_of_device.cc
+++ /dev/null
@@ -1,98 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2019 The Android Open Source Project
- *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at:
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- *
- ******************************************************************************/
-
-#include "class_of_device.h"
-
-#include <stdint.h>
-#include <algorithm>
-#include <sstream>
-#include <vector>
-
-#include "os/log.h"
-
-namespace bluetooth {
-namespace common {
-
-static_assert(sizeof(ClassOfDevice) == ClassOfDevice::kLength, "ClassOfDevice must be 3 bytes long!");
-
-ClassOfDevice::ClassOfDevice(const uint8_t (&class_of_device)[kLength]) {
-  std::copy(class_of_device, class_of_device + kLength, cod);
-};
-
-std::string ClassOfDevice::ToString() const {
-  char buffer[] = "000-0-00";
-  std::snprintf(&buffer[0], sizeof(buffer),
-      "%03x-%01x-%02x", (static_cast<uint16_t>(cod[2]) << 4) | cod[1] >> 4, cod[1] & 0x0f, cod[0]);
-  std::string str(buffer);
-  return str;
-
-}
-
-bool ClassOfDevice::FromString(const std::string& from, ClassOfDevice& to) {
-  ClassOfDevice new_cod;
-  if (from.length() != 8) return false;
-
-  std::istringstream stream(from);
-  std::string token;
-  int index = 0;
-  uint16_t values[3];
-
-  while (getline(stream, token, '-')) {
-    if (index >= 3) {
-      return false;
-    }
-
-    if (index == 0 && token.length() != 3) {
-      return false;
-    } else if (index == 1 && token.length() != 1) {
-      return false;
-    } else if (index == 2 && token.length() != 2) {
-      return false;
-    }
-    char* temp = nullptr;
-    values[index] = strtol(token.c_str(), &temp, 16);
-    if (*temp != '\0') {
-      return false;
-    }
-
-    index++;
-  }
-
-  if (index != 3) {
-    return false;
-  }
-
-  new_cod.cod[0] = values[2];
-  new_cod.cod[1] = values[1] | ((values[0] & 0xf) << 4);
-  new_cod.cod[2] = values[0] >> 4;
-
-  to = new_cod;
-  return true;
-}
-
-size_t ClassOfDevice::FromOctets(const uint8_t* from) {
-  std::copy(from, from + kLength, cod);
-  return kLength;
-};
-
-bool ClassOfDevice::IsValid(const std::string& cod) {
-  ClassOfDevice tmp;
-  return ClassOfDevice::FromString(cod, tmp);
-}
-}  // namespace common
-}  // namespace bluetooth
diff --git a/gd/common/class_of_device.h b/gd/common/class_of_device.h
deleted file mode 100644
index 983f128..0000000
--- a/gd/common/class_of_device.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2019 The Android Open Source Project
- *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at:
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- *
- ******************************************************************************/
-
-#pragma once
-
-#include <string>
-
-namespace bluetooth {
-namespace common {
-
-class ClassOfDevice final {
- public:
-  static constexpr unsigned int kLength = 3;
-
-  uint8_t cod[kLength];
-
-  ClassOfDevice() = default;
-  ClassOfDevice(const uint8_t (&class_of_device)[kLength]);
-
-  bool operator==(const ClassOfDevice& rhs) const {
-    return (std::memcmp(cod, rhs.cod, sizeof(cod)) == 0);
-  }
-
-  std::string ToString() const;
-
-  // Converts |string| to ClassOfDevice and places it in |to|. If |from| does
-  // not represent a Class of Device, |to| is not modified and this function
-  // returns false. Otherwise, it returns true.
-  static bool FromString(const std::string& from, ClassOfDevice& to);
-
-  // Copies |from| raw Class of Device octets to the local object.
-  // Returns the number of copied octets (always ClassOfDevice::kLength)
-  size_t FromOctets(const uint8_t* from);
-
-  static bool IsValid(const std::string& class_of_device);
-};
-
-inline std::ostream& operator<<(std::ostream& os, const ClassOfDevice& c) {
-  os << c.ToString();
-  return os;
-}
-
-}  // namespace common
-}  // namespace bluetooth
diff --git a/gd/common/class_of_device_unittest.cc b/gd/common/class_of_device_unittest.cc
deleted file mode 100644
index abd4a59..0000000
--- a/gd/common/class_of_device_unittest.cc
+++ /dev/null
@@ -1,98 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2019 The Android Open Source Project
- *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at:
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- *
- ******************************************************************************/
-
-#include <gtest/gtest.h>
-
-#include "common/class_of_device.h"
-
-using bluetooth::common::ClassOfDevice;
-
-static const char* test_class = "efc-d-ab";
-static const uint8_t test_bytes[]{0xab, 0xcd, 0xef};
-
-TEST(ClassOfDeviceUnittest, test_constructor_array) {
-  ClassOfDevice cod(test_bytes);
-
-  ASSERT_EQ(test_bytes[0], cod.cod[0]);
-  ASSERT_EQ(test_bytes[1], cod.cod[1]);
-  ASSERT_EQ(test_bytes[2], cod.cod[2]);
-
-  std::string ret = cod.ToString();
-
-  ASSERT_STREQ(test_class, ret.c_str());
-}
-
-TEST(ClassOfDeviceUnittest, test_to_from_str) {
-  ClassOfDevice cod;
-  ClassOfDevice::FromString(test_class, cod);
-
-  ASSERT_EQ(test_bytes[0], cod.cod[0]);
-  ASSERT_EQ(test_bytes[1], cod.cod[1]);
-  ASSERT_EQ(test_bytes[2], cod.cod[2]);
-
-  std::string ret = cod.ToString();
-
-  ASSERT_STREQ(test_class, ret.c_str());
-}
-
-TEST(ClassOfDeviceUnittest, test_from_octets) {
-  ClassOfDevice cod;
-  size_t expected_result = ClassOfDevice::kLength;
-  ASSERT_EQ(expected_result, cod.FromOctets(test_bytes));
-
-  ASSERT_EQ(test_bytes[0], cod.cod[0]);
-  ASSERT_EQ(test_bytes[1], cod.cod[1]);
-  ASSERT_EQ(test_bytes[2], cod.cod[2]);
-
-  std::string ret = cod.ToString();
-
-  ASSERT_STREQ(test_class, ret.c_str());
-}
-
-TEST(ClassOfDeviceTest, test_copy) {
-  ClassOfDevice cod1;
-  ClassOfDevice cod2;
-  ClassOfDevice::FromString(test_class, cod1);
-  cod2 = cod1;
-
-  ASSERT_EQ(cod1.cod[0], cod2.cod[0]);
-  ASSERT_EQ(cod1.cod[1], cod2.cod[1]);
-  ASSERT_EQ(cod1.cod[2], cod2.cod[2]);
-}
-
-TEST(ClassOfDeviceTest, IsValid) {
-  EXPECT_FALSE(ClassOfDevice::IsValid(""));
-  EXPECT_FALSE(ClassOfDevice::IsValid("000000"));
-  EXPECT_FALSE(ClassOfDevice::IsValid("00-00-00"));
-  EXPECT_FALSE(ClassOfDevice::IsValid("000-0-0"));
-  EXPECT_TRUE(ClassOfDevice::IsValid("000-0-00"));
-  EXPECT_TRUE(ClassOfDevice::IsValid("ABc-d-00"));
-  EXPECT_TRUE(ClassOfDevice::IsValid("aBc-D-eF"));
-}
-
-TEST(ClassOfDeviceTest, classOfDeviceFromString) {
-  ClassOfDevice cod;
-
-  EXPECT_TRUE(ClassOfDevice::FromString("000-0-00", cod));
-  const ClassOfDevice result0 = {{0x00, 0x00, 0x00}};
-  EXPECT_EQ(0, memcmp(&cod, &result0, sizeof(cod)));
-
-  EXPECT_TRUE(ClassOfDevice::FromString("ab2-1-4C", cod));
-  const ClassOfDevice result1 = {{0x4c, 0x21, 0xab}};
-  EXPECT_EQ(0, memcmp(&cod, &result1, sizeof(cod)));
-}
diff --git a/gd/common/link_key.cc b/gd/common/link_key.cc
new file mode 100644
index 0000000..12b60cd
--- /dev/null
+++ b/gd/common/link_key.cc
@@ -0,0 +1,57 @@
+/******************************************************************************
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#include "link_key.h"
+
+namespace bluetooth {
+namespace common {
+
+const LinkKey LinkKey::kExample{
+    {0x4C, 0x68, 0x38, 0x41, 0x39, 0xf5, 0x74, 0xd8, 0x36, 0xbc, 0xf3, 0x4e, 0x9d, 0xfb, 0x01, 0xbf}};
+
+LinkKey::LinkKey(const uint8_t (&data)[16]) {
+  std::copy(data, data + kLength, link_key);
+}
+
+std::string LinkKey::ToString() const {
+  char buffer[33] = "";
+  for (int i = 0; i < 16; i++) {
+    std::snprintf(&buffer[i * 2], 3, "%02x", link_key[i]);
+  }
+  std::string str(buffer);
+  return str;
+}
+
+bool LinkKey::FromString(const std::string& from, bluetooth::common::LinkKey& to) {
+  LinkKey new_link_key;
+
+  if (from.length() != 32) {
+    return false;
+  }
+
+  char* temp = nullptr;
+  for (int i = 0; i < 16; i++) {
+    new_link_key.link_key[i] = strtol(from.substr(i * 2, 2).c_str(), &temp, 16);
+  }
+
+  to = new_link_key;
+  return true;
+}
+
+}  // namespace common
+}  // namespace bluetooth
diff --git a/gd/common/link_key.h b/gd/common/link_key.h
new file mode 100644
index 0000000..b2bf394
--- /dev/null
+++ b/gd/common/link_key.h
@@ -0,0 +1,41 @@
+/******************************************************************************
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#pragma once
+
+#include <string>
+
+namespace bluetooth {
+namespace common {
+
+class LinkKey final {
+ public:
+  LinkKey() = default;
+  LinkKey(const uint8_t (&data)[16]);
+
+  static constexpr unsigned int kLength = 16;
+  uint8_t link_key[kLength];
+
+  std::string ToString() const;
+  static bool FromString(const std::string& from, LinkKey& to);
+
+  static const LinkKey kExample;
+};
+
+}  // namespace common
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/common/link_key_unittest.cc b/gd/common/link_key_unittest.cc
new file mode 100644
index 0000000..6529564
--- /dev/null
+++ b/gd/common/link_key_unittest.cc
@@ -0,0 +1,50 @@
+/******************************************************************************
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#include "common/link_key.h"
+#include <gtest/gtest.h>
+#include "os/log.h"
+
+using bluetooth::common::LinkKey;
+
+static const char* test_link_key = "4c68384139f574d836bcf34e9dfb01bf\0";
+
+TEST(LinkKeyUnittest, test_constructor_array) {
+  uint8_t data[LinkKey::kLength] = {0x4c, 0x87, 0x49, 0xe1, 0x2e, 0x55, 0x0f, 0x7f,
+                                    0x60, 0x8b, 0x4f, 0x96, 0xd7, 0xc5, 0xbc, 0x2a};
+
+  LinkKey link_key(data);
+
+  for (int i = 0; i < LinkKey::kLength; i++) {
+    ASSERT_EQ(data[i], link_key.link_key[i]);
+  }
+}
+
+TEST(LinkKeyUnittest, test_from_str) {
+  LinkKey link_key;
+  LinkKey::FromString(test_link_key, link_key);
+
+  for (int i = 0; i < LinkKey::kLength; i++) {
+    ASSERT_EQ(LinkKey::kExample.link_key[i], link_key.link_key[i]);
+  }
+}
+
+TEST(LinkKeyUnittest, test_to_str) {
+  std::string str = LinkKey::kExample.ToString();
+  ASSERT_STREQ(str.c_str(), test_link_key);
+}
\ No newline at end of file
diff --git a/gd/common/observer_registry.h b/gd/common/observer_registry.h
new file mode 100644
index 0000000..99de57f
--- /dev/null
+++ b/gd/common/observer_registry.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <array>
+
+#include "common/bind.h"
+#include "common/callback.h"
+#include "os/log.h"
+
+namespace bluetooth {
+namespace common {
+
+// Tracks an observer registration on client (observer) code. Register() returns a wrapped callback object which can
+// be passed to server's register API. Unregister() invalidates the wrapped callback so all callbacks that are posted
+// to the client handler after the client called Unregister() call and before the server processed the Unregister()
+// call on its handler, are dropped.
+// Note: Register() invalidates the previous registration.
+class SingleObserverRegistry {
+ public:
+  template <typename R, typename... T>
+  decltype(auto) Register(Callback<R(T...)> callback) {
+    session_++;
+    return Bind(&SingleObserverRegistry::callback_wrapper<R, T...>, Unretained(this), session_, callback);
+  }
+
+  void Unregister() {
+    session_++;
+  }
+
+ private:
+  template <typename R, typename... T>
+  void callback_wrapper(int session, Callback<R(T...)> callback, T... t) {
+    if (session == session_) {
+      callback.Run(std::forward<T>(t)...);
+    }
+  }
+
+  uint8_t session_ = 0;
+};
+
+// Tracks observer registration for multiple event type. Each event type is represented as an integer in [0, Capacity).
+template <int Capacity = 10>
+class MultipleObserverRegistry {
+ public:
+  template <typename R, typename... T>
+  decltype(auto) Register(int event_type, Callback<R(T...)> callback) {
+    ASSERT(event_type < Capacity);
+    return registry_[event_type].Register(callback);
+  }
+
+  void Unregister(int event_type) {
+    ASSERT(event_type < Capacity);
+    registry_[event_type].Unregister();
+  }
+
+  std::array<SingleObserverRegistry, Capacity> registry_;
+};
+
+}  // namespace common
+}  // namespace bluetooth
diff --git a/gd/common/observer_registry_test.cc b/gd/common/observer_registry_test.cc
new file mode 100644
index 0000000..f1233a8
--- /dev/null
+++ b/gd/common/observer_registry_test.cc
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "common/observer_registry.h"
+
+#include "common/bind.h"
+#include "gtest/gtest.h"
+
+namespace bluetooth {
+namespace common {
+
+class SingleObserverRegistryTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    registry_ = new SingleObserverRegistry;
+  }
+
+  void TearDown() override {
+    delete registry_;
+  }
+
+  SingleObserverRegistry* registry_;
+};
+
+void Increment(int* count) {
+  (*count)++;
+}
+
+void IncrementBy(int* count, int n) {
+  (*count) += n;
+}
+
+TEST_F(SingleObserverRegistryTest, wrapped_callback) {
+  int count = 0;
+  auto wrapped_callback = registry_->Register(Bind(&Increment, Unretained(&count)));
+  wrapped_callback.Run();
+  EXPECT_EQ(count, 1);
+  wrapped_callback.Run();
+  EXPECT_EQ(count, 2);
+  wrapped_callback.Run();
+  EXPECT_EQ(count, 3);
+  registry_->Unregister();
+}
+
+TEST_F(SingleObserverRegistryTest, unregister) {
+  int count = 0;
+  auto wrapped_callback = registry_->Register(Bind(&Increment, Unretained(&count)));
+  registry_->Unregister();
+  wrapped_callback.Run();
+  EXPECT_EQ(count, 0);
+}
+
+TEST_F(SingleObserverRegistryTest, second_register) {
+  int count = 0;
+  auto wrapped_callback = registry_->Register(Bind(&Increment, Unretained(&count)));
+  registry_->Unregister();
+  auto wrapped_callback2 = registry_->Register(Bind(&Increment, Unretained(&count)));
+  wrapped_callback.Run();
+  EXPECT_EQ(count, 0);
+  wrapped_callback2.Run();
+  EXPECT_EQ(count, 1);
+}
+
+class MultipleObserverRegistryTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    registry_ = new MultipleObserverRegistry<2>;
+  }
+
+  void TearDown() override {
+    delete registry_;
+  }
+
+  MultipleObserverRegistry<2>* registry_;
+};
+
+TEST_F(MultipleObserverRegistryTest, single_wrapped_callback) {
+  int count = 0;
+  auto wrapped_callback = registry_->Register(0, Bind(&Increment, Unretained(&count)));
+  wrapped_callback.Run();
+  EXPECT_EQ(count, 1);
+  wrapped_callback.Run();
+  EXPECT_EQ(count, 2);
+  wrapped_callback.Run();
+  EXPECT_EQ(count, 3);
+  registry_->Unregister(0);
+}
+
+TEST_F(MultipleObserverRegistryTest, multiple_wrapped_callback) {
+  int count = 0;
+  auto wrapped_callback0 = registry_->Register(0, Bind(&Increment, Unretained(&count)));
+  auto wrapped_callback1 = registry_->Register(1, Bind(&IncrementBy, Unretained(&count), 10));
+  wrapped_callback0.Run();
+  EXPECT_EQ(count, 1);
+  wrapped_callback1.Run();
+  EXPECT_EQ(count, 11);
+  registry_->Unregister(0);
+  wrapped_callback0.Run();
+  EXPECT_EQ(count, 11);
+  wrapped_callback1.Run();
+  EXPECT_EQ(count, 21);
+  registry_->Unregister(1);
+  EXPECT_EQ(count, 21);
+}
+
+}  // namespace common
+}  // namespace bluetooth
diff --git a/gd/common/testing/bind_test_util.h b/gd/common/testing/bind_test_util.h
new file mode 100644
index 0000000..7db9204
--- /dev/null
+++ b/gd/common/testing/bind_test_util.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "base/test/bind_test_util.h"
+
+namespace bluetooth {
+namespace common {
+namespace testing {
+
+using base::BindLambdaForTesting;
+
+}  // namespace testing
+}  // namespace common
+}  // namespace bluetooth
diff --git a/gd/common/testing/wired_pair_of_bidi_queues.h b/gd/common/testing/wired_pair_of_bidi_queues.h
new file mode 100644
index 0000000..92c6a1f
--- /dev/null
+++ b/gd/common/testing/wired_pair_of_bidi_queues.h
@@ -0,0 +1,97 @@
+/******************************************************************************
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#pragma once
+
+#include <memory>
+
+#include "common/bidi_queue.h"
+#include "os/handler.h"
+#include "packet/base_packet_builder.h"
+#include "packet/bit_inserter.h"
+#include "packet/packet_view.h"
+
+namespace bluetooth {
+namespace common {
+namespace testing {
+
+/* This class is a pair of BiDiQueues, that have down ends "wired" together. It can be used i.e. to mock L2cap
+ * interface, and provide two queues, where each sends packets of type A, and receives packets of type B */
+template <class A, class B, std::unique_ptr<B> (*A_TO_B)(std::unique_ptr<A>)>
+class WiredPairOfBiDiQueues {
+  void dequeue_callback_a() {
+    auto down_thing = queue_a_.GetDownEnd()->TryDequeue();
+    if (!down_thing) LOG_ERROR("Received dequeue, but no data ready...");
+
+    down_buffer_b_.Enqueue(A_TO_B(std::move(down_thing)), handler_);
+  }
+
+  void dequeue_callback_b() {
+    auto down_thing = queue_b_.GetDownEnd()->TryDequeue();
+    if (!down_thing) LOG_ERROR("Received dequeue, but no data ready...");
+
+    down_buffer_a_.Enqueue(A_TO_B(std::move(down_thing)), handler_);
+  }
+
+  os::Handler* handler_;
+  common::BidiQueue<B, A> queue_a_{10};
+  common::BidiQueue<B, A> queue_b_{10};
+  os::EnqueueBuffer<B> down_buffer_a_{queue_a_.GetDownEnd()};
+  os::EnqueueBuffer<B> down_buffer_b_{queue_b_.GetDownEnd()};
+
+ public:
+  WiredPairOfBiDiQueues(os::Handler* handler) : handler_(handler) {
+    queue_a_.GetDownEnd()->RegisterDequeue(
+        handler_, common::Bind(&WiredPairOfBiDiQueues::dequeue_callback_a, common::Unretained(this)));
+    queue_b_.GetDownEnd()->RegisterDequeue(
+        handler_, common::Bind(&WiredPairOfBiDiQueues::dequeue_callback_b, common::Unretained(this)));
+  }
+
+  ~WiredPairOfBiDiQueues() {
+    queue_a_.GetDownEnd()->UnregisterDequeue();
+    queue_b_.GetDownEnd()->UnregisterDequeue();
+  }
+
+  /* This methd returns the UpEnd of queue A */
+  common::BidiQueueEnd<A, B>* GetQueueAUpEnd() {
+    return queue_a_.GetUpEnd();
+  }
+
+  /* This methd returns the UpEnd of queue B */
+  common::BidiQueueEnd<A, B>* GetQueueBUpEnd() {
+    return queue_b_.GetUpEnd();
+  }
+};
+
+namespace {
+std::unique_ptr<packet::PacketView<packet::kLittleEndian>> BuilderToView(
+    std::unique_ptr<packet::BasePacketBuilder> up_thing) {
+  auto bytes = std::make_shared<std::vector<uint8_t>>();
+  bluetooth::packet::BitInserter i(*bytes);
+  bytes->reserve(up_thing->size());
+  up_thing->Serialize(i);
+  return std::make_unique<packet::PacketView<packet::kLittleEndian>>(bytes);
+}
+}  // namespace
+
+using WiredPairOfL2capQueues =
+    WiredPairOfBiDiQueues<packet::BasePacketBuilder, packet::PacketView<packet::kLittleEndian>, BuilderToView>;
+
+}  // namespace testing
+}  // namespace common
+}  // namespace bluetooth
diff --git a/gd/crypto_toolbox/Android.bp b/gd/crypto_toolbox/Android.bp
new file mode 100644
index 0000000..fd2e6ed
--- /dev/null
+++ b/gd/crypto_toolbox/Android.bp
@@ -0,0 +1,15 @@
+filegroup {
+    name: "BluetoothCryptoToolboxSources",
+    srcs: [
+        "aes.cc",
+        "aes_cmac.cc",
+        "crypto_toolbox.cc",
+    ]
+}
+
+filegroup {
+    name: "BluetoothCryptoToolboxTestSources",
+    srcs: [
+        "crypto_toolbox_test.cc",
+    ]
+}
\ No newline at end of file
diff --git a/gd/crypto_toolbox/aes.cc b/gd/crypto_toolbox/aes.cc
new file mode 100644
index 0000000..f53894e
--- /dev/null
+++ b/gd/crypto_toolbox/aes.cc
@@ -0,0 +1,950 @@
+/*
+ ---------------------------------------------------------------------------
+ Copyright (c) 1998-2008, Brian Gladman, Worcester, UK. All rights reserved.
+
+ LICENSE TERMS
+
+ The redistribution and use of this software (with or without changes)
+ is allowed without the payment of fees or royalties provided that:
+
+  1. source code distributions include the above copyright notice, this
+     list of conditions and the following disclaimer;
+
+  2. binary distributions include the above copyright notice, this list
+     of conditions and the following disclaimer in their documentation;
+
+  3. the name of the copyright holder is not used to endorse products
+     built using this software without specific written permission.
+
+ DISCLAIMER
+
+ This software is provided 'as is' with no explicit or implied warranties
+ in respect of its properties, including, but not limited to, correctness
+ and/or fitness for purpose.
+ ---------------------------------------------------------------------------
+ Issue 09/09/2006
+
+ This is an AES implementation that uses only 8-bit byte operations on the
+ cipher state (there are options to use 32-bit types if available).
+
+ The combination of mix columns and byte substitution used here is based on
+ that developed by Karl Malbrain. His contribution is acknowledged.
+ */
+
+/* define if you have a fast memcpy function on your system */
+#if 1
+#define HAVE_MEMCPY
+#include <string.h>
+#if 0
+#if defined(_MSC_VER)
+#include <intrin.h>
+#pragma intrinsic(memcpy)
+#endif
+#endif
+#endif
+
+#include <stdint.h>
+#include <stdlib.h>
+
+/* define if you have fast 32-bit types on your system */
+#if 1
+#define HAVE_UINT_32T
+#endif
+
+/* define if you don't want any tables */
+#if 1
+#define USE_TABLES
+#endif
+
+/*  On Intel Core 2 duo VERSION_1 is faster */
+
+/* alternative versions (test for performance on your system) */
+#if 1
+#define VERSION_1
+#endif
+
+#include "aes.h"
+
+#if defined(HAVE_UINT_32T)
+typedef uint32_t uint_32t;
+#endif
+
+/* functions for finite field multiplication in the AES Galois field    */
+
+#define WPOLY 0x011b
+#define BPOLY 0x1b
+#define DPOLY 0x008d
+
+#define f1(x) (x)
+#define f2(x) (((x) << 1) ^ ((((x) >> 7) & 1) * WPOLY))
+#define f4(x) \
+  (((x) << 2) ^ ((((x) >> 6) & 1) * WPOLY) ^ ((((x) >> 6) & 2) * WPOLY))
+#define f8(x)                                                             \
+  (((x) << 3) ^ ((((x) >> 5) & 1) * WPOLY) ^ ((((x) >> 5) & 2) * WPOLY) ^ \
+   ((((x) >> 5) & 4) * WPOLY))
+#define d2(x) (((x) >> 1) ^ ((x)&1 ? DPOLY : 0))
+
+#define f3(x) (f2(x) ^ (x))
+#define f9(x) (f8(x) ^ (x))
+#define fb(x) (f8(x) ^ f2(x) ^ (x))
+#define fd(x) (f8(x) ^ f4(x) ^ (x))
+#define fe(x) (f8(x) ^ f4(x) ^ f2(x))
+
+#if defined(USE_TABLES)
+
+#define sb_data(w)                                                          \
+  { /* S Box data values */                                                 \
+    w(0x63), w(0x7c), w(0x77), w(0x7b), w(0xf2), w(0x6b), w(0x6f), w(0xc5), \
+        w(0x30), w(0x01), w(0x67), w(0x2b), w(0xfe), w(0xd7), w(0xab),      \
+        w(0x76), w(0xca), w(0x82), w(0xc9), w(0x7d), w(0xfa), w(0x59),      \
+        w(0x47), w(0xf0), w(0xad), w(0xd4), w(0xa2), w(0xaf), w(0x9c),      \
+        w(0xa4), w(0x72), w(0xc0), w(0xb7), w(0xfd), w(0x93), w(0x26),      \
+        w(0x36), w(0x3f), w(0xf7), w(0xcc), w(0x34), w(0xa5), w(0xe5),      \
+        w(0xf1), w(0x71), w(0xd8), w(0x31), w(0x15), w(0x04), w(0xc7),      \
+        w(0x23), w(0xc3), w(0x18), w(0x96), w(0x05), w(0x9a), w(0x07),      \
+        w(0x12), w(0x80), w(0xe2), w(0xeb), w(0x27), w(0xb2), w(0x75),      \
+        w(0x09), w(0x83), w(0x2c), w(0x1a), w(0x1b), w(0x6e), w(0x5a),      \
+        w(0xa0), w(0x52), w(0x3b), w(0xd6), w(0xb3), w(0x29), w(0xe3),      \
+        w(0x2f), w(0x84), w(0x53), w(0xd1), w(0x00), w(0xed), w(0x20),      \
+        w(0xfc), w(0xb1), w(0x5b), w(0x6a), w(0xcb), w(0xbe), w(0x39),      \
+        w(0x4a), w(0x4c), w(0x58), w(0xcf), w(0xd0), w(0xef), w(0xaa),      \
+        w(0xfb), w(0x43), w(0x4d), w(0x33), w(0x85), w(0x45), w(0xf9),      \
+        w(0x02), w(0x7f), w(0x50), w(0x3c), w(0x9f), w(0xa8), w(0x51),      \
+        w(0xa3), w(0x40), w(0x8f), w(0x92), w(0x9d), w(0x38), w(0xf5),      \
+        w(0xbc), w(0xb6), w(0xda), w(0x21), w(0x10), w(0xff), w(0xf3),      \
+        w(0xd2), w(0xcd), w(0x0c), w(0x13), w(0xec), w(0x5f), w(0x97),      \
+        w(0x44), w(0x17), w(0xc4), w(0xa7), w(0x7e), w(0x3d), w(0x64),      \
+        w(0x5d), w(0x19), w(0x73), w(0x60), w(0x81), w(0x4f), w(0xdc),      \
+        w(0x22), w(0x2a), w(0x90), w(0x88), w(0x46), w(0xee), w(0xb8),      \
+        w(0x14), w(0xde), w(0x5e), w(0x0b), w(0xdb), w(0xe0), w(0x32),      \
+        w(0x3a), w(0x0a), w(0x49), w(0x06), w(0x24), w(0x5c), w(0xc2),      \
+        w(0xd3), w(0xac), w(0x62), w(0x91), w(0x95), w(0xe4), w(0x79),      \
+        w(0xe7), w(0xc8), w(0x37), w(0x6d), w(0x8d), w(0xd5), w(0x4e),      \
+        w(0xa9), w(0x6c), w(0x56), w(0xf4), w(0xea), w(0x65), w(0x7a),      \
+        w(0xae), w(0x08), w(0xba), w(0x78), w(0x25), w(0x2e), w(0x1c),      \
+        w(0xa6), w(0xb4), w(0xc6), w(0xe8), w(0xdd), w(0x74), w(0x1f),      \
+        w(0x4b), w(0xbd), w(0x8b), w(0x8a), w(0x70), w(0x3e), w(0xb5),      \
+        w(0x66), w(0x48), w(0x03), w(0xf6), w(0x0e), w(0x61), w(0x35),      \
+        w(0x57), w(0xb9), w(0x86), w(0xc1), w(0x1d), w(0x9e), w(0xe1),      \
+        w(0xf8), w(0x98), w(0x11), w(0x69), w(0xd9), w(0x8e), w(0x94),      \
+        w(0x9b), w(0x1e), w(0x87), w(0xe9), w(0xce), w(0x55), w(0x28),      \
+        w(0xdf), w(0x8c), w(0xa1), w(0x89), w(0x0d), w(0xbf), w(0xe6),      \
+        w(0x42), w(0x68), w(0x41), w(0x99), w(0x2d), w(0x0f), w(0xb0),      \
+        w(0x54), w(0xbb), w(0x16)                                           \
+  }
+
+#define isb_data(w)                                                         \
+  { /* inverse S Box data values */                                         \
+    w(0x52), w(0x09), w(0x6a), w(0xd5), w(0x30), w(0x36), w(0xa5), w(0x38), \
+        w(0xbf), w(0x40), w(0xa3), w(0x9e), w(0x81), w(0xf3), w(0xd7),      \
+        w(0xfb), w(0x7c), w(0xe3), w(0x39), w(0x82), w(0x9b), w(0x2f),      \
+        w(0xff), w(0x87), w(0x34), w(0x8e), w(0x43), w(0x44), w(0xc4),      \
+        w(0xde), w(0xe9), w(0xcb), w(0x54), w(0x7b), w(0x94), w(0x32),      \
+        w(0xa6), w(0xc2), w(0x23), w(0x3d), w(0xee), w(0x4c), w(0x95),      \
+        w(0x0b), w(0x42), w(0xfa), w(0xc3), w(0x4e), w(0x08), w(0x2e),      \
+        w(0xa1), w(0x66), w(0x28), w(0xd9), w(0x24), w(0xb2), w(0x76),      \
+        w(0x5b), w(0xa2), w(0x49), w(0x6d), w(0x8b), w(0xd1), w(0x25),      \
+        w(0x72), w(0xf8), w(0xf6), w(0x64), w(0x86), w(0x68), w(0x98),      \
+        w(0x16), w(0xd4), w(0xa4), w(0x5c), w(0xcc), w(0x5d), w(0x65),      \
+        w(0xb6), w(0x92), w(0x6c), w(0x70), w(0x48), w(0x50), w(0xfd),      \
+        w(0xed), w(0xb9), w(0xda), w(0x5e), w(0x15), w(0x46), w(0x57),      \
+        w(0xa7), w(0x8d), w(0x9d), w(0x84), w(0x90), w(0xd8), w(0xab),      \
+        w(0x00), w(0x8c), w(0xbc), w(0xd3), w(0x0a), w(0xf7), w(0xe4),      \
+        w(0x58), w(0x05), w(0xb8), w(0xb3), w(0x45), w(0x06), w(0xd0),      \
+        w(0x2c), w(0x1e), w(0x8f), w(0xca), w(0x3f), w(0x0f), w(0x02),      \
+        w(0xc1), w(0xaf), w(0xbd), w(0x03), w(0x01), w(0x13), w(0x8a),      \
+        w(0x6b), w(0x3a), w(0x91), w(0x11), w(0x41), w(0x4f), w(0x67),      \
+        w(0xdc), w(0xea), w(0x97), w(0xf2), w(0xcf), w(0xce), w(0xf0),      \
+        w(0xb4), w(0xe6), w(0x73), w(0x96), w(0xac), w(0x74), w(0x22),      \
+        w(0xe7), w(0xad), w(0x35), w(0x85), w(0xe2), w(0xf9), w(0x37),      \
+        w(0xe8), w(0x1c), w(0x75), w(0xdf), w(0x6e), w(0x47), w(0xf1),      \
+        w(0x1a), w(0x71), w(0x1d), w(0x29), w(0xc5), w(0x89), w(0x6f),      \
+        w(0xb7), w(0x62), w(0x0e), w(0xaa), w(0x18), w(0xbe), w(0x1b),      \
+        w(0xfc), w(0x56), w(0x3e), w(0x4b), w(0xc6), w(0xd2), w(0x79),      \
+        w(0x20), w(0x9a), w(0xdb), w(0xc0), w(0xfe), w(0x78), w(0xcd),      \
+        w(0x5a), w(0xf4), w(0x1f), w(0xdd), w(0xa8), w(0x33), w(0x88),      \
+        w(0x07), w(0xc7), w(0x31), w(0xb1), w(0x12), w(0x10), w(0x59),      \
+        w(0x27), w(0x80), w(0xec), w(0x5f), w(0x60), w(0x51), w(0x7f),      \
+        w(0xa9), w(0x19), w(0xb5), w(0x4a), w(0x0d), w(0x2d), w(0xe5),      \
+        w(0x7a), w(0x9f), w(0x93), w(0xc9), w(0x9c), w(0xef), w(0xa0),      \
+        w(0xe0), w(0x3b), w(0x4d), w(0xae), w(0x2a), w(0xf5), w(0xb0),      \
+        w(0xc8), w(0xeb), w(0xbb), w(0x3c), w(0x83), w(0x53), w(0x99),      \
+        w(0x61), w(0x17), w(0x2b), w(0x04), w(0x7e), w(0xba), w(0x77),      \
+        w(0xd6), w(0x26), w(0xe1), w(0x69), w(0x14), w(0x63), w(0x55),      \
+        w(0x21), w(0x0c), w(0x7d)                                           \
+  }
+
+#define mm_data(w)                                                          \
+  { /* basic data for forming finite field tables */                        \
+    w(0x00), w(0x01), w(0x02), w(0x03), w(0x04), w(0x05), w(0x06), w(0x07), \
+        w(0x08), w(0x09), w(0x0a), w(0x0b), w(0x0c), w(0x0d), w(0x0e),      \
+        w(0x0f), w(0x10), w(0x11), w(0x12), w(0x13), w(0x14), w(0x15),      \
+        w(0x16), w(0x17), w(0x18), w(0x19), w(0x1a), w(0x1b), w(0x1c),      \
+        w(0x1d), w(0x1e), w(0x1f), w(0x20), w(0x21), w(0x22), w(0x23),      \
+        w(0x24), w(0x25), w(0x26), w(0x27), w(0x28), w(0x29), w(0x2a),      \
+        w(0x2b), w(0x2c), w(0x2d), w(0x2e), w(0x2f), w(0x30), w(0x31),      \
+        w(0x32), w(0x33), w(0x34), w(0x35), w(0x36), w(0x37), w(0x38),      \
+        w(0x39), w(0x3a), w(0x3b), w(0x3c), w(0x3d), w(0x3e), w(0x3f),      \
+        w(0x40), w(0x41), w(0x42), w(0x43), w(0x44), w(0x45), w(0x46),      \
+        w(0x47), w(0x48), w(0x49), w(0x4a), w(0x4b), w(0x4c), w(0x4d),      \
+        w(0x4e), w(0x4f), w(0x50), w(0x51), w(0x52), w(0x53), w(0x54),      \
+        w(0x55), w(0x56), w(0x57), w(0x58), w(0x59), w(0x5a), w(0x5b),      \
+        w(0x5c), w(0x5d), w(0x5e), w(0x5f), w(0x60), w(0x61), w(0x62),      \
+        w(0x63), w(0x64), w(0x65), w(0x66), w(0x67), w(0x68), w(0x69),      \
+        w(0x6a), w(0x6b), w(0x6c), w(0x6d), w(0x6e), w(0x6f), w(0x70),      \
+        w(0x71), w(0x72), w(0x73), w(0x74), w(0x75), w(0x76), w(0x77),      \
+        w(0x78), w(0x79), w(0x7a), w(0x7b), w(0x7c), w(0x7d), w(0x7e),      \
+        w(0x7f), w(0x80), w(0x81), w(0x82), w(0x83), w(0x84), w(0x85),      \
+        w(0x86), w(0x87), w(0x88), w(0x89), w(0x8a), w(0x8b), w(0x8c),      \
+        w(0x8d), w(0x8e), w(0x8f), w(0x90), w(0x91), w(0x92), w(0x93),      \
+        w(0x94), w(0x95), w(0x96), w(0x97), w(0x98), w(0x99), w(0x9a),      \
+        w(0x9b), w(0x9c), w(0x9d), w(0x9e), w(0x9f), w(0xa0), w(0xa1),      \
+        w(0xa2), w(0xa3), w(0xa4), w(0xa5), w(0xa6), w(0xa7), w(0xa8),      \
+        w(0xa9), w(0xaa), w(0xab), w(0xac), w(0xad), w(0xae), w(0xaf),      \
+        w(0xb0), w(0xb1), w(0xb2), w(0xb3), w(0xb4), w(0xb5), w(0xb6),      \
+        w(0xb7), w(0xb8), w(0xb9), w(0xba), w(0xbb), w(0xbc), w(0xbd),      \
+        w(0xbe), w(0xbf), w(0xc0), w(0xc1), w(0xc2), w(0xc3), w(0xc4),      \
+        w(0xc5), w(0xc6), w(0xc7), w(0xc8), w(0xc9), w(0xca), w(0xcb),      \
+        w(0xcc), w(0xcd), w(0xce), w(0xcf), w(0xd0), w(0xd1), w(0xd2),      \
+        w(0xd3), w(0xd4), w(0xd5), w(0xd6), w(0xd7), w(0xd8), w(0xd9),      \
+        w(0xda), w(0xdb), w(0xdc), w(0xdd), w(0xde), w(0xdf), w(0xe0),      \
+        w(0xe1), w(0xe2), w(0xe3), w(0xe4), w(0xe5), w(0xe6), w(0xe7),      \
+        w(0xe8), w(0xe9), w(0xea), w(0xeb), w(0xec), w(0xed), w(0xee),      \
+        w(0xef), w(0xf0), w(0xf1), w(0xf2), w(0xf3), w(0xf4), w(0xf5),      \
+        w(0xf6), w(0xf7), w(0xf8), w(0xf9), w(0xfa), w(0xfb), w(0xfc),      \
+        w(0xfd), w(0xfe), w(0xff)                                           \
+  }
+
+static const uint_8t sbox[256] = sb_data(f1);
+static const uint_8t isbox[256] = isb_data(f1);
+
+static const uint_8t gfm2_sbox[256] = sb_data(f2);
+static const uint_8t gfm3_sbox[256] = sb_data(f3);
+
+static const uint_8t gfmul_9[256] = mm_data(f9);
+static const uint_8t gfmul_b[256] = mm_data(fb);
+static const uint_8t gfmul_d[256] = mm_data(fd);
+static const uint_8t gfmul_e[256] = mm_data(fe);
+
+#define s_box(x) sbox[(x)]
+#define is_box(x) isbox[(x)]
+#define gfm2_sb(x) gfm2_sbox[(x)]
+#define gfm3_sb(x) gfm3_sbox[(x)]
+#define gfm_9(x) gfmul_9[(x)]
+#define gfm_b(x) gfmul_b[(x)]
+#define gfm_d(x) gfmul_d[(x)]
+#define gfm_e(x) gfmul_e[(x)]
+
+#else
+
+/* this is the high bit of x right shifted by 1 */
+/* position. Since the starting polynomial has  */
+/* 9 bits (0x11b), this right shift keeps the   */
+/* values of all top bits within a byte         */
+
+static uint_8t hibit(const uint_8t x) {
+  uint_8t r = (uint_8t)((x >> 1) | (x >> 2));
+
+  r |= (r >> 2);
+  r |= (r >> 4);
+  return (r + 1) >> 1;
+}
+
+/* return the inverse of the finite field element x */
+
+static uint_8t gf_inv(const uint_8t x) {
+  uint_8t p1 = x, p2 = BPOLY, n1 = hibit(x), n2 = 0x80, v1 = 1, v2 = 0;
+
+  if (x < 2) return x;
+
+  for (;;) {
+    if (n1)
+      while (n2 >= n1) /* divide polynomial p2 by p1    */
+      {
+        n2 /= n1;               /* shift smaller polynomial left */
+        p2 ^= (p1 * n2) & 0xff; /* and remove from larger one    */
+        v2 ^= (v1 * n2);        /* shift accumulated value and   */
+        n2 = hibit(p2);         /* add into result               */
+      }
+    else
+      return v1;
+
+    if (n2) /* repeat with values swapped    */
+      while (n1 >= n2) {
+        n1 /= n2;
+        p1 ^= p2 * n1;
+        v1 ^= v2 * n1;
+        n1 = hibit(p1);
+      }
+    else
+      return v2;
+  }
+}
+
+/* The forward and inverse affine transformations used in the S-box */
+uint_8t fwd_affine(const uint_8t x) {
+#if defined(HAVE_UINT_32T)
+  uint_32t w = x;
+  w ^= (w << 1) ^ (w << 2) ^ (w << 3) ^ (w << 4);
+  return 0x63 ^ ((w ^ (w >> 8)) & 0xff);
+#else
+  return 0x63 ^ x ^ (x << 1) ^ (x << 2) ^ (x << 3) ^ (x << 4) ^ (x >> 7) ^
+         (x >> 6) ^ (x >> 5) ^ (x >> 4);
+#endif
+}
+
+uint_8t inv_affine(const uint_8t x) {
+#if defined(HAVE_UINT_32T)
+  uint_32t w = x;
+  w = (w << 1) ^ (w << 3) ^ (w << 6);
+  return 0x05 ^ ((w ^ (w >> 8)) & 0xff);
+#else
+  return 0x05 ^ (x << 1) ^ (x << 3) ^ (x << 6) ^ (x >> 7) ^ (x >> 5) ^ (x >> 2);
+#endif
+}
+
+#define s_box(x) fwd_affine(gf_inv(x))
+#define is_box(x) gf_inv(inv_affine(x))
+#define gfm2_sb(x) f2(s_box(x))
+#define gfm3_sb(x) f3(s_box(x))
+#define gfm_9(x) f9(x)
+#define gfm_b(x) fb(x)
+#define gfm_d(x) fd(x)
+#define gfm_e(x) fe(x)
+
+#endif
+
+#if defined(HAVE_MEMCPY)
+#define block_copy_nn(d, s, l) memcpy(d, s, l)
+#define block_copy(d, s) memcpy(d, s, N_BLOCK)
+#else
+#define block_copy_nn(d, s, l) copy_block_nn(d, s, l)
+#define block_copy(d, s) copy_block(d, s)
+#endif
+
+#if !defined(HAVE_MEMCPY)
+static void copy_block(void* d, const void* s) {
+#if defined(HAVE_UINT_32T)
+  ((uint_32t*)d)[0] = ((uint_32t*)s)[0];
+  ((uint_32t*)d)[1] = ((uint_32t*)s)[1];
+  ((uint_32t*)d)[2] = ((uint_32t*)s)[2];
+  ((uint_32t*)d)[3] = ((uint_32t*)s)[3];
+#else
+  ((uint_8t*)d)[0] = ((uint_8t*)s)[0];
+  ((uint_8t*)d)[1] = ((uint_8t*)s)[1];
+  ((uint_8t*)d)[2] = ((uint_8t*)s)[2];
+  ((uint_8t*)d)[3] = ((uint_8t*)s)[3];
+  ((uint_8t*)d)[4] = ((uint_8t*)s)[4];
+  ((uint_8t*)d)[5] = ((uint_8t*)s)[5];
+  ((uint_8t*)d)[6] = ((uint_8t*)s)[6];
+  ((uint_8t*)d)[7] = ((uint_8t*)s)[7];
+  ((uint_8t*)d)[8] = ((uint_8t*)s)[8];
+  ((uint_8t*)d)[9] = ((uint_8t*)s)[9];
+  ((uint_8t*)d)[10] = ((uint_8t*)s)[10];
+  ((uint_8t*)d)[11] = ((uint_8t*)s)[11];
+  ((uint_8t*)d)[12] = ((uint_8t*)s)[12];
+  ((uint_8t*)d)[13] = ((uint_8t*)s)[13];
+  ((uint_8t*)d)[14] = ((uint_8t*)s)[14];
+  ((uint_8t*)d)[15] = ((uint_8t*)s)[15];
+#endif
+}
+
+static void copy_block_nn(void* d, const void* s, uint_8t nn) {
+  while (nn--) *((uint_8t*)d)++ = *((uint_8t*)s)++;
+}
+#endif
+
+static void xor_block(void* d, const void* s) {
+#if defined(HAVE_UINT_32T)
+  ((uint_32t*)d)[0] ^= ((uint_32t*)s)[0];
+  ((uint_32t*)d)[1] ^= ((uint_32t*)s)[1];
+  ((uint_32t*)d)[2] ^= ((uint_32t*)s)[2];
+  ((uint_32t*)d)[3] ^= ((uint_32t*)s)[3];
+#else
+  ((uint_8t*)d)[0] ^= ((uint_8t*)s)[0];
+  ((uint_8t*)d)[1] ^= ((uint_8t*)s)[1];
+  ((uint_8t*)d)[2] ^= ((uint_8t*)s)[2];
+  ((uint_8t*)d)[3] ^= ((uint_8t*)s)[3];
+  ((uint_8t*)d)[4] ^= ((uint_8t*)s)[4];
+  ((uint_8t*)d)[5] ^= ((uint_8t*)s)[5];
+  ((uint_8t*)d)[6] ^= ((uint_8t*)s)[6];
+  ((uint_8t*)d)[7] ^= ((uint_8t*)s)[7];
+  ((uint_8t*)d)[8] ^= ((uint_8t*)s)[8];
+  ((uint_8t*)d)[9] ^= ((uint_8t*)s)[9];
+  ((uint_8t*)d)[10] ^= ((uint_8t*)s)[10];
+  ((uint_8t*)d)[11] ^= ((uint_8t*)s)[11];
+  ((uint_8t*)d)[12] ^= ((uint_8t*)s)[12];
+  ((uint_8t*)d)[13] ^= ((uint_8t*)s)[13];
+  ((uint_8t*)d)[14] ^= ((uint_8t*)s)[14];
+  ((uint_8t*)d)[15] ^= ((uint_8t*)s)[15];
+#endif
+}
+
+static void copy_and_key(void* d, const void* s, const void* k) {
+#if defined(HAVE_UINT_32T)
+  ((uint_32t*)d)[0] = ((uint_32t*)s)[0] ^ ((uint_32t*)k)[0];
+  ((uint_32t*)d)[1] = ((uint_32t*)s)[1] ^ ((uint_32t*)k)[1];
+  ((uint_32t*)d)[2] = ((uint_32t*)s)[2] ^ ((uint_32t*)k)[2];
+  ((uint_32t*)d)[3] = ((uint_32t*)s)[3] ^ ((uint_32t*)k)[3];
+#elif 1
+  ((uint_8t*)d)[0] = ((uint_8t*)s)[0] ^ ((uint_8t*)k)[0];
+  ((uint_8t*)d)[1] = ((uint_8t*)s)[1] ^ ((uint_8t*)k)[1];
+  ((uint_8t*)d)[2] = ((uint_8t*)s)[2] ^ ((uint_8t*)k)[2];
+  ((uint_8t*)d)[3] = ((uint_8t*)s)[3] ^ ((uint_8t*)k)[3];
+  ((uint_8t*)d)[4] = ((uint_8t*)s)[4] ^ ((uint_8t*)k)[4];
+  ((uint_8t*)d)[5] = ((uint_8t*)s)[5] ^ ((uint_8t*)k)[5];
+  ((uint_8t*)d)[6] = ((uint_8t*)s)[6] ^ ((uint_8t*)k)[6];
+  ((uint_8t*)d)[7] = ((uint_8t*)s)[7] ^ ((uint_8t*)k)[7];
+  ((uint_8t*)d)[8] = ((uint_8t*)s)[8] ^ ((uint_8t*)k)[8];
+  ((uint_8t*)d)[9] = ((uint_8t*)s)[9] ^ ((uint_8t*)k)[9];
+  ((uint_8t*)d)[10] = ((uint_8t*)s)[10] ^ ((uint_8t*)k)[10];
+  ((uint_8t*)d)[11] = ((uint_8t*)s)[11] ^ ((uint_8t*)k)[11];
+  ((uint_8t*)d)[12] = ((uint_8t*)s)[12] ^ ((uint_8t*)k)[12];
+  ((uint_8t*)d)[13] = ((uint_8t*)s)[13] ^ ((uint_8t*)k)[13];
+  ((uint_8t*)d)[14] = ((uint_8t*)s)[14] ^ ((uint_8t*)k)[14];
+  ((uint_8t*)d)[15] = ((uint_8t*)s)[15] ^ ((uint_8t*)k)[15];
+#else
+  block_copy(d, s);
+  xor_block(d, k);
+#endif
+}
+
+static void add_round_key(uint_8t d[N_BLOCK], const uint_8t k[N_BLOCK]) {
+  xor_block(d, k);
+}
+
+static void shift_sub_rows(uint_8t st[N_BLOCK]) {
+  uint_8t tt;
+
+  st[0] = s_box(st[0]);
+  st[4] = s_box(st[4]);
+  st[8] = s_box(st[8]);
+  st[12] = s_box(st[12]);
+
+  tt = st[1];
+  st[1] = s_box(st[5]);
+  st[5] = s_box(st[9]);
+  st[9] = s_box(st[13]);
+  st[13] = s_box(tt);
+
+  tt = st[2];
+  st[2] = s_box(st[10]);
+  st[10] = s_box(tt);
+  tt = st[6];
+  st[6] = s_box(st[14]);
+  st[14] = s_box(tt);
+
+  tt = st[15];
+  st[15] = s_box(st[11]);
+  st[11] = s_box(st[7]);
+  st[7] = s_box(st[3]);
+  st[3] = s_box(tt);
+}
+
+static void inv_shift_sub_rows(uint_8t st[N_BLOCK]) {
+  uint_8t tt;
+
+  st[0] = is_box(st[0]);
+  st[4] = is_box(st[4]);
+  st[8] = is_box(st[8]);
+  st[12] = is_box(st[12]);
+
+  tt = st[13];
+  st[13] = is_box(st[9]);
+  st[9] = is_box(st[5]);
+  st[5] = is_box(st[1]);
+  st[1] = is_box(tt);
+
+  tt = st[2];
+  st[2] = is_box(st[10]);
+  st[10] = is_box(tt);
+  tt = st[6];
+  st[6] = is_box(st[14]);
+  st[14] = is_box(tt);
+
+  tt = st[3];
+  st[3] = is_box(st[7]);
+  st[7] = is_box(st[11]);
+  st[11] = is_box(st[15]);
+  st[15] = is_box(tt);
+}
+
+#if defined(VERSION_1)
+static void mix_sub_columns(uint_8t dt[N_BLOCK]) {
+  uint_8t st[N_BLOCK];
+  block_copy(st, dt);
+#else
+static void mix_sub_columns(uint_8t dt[N_BLOCK], uint_8t st[N_BLOCK]) {
+#endif
+  dt[0] = gfm2_sb(st[0]) ^ gfm3_sb(st[5]) ^ s_box(st[10]) ^ s_box(st[15]);
+  dt[1] = s_box(st[0]) ^ gfm2_sb(st[5]) ^ gfm3_sb(st[10]) ^ s_box(st[15]);
+  dt[2] = s_box(st[0]) ^ s_box(st[5]) ^ gfm2_sb(st[10]) ^ gfm3_sb(st[15]);
+  dt[3] = gfm3_sb(st[0]) ^ s_box(st[5]) ^ s_box(st[10]) ^ gfm2_sb(st[15]);
+
+  dt[4] = gfm2_sb(st[4]) ^ gfm3_sb(st[9]) ^ s_box(st[14]) ^ s_box(st[3]);
+  dt[5] = s_box(st[4]) ^ gfm2_sb(st[9]) ^ gfm3_sb(st[14]) ^ s_box(st[3]);
+  dt[6] = s_box(st[4]) ^ s_box(st[9]) ^ gfm2_sb(st[14]) ^ gfm3_sb(st[3]);
+  dt[7] = gfm3_sb(st[4]) ^ s_box(st[9]) ^ s_box(st[14]) ^ gfm2_sb(st[3]);
+
+  dt[8] = gfm2_sb(st[8]) ^ gfm3_sb(st[13]) ^ s_box(st[2]) ^ s_box(st[7]);
+  dt[9] = s_box(st[8]) ^ gfm2_sb(st[13]) ^ gfm3_sb(st[2]) ^ s_box(st[7]);
+  dt[10] = s_box(st[8]) ^ s_box(st[13]) ^ gfm2_sb(st[2]) ^ gfm3_sb(st[7]);
+  dt[11] = gfm3_sb(st[8]) ^ s_box(st[13]) ^ s_box(st[2]) ^ gfm2_sb(st[7]);
+
+  dt[12] = gfm2_sb(st[12]) ^ gfm3_sb(st[1]) ^ s_box(st[6]) ^ s_box(st[11]);
+  dt[13] = s_box(st[12]) ^ gfm2_sb(st[1]) ^ gfm3_sb(st[6]) ^ s_box(st[11]);
+  dt[14] = s_box(st[12]) ^ s_box(st[1]) ^ gfm2_sb(st[6]) ^ gfm3_sb(st[11]);
+  dt[15] = gfm3_sb(st[12]) ^ s_box(st[1]) ^ s_box(st[6]) ^ gfm2_sb(st[11]);
+}
+
+#if defined(VERSION_1)
+static void inv_mix_sub_columns(uint_8t dt[N_BLOCK]) {
+  uint_8t st[N_BLOCK];
+  block_copy(st, dt);
+#else
+static void inv_mix_sub_columns(uint_8t dt[N_BLOCK], uint_8t st[N_BLOCK]) {
+#endif
+  dt[0] = is_box(gfm_e(st[0]) ^ gfm_b(st[1]) ^ gfm_d(st[2]) ^ gfm_9(st[3]));
+  dt[5] = is_box(gfm_9(st[0]) ^ gfm_e(st[1]) ^ gfm_b(st[2]) ^ gfm_d(st[3]));
+  dt[10] = is_box(gfm_d(st[0]) ^ gfm_9(st[1]) ^ gfm_e(st[2]) ^ gfm_b(st[3]));
+  dt[15] = is_box(gfm_b(st[0]) ^ gfm_d(st[1]) ^ gfm_9(st[2]) ^ gfm_e(st[3]));
+
+  dt[4] = is_box(gfm_e(st[4]) ^ gfm_b(st[5]) ^ gfm_d(st[6]) ^ gfm_9(st[7]));
+  dt[9] = is_box(gfm_9(st[4]) ^ gfm_e(st[5]) ^ gfm_b(st[6]) ^ gfm_d(st[7]));
+  dt[14] = is_box(gfm_d(st[4]) ^ gfm_9(st[5]) ^ gfm_e(st[6]) ^ gfm_b(st[7]));
+  dt[3] = is_box(gfm_b(st[4]) ^ gfm_d(st[5]) ^ gfm_9(st[6]) ^ gfm_e(st[7]));
+
+  dt[8] = is_box(gfm_e(st[8]) ^ gfm_b(st[9]) ^ gfm_d(st[10]) ^ gfm_9(st[11]));
+  dt[13] = is_box(gfm_9(st[8]) ^ gfm_e(st[9]) ^ gfm_b(st[10]) ^ gfm_d(st[11]));
+  dt[2] = is_box(gfm_d(st[8]) ^ gfm_9(st[9]) ^ gfm_e(st[10]) ^ gfm_b(st[11]));
+  dt[7] = is_box(gfm_b(st[8]) ^ gfm_d(st[9]) ^ gfm_9(st[10]) ^ gfm_e(st[11]));
+
+  dt[12] =
+      is_box(gfm_e(st[12]) ^ gfm_b(st[13]) ^ gfm_d(st[14]) ^ gfm_9(st[15]));
+  dt[1] = is_box(gfm_9(st[12]) ^ gfm_e(st[13]) ^ gfm_b(st[14]) ^ gfm_d(st[15]));
+  dt[6] = is_box(gfm_d(st[12]) ^ gfm_9(st[13]) ^ gfm_e(st[14]) ^ gfm_b(st[15]));
+  dt[11] =
+      is_box(gfm_b(st[12]) ^ gfm_d(st[13]) ^ gfm_9(st[14]) ^ gfm_e(st[15]));
+}
+
+#if defined(AES_ENC_PREKEYED) || defined(AES_DEC_PREKEYED)
+
+/*  Set the cipher key for the pre-keyed version */
+/*  NOTE: If the length_type used for the key length is an
+    unsigned 8-bit character, a key length of 256 bits must
+    be entered as a length in bytes (valid inputs are hence
+    128, 192, 16, 24 and 32).
+*/
+
+return_type aes_set_key(const unsigned char key[], length_type keylen,
+                        aes_context ctx[1]) {
+  uint_8t cc, rc, hi;
+
+  switch (keylen) {
+    case 16:
+    case 128: /* length in bits (128 = 8*16) */
+      keylen = 16;
+      break;
+    case 24:
+    case 192: /* length in bits (192 = 8*24) */
+      keylen = 24;
+      break;
+    case 32:
+      /*    case 256:           length in bits (256 = 8*32) */
+      keylen = 32;
+      break;
+    default:
+      ctx->rnd = 0;
+      return (return_type)-1;
+  }
+  block_copy_nn(ctx->ksch, key, keylen);
+  hi = (keylen + 28) << 2;
+  ctx->rnd = (hi >> 4) - 1;
+  for (cc = keylen, rc = 1; cc < hi; cc += 4) {
+    uint_8t tt, t0, t1, t2, t3;
+
+    t0 = ctx->ksch[cc - 4];
+    t1 = ctx->ksch[cc - 3];
+    t2 = ctx->ksch[cc - 2];
+    t3 = ctx->ksch[cc - 1];
+    if (cc % keylen == 0) {
+      tt = t0;
+      t0 = s_box(t1) ^ rc;
+      t1 = s_box(t2);
+      t2 = s_box(t3);
+      t3 = s_box(tt);
+      rc = f2(rc);
+    } else if (keylen > 24 && cc % keylen == 16) {
+      t0 = s_box(t0);
+      t1 = s_box(t1);
+      t2 = s_box(t2);
+      t3 = s_box(t3);
+    }
+    tt = cc - keylen;
+    ctx->ksch[cc + 0] = ctx->ksch[tt + 0] ^ t0;
+    ctx->ksch[cc + 1] = ctx->ksch[tt + 1] ^ t1;
+    ctx->ksch[cc + 2] = ctx->ksch[tt + 2] ^ t2;
+    ctx->ksch[cc + 3] = ctx->ksch[tt + 3] ^ t3;
+  }
+  return 0;
+}
+
+#endif
+
+#if defined(AES_ENC_PREKEYED)
+
+/*  Encrypt a single block of 16 bytes */
+
+return_type aes_encrypt(const unsigned char in[N_BLOCK],
+                        unsigned char out[N_BLOCK], const aes_context ctx[1]) {
+  if (ctx->rnd) {
+    uint_8t s1[N_BLOCK], r;
+    copy_and_key(s1, in, ctx->ksch);
+
+    for (r = 1; r < ctx->rnd; ++r)
+#if defined(VERSION_1)
+    {
+      mix_sub_columns(s1);
+      add_round_key(s1, ctx->ksch + r * N_BLOCK);
+    }
+#else
+    {
+      uint_8t s2[N_BLOCK];
+      mix_sub_columns(s2, s1);
+      copy_and_key(s1, s2, ctx->ksch + r * N_BLOCK);
+    }
+#endif
+    shift_sub_rows(s1);
+    copy_and_key(out, s1, ctx->ksch + r * N_BLOCK);
+  } else
+    return (return_type)-1;
+  return 0;
+}
+
+/* CBC encrypt a number of blocks (input and return an IV) */
+
+return_type aes_cbc_encrypt(const unsigned char* in, unsigned char* out,
+                            int n_block, unsigned char iv[N_BLOCK],
+                            const aes_context ctx[1]) {
+  while (n_block--) {
+    xor_block(iv, in);
+    if (aes_encrypt(iv, iv, ctx) != EXIT_SUCCESS) return EXIT_FAILURE;
+    memcpy(out, iv, N_BLOCK);
+    in += N_BLOCK;
+    out += N_BLOCK;
+  }
+  return EXIT_SUCCESS;
+}
+
+#endif
+
+#if defined(AES_DEC_PREKEYED)
+
+/*  Decrypt a single block of 16 bytes */
+
+return_type aes_decrypt(const unsigned char in[N_BLOCK],
+                        unsigned char out[N_BLOCK], const aes_context ctx[1]) {
+  if (ctx->rnd) {
+    uint_8t s1[N_BLOCK], r;
+    copy_and_key(s1, in, ctx->ksch + ctx->rnd * N_BLOCK);
+    inv_shift_sub_rows(s1);
+
+    for (r = ctx->rnd; --r;)
+#if defined(VERSION_1)
+    {
+      add_round_key(s1, ctx->ksch + r * N_BLOCK);
+      inv_mix_sub_columns(s1);
+    }
+#else
+    {
+      uint_8t s2[N_BLOCK];
+      copy_and_key(s2, s1, ctx->ksch + r * N_BLOCK);
+      inv_mix_sub_columns(s1, s2);
+    }
+#endif
+    copy_and_key(out, s1, ctx->ksch);
+  } else
+    return (return_type)-1;
+  return 0;
+}
+
+/* CBC decrypt a number of blocks (input and return an IV) */
+
+return_type aes_cbc_decrypt(const unsigned char* in, unsigned char* out,
+                            int n_block, unsigned char iv[N_BLOCK],
+                            const aes_context ctx[1]) {
+  while (n_block--) {
+    uint_8t tmp[N_BLOCK];
+
+    memcpy(tmp, in, N_BLOCK);
+    if (aes_decrypt(in, out, ctx) != EXIT_SUCCESS) return EXIT_FAILURE;
+    xor_block(out, iv);
+    memcpy(iv, tmp, N_BLOCK);
+    in += N_BLOCK;
+    out += N_BLOCK;
+  }
+  return EXIT_SUCCESS;
+}
+
+#endif
+
+#if defined(AES_ENC_128_OTFK)
+
+/*  The 'on the fly' encryption key update for for 128 bit keys */
+
+static void update_encrypt_key_128(uint_8t k[N_BLOCK], uint_8t* rc) {
+  uint_8t cc;
+
+  k[0] ^= s_box(k[13]) ^ *rc;
+  k[1] ^= s_box(k[14]);
+  k[2] ^= s_box(k[15]);
+  k[3] ^= s_box(k[12]);
+  *rc = f2(*rc);
+
+  for (cc = 4; cc < 16; cc += 4) {
+    k[cc + 0] ^= k[cc - 4];
+    k[cc + 1] ^= k[cc - 3];
+    k[cc + 2] ^= k[cc - 2];
+    k[cc + 3] ^= k[cc - 1];
+  }
+}
+
+/*  Encrypt a single block of 16 bytes with 'on the fly' 128 bit keying */
+
+void aes_encrypt_128(const unsigned char in[N_BLOCK],
+                     unsigned char out[N_BLOCK],
+                     const unsigned char key[N_BLOCK],
+                     unsigned char o_key[N_BLOCK]) {
+  uint_8t s1[N_BLOCK], r, rc = 1;
+
+  if (o_key != key) block_copy(o_key, key);
+  copy_and_key(s1, in, o_key);
+
+  for (r = 1; r < 10; ++r)
+#if defined(VERSION_1)
+  {
+    mix_sub_columns(s1);
+    update_encrypt_key_128(o_key, &rc);
+    add_round_key(s1, o_key);
+  }
+#else
+  {
+    uint_8t s2[N_BLOCK];
+    mix_sub_columns(s2, s1);
+    update_encrypt_key_128(o_key, &rc);
+    copy_and_key(s1, s2, o_key);
+  }
+#endif
+
+  shift_sub_rows(s1);
+  update_encrypt_key_128(o_key, &rc);
+  copy_and_key(out, s1, o_key);
+}
+
+#endif
+
+#if defined(AES_DEC_128_OTFK)
+
+/*  The 'on the fly' decryption key update for for 128 bit keys */
+
+static void update_decrypt_key_128(uint_8t k[N_BLOCK], uint_8t* rc) {
+  uint_8t cc;
+
+  for (cc = 12; cc > 0; cc -= 4) {
+    k[cc + 0] ^= k[cc - 4];
+    k[cc + 1] ^= k[cc - 3];
+    k[cc + 2] ^= k[cc - 2];
+    k[cc + 3] ^= k[cc - 1];
+  }
+  *rc = d2(*rc);
+  k[0] ^= s_box(k[13]) ^ *rc;
+  k[1] ^= s_box(k[14]);
+  k[2] ^= s_box(k[15]);
+  k[3] ^= s_box(k[12]);
+}
+
+/*  Decrypt a single block of 16 bytes with 'on the fly' 128 bit keying */
+
+void aes_decrypt_128(const unsigned char in[N_BLOCK],
+                     unsigned char out[N_BLOCK],
+                     const unsigned char key[N_BLOCK],
+                     unsigned char o_key[N_BLOCK]) {
+  uint_8t s1[N_BLOCK], r, rc = 0x6c;
+  if (o_key != key) block_copy(o_key, key);
+
+  copy_and_key(s1, in, o_key);
+  inv_shift_sub_rows(s1);
+
+  for (r = 10; --r;)
+#if defined(VERSION_1)
+  {
+    update_decrypt_key_128(o_key, &rc);
+    add_round_key(s1, o_key);
+    inv_mix_sub_columns(s1);
+  }
+#else
+  {
+    uint_8t s2[N_BLOCK];
+    update_decrypt_key_128(o_key, &rc);
+    copy_and_key(s2, s1, o_key);
+    inv_mix_sub_columns(s1, s2);
+  }
+#endif
+  update_decrypt_key_128(o_key, &rc);
+  copy_and_key(out, s1, o_key);
+}
+
+#endif
+
+#if defined(AES_ENC_256_OTFK)
+
+/*  The 'on the fly' encryption key update for for 256 bit keys */
+
+static void update_encrypt_key_256(uint_8t k[2 * N_BLOCK], uint_8t* rc) {
+  uint_8t cc;
+
+  k[0] ^= s_box(k[29]) ^ *rc;
+  k[1] ^= s_box(k[30]);
+  k[2] ^= s_box(k[31]);
+  k[3] ^= s_box(k[28]);
+  *rc = f2(*rc);
+
+  for (cc = 4; cc < 16; cc += 4) {
+    k[cc + 0] ^= k[cc - 4];
+    k[cc + 1] ^= k[cc - 3];
+    k[cc + 2] ^= k[cc - 2];
+    k[cc + 3] ^= k[cc - 1];
+  }
+
+  k[16] ^= s_box(k[12]);
+  k[17] ^= s_box(k[13]);
+  k[18] ^= s_box(k[14]);
+  k[19] ^= s_box(k[15]);
+
+  for (cc = 20; cc < 32; cc += 4) {
+    k[cc + 0] ^= k[cc - 4];
+    k[cc + 1] ^= k[cc - 3];
+    k[cc + 2] ^= k[cc - 2];
+    k[cc + 3] ^= k[cc - 1];
+  }
+}
+
+/*  Encrypt a single block of 16 bytes with 'on the fly' 256 bit keying */
+
+void aes_encrypt_256(const unsigned char in[N_BLOCK],
+                     unsigned char out[N_BLOCK],
+                     const unsigned char key[2 * N_BLOCK],
+                     unsigned char o_key[2 * N_BLOCK]) {
+  uint_8t s1[N_BLOCK], r, rc = 1;
+  if (o_key != key) {
+    block_copy(o_key, key);
+    block_copy(o_key + 16, key + 16);
+  }
+  copy_and_key(s1, in, o_key);
+
+  for (r = 1; r < 14; ++r)
+#if defined(VERSION_1)
+  {
+    mix_sub_columns(s1);
+    if (r & 1)
+      add_round_key(s1, o_key + 16);
+    else {
+      update_encrypt_key_256(o_key, &rc);
+      add_round_key(s1, o_key);
+    }
+  }
+#else
+  {
+    uint_8t s2[N_BLOCK];
+    mix_sub_columns(s2, s1);
+    if (r & 1)
+      copy_and_key(s1, s2, o_key + 16);
+    else {
+      update_encrypt_key_256(o_key, &rc);
+      copy_and_key(s1, s2, o_key);
+    }
+  }
+#endif
+
+  shift_sub_rows(s1);
+  update_encrypt_key_256(o_key, &rc);
+  copy_and_key(out, s1, o_key);
+}
+
+#endif
+
+#if defined(AES_DEC_256_OTFK)
+
+/*  The 'on the fly' encryption key update for for 256 bit keys */
+
+static void update_decrypt_key_256(uint_8t k[2 * N_BLOCK], uint_8t* rc) {
+  uint_8t cc;
+
+  for (cc = 28; cc > 16; cc -= 4) {
+    k[cc + 0] ^= k[cc - 4];
+    k[cc + 1] ^= k[cc - 3];
+    k[cc + 2] ^= k[cc - 2];
+    k[cc + 3] ^= k[cc - 1];
+  }
+
+  k[16] ^= s_box(k[12]);
+  k[17] ^= s_box(k[13]);
+  k[18] ^= s_box(k[14]);
+  k[19] ^= s_box(k[15]);
+
+  for (cc = 12; cc > 0; cc -= 4) {
+    k[cc + 0] ^= k[cc - 4];
+    k[cc + 1] ^= k[cc - 3];
+    k[cc + 2] ^= k[cc - 2];
+    k[cc + 3] ^= k[cc - 1];
+  }
+
+  *rc = d2(*rc);
+  k[0] ^= s_box(k[29]) ^ *rc;
+  k[1] ^= s_box(k[30]);
+  k[2] ^= s_box(k[31]);
+  k[3] ^= s_box(k[28]);
+}
+
+/*  Decrypt a single block of 16 bytes with 'on the fly'
+    256 bit keying
+*/
+void aes_decrypt_256(const unsigned char in[N_BLOCK],
+                     unsigned char out[N_BLOCK],
+                     const unsigned char key[2 * N_BLOCK],
+                     unsigned char o_key[2 * N_BLOCK]) {
+  uint_8t s1[N_BLOCK], r, rc = 0x80;
+
+  if (o_key != key) {
+    block_copy(o_key, key);
+    block_copy(o_key + 16, key + 16);
+  }
+
+  copy_and_key(s1, in, o_key);
+  inv_shift_sub_rows(s1);
+
+  for (r = 14; --r;)
+#if defined(VERSION_1)
+  {
+    if ((r & 1)) {
+      update_decrypt_key_256(o_key, &rc);
+      add_round_key(s1, o_key + 16);
+    } else
+      add_round_key(s1, o_key);
+    inv_mix_sub_columns(s1);
+  }
+#else
+  {
+    uint_8t s2[N_BLOCK];
+    if ((r & 1)) {
+      update_decrypt_key_256(o_key, &rc);
+      copy_and_key(s2, s1, o_key + 16);
+    } else
+      copy_and_key(s2, s1, o_key);
+    inv_mix_sub_columns(s1, s2);
+  }
+#endif
+  copy_and_key(out, s1, o_key);
+}
+
+#endif
diff --git a/gd/crypto_toolbox/aes.h b/gd/crypto_toolbox/aes.h
new file mode 100644
index 0000000..2ff6fbd
--- /dev/null
+++ b/gd/crypto_toolbox/aes.h
@@ -0,0 +1,154 @@
+/*
+ ---------------------------------------------------------------------------
+ Copyright (c) 1998-2008, Brian Gladman, Worcester, UK. All rights reserved.
+
+ LICENSE TERMS
+
+ The redistribution and use of this software (with or without changes)
+ is allowed without the payment of fees or royalties provided that:
+
+  1. source code distributions include the above copyright notice, this
+     list of conditions and the following disclaimer;
+
+  2. binary distributions include the above copyright notice, this list
+     of conditions and the following disclaimer in their documentation;
+
+  3. the name of the copyright holder is not used to endorse products
+     built using this software without specific written permission.
+
+ DISCLAIMER
+
+ This software is provided 'as is' with no explicit or implied warranties
+ in respect of its properties, including, but not limited to, correctness
+ and/or fitness for purpose.
+ ---------------------------------------------------------------------------
+ Issue 09/09/2006
+
+ This is an AES implementation that uses only 8-bit byte operations on the
+ cipher state.
+ */
+
+#ifndef AES_H
+#define AES_H
+
+#if 1
+#define AES_ENC_PREKEYED /* AES encryption with a precomputed key schedule  */
+#endif
+#if 1
+#define AES_DEC_PREKEYED /* AES decryption with a precomputed key schedule  */
+#endif
+#if 1
+#define AES_ENC_128_OTFK /* AES encryption with 'on the fly' 128 bit keying */
+#endif
+#if 1
+#define AES_DEC_128_OTFK /* AES decryption with 'on the fly' 128 bit keying */
+#endif
+#if 1
+#define AES_ENC_256_OTFK /* AES encryption with 'on the fly' 256 bit keying */
+#endif
+#if 1
+#define AES_DEC_256_OTFK /* AES decryption with 'on the fly' 256 bit keying */
+#endif
+
+#define N_ROW 4
+#define N_COL 4
+#define N_BLOCK (N_ROW * N_COL)
+#define N_MAX_ROUNDS 14
+
+typedef unsigned char uint_8t;
+
+typedef uint_8t return_type;
+
+/*  Warning: The key length for 256 bit keys overflows a byte
+    (see comment below)
+*/
+
+typedef uint_8t length_type;
+
+typedef struct {
+  uint_8t ksch[(N_MAX_ROUNDS + 1) * N_BLOCK];
+  uint_8t rnd;
+} aes_context;
+
+/*  The following calls are for a precomputed key schedule
+
+    NOTE: If the length_type used for the key length is an
+    unsigned 8-bit character, a key length of 256 bits must
+    be entered as a length in bytes (valid inputs are hence
+    128, 192, 16, 24 and 32).
+*/
+
+#if defined(AES_ENC_PREKEYED) || defined(AES_DEC_PREKEYED)
+
+return_type aes_set_key(const unsigned char key[], length_type keylen,
+                        aes_context ctx[1]);
+#endif
+
+#if defined(AES_ENC_PREKEYED)
+
+return_type aes_encrypt(const unsigned char in[N_BLOCK],
+                        unsigned char out[N_BLOCK], const aes_context ctx[1]);
+
+return_type aes_cbc_encrypt(const unsigned char* in, unsigned char* out,
+                            int n_block, unsigned char iv[N_BLOCK],
+                            const aes_context ctx[1]);
+#endif
+
+#if defined(AES_DEC_PREKEYED)
+
+return_type aes_decrypt(const unsigned char in[N_BLOCK],
+                        unsigned char out[N_BLOCK], const aes_context ctx[1]);
+
+return_type aes_cbc_decrypt(const unsigned char* in, unsigned char* out,
+                            int n_block, unsigned char iv[N_BLOCK],
+                            const aes_context ctx[1]);
+#endif
+
+/*  The following calls are for 'on the fly' keying.  In this case the
+    encryption and decryption keys are different.
+
+    The encryption subroutines take a key in an array of bytes in
+    key[L] where L is 16, 24 or 32 bytes for key lengths of 128,
+    192, and 256 bits respectively.  They then encrypts the input
+    data, in[] with this key and put the reult in the output array
+    out[].  In addition, the second key array, o_key[L], is used
+    to output the key that is needed by the decryption subroutine
+    to reverse the encryption operation.  The two key arrays can
+    be the same array but in this case the original key will be
+    overwritten.
+
+    In the same way, the decryption subroutines output keys that
+    can be used to reverse their effect when used for encryption.
+
+    Only 128 and 256 bit keys are supported in these 'on the fly'
+    modes.
+*/
+
+#if defined(AES_ENC_128_OTFK)
+void aes_encrypt_128(const unsigned char in[N_BLOCK],
+                     unsigned char out[N_BLOCK],
+                     const unsigned char key[N_BLOCK], uint_8t o_key[N_BLOCK]);
+#endif
+
+#if defined(AES_DEC_128_OTFK)
+void aes_decrypt_128(const unsigned char in[N_BLOCK],
+                     unsigned char out[N_BLOCK],
+                     const unsigned char key[N_BLOCK],
+                     unsigned char o_key[N_BLOCK]);
+#endif
+
+#if defined(AES_ENC_256_OTFK)
+void aes_encrypt_256(const unsigned char in[N_BLOCK],
+                     unsigned char out[N_BLOCK],
+                     const unsigned char key[2 * N_BLOCK],
+                     unsigned char o_key[2 * N_BLOCK]);
+#endif
+
+#if defined(AES_DEC_256_OTFK)
+void aes_decrypt_256(const unsigned char in[N_BLOCK],
+                     unsigned char out[N_BLOCK],
+                     const unsigned char key[2 * N_BLOCK],
+                     unsigned char o_key[2 * N_BLOCK]);
+#endif
+
+#endif
diff --git a/gd/crypto_toolbox/aes_cmac.cc b/gd/crypto_toolbox/aes_cmac.cc
new file mode 100644
index 0000000..752027e
--- /dev/null
+++ b/gd/crypto_toolbox/aes_cmac.cc
@@ -0,0 +1,203 @@
+/******************************************************************************
+ *
+ *  Copyright 2008-2012 Broadcom Corporation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+/******************************************************************************
+ *
+ *  This file contains the implementation of the AES128 and AES CMAC algorithm.
+ *
+ ******************************************************************************/
+
+#include "crypto_toolbox/aes.h"
+#include "crypto_toolbox/crypto_toolbox.h"
+
+namespace bluetooth {
+namespace crypto_toolbox {
+
+namespace {
+
+typedef struct {
+  uint8_t* text;
+  uint16_t len;
+  uint16_t round;
+} tCMAC_CB;
+
+thread_local tCMAC_CB cmac_cb;
+
+/* Rb for AES-128 as block cipher, LSB as [0] */
+Octet16 const_Rb{0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+/** utility function to do an biteise exclusive-OR of two bit strings of the
+ * length of OCTET16_LEN. Result is stored in first argument.
+ */
+static void xor_128(Octet16* a, const Octet16& b) {
+  // CHECK(a);
+  uint8_t i, *aa = a->data();
+  const uint8_t* bb = b.data();
+
+  for (i = 0; i < OCTET16_LEN; i++) {
+    aa[i] = aa[i] ^ bb[i];
+  }
+}
+}  // namespace
+
+/* This function computes AES_128(key, message) */
+Octet16 aes_128(const Octet16& key, const Octet16& message) {
+  Octet16 key_reversed;
+  Octet16 message_reversed;
+  Octet16 output;
+
+  std::reverse_copy(key.begin(), key.end(), key_reversed.begin());
+  std::reverse_copy(message.begin(), message.end(), message_reversed.begin());
+
+  aes_context ctx;
+  aes_set_key(key_reversed.data(), key_reversed.size(), &ctx);
+  aes_encrypt(message_reversed.data(), output.data(), &ctx);
+
+  std::reverse(output.begin(), output.end());
+  return output;
+}
+
+/** utility function to padding the given text to be a 128 bits data. The
+ * parameter dest is input and output parameter, it must point to a
+ * OCTET16_LEN memory space; where include length bytes valid data. */
+static void padding(Octet16* dest, uint8_t length) {
+  uint8_t i, *p = dest->data();
+  /* original last block */
+  for (i = length; i < OCTET16_LEN; i++) p[OCTET16_LEN - i - 1] = (i == length) ? 0x80 : 0;
+}
+
+/** utility function to left shift one bit for a 128 bits value. */
+static void leftshift_onebit(uint8_t* input, uint8_t* output) {
+  uint8_t i, overflow = 0, next_overflow = 0;
+  /* input[0] is LSB */
+  for (i = 0; i < OCTET16_LEN; i++) {
+    next_overflow = (input[i] & 0x80) ? 1 : 0;
+    output[i] = (input[i] << 1) | overflow;
+    overflow = next_overflow;
+  }
+  return;
+}
+
+/** This function is the calculation of block cipher using AES-128. */
+static Octet16 cmac_aes_k_calculate(const Octet16& key) {
+  Octet16 output;
+  Octet16 x{0};  // zero initialized
+
+  uint8_t i = 1;
+  while (i <= cmac_cb.round) {
+    /* Mi' := Mi (+) X  */
+    xor_128((Octet16*)&cmac_cb.text[(cmac_cb.round - i) * OCTET16_LEN], x);
+
+    output = aes_128(key, &cmac_cb.text[(cmac_cb.round - i) * OCTET16_LEN], OCTET16_LEN);
+    x = output;
+    i++;
+  }
+
+  return output;
+}
+
+/** This function proceeed to prepare the last block of message Mn depending on
+ * the size of the message.
+ */
+static void cmac_prepare_last_block(const Octet16& k1, const Octet16& k2) {
+  //    uint8_t     x[16] = {0};
+  bool flag;
+
+  /* last block is a complete block set flag to 1 */
+  flag = ((cmac_cb.len % OCTET16_LEN) == 0 && cmac_cb.len != 0) ? true : false;
+
+  if (flag) { /* last block is complete block */
+    xor_128((Octet16*)&cmac_cb.text[0], k1);
+  } else /* padding then xor with k2 */
+  {
+    padding((Octet16*)&cmac_cb.text[0], (uint8_t)(cmac_cb.len % 16));
+
+    xor_128((Octet16*)&cmac_cb.text[0], k2);
+  }
+}
+
+/** This is the function to generate the two subkeys.
+ * |key| is CMAC key, expect SRK when used by SMP.
+ */
+static void cmac_generate_subkey(const Octet16& key) {
+  Octet16 zero{};
+  Octet16 p = aes_128(key, zero.data(), OCTET16_LEN);
+
+  Octet16 k1, k2;
+  uint8_t* pp = p.data();
+
+  /* If MSB(L) = 0, then K1 = L << 1 */
+  if ((pp[OCTET16_LEN - 1] & 0x80) != 0) {
+    /* Else K1 = ( L << 1 ) (+) Rb */
+    leftshift_onebit(pp, k1.data());
+    xor_128(&k1, const_Rb);
+  } else {
+    leftshift_onebit(pp, k1.data());
+  }
+
+  if ((k1[OCTET16_LEN - 1] & 0x80) != 0) {
+    /* K2 =  (K1 << 1) (+) Rb */
+    leftshift_onebit(k1.data(), k2.data());
+    xor_128(&k2, const_Rb);
+  } else {
+    /* If MSB(K1) = 0, then K2 = K1 << 1 */
+    leftshift_onebit(k1.data(), k2.data());
+  }
+
+  cmac_prepare_last_block(k1, k2);
+}
+
+/** key - CMAC key in little endian order
+ *  input - text to be signed in little endian byte order.
+ *  length - length of the input in byte.
+ */
+Octet16 aes_cmac(const Octet16& key, const uint8_t* input, uint16_t length) {
+  uint32_t len;
+  uint16_t diff;
+  /* n is number of rounds */
+  uint16_t n = (length + OCTET16_LEN - 1) / OCTET16_LEN;
+
+  if (n == 0) n = 1;
+  len = n * OCTET16_LEN;
+
+  /* allocate a memory space of multiple of 16 bytes to hold text  */
+  cmac_cb.text = (uint8_t*)alloca(len);
+  cmac_cb.round = n;
+  diff = len - length;
+
+  if (input != NULL && length > 0) {
+    memcpy(&cmac_cb.text[diff], input, (int)length);
+    cmac_cb.len = length;
+  } else {
+    cmac_cb.len = 0;
+  }
+
+  /* prepare calculation for subkey s and last block of data */
+  cmac_generate_subkey(key);
+  /* start calculation */
+  Octet16 signature = cmac_aes_k_calculate(key);
+
+  /* clean up */
+  memset(&cmac_cb, 0, sizeof(tCMAC_CB));
+  // cmac_cb.text is auto-freed by alloca
+
+  return signature;
+}
+
+}  // namespace crypto_toolbox
+}  // namespace bluetooth
diff --git a/gd/crypto_toolbox/crypto_toolbox.cc b/gd/crypto_toolbox/crypto_toolbox.cc
new file mode 100644
index 0000000..adb4956
--- /dev/null
+++ b/gd/crypto_toolbox/crypto_toolbox.cc
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "crypto_toolbox/crypto_toolbox.h"
+#include "crypto_toolbox/aes.h"
+
+#include <endian.h>
+#include <algorithm>
+
+namespace bluetooth {
+namespace crypto_toolbox {
+
+constexpr int OCTET32_LEN = 32;
+
+Octet16 h6(const Octet16& w, std::array<uint8_t, 4> keyid) {
+  return aes_cmac(w, keyid.data(), keyid.size());
+}
+
+Octet16 h7(const Octet16& salt, const Octet16& w) {
+  return aes_cmac(salt, w.data(), w.size());
+}
+
+Octet16 f4(uint8_t* u, uint8_t* v, const Octet16& x, uint8_t z) {
+  constexpr size_t msg_len = OCTET32_LEN /* U size */ + OCTET32_LEN /* V size */ + 1 /* Z size */;
+
+  // DVLOG(2) << "U=" << HexEncode(u, OCTET32_LEN) << ", V=" << HexEncode(v, OCTET32_LEN)
+  //          << ", X=" << HexEncode(x.data(), x.size()) << ", Z=" << std::hex << +z;
+
+  std::array<uint8_t, msg_len> msg;
+  auto it = msg.begin();
+  it = std::copy(&z, &z + 1, it);
+  it = std::copy(v, v + OCTET32_LEN, it);
+  it = std::copy(u, u + OCTET32_LEN, it);
+  return aes_cmac(x, msg.data(), msg.size());
+}
+
+/** helper for f5 */
+static Octet16 calculate_mac_key_or_ltk(const Octet16& t, uint8_t counter, uint8_t* key_id, const Octet16& n1,
+                                        const Octet16& n2, uint8_t* a1, uint8_t* a2, uint8_t* length) {
+  constexpr size_t msg_len = 1 /* Counter size */ + 4 /* keyID size */ + OCTET16_LEN /* N1 size */ +
+                             OCTET16_LEN /* N2 size */ + 7 /* A1 size*/ + 7 /* A2 size*/ + 2 /* Length size */;
+
+  std::array<uint8_t, msg_len> msg;
+  auto it = msg.begin();
+  it = std::copy(length, length + 2, it);
+  it = std::copy(a2, a2 + 7, it);
+  it = std::copy(a1, a1 + 7, it);
+  it = std::copy(n2.begin(), n2.end(), it);
+  it = std::copy(n1.begin(), n1.end(), it);
+  it = std::copy(key_id, key_id + 4, it);
+  it = std::copy(&counter, &counter + 1, it);
+
+  return aes_cmac(t, msg.data(), msg.size());
+}
+
+void f5(uint8_t* w, const Octet16& n1, const Octet16& n2, uint8_t* a1, uint8_t* a2, Octet16* mac_key, Octet16* ltk) {
+  // DVLOG(2) << __func__ << "W=" << HexEncode(w, OCTET32_LEN) << ", N1=" << HexEncode(n1.data(), n1.size())
+  //          << ", N2=" << HexEncode(n2.data(), n2.size()) << ", A1=" << HexEncode(a1, 7) << ", A2=" << HexEncode(a2,
+  //          7);
+
+  const Octet16 salt{0xBE, 0x83, 0x60, 0x5A, 0xDB, 0x0B, 0x37, 0x60, 0x38, 0xA5, 0xF5, 0xAA, 0x91, 0x83, 0x88, 0x6C};
+  Octet16 t = aes_cmac(salt, w, OCTET32_LEN);
+
+  // DVLOG(2) << "T=" << HexEncode(t.data(), t.size());
+
+  uint8_t key_id[4] = {0x65, 0x6c, 0x74, 0x62}; /* 0x62746c65 */
+  uint8_t length[2] = {0x00, 0x01};             /* 0x0100 */
+
+  *mac_key = calculate_mac_key_or_ltk(t, 0, key_id, n1, n2, a1, a2, length);
+
+  *ltk = calculate_mac_key_or_ltk(t, 1, key_id, n1, n2, a1, a2, length);
+
+  // DVLOG(2) << "mac_key=" << HexEncode(mac_key->data(), mac_key->size());
+  // DVLOG(2) << "ltk=" << HexEncode(ltk->data(), ltk->size());
+}
+
+Octet16 f6(const Octet16& w, const Octet16& n1, const Octet16& n2, const Octet16& r, uint8_t* iocap, uint8_t* a1,
+           uint8_t* a2) {
+  const uint8_t msg_len = OCTET16_LEN /* N1 size */ + OCTET16_LEN /* N2 size */ + OCTET16_LEN /* R size */ +
+                          3 /* IOcap size */ + 7 /* A1 size*/ + 7 /* A2 size*/;
+
+  // DVLOG(2) << __func__ << "W=" << HexEncode(w.data(), w.size()) << ", N1=" << HexEncode(n1.data(), n1.size())
+  //          << ", N2=" << HexEncode(n2.data(), n2.size()) << ", R=" << HexEncode(r.data(), r.size())
+  //          << ", IOcap=" << HexEncode(iocap, 3) << ", A1=" << HexEncode(a1, 7) << ", A2=" << HexEncode(a2, 7);
+
+  std::array<uint8_t, msg_len> msg;
+  auto it = msg.begin();
+  it = std::copy(a2, a2 + 7, it);
+  it = std::copy(a1, a1 + 7, it);
+  it = std::copy(iocap, iocap + 3, it);
+  it = std::copy(r.begin(), r.end(), it);
+  it = std::copy(n2.begin(), n2.end(), it);
+  it = std::copy(n1.begin(), n1.end(), it);
+
+  return aes_cmac(w, msg.data(), msg.size());
+}
+
+uint32_t g2(uint8_t* u, uint8_t* v, const Octet16& x, const Octet16& y) {
+  constexpr size_t msg_len = OCTET32_LEN /* U size */ + OCTET32_LEN /* V size */
+                             + OCTET16_LEN /* Y size */;
+
+  // DVLOG(2) << __func__ << "U=" << HexEncode(u, OCTET32_LEN) << ", V=" << HexEncode(v, OCTET32_LEN)
+  //          << ", X=" << HexEncode(x.data(), x.size()) << ", Y=" << HexEncode(y.data(), y.size());
+
+  std::array<uint8_t, msg_len> msg;
+  auto it = msg.begin();
+  it = std::copy(y.begin(), y.end(), it);
+  it = std::copy(v, v + OCTET32_LEN, it);
+  it = std::copy(u, u + OCTET32_LEN, it);
+
+  Octet16 cmac = aes_cmac(x, msg.data(), msg.size());
+
+  /* vres = cmac mod 2**32 mod 10**6 */
+  return le32toh(*(uint32_t*)cmac.data()) % 1000000;
+}
+
+Octet16 ltk_to_link_key(const Octet16& ltk, bool use_h7) {
+  Octet16 ilk; /* intermidiate link key */
+  if (use_h7) {
+    constexpr Octet16 salt{0x31, 0x70, 0x6D, 0x74, 0x00, 0x00, 0x00, 0x00,
+                           0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+    ilk = h7(salt, ltk);
+  } else {
+    /* "tmp1" mapping to extended ASCII, little endian*/
+    constexpr std::array<uint8_t, 4> keyID_tmp1 = {0x31, 0x70, 0x6D, 0x74};
+    ilk = h6(ltk, keyID_tmp1);
+  }
+
+  /* "lebr" mapping to extended ASCII, little endian */
+  constexpr std::array<uint8_t, 4> keyID_lebr = {0x72, 0x62, 0x65, 0x6c};
+  return h6(ilk, keyID_lebr);
+}
+
+Octet16 link_key_to_ltk(const Octet16& link_key, bool use_h7) {
+  Octet16 iltk; /* intermidiate long term key */
+  if (use_h7) {
+    constexpr Octet16 salt{0x32, 0x70, 0x6D, 0x74, 0x00, 0x00, 0x00, 0x00,
+                           0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+    iltk = h7(salt, link_key);
+  } else {
+    /* "tmp2" mapping to extended ASCII, little endian */
+    constexpr std::array<uint8_t, 4> keyID_tmp2 = {0x32, 0x70, 0x6D, 0x74};
+    iltk = h6(link_key, keyID_tmp2);
+  }
+
+  /* "brle" mapping to extended ASCII, little endian */
+  constexpr std::array<uint8_t, 4> keyID_brle = {0x65, 0x6c, 0x72, 0x62};
+  return h6(iltk, keyID_brle);
+}
+
+Octet16 c1(const Octet16& k, const Octet16& r, const uint8_t* preq, const uint8_t* pres, const uint8_t iat,
+           const uint8_t* ia, const uint8_t rat, const uint8_t* ra) {
+  Octet16 p1;
+  auto it = p1.begin();
+  it = std::copy(&iat, &iat + 1, it);
+  it = std::copy(&rat, &rat + 1, it);
+  it = std::copy(preq, preq + 7, it);
+  it = std::copy(pres, pres + 7, it);
+
+  for (uint8_t i = 0; i < OCTET16_LEN; i++) {
+    p1[i] = r[i] ^ p1[i];
+  }
+
+  Octet16 p1bis = aes_128(k, p1);
+
+  std::array<uint8_t, 4> padding{0};
+  Octet16 p2;
+  it = p2.begin();
+  it = std::copy(ra, ra + 6, it);
+  it = std::copy(ia, ia + 6, it);
+  it = std::copy(padding.begin(), padding.end(), it);
+
+  for (uint8_t i = 0; i < OCTET16_LEN; i++) {
+    p2[i] = p1bis[i] ^ p2[i];
+  }
+
+  return aes_128(k, p2);
+}
+
+Octet16 s1(const Octet16& k, const Octet16& r1, const Octet16& r2) {
+  Octet16 text{0};
+  constexpr uint8_t BT_OCTET8_LEN = 8;
+  memcpy(text.data(), r1.data(), BT_OCTET8_LEN);
+  memcpy(text.data() + BT_OCTET8_LEN, r2.data(), BT_OCTET8_LEN);
+
+  return aes_128(k, text);
+}
+
+}  // namespace crypto_toolbox
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/crypto_toolbox/crypto_toolbox.h b/gd/crypto_toolbox/crypto_toolbox.h
new file mode 100644
index 0000000..25f7f69
--- /dev/null
+++ b/gd/crypto_toolbox/crypto_toolbox.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <array>
+
+namespace bluetooth {
+namespace crypto_toolbox {
+
+constexpr int OCTET16_LEN = 16;
+using Octet16 = std::array<uint8_t, OCTET16_LEN>;
+
+Octet16 c1(const Octet16& k, const Octet16& r, const uint8_t* pres, const uint8_t* preq, const uint8_t iat,
+           const uint8_t* ia, const uint8_t rat, const uint8_t* ra);
+Octet16 s1(const Octet16& k, const Octet16& r1, const Octet16& r2);
+
+extern Octet16 aes_128(const Octet16& key, const Octet16& message);
+extern Octet16 aes_cmac(const Octet16& key, const uint8_t* message, uint16_t length);
+extern Octet16 f4(uint8_t* u, uint8_t* v, const Octet16& x, uint8_t z);
+extern void f5(uint8_t* w, const Octet16& n1, const Octet16& n2, uint8_t* a1, uint8_t* a2, Octet16* mac_key,
+               Octet16* ltk);
+extern Octet16 f6(const Octet16& w, const Octet16& n1, const Octet16& n2, const Octet16& r, uint8_t* iocap, uint8_t* a1,
+                  uint8_t* a2);
+extern Octet16 h6(const Octet16& w, std::array<uint8_t, 4> keyid);
+extern Octet16 h7(const Octet16& salt, const Octet16& w);
+extern uint32_t g2(uint8_t* u, uint8_t* v, const Octet16& x, const Octet16& y);
+extern Octet16 ltk_to_link_key(const Octet16& ltk, bool use_h7);
+extern Octet16 link_key_to_ltk(const Octet16& link_key, bool use_h7);
+
+/* This function computes AES_128(key, message). |key| must be 128bit.
+ * |message| can be at most 16 bytes long, it's length in bytes is given in
+ * |length| */
+inline Octet16 aes_128(const Octet16& key, const uint8_t* message, const uint8_t length) {
+  // CHECK(length <= OCTET16_LEN) << "you tried aes_128 more than 16 bytes!";
+  Octet16 msg{0};
+  std::copy(message, message + length, msg.begin());
+  return aes_128(key, msg);
+}
+
+// |tlen| - lenth of mac desired
+// |p_signature| - data pointer to where signed data to be stored, tlen long.
+inline void aes_cmac(const Octet16& key, const uint8_t* message, uint16_t length, uint16_t tlen, uint8_t* p_signature) {
+  Octet16 signature = aes_cmac(key, message, length);
+
+  uint8_t* p_mac = signature.data() + (OCTET16_LEN - tlen);
+  memcpy(p_signature, p_mac, tlen);
+}
+
+inline Octet16 aes_cmac(const Octet16& key, const Octet16& message) {
+  return aes_cmac(key, message.data(), message.size());
+}
+
+}  // namespace crypto_toolbox
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/crypto_toolbox/crypto_toolbox_test.cc b/gd/crypto_toolbox/crypto_toolbox_test.cc
new file mode 100644
index 0000000..b5674c1
--- /dev/null
+++ b/gd/crypto_toolbox/crypto_toolbox_test.cc
@@ -0,0 +1,355 @@
+/******************************************************************************
+ *
+ *  Copyright 2018 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#include <gtest/gtest.h>
+
+#include "crypto_toolbox/aes.h"
+#include "crypto_toolbox/crypto_toolbox.h"
+
+#include <vector>
+
+namespace bluetooth {
+namespace crypto_toolbox {
+
+// BT Spec 5.0 | Vol 3, Part H D.1
+TEST(CryptoToolboxTest, bt_spec_test_d_1_test) {
+  uint8_t k[] = {0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c};
+
+  uint8_t m[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+  uint8_t aes_cmac_k_m[] = {0x7d, 0xf7, 0x6b, 0x0c, 0x1a, 0xb8, 0x99, 0xb3,
+                            0x3e, 0x42, 0xf0, 0x47, 0xb9, 0x1b, 0x54, 0x6f};
+
+  uint8_t output[16];
+  aes_context ctx;
+  aes_set_key(k, sizeof(k), &ctx);
+  aes_encrypt(m, output, &ctx); /* outputs in byte 48 to byte 63 */
+
+  EXPECT_TRUE(memcmp(output, aes_cmac_k_m, OCTET16_LEN) == 0);
+
+  // useful for debugging
+  // LOG(INFO) << "k " << base::HexEncode(k, OCTET16_LEN);
+  // LOG(INFO) << "m " << base::HexEncode(m, sizeof(m));
+  // LOG(INFO) << "output " << base::HexEncode(output, OCTET16_LEN);
+}
+
+// BT Spec 5.0 | Vol 3, Part H D.1.1
+TEST(CryptoToolboxTest, bt_spec_example_d_1_1_test) {
+  Octet16 k{0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c};
+
+  Octet16 aes_cmac_k_m{0xbb, 0x1d, 0x69, 0x29, 0xe9, 0x59, 0x37, 0x28, 0x7f, 0xa3, 0x7d, 0x12, 0x9b, 0x75, 0x67, 0x46};
+
+  // algorithm expect all input to be in little endian format, so reverse
+  std::reverse(std::begin(k), std::end(k));
+  std::reverse(std::begin(aes_cmac_k_m), std::end(aes_cmac_k_m));
+
+  Octet16 output = aes_cmac(k, nullptr /* empty message */, 0);
+
+  EXPECT_EQ(output, aes_cmac_k_m);
+
+  // useful for debugging
+  // LOG(INFO) << "k " << base::HexEncode(k.data(), k.size());
+  // LOG(INFO) << "aes_cmac(k,nullptr) "
+  //           << base::HexEncode(output.data(), output.size());
+}
+
+// BT Spec 5.0 | Vol 3, Part H D.1.2
+TEST(CryptoToolboxTest, bt_spec_example_d_1_2_test) {
+  Octet16 k{0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c};
+
+  Octet16 m = {0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a};
+
+  Octet16 aes_cmac_k_m{0x07, 0x0a, 0x16, 0xb4, 0x6b, 0x4d, 0x41, 0x44, 0xf7, 0x9b, 0xdd, 0x9d, 0xd0, 0x4a, 0x28, 0x7c};
+
+  // algorithm expect all input to be in little endian format, so reverse
+  std::reverse(std::begin(k), std::end(k));
+  std::reverse(std::begin(m), std::end(m));
+  std::reverse(std::begin(aes_cmac_k_m), std::end(aes_cmac_k_m));
+
+  Octet16 output = aes_cmac(k, m);
+
+  EXPECT_EQ(output, aes_cmac_k_m);
+
+  // useful for debugging
+  // LOG(INFO) << "k " << base::HexEncode(k.data(), k.size());
+  // LOG(INFO) << "m " << base::HexEncode(m, sizeof(m));
+  // LOG(INFO) << "aes_cmac(k,m) "
+  //           << base::HexEncode(output.data(), output.size());
+}
+
+// BT Spec 5.0 | Vol 3, Part H D.1.3
+TEST(CryptoToolboxTest, bt_spec_example_d_1_3_test) {
+  Octet16 k{0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c};
+
+  uint8_t m[] = {0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93,
+                 0x17, 0x2a, 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac,
+                 0x45, 0xaf, 0x8e, 0x51, 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11};
+
+  Octet16 aes_cmac_k_m{0xdf, 0xa6, 0x67, 0x47, 0xde, 0x9a, 0xe6, 0x30, 0x30, 0xca, 0x32, 0x61, 0x14, 0x97, 0xc8, 0x27};
+
+  // algorithm expect all input to be in little endian format, so reverse
+  std::reverse(std::begin(k), std::end(k));
+  std::reverse(std::begin(m), std::end(m));
+  std::reverse(std::begin(aes_cmac_k_m), std::end(aes_cmac_k_m));
+
+  Octet16 output = aes_cmac(k, m, sizeof(m));
+  EXPECT_EQ(output, aes_cmac_k_m);
+}
+
+// BT Spec 5.0 | Vol 3, Part H D.1.4
+TEST(CryptoToolboxTest, bt_spec_example_d_1_4_test) {
+  Octet16 k{0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c};
+
+  uint8_t m[] = {0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
+                 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
+                 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
+                 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10};
+
+  Octet16 aes_cmac_k_m{0x51, 0xf0, 0xbe, 0xbf, 0x7e, 0x3b, 0x9d, 0x92, 0xfc, 0x49, 0x74, 0x17, 0x79, 0x36, 0x3c, 0xfe};
+
+  // algorithm expect all input to be in little endian format, so reverse
+  std::reverse(std::begin(k), std::end(k));
+  std::reverse(std::begin(m), std::end(m));
+  std::reverse(std::begin(aes_cmac_k_m), std::end(aes_cmac_k_m));
+
+  Octet16 output = aes_cmac(k, m, sizeof(m));
+
+  EXPECT_EQ(output, aes_cmac_k_m);
+}
+
+// BT Spec 5.0 | Vol 3, Part H D.2
+TEST(CryptoToolboxTest, bt_spec_example_d_2_test) {
+  std::vector<uint8_t> u{0x20, 0xb0, 0x03, 0xd2, 0xf2, 0x97, 0xbe, 0x2c, 0x5e, 0x2c, 0x83,
+                         0xa7, 0xe9, 0xf9, 0xa5, 0xb9, 0xef, 0xf4, 0x91, 0x11, 0xac, 0xf4,
+                         0xfd, 0xdb, 0xcc, 0x03, 0x01, 0x48, 0x0e, 0x35, 0x9d, 0xe6};
+  std::vector<uint8_t> v{0x55, 0x18, 0x8b, 0x3d, 0x32, 0xf6, 0xbb, 0x9a, 0x90, 0x0a, 0xfc,
+                         0xfb, 0xee, 0xd4, 0xe7, 0x2a, 0x59, 0xcb, 0x9a, 0xc2, 0xf1, 0x9d,
+                         0x7c, 0xfb, 0x6b, 0x4f, 0xdd, 0x49, 0xf4, 0x7f, 0xc5, 0xfd};
+  Octet16 x{0xd5, 0xcb, 0x84, 0x54, 0xd1, 0x77, 0x73, 0x3e, 0xff, 0xff, 0xb2, 0xec, 0x71, 0x2b, 0xae, 0xab};
+  uint8_t z = 0x00;
+
+  Octet16 aes_cmac_k_m{0xf2, 0xc9, 0x16, 0xf1, 0x07, 0xa9, 0xbd, 0x1c, 0xf1, 0xed, 0xa1, 0xbe, 0xa9, 0x74, 0x87, 0x2d};
+
+  // algorithm expect all input to be in little endian format, so reverse
+  std::reverse(std::begin(u), std::end(u));
+  std::reverse(std::begin(v), std::end(v));
+  std::reverse(std::begin(x), std::end(x));
+  std::reverse(std::begin(aes_cmac_k_m), std::end(aes_cmac_k_m));
+
+  Octet16 output = f4(u.data(), v.data(), x, z);
+
+  EXPECT_EQ(output, aes_cmac_k_m);
+}
+
+// BT Spec 5.0 | Vol 3, Part H D.3
+TEST(CryptoToolboxTest, bt_spec_example_d_3_test) {
+  std::array<uint8_t, 32> dhkey_w{0xec, 0x02, 0x34, 0xa3, 0x57, 0xc8, 0xad, 0x05, 0x34, 0x10, 0x10,
+                                  0xa6, 0x0a, 0x39, 0x7d, 0x9b, 0x99, 0x79, 0x6b, 0x13, 0xb4, 0xf8,
+                                  0x66, 0xf1, 0x86, 0x8d, 0x34, 0xf3, 0x73, 0xbf, 0xa6, 0x98};
+  Octet16 n1{0xd5, 0xcb, 0x84, 0x54, 0xd1, 0x77, 0x73, 0x3e, 0xff, 0xff, 0xb2, 0xec, 0x71, 0x2b, 0xae, 0xab};
+  Octet16 n2{0xa6, 0xe8, 0xe7, 0xcc, 0x25, 0xa7, 0x5f, 0x6e, 0x21, 0x65, 0x83, 0xf7, 0xff, 0x3d, 0xc4, 0xcf};
+  std::array<uint8_t, 7> a1{0x00, 0x56, 0x12, 0x37, 0x37, 0xbf, 0xce};
+  std::array<uint8_t, 7> a2{0x00, 0xa7, 0x13, 0x70, 0x2d, 0xcf, 0xc1};
+
+  Octet16 expected_ltk{0x69, 0x86, 0x79, 0x11, 0x69, 0xd7, 0xcd, 0x23, 0x98, 0x05, 0x22, 0xb5, 0x94, 0x75, 0x0a, 0x38};
+  Octet16 expected_mac_key{0x29, 0x65, 0xf1, 0x76, 0xa1, 0x08, 0x4a, 0x02,
+                           0xfd, 0x3f, 0x6a, 0x20, 0xce, 0x63, 0x6e, 0x20};
+
+  // algorithm expect all input to be in little endian format, so reverse
+  std::reverse(std::begin(dhkey_w), std::end(dhkey_w));
+  std::reverse(std::begin(n1), std::end(n1));
+  std::reverse(std::begin(n2), std::end(n2));
+  std::reverse(std::begin(a1), std::end(a1));
+  std::reverse(std::begin(a2), std::end(a2));
+  std::reverse(std::begin(expected_ltk), std::end(expected_ltk));
+  std::reverse(std::begin(expected_mac_key), std::end(expected_mac_key));
+
+  Octet16 mac_key, ltk;
+  f5(dhkey_w.data(), n1, n2, a1.data(), a2.data(), &mac_key, &ltk);
+
+  EXPECT_EQ(mac_key, expected_mac_key);
+  EXPECT_EQ(ltk, expected_ltk);
+}
+
+// BT Spec 5.0 | Vol 3, Part H D.4
+TEST(CryptoToolboxTest, bt_spec_example_d_4_test) {
+  Octet16 n1{0xd5, 0xcb, 0x84, 0x54, 0xd1, 0x77, 0x73, 0x3e, 0xff, 0xff, 0xb2, 0xec, 0x71, 0x2b, 0xae, 0xab};
+  Octet16 n2{0xa6, 0xe8, 0xe7, 0xcc, 0x25, 0xa7, 0x5f, 0x6e, 0x21, 0x65, 0x83, 0xf7, 0xff, 0x3d, 0xc4, 0xcf};
+  Octet16 r{0x12, 0xa3, 0x34, 0x3b, 0xb4, 0x53, 0xbb, 0x54, 0x08, 0xda, 0x42, 0xd2, 0x0c, 0x2d, 0x0f, 0xc8};
+  std::vector<uint8_t> IOcap{0x01, 0x01, 0x02};
+  std::vector<uint8_t> a1{0x00, 0x56, 0x12, 0x37, 0x37, 0xbf, 0xce};
+  std::vector<uint8_t> a2{0x00, 0xa7, 0x13, 0x70, 0x2d, 0xcf, 0xc1};
+
+  Octet16 MacKey{0x29, 0x65, 0xf1, 0x76, 0xa1, 0x08, 0x4a, 0x02, 0xfd, 0x3f, 0x6a, 0x20, 0xce, 0x63, 0x6e, 0x20};
+
+  Octet16 expected_aes_cmac{0xe3, 0xc4, 0x73, 0x98, 0x9c, 0xd0, 0xe8, 0xc5,
+                            0xd2, 0x6c, 0x0b, 0x09, 0xda, 0x95, 0x8f, 0x61};
+
+  // algorithm expect all input to be in little endian format, so reverse
+  std::reverse(std::begin(n1), std::end(n1));
+  std::reverse(std::begin(n2), std::end(n2));
+  std::reverse(std::begin(r), std::end(r));
+  std::reverse(std::begin(IOcap), std::end(IOcap));
+  std::reverse(std::begin(a1), std::end(a1));
+  std::reverse(std::begin(a2), std::end(a2));
+  std::reverse(std::begin(MacKey), std::end(MacKey));
+  std::reverse(std::begin(expected_aes_cmac), std::end(expected_aes_cmac));
+
+  Octet16 aes_cmac = f6(MacKey, n1, n2, r, IOcap.data(), a1.data(), a2.data());
+
+  EXPECT_EQ(aes_cmac, expected_aes_cmac);
+}
+
+// BT Spec 5.0 | Vol 3, Part H D.5
+TEST(CryptoToolboxTest, bt_spec_example_d_5_test) {
+  std::array<uint8_t, 32> u{0x20, 0xb0, 0x03, 0xd2, 0xf2, 0x97, 0xbe, 0x2c, 0x5e, 0x2c, 0x83,
+                            0xa7, 0xe9, 0xf9, 0xa5, 0xb9, 0xef, 0xf4, 0x91, 0x11, 0xac, 0xf4,
+                            0xfd, 0xdb, 0xcc, 0x03, 0x01, 0x48, 0x0e, 0x35, 0x9d, 0xe6};
+  std::array<uint8_t, 32> v{0x55, 0x18, 0x8b, 0x3d, 0x32, 0xf6, 0xbb, 0x9a, 0x90, 0x0a, 0xfc,
+                            0xfb, 0xee, 0xd4, 0xe7, 0x2a, 0x59, 0xcb, 0x9a, 0xc2, 0xf1, 0x9d,
+                            0x7c, 0xfb, 0x6b, 0x4f, 0xdd, 0x49, 0xf4, 0x7f, 0xc5, 0xfd};
+
+  Octet16 x{0xd5, 0xcb, 0x84, 0x54, 0xd1, 0x77, 0x73, 0x3e, 0xff, 0xff, 0xb2, 0xec, 0x71, 0x2b, 0xae, 0xab};
+  Octet16 y{0xa6, 0xe8, 0xe7, 0xcc, 0x25, 0xa7, 0x5f, 0x6e, 0x21, 0x65, 0x83, 0xf7, 0xff, 0x3d, 0xc4, 0xcf};
+
+  // algorithm expect all input to be in little endian format, so reverse
+  std::reverse(std::begin(u), std::end(u));
+  std::reverse(std::begin(v), std::end(v));
+  std::reverse(std::begin(x), std::end(x));
+  std::reverse(std::begin(y), std::end(y));
+
+  uint32_t val = g2(u.data(), v.data(), x, y);
+
+  /* the returned value is already mod 1000000, so do mod on the test result
+   * value too */
+  EXPECT_EQ(val, 0x2f9ed5baU % 1000000);
+}
+
+// BT Spec 5.0 | Vol 3, Part H D.6
+TEST(CryptoToolboxTest, bt_spec_example_d_6_test) {
+  Octet16 key{0xec, 0x02, 0x34, 0xa3, 0x57, 0xc8, 0xad, 0x05, 0x34, 0x10, 0x10, 0xa6, 0x0a, 0x39, 0x7d, 0x9b};
+  std::array<uint8_t, 4> keyID{0x6c, 0x65, 0x62, 0x72};
+  Octet16 expected_aes_cmac{0x2d, 0x9a, 0xe1, 0x02, 0xe7, 0x6d, 0xc9, 0x1c,
+                            0xe8, 0xd3, 0xa9, 0xe2, 0x80, 0xb1, 0x63, 0x99};
+
+  // algorithm expect all input to be in little endian format, so reverse
+  std::reverse(std::begin(key), std::end(key));
+  std::reverse(std::begin(keyID), std::end(keyID));
+  std::reverse(std::begin(expected_aes_cmac), std::end(expected_aes_cmac));
+
+  Octet16 aes_cmac = h6(key, keyID);
+  EXPECT_EQ(aes_cmac, expected_aes_cmac);
+}
+
+// BT Spec 5.0 | Vol 3, Part H D.7
+TEST(CryptoToolboxTest, bt_spec_example_d_7_test) {
+  Octet16 IRK{0xec, 0x02, 0x34, 0xa3, 0x57, 0xc8, 0xad, 0x05, 0x34, 0x10, 0x10, 0xa6, 0x0a, 0x39, 0x7d, 0x9b};
+  Octet16 prand{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x81, 0x94};
+  Octet16 expected_aes_128{0x15, 0x9d, 0x5f, 0xb7, 0x2e, 0xbe, 0x23, 0x11,
+                           0xa4, 0x8c, 0x1b, 0xdc, 0xc4, 0x0d, 0xfb, 0xaa};
+  std::array<uint8_t, 3> expected_ah{0x0d, 0xfb, 0xaa};
+
+  // algorithm expect all input to be in little endian format, so reverse
+  std::reverse(std::begin(IRK), std::end(IRK));
+  std::reverse(std::begin(prand), std::end(prand));
+  std::reverse(std::begin(expected_aes_128), std::end(expected_aes_128));
+  std::reverse(std::begin(expected_ah), std::end(expected_ah));
+
+  Octet16 result = aes_128(IRK, prand.data(), 3);
+  EXPECT_EQ(expected_aes_128, result);
+
+  // little/big endian 24 bits
+  EXPECT_EQ(result[0], expected_ah[0]);
+  EXPECT_EQ(result[1], expected_ah[1]);
+  EXPECT_EQ(result[2], expected_ah[2]);
+}
+
+// BT Spec 5.0 | Vol 3, Part H D.8
+TEST(CryptoToolboxTest, bt_spec_example_d_8_test) {
+  Octet16 Key{0xec, 0x02, 0x34, 0xa3, 0x57, 0xc8, 0xad, 0x05, 0x34, 0x10, 0x10, 0xa6, 0x0a, 0x39, 0x7d, 0x9b};
+  Octet16 SALT{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x6D, 0x70, 0x31};
+  Octet16 expected_aes_cmac{0xfb, 0x17, 0x35, 0x97, 0xc6, 0xa3, 0xc0, 0xec,
+                            0xd2, 0x99, 0x8c, 0x2a, 0x75, 0xa5, 0x70, 0x11};
+
+  // algorithm expect all input to be in little endian format, so reverse
+  std::reverse(std::begin(Key), std::end(Key));
+  std::reverse(std::begin(SALT), std::end(SALT));
+  std::reverse(std::begin(expected_aes_cmac), std::end(expected_aes_cmac));
+
+  Octet16 aes_cmac = h7(SALT, Key);
+  EXPECT_EQ(expected_aes_cmac, aes_cmac);
+}
+
+extern Octet16 smp_calculate_ltk_to_link_key(const Octet16& ltk, bool use_h7);
+
+// BT Spec 5.0 | Vol 3, Part H D.9
+TEST(CryptoToolboxTest, bt_spec_example_d_9_test) {
+  Octet16 LTK{0x36, 0x8d, 0xf9, 0xbc, 0xe3, 0x26, 0x4b, 0x58, 0xbd, 0x06, 0x6c, 0x33, 0x33, 0x4f, 0xbf, 0x64};
+  Octet16 expected_link_key{0x28, 0x7a, 0xd3, 0x79, 0xdc, 0xa4, 0x02, 0x53,
+                            0x0a, 0x39, 0xf1, 0xf4, 0x30, 0x47, 0xb8, 0x35};
+
+  // algorithm expect all input to be in little endian format, so reverse
+  std::reverse(std::begin(LTK), std::end(LTK));
+  std::reverse(std::begin(expected_link_key), std::end(expected_link_key));
+
+  Octet16 link_key = ltk_to_link_key(LTK, true);
+  EXPECT_EQ(expected_link_key, link_key);
+}
+
+// BT Spec 5.0 | Vol 3, Part H D.10
+TEST(CryptoToolboxTest, bt_spec_example_d_10_test) {
+  Octet16 LTK{0x36, 0x8d, 0xf9, 0xbc, 0xe3, 0x26, 0x4b, 0x58, 0xbd, 0x06, 0x6c, 0x33, 0x33, 0x4f, 0xbf, 0x64};
+  Octet16 expected_link_key{0xbc, 0x1c, 0xa4, 0xef, 0x63, 0x3f, 0xc1, 0xbd,
+                            0x0d, 0x82, 0x30, 0xaf, 0xee, 0x38, 0x8f, 0xb0};
+
+  // algorithm expect all input to be in little endian format, so reverse
+  std::reverse(std::begin(LTK), std::end(LTK));
+  std::reverse(std::begin(expected_link_key), std::end(expected_link_key));
+
+  Octet16 link_key = ltk_to_link_key(LTK, false);
+  EXPECT_EQ(expected_link_key, link_key);
+}
+
+// // BT Spec 5.0 | Vol 3, Part H D.11
+TEST(CryptoToolboxTest, bt_spec_example_d_11_test) {
+  Octet16 link_key{0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00};
+  Octet16 expected_ltk{0xe8, 0x5e, 0x09, 0xeb, 0x5e, 0xcc, 0xb3, 0xe2, 0x69, 0x41, 0x8a, 0x13, 0x32, 0x11, 0xbc, 0x79};
+
+  // algorithm expect all input to be in little endian format, so reverse
+  std::reverse(std::begin(link_key), std::end(link_key));
+  std::reverse(std::begin(expected_ltk), std::end(expected_ltk));
+
+  Octet16 ltk = link_key_to_ltk(link_key, true);
+  EXPECT_EQ(expected_ltk, ltk);
+}
+
+// BT Spec 5.0 | Vol 3, Part H D.12
+TEST(CryptoToolboxTest, bt_spec_example_d_12_test) {
+  Octet16 link_key{0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00};
+  Octet16 expected_ltk{0xa8, 0x13, 0xfb, 0x72, 0xf1, 0xa3, 0xdf, 0xa1, 0x8a, 0x2c, 0x9a, 0x43, 0xf1, 0x0d, 0x0a, 0x30};
+
+  // algorithm expect all input to be in little endian format, so reverse
+  std::reverse(std::begin(link_key), std::end(link_key));
+  std::reverse(std::begin(expected_ltk), std::end(expected_ltk));
+
+  Octet16 ltk = link_key_to_ltk(link_key, false);
+  EXPECT_EQ(expected_ltk, ltk);
+}
+
+}  // namespace crypto_toolbox
+}  // namespace bluetooth
diff --git a/gd/facade/common.proto b/gd/facade/common.proto
new file mode 100644
index 0000000..b1176a4
--- /dev/null
+++ b/gd/facade/common.proto
@@ -0,0 +1,24 @@
+syntax = "proto3";
+
+package bluetooth.facade;
+
+message BluetoothAddress {
+  bytes address = 1;
+}
+
+enum BluetoothAddressTypeEnum {
+  PUBLIC_DEVICE_ADDRESS = 0x0;
+  RANDOM_DEVICE_ADDRESS = 0x1;
+  PUBLIC_IDENTITY_ADDRESS = 0x2;
+  RANDOM_IDENTITY_ADDRESS = 0x3;
+}
+
+message BluetoothAddressWithType {
+  BluetoothAddress address = 1;
+  BluetoothAddressTypeEnum type = 2;
+}
+
+enum BluetoothPeerAddressTypeEnum {
+  PUBLIC_DEVICE_OR_IDENTITY_ADDRESS = 0x0;
+  RANDOM_DEVICE_OR_IDENTITY_ADDRESS = 0x1;
+}
diff --git a/gd/facade/facade_main.cc b/gd/facade/facade_main.cc
new file mode 100644
index 0000000..8ff1e72
--- /dev/null
+++ b/gd/facade/facade_main.cc
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "stack_manager.h"
+
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <csignal>
+#include <cstring>
+#include <string>
+#include <thread>
+
+#include "facade/grpc_root_server.h"
+#include "grpc/grpc_module.h"
+#include "hal/hci_hal.h"
+#include "hal/hci_hal_host_rootcanal.h"
+#include "hal/snoop_logger.h"
+
+using ::bluetooth::hal::HciHalHostRootcanalConfig;
+using ::bluetooth::StackManager;
+using ::bluetooth::grpc::GrpcModule;
+using ::bluetooth::ModuleList;
+using ::bluetooth::os::Thread;
+
+namespace {
+::bluetooth::facade::GrpcRootServer grpc_root_server;
+
+void interrupt_handler(int) {
+  grpc_root_server.StopServer();
+}
+}  // namespace
+
+// The entry point for the binary with libbluetooth + facades
+int main(int argc, const char** argv) {
+  int root_server_port = 8897;
+  int grpc_port = 8899;
+  int signal_port = 8895;
+
+  const std::string arg_grpc_root_server_port = "--root-server-port=";
+  const std::string arg_grpc_server_port = "--grpc-port=";
+  const std::string arg_rootcanal_port = "--rootcanal-port=";
+  const std::string arg_signal_port = "--signal-port=";
+  const std::string arg_btsnoop_path = "--btsnoop=";
+  std::string btsnoop_path;
+  for (int i = 1; i < argc; i++) {
+    std::string arg = argv[i];
+    if (arg.find(arg_grpc_root_server_port) == 0) {
+      auto port_number = arg.substr(arg_grpc_root_server_port.size());
+      root_server_port = std::stoi(port_number);
+    }
+    if (arg.find(arg_grpc_server_port) == 0) {
+      auto port_number = arg.substr(arg_grpc_server_port.size());
+      grpc_port = std::stoi(port_number);
+    }
+    if (arg.find(arg_rootcanal_port) == 0) {
+      auto port_number = arg.substr(arg_rootcanal_port.size());
+      HciHalHostRootcanalConfig::Get()->SetPort(std::stoi(port_number));
+    }
+    if (arg.find(arg_btsnoop_path) == 0) {
+      btsnoop_path = arg.substr(arg_btsnoop_path.size());
+      ::bluetooth::hal::SnoopLogger::SetFilePath(btsnoop_path);
+    }
+    if (arg.find(arg_signal_port) == 0) {
+      auto port_number = arg.substr(arg_signal_port.size());
+      signal_port = std::stoi(port_number);
+    }
+  }
+
+  signal(SIGINT, interrupt_handler);
+  grpc_root_server.StartServer("0.0.0.0", root_server_port, grpc_port);
+  int tester_signal_socket = socket(AF_INET, SOCK_STREAM, 0);
+  struct sockaddr_in addr;
+  memset(&addr, 0, sizeof(addr));
+  addr.sin_family = AF_INET;
+  addr.sin_port = htons(signal_port);
+  addr.sin_addr.s_addr = htonl(INADDR_ANY);
+  connect(tester_signal_socket, (sockaddr*)&addr, sizeof(addr));
+  close(tester_signal_socket);
+  auto wait_thread = std::thread([] { grpc_root_server.RunGrpcLoop(); });
+  wait_thread.join();
+
+  return 0;
+}
diff --git a/gd/facade/grpc_root_server.cc b/gd/facade/grpc_root_server.cc
new file mode 100644
index 0000000..6a8e5de
--- /dev/null
+++ b/gd/facade/grpc_root_server.cc
@@ -0,0 +1,192 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "facade/grpc_root_server.h"
+
+#include <string>
+
+#include "facade/read_only_property_server.h"
+#include "facade/rootservice.grpc.pb.h"
+#include "grpc/grpc_module.h"
+#include "hal/facade.h"
+#include "hci/facade/acl_manager_facade.h"
+#include "hci/facade/controller_facade.h"
+#include "hci/facade/facade.h"
+#include "hci/facade/le_acl_manager_facade.h"
+#include "hci/facade/le_advertising_manager_facade.h"
+#include "hci/facade/le_scanning_manager_facade.h"
+#include "hci/hci_layer.h"
+#include "hci/le_advertising_manager.h"
+#include "hci/le_scanning_manager.h"
+#include "l2cap/classic/facade.h"
+#include "neighbor/connectability.h"
+#include "neighbor/discoverability.h"
+#include "neighbor/facade/facade.h"
+#include "neighbor/inquiry.h"
+#include "neighbor/name.h"
+#include "neighbor/page.h"
+#include "os/log.h"
+#include "os/thread.h"
+#include "security/facade.h"
+#include "security/security_module.h"
+#include "shim/dumpsys.h"
+#include "shim/l2cap.h"
+#include "stack_manager.h"
+#include "storage/legacy.h"
+
+namespace bluetooth {
+namespace facade {
+
+using ::bluetooth::grpc::GrpcModule;
+using ::bluetooth::os::Thread;
+
+namespace {
+class RootFacadeService : public ::bluetooth::facade::RootFacade::Service {
+ public:
+  RootFacadeService(int grpc_port) : grpc_port_(grpc_port) {}
+
+  ::grpc::Status StartStack(::grpc::ServerContext* context, const ::bluetooth::facade::StartStackRequest* request,
+                            ::bluetooth::facade::StartStackResponse* response) override {
+    if (is_running_) {
+      return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "stack is running");
+    }
+
+    ModuleList modules;
+    modules.add<::bluetooth::grpc::GrpcModule>();
+
+    BluetoothModule module_under_test = request->module_under_test();
+    switch (module_under_test) {
+      case BluetoothModule::HAL:
+        modules.add<::bluetooth::hal::HciHalFacadeModule>();
+        break;
+      case BluetoothModule::HCI:
+        modules.add<::bluetooth::facade::ReadOnlyPropertyServerModule>();
+        modules.add<::bluetooth::hci::facade::HciLayerFacadeModule>();
+        break;
+      case BluetoothModule::HCI_INTERFACES:
+        modules.add<::bluetooth::facade::ReadOnlyPropertyServerModule>();
+        modules.add<::bluetooth::hci::facade::HciLayerFacadeModule>();
+        modules.add<::bluetooth::hci::facade::AclManagerFacadeModule>();
+        modules.add<::bluetooth::hci::facade::ControllerFacadeModule>();
+        modules.add<::bluetooth::hci::facade::LeAclManagerFacadeModule>();
+        modules.add<::bluetooth::hci::facade::LeAdvertisingManagerFacadeModule>();
+        modules.add<::bluetooth::hci::facade::LeScanningManagerFacadeModule>();
+        modules.add<::bluetooth::neighbor::facade::NeighborFacadeModule>();
+        break;
+      case BluetoothModule::L2CAP:
+        modules.add<::bluetooth::hci::facade::ControllerFacadeModule>();
+        modules.add<::bluetooth::neighbor::facade::NeighborFacadeModule>();
+        modules.add<::bluetooth::facade::ReadOnlyPropertyServerModule>();
+        modules.add<::bluetooth::l2cap::classic::L2capClassicModuleFacadeModule>();
+        modules.add<::bluetooth::hci::facade::HciLayerFacadeModule>();
+        break;
+      case BluetoothModule::SECURITY:
+        modules.add<::bluetooth::facade::ReadOnlyPropertyServerModule>();
+        modules.add<::bluetooth::hci::facade::ControllerFacadeModule>();
+        modules.add<::bluetooth::security::SecurityModuleFacadeModule>();
+        modules.add<::bluetooth::neighbor::facade::NeighborFacadeModule>();
+        modules.add<::bluetooth::l2cap::classic::L2capClassicModuleFacadeModule>();
+        modules.add<::bluetooth::hci::facade::HciLayerFacadeModule>();
+        modules.add<::bluetooth::hci::facade::ControllerFacadeModule>();
+        modules.add<::bluetooth::hci::facade::LeAdvertisingManagerFacadeModule>();
+        modules.add<::bluetooth::hci::facade::LeScanningManagerFacadeModule>();
+        break;
+      case BluetoothModule::SHIM:
+        modules.add<::bluetooth::neighbor::ConnectabilityModule>();
+        modules.add<::bluetooth::neighbor::DiscoverabilityModule>();
+        modules.add<::bluetooth::neighbor::InquiryModule>();
+        modules.add<::bluetooth::neighbor::NameModule>();
+        modules.add<::bluetooth::shim::Dumpsys>();
+        modules.add<::bluetooth::shim::L2cap>();
+        modules.add<::bluetooth::neighbor::PageModule>();
+        modules.add<::bluetooth::hci::HciLayer>();
+        modules.add<::bluetooth::hci::LeAdvertisingManager>();
+        modules.add<::bluetooth::hci::LeScanningManager>();
+        modules.add<::bluetooth::security::SecurityModule>();
+        modules.add<::bluetooth::storage::LegacyModule>();
+        break;
+      default:
+        return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "invalid module under test");
+    }
+
+    stack_thread_ = new Thread("stack_thread", Thread::Priority::NORMAL);
+    stack_manager_.StartUp(&modules, stack_thread_);
+
+    GrpcModule* grpc_module = stack_manager_.GetInstance<GrpcModule>();
+    grpc_module->StartServer("0.0.0.0", grpc_port_);
+
+    grpc_loop_thread_ = new std::thread([grpc_module] { grpc_module->RunGrpcLoop(); });
+    is_running_ = true;
+
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status StopStack(::grpc::ServerContext* context, const ::bluetooth::facade::StopStackRequest* request,
+                           ::bluetooth::facade::StopStackResponse* response) override {
+    if (!is_running_) {
+      return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "stack is not running");
+    }
+
+    stack_manager_.GetInstance<GrpcModule>()->StopServer();
+    grpc_loop_thread_->join();
+    delete grpc_loop_thread_;
+
+    stack_manager_.ShutDown();
+    delete stack_thread_;
+    is_running_ = false;
+    return ::grpc::Status::OK;
+  }
+
+ private:
+  Thread* stack_thread_ = nullptr;
+  bool is_running_ = false;
+  std::thread* grpc_loop_thread_ = nullptr;
+  StackManager stack_manager_;
+  int grpc_port_ = 8898;
+};
+
+RootFacadeService* root_facade_service;
+}  // namespace
+
+void GrpcRootServer::StartServer(const std::string& address, int grpc_root_server_port, int grpc_port) {
+  ASSERT(!started_);
+  started_ = true;
+
+  std::string listening_port = address + ":" + std::to_string(grpc_root_server_port);
+  ::grpc::ServerBuilder builder;
+  root_facade_service = new RootFacadeService(grpc_port);
+  builder.RegisterService(root_facade_service);
+  builder.AddListeningPort(listening_port, ::grpc::InsecureServerCredentials());
+  server_ = builder.BuildAndStart();
+
+  ASSERT(server_ != nullptr);
+}
+
+void GrpcRootServer::StopServer() {
+  ASSERT(started_);
+  server_->Shutdown();
+  started_ = false;
+  server_.reset();
+  delete root_facade_service;
+}
+
+void GrpcRootServer::RunGrpcLoop() {
+  ASSERT(started_);
+  server_->Wait();
+}
+
+}  // namespace facade
+}  // namespace bluetooth
diff --git a/gd/facade/grpc_root_server.h b/gd/facade/grpc_root_server.h
new file mode 100644
index 0000000..9deba35
--- /dev/null
+++ b/gd/facade/grpc_root_server.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <memory>
+#include <string>
+
+#include <grpc++/grpc++.h>
+
+namespace bluetooth {
+namespace facade {
+
+class GrpcRootServer {
+ public:
+  void StartServer(const std::string& address, int grpc_root_server_port, int grpc_port);
+
+  void StopServer();
+
+  void RunGrpcLoop();
+
+ private:
+  bool started_ = false;
+  std::unique_ptr<::grpc::Server> server_ = nullptr;
+};
+
+}  // namespace facade
+}  // namespace bluetooth
diff --git a/gd/facade/read_only_property_server.cc b/gd/facade/read_only_property_server.cc
new file mode 100644
index 0000000..3c57b87
--- /dev/null
+++ b/gd/facade/read_only_property_server.cc
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "facade/read_only_property_server.h"
+#include "hci/controller.h"
+
+namespace bluetooth {
+namespace facade {
+
+class ReadOnlyPropertyService : public ReadOnlyProperty::Service {
+ public:
+  ReadOnlyPropertyService(hci::Controller* controller) : controller_(controller) {}
+  ::grpc::Status ReadLocalAddress(::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
+                                  ::bluetooth::facade::BluetoothAddress* response) override {
+    auto address = controller_->GetControllerMacAddress().ToString();
+    response->set_address(address);
+    return ::grpc::Status::OK;
+  }
+
+ private:
+  hci::Controller* controller_;
+};
+
+void ReadOnlyPropertyServerModule::ListDependencies(ModuleList* list) {
+  GrpcFacadeModule::ListDependencies(list);
+  list->add<hci::Controller>();
+}
+void ReadOnlyPropertyServerModule::Start() {
+  GrpcFacadeModule::Start();
+  service_ = std::make_unique<ReadOnlyPropertyService>(GetDependency<hci::Controller>());
+}
+void ReadOnlyPropertyServerModule::Stop() {
+  service_.reset();
+  GrpcFacadeModule::Stop();
+}
+::grpc::Service* ReadOnlyPropertyServerModule::GetService() const {
+  return service_.get();
+}
+
+const ModuleFactory ReadOnlyPropertyServerModule::Factory =
+    ::bluetooth::ModuleFactory([]() { return new ReadOnlyPropertyServerModule(); });
+
+}  // namespace facade
+}  // namespace bluetooth
diff --git a/gd/facade/read_only_property_server.h b/gd/facade/read_only_property_server.h
new file mode 100644
index 0000000..dfac424
--- /dev/null
+++ b/gd/facade/read_only_property_server.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include <grpc++/grpc++.h>
+
+#include "facade/rootservice.grpc.pb.h"
+#include "grpc/grpc_module.h"
+
+namespace bluetooth {
+namespace facade {
+
+class ReadOnlyPropertyService;
+
+class ReadOnlyPropertyServerModule : public ::bluetooth::grpc::GrpcFacadeModule {
+ public:
+  static const ModuleFactory Factory;
+
+  void ListDependencies(ModuleList* list) override;
+  void Start() override;
+  void Stop() override;
+  ::grpc::Service* GetService() const override;
+
+ private:
+  std::unique_ptr<ReadOnlyPropertyService> service_;
+};
+
+}  // namespace facade
+}  // namespace bluetooth
diff --git a/gd/facade/rootservice.proto b/gd/facade/rootservice.proto
new file mode 100644
index 0000000..6f2efcb
--- /dev/null
+++ b/gd/facade/rootservice.proto
@@ -0,0 +1,34 @@
+syntax = "proto3";
+
+package bluetooth.facade;
+
+import "google/protobuf/empty.proto";
+import "facade/common.proto";
+
+service RootFacade {
+  rpc StartStack(StartStackRequest) returns (StartStackResponse) {}
+  rpc StopStack(StopStackRequest) returns (StopStackResponse) {}
+}
+
+enum BluetoothModule {
+  HAL = 0;
+  HCI = 1;
+  HCI_INTERFACES = 2;
+  L2CAP = 3;
+  SECURITY = 4;
+  SHIM = 5;
+}
+
+message StartStackRequest {
+  BluetoothModule module_under_test = 1;
+}
+
+message StartStackResponse {}
+
+message StopStackRequest {}
+
+message StopStackResponse {}
+
+service ReadOnlyProperty {
+  rpc ReadLocalAddress(google.protobuf.Empty) returns (facade.BluetoothAddress) {}
+}
diff --git a/gd/fuzz_test.cc b/gd/fuzz_test.cc
new file mode 100644
index 0000000..ef358b5
--- /dev/null
+++ b/gd/fuzz_test.cc
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stddef.h>
+#include <stdint.h>
+
+extern void RunL2capClassicDynamicChannelAllocatorFuzzTest(const uint8_t* data, size_t size);
+extern void RunL2capPacketFuzzTest(const uint8_t* data, size_t size);
+extern void RunHciPacketFuzzTest(const uint8_t* data, size_t size);
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  RunL2capClassicDynamicChannelAllocatorFuzzTest(data, size);
+  RunL2capPacketFuzzTest(data, size);
+  RunHciPacketFuzzTest(data, size);
+  return 0;
+}
\ No newline at end of file
diff --git a/gd/grpc/grpc_event_queue.h b/gd/grpc/grpc_event_queue.h
new file mode 100644
index 0000000..3871679
--- /dev/null
+++ b/gd/grpc/grpc_event_queue.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <grpc++/grpc++.h>
+
+#include <atomic>
+#include <chrono>
+#include <utility>
+
+#include "common/blocking_queue.h"
+#include "facade/common.pb.h"
+#include "os/log.h"
+
+namespace bluetooth {
+namespace grpc {
+
+template <typename T>
+class GrpcEventQueue {
+ public:
+  /**
+   * Create a GrpcEventQueue that can be used to shuffle event from one thread to another
+   * @param log_name
+   */
+  explicit GrpcEventQueue(std::string log_name) : log_name_(std::move(log_name)){};
+
+  /**
+   * Run the event loop and blocks until client cancels the stream request
+   * Event queue will be cleared before entering the loop. Hence, only events occurred after gRPC request will be
+   * delivered to the user. Hence user is advised to run the loop before generating pending events.
+   *
+   * @param context client context
+   * @param writer output writer
+   * @return gRPC status
+   */
+  ::grpc::Status RunLoop(::grpc::ServerContext* context, ::grpc::ServerWriter<T>* writer) {
+    using namespace std::chrono_literals;
+    LOG_INFO("%s: Entering Loop", log_name_.c_str());
+    pending_events_.clear();
+    running_ = true;
+    while (!context->IsCancelled()) {
+      // Wait for 500 ms so that cancellation can be caught in amortized 250 ms latency
+      if (pending_events_.wait_to_take(500ms)) {
+        LOG_DEBUG("%s: Got event after queue", log_name_.c_str());
+        writer->Write(pending_events_.take());
+      }
+    }
+    running_ = false;
+    LOG_INFO("%s: Exited Loop", log_name_.c_str());
+    return ::grpc::Status::OK;
+  }
+
+  /**
+   * Called when there is an incoming event
+   * @param event incoming event
+   */
+  void OnIncomingEvent(T event) {
+    if (!running_) {
+      LOG_DEBUG("%s: Discarding an event while not running the loop", log_name_.c_str());
+      return;
+    }
+    LOG_DEBUG("%s: Got event before queue", log_name_.c_str());
+    pending_events_.push(std::move(event));
+  }
+
+ private:
+  std::string log_name_;
+  std::atomic<bool> running_ = false;
+  common::BlockingQueue<T> pending_events_;
+};
+
+}  // namespace grpc
+}  // namespace bluetooth
diff --git a/gd/grpc/grpc_module.cc b/gd/grpc/grpc_module.cc
new file mode 100644
index 0000000..bab8899
--- /dev/null
+++ b/gd/grpc/grpc_module.cc
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "grpc/grpc_module.h"
+
+#include "os/log.h"
+
+using ::grpc::Server;
+using ::grpc::ServerBuilder;
+
+namespace bluetooth {
+namespace grpc {
+
+void GrpcModule::ListDependencies(ModuleList* list) {
+}
+
+void GrpcModule::Start() {
+  ASSERT(!started_);
+}
+
+void GrpcModule::Stop() {
+  ASSERT(!started_);
+}
+
+void GrpcModule::StartServer(const std::string& address, int port) {
+  ASSERT(!started_);
+  started_ = true;
+
+  std::string listening_port = address + ":" + std::to_string(port);
+  ServerBuilder builder;
+
+  for (const auto& facade : facades_) {
+    builder.RegisterService(facade->GetService());
+  }
+
+  builder.AddListeningPort(listening_port, ::grpc::InsecureServerCredentials());
+  completion_queue_ = builder.AddCompletionQueue();
+  server_ = builder.BuildAndStart();
+  ASSERT(server_ != nullptr);
+
+  for (const auto& facade : facades_) {
+    facade->OnServerStarted();
+  }
+}
+
+void GrpcModule::StopServer() {
+  ASSERT(started_);
+
+  server_->Shutdown();
+  completion_queue_->Shutdown();
+
+  for (const auto& facade : facades_) {
+    facade->OnServerStopped();
+  }
+
+  started_ = false;
+}
+
+void GrpcModule::Register(GrpcFacadeModule* facade) {
+  ASSERT(!started_);
+
+  facades_.push_back(facade);
+}
+
+void GrpcModule::Unregister(GrpcFacadeModule* facade) {
+  ASSERT(!started_);
+
+  for (auto it = facades_.begin(); it != facades_.end(); it++) {
+    if (*it == facade) {
+      facades_.erase(it);
+      return;
+    }
+  }
+
+  ASSERT(false);
+}
+
+void GrpcModule::RunGrpcLoop() {
+  void* tag;
+  bool ok;
+  while (true) {
+    if (!completion_queue_->Next(&tag, &ok)) {
+      LOG_INFO("gRPC is shutdown");
+      break;
+    }
+  }
+}
+
+std::string GrpcModule::ToString() const {
+  return "Grpc Module";
+}
+
+const ::bluetooth::ModuleFactory GrpcModule::Factory = ::bluetooth::ModuleFactory([]() {
+  return new GrpcModule();
+});
+
+
+void GrpcFacadeModule::ListDependencies(ModuleList* list) {
+  list->add<GrpcModule>();
+}
+
+void GrpcFacadeModule::Start() {
+  GetDependency<GrpcModule>()->Register(this);
+}
+
+void GrpcFacadeModule::Stop() {
+  GetDependency<GrpcModule>()->Unregister(this);
+}
+
+std::string GrpcFacadeModule::ToString() const {
+  return "Grpc Facade Module";
+}
+
+}  // namespace grpc
+}  // namespace bluetooth
diff --git a/gd/grpc/grpc_module.h b/gd/grpc/grpc_module.h
new file mode 100644
index 0000000..8f50921
--- /dev/null
+++ b/gd/grpc/grpc_module.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <functional>
+#include <vector>
+
+#include <grpc++/grpc++.h>
+#include <module.h>
+
+namespace bluetooth {
+namespace grpc {
+
+class GrpcFacadeModule;
+
+class GrpcModule : public ::bluetooth::Module {
+ public:
+  static const ModuleFactory Factory;
+
+  void StartServer(const std::string& address, int port);
+
+  void StopServer();
+
+  void Register(GrpcFacadeModule* facade);
+
+  void Unregister(GrpcFacadeModule* facade);
+
+  // Blocks for incoming gRPC requests
+  void RunGrpcLoop();
+
+ protected:
+  void ListDependencies(ModuleList* list) override;
+
+  void Start() override;
+
+  void Stop() override;
+
+  std::string ToString() const override;
+
+ private:
+  bool started_;
+  std::unique_ptr<::grpc::Server> server_ = nullptr;
+  std::unique_ptr<::grpc::ServerCompletionQueue> completion_queue_ = nullptr;
+  std::vector<GrpcFacadeModule*> facades_;
+};
+
+class GrpcFacadeModule : public ::bluetooth::Module {
+ friend GrpcModule;
+ protected:
+  void ListDependencies(ModuleList* list) override;
+
+  void Start() override;
+
+  void Stop() override;
+
+  virtual ::grpc::Service* GetService() const = 0;
+
+  virtual void OnServerStarted() {}
+
+  virtual void OnServerStopped() {}
+
+  std::string ToString() const override;
+};
+
+}  // namespace grpc
+}  // namespace bluetooth
diff --git a/gd/hal/Android.bp b/gd/hal/Android.bp
new file mode 100644
index 0000000..41b87fc
--- /dev/null
+++ b/gd/hal/Android.bp
@@ -0,0 +1,41 @@
+filegroup {
+    name: "BluetoothHalSources",
+    srcs: [
+        "snoop_logger.cc",
+    ],
+}
+
+filegroup {
+    name: "BluetoothHalSources_hci_rootcanal",
+    srcs: [
+        "hci_hal_host_rootcanal.cc",
+    ],
+}
+
+filegroup {
+    name: "BluetoothHalSources_hci_android_hidl",
+    srcs: [
+        "hci_hal_android_hidl.cc",
+    ],
+}
+
+filegroup {
+    name: "BluetoothHalTestSources_hci_rootcanal",
+    srcs: [
+        "hci_hal_host_rootcanal_test.cc",
+    ],
+}
+
+filegroup {
+    name: "BluetoothHalTestSources_hci_android_hidl",
+    srcs: [
+        "hci_hal_android_hidl_test.cc",
+    ],
+}
+
+filegroup {
+    name: "BluetoothFacade_hci_hal",
+    srcs: [
+        "facade.cc",
+    ],
+}
diff --git a/gd/hal/cert/simple_hal_test.py b/gd/hal/cert/simple_hal_test.py
new file mode 100644
index 0000000..7094787
--- /dev/null
+++ b/gd/hal/cert/simple_hal_test.py
@@ -0,0 +1,419 @@
+#!/usr/bin/env python3
+#
+#   Copyright 2019 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+from datetime import timedelta
+
+from cert.gd_base_test_facade_only import GdFacadeOnlyBaseTestClass
+from cert.event_callback_stream import EventCallbackStream
+from cert.event_asserts import EventAsserts
+from google.protobuf import empty_pb2
+from facade import rootservice_pb2 as facade_rootservice_pb2
+from hal import facade_pb2 as hal_facade_pb2
+from bluetooth_packets_python3 import hci_packets
+import bluetooth_packets_python3 as bt_packets
+
+
+class SimpleHalTest(GdFacadeOnlyBaseTestClass):
+
+    def send_cert_hci_command(self, command):
+        self.cert_device.hal.SendHciCommand(
+            hal_facade_pb2.HciCommandPacket(payload=bytes(command.Serialize())))
+
+    def send_cert_acl_data(self, handle, pb_flag, b_flag, acl):
+        lower = handle & 0xff
+        upper = (handle >> 8) & 0xf
+        upper = upper | int(pb_flag) & 0x3
+        upper = upper | ((int(b_flag) & 0x3) << 2)
+        lower_length = len(acl) & 0xff
+        upper_length = (len(acl) & 0xff00) >> 8
+        concatenated = bytes([lower, upper, lower_length, upper_length] +
+                             list(acl))
+        self.cert_device.hal.SendHciAcl(
+            hal_facade_pb2.HciAclPacket(payload=concatenated))
+
+    def send_dut_hci_command(self, command):
+        self.device_under_test.hal.SendHciCommand(
+            hal_facade_pb2.HciCommandPacket(payload=bytes(command.Serialize())))
+
+    def send_dut_acl_data(self, handle, pb_flag, b_flag, acl):
+        lower = handle & 0xff
+        upper = (handle >> 8) & 0xf
+        upper = upper | int(pb_flag) & 0x3
+        upper = upper | ((int(b_flag) & 0x3) << 2)
+        lower_length = len(acl) & 0xff
+        upper_length = (len(acl) & 0xff00) >> 8
+        concatenated = bytes([lower, upper, lower_length, upper_length] +
+                             list(acl))
+        self.device_under_test.hal.SendHciAcl(
+            hal_facade_pb2.HciAclPacket(payload=concatenated))
+
+    def setup_test(self):
+        self.device_under_test.rootservice.StartStack(
+            facade_rootservice_pb2.StartStackRequest(
+                module_under_test=facade_rootservice_pb2.BluetoothModule.Value(
+                    'HAL'),))
+        self.cert_device.rootservice.StartStack(
+            facade_rootservice_pb2.StartStackRequest(
+                module_under_test=facade_rootservice_pb2.BluetoothModule.Value(
+                    'HAL'),))
+
+        self.device_under_test.wait_channel_ready()
+        self.cert_device.wait_channel_ready()
+
+        self.send_dut_hci_command(hci_packets.ResetBuilder())
+        self.send_cert_hci_command(hci_packets.ResetBuilder())
+
+    def teardown_test(self):
+        self.device_under_test.rootservice.StopStack(
+            facade_rootservice_pb2.StopStackRequest())
+        self.cert_device.rootservice.StopStack(
+            facade_rootservice_pb2.StopStackRequest())
+
+    def test_none_event(self):
+        with EventCallbackStream(
+                self.device_under_test.hal.FetchHciEvent(
+                    empty_pb2.Empty())) as hci_event_stream:
+            hci_event_asserts = EventAsserts(hci_event_stream)
+            hci_event_asserts.assert_none(timeout=timedelta(seconds=1))
+
+    def test_fetch_hci_event(self):
+        with EventCallbackStream(
+                self.device_under_test.hal.FetchHciEvent(
+                    empty_pb2.Empty())) as hci_event_stream:
+
+            hci_event_asserts = EventAsserts(hci_event_stream)
+
+            self.send_dut_hci_command(
+                hci_packets.LeAddDeviceToWhiteListBuilder(
+                    hci_packets.WhiteListAddressType.RANDOM,
+                    '0C:05:04:03:02:01'))
+            event = hci_packets.LeAddDeviceToWhiteListCompleteBuilder(
+                1, hci_packets.ErrorCode.SUCCESS)
+            hci_event_asserts.assert_event_occurs(
+                lambda packet: bytes(event.Serialize()) in packet.payload)
+
+    def test_loopback_hci_command(self):
+        with EventCallbackStream(
+                self.device_under_test.hal.FetchHciEvent(
+                    empty_pb2.Empty())) as hci_event_stream:
+
+            self.send_dut_hci_command(
+                hci_packets.WriteLoopbackModeBuilder(
+                    hci_packets.LoopbackMode.ENABLE_LOCAL))
+
+            hci_event_asserts = EventAsserts(hci_event_stream)
+
+            command = hci_packets.LeAddDeviceToWhiteListBuilder(
+                hci_packets.WhiteListAddressType.RANDOM, '0C:05:04:03:02:01')
+            self.send_dut_hci_command(command)
+            hci_event_asserts.assert_event_occurs(
+                lambda packet: bytes(command.Serialize()) in packet.payload)
+
+    def test_inquiry_from_dut(self):
+        with EventCallbackStream(
+                self.device_under_test.hal.FetchHciEvent(
+                    empty_pb2.Empty())) as hci_event_stream:
+            hci_event_asserts = EventAsserts(hci_event_stream)
+            self.send_cert_hci_command(
+                hci_packets.WriteScanEnableBuilder(
+                    hci_packets.ScanEnable.INQUIRY_AND_PAGE_SCAN))
+            lap = hci_packets.Lap()
+            lap.lap = 0x33
+            self.send_dut_hci_command(
+                hci_packets.InquiryBuilder(lap, 0x30, 0xff))
+            hci_event_asserts.assert_event_occurs(
+                lambda packet: b'\x02\x0f' in packet.payload
+                # Expecting an HCI Event (code 0x02, length 0x0f)
+            )
+
+    def test_le_ad_scan_cert_advertises(self):
+        with EventCallbackStream(
+                self.device_under_test.hal.FetchHciEvent(
+                    empty_pb2.Empty())) as hci_event_stream:
+            hci_event_asserts = EventAsserts(hci_event_stream)
+
+            # DUT scans
+            self.send_dut_hci_command(
+                hci_packets.LeSetRandomAddressBuilder('0D:05:04:03:02:01'))
+            phy_scan_params = hci_packets.PhyScanParameters()
+            phy_scan_params.le_scan_interval = 6553
+            phy_scan_params.le_scan_window = 6553
+            phy_scan_params.le_scan_type = hci_packets.LeScanType.ACTIVE
+
+            self.send_dut_hci_command(
+                hci_packets.LeSetExtendedScanParametersBuilder(
+                    hci_packets.AddressType.RANDOM_DEVICE_ADDRESS,
+                    hci_packets.LeSetScanningFilterPolicy.ACCEPT_ALL, 1,
+                    [phy_scan_params]))
+            self.send_dut_hci_command(
+                hci_packets.LeSetExtendedScanEnableBuilder(
+                    hci_packets.Enable.ENABLED,
+                    hci_packets.FilterDuplicates.DISABLED, 0, 0))
+
+            # CERT Advertises
+            advertising_handle = 0
+            self.send_cert_hci_command(
+                hci_packets.LeSetExtendedAdvertisingLegacyParametersBuilder(
+                    advertising_handle,
+                    hci_packets.LegacyAdvertisingProperties.ADV_IND,
+                    512,
+                    768,
+                    7,
+                    hci_packets.OwnAddressType.RANDOM_DEVICE_ADDRESS,
+                    hci_packets.PeerAddressType.
+                    PUBLIC_DEVICE_OR_IDENTITY_ADDRESS,
+                    'A6:A5:A4:A3:A2:A1',
+                    hci_packets.AdvertisingFilterPolicy.ALL_DEVICES,
+                    0x7F,
+                    0,  # SID
+                    hci_packets.Enable.DISABLED  # Scan request notification
+                ))
+
+            self.send_cert_hci_command(
+                hci_packets.LeSetExtendedAdvertisingRandomAddressBuilder(
+                    advertising_handle, '0C:05:04:03:02:01'))
+
+            gap_name = hci_packets.GapData()
+            gap_name.data_type = hci_packets.GapDataType.COMPLETE_LOCAL_NAME
+            gap_name.data = list(bytes(b'Im_A_Cert!'))  # TODO: Fix and remove !
+
+            self.send_cert_hci_command(
+                hci_packets.LeSetExtendedAdvertisingDataBuilder(
+                    advertising_handle,
+                    hci_packets.Operation.COMPLETE_ADVERTISEMENT,
+                    hci_packets.FragmentPreference.CONTROLLER_SHOULD_NOT,
+                    [gap_name]))
+            enabled_set = hci_packets.EnabledSet()
+            enabled_set.advertising_handle = advertising_handle
+            enabled_set.duration = 0
+            enabled_set.max_extended_advertising_events = 0
+            self.send_cert_hci_command(
+                hci_packets.LeSetExtendedAdvertisingEnableBuilder(
+                    hci_packets.Enable.ENABLED, [enabled_set]))
+
+            hci_event_asserts.assert_event_occurs(
+                lambda packet: b'Im_A_Cert' in packet.payload)
+
+            # Disable Advertising
+            self.send_cert_hci_command(
+                hci_packets.LeSetExtendedAdvertisingEnableBuilder(
+                    hci_packets.Enable.DISABLED, [enabled_set]))
+
+            # Disable Scanning
+            self.send_dut_hci_command(
+                hci_packets.LeSetExtendedScanEnableBuilder(
+                    hci_packets.Enable.ENABLED,
+                    hci_packets.FilterDuplicates.DISABLED, 0, 0))
+
+    def test_le_connection_dut_advertises(self):
+        with EventCallbackStream(self.device_under_test.hal.FetchHciEvent(empty_pb2.Empty())) as hci_event_stream, \
+                EventCallbackStream(self.cert_device.hal.FetchHciEvent(empty_pb2.Empty())) as cert_hci_event_stream, \
+                EventCallbackStream(self.device_under_test.hal.FetchHciAcl(empty_pb2.Empty())) as acl_data_stream, \
+                EventCallbackStream(self.cert_device.hal.FetchHciAcl(empty_pb2.Empty())) as cert_acl_data_stream:
+
+            hci_event_asserts = EventAsserts(hci_event_stream)
+            cert_hci_event_asserts = EventAsserts(cert_hci_event_stream)
+            acl_data_asserts = EventAsserts(acl_data_stream)
+            cert_acl_data_asserts = EventAsserts(cert_acl_data_stream)
+
+            # Cert Connects
+            self.send_cert_hci_command(
+                hci_packets.LeSetRandomAddressBuilder('0C:05:04:03:02:01'))
+            phy_scan_params = hci_packets.LeCreateConnPhyScanParameters()
+            phy_scan_params.scan_interval = 0x60
+            phy_scan_params.scan_window = 0x30
+            phy_scan_params.conn_interval_min = 0x18
+            phy_scan_params.conn_interval_max = 0x28
+            phy_scan_params.conn_latency = 0
+            phy_scan_params.supervision_timeout = 0x1f4
+            phy_scan_params.min_ce_length = 0
+            phy_scan_params.max_ce_length = 0
+            self.send_cert_hci_command(
+                hci_packets.LeExtendedCreateConnectionBuilder(
+                    hci_packets.InitiatorFilterPolicy.USE_PEER_ADDRESS,
+                    hci_packets.OwnAddressType.RANDOM_DEVICE_ADDRESS,
+                    hci_packets.AddressType.RANDOM_DEVICE_ADDRESS,
+                    '0D:05:04:03:02:01', 1, [phy_scan_params]))
+
+            # DUT Advertises
+            advertising_handle = 0
+            self.send_dut_hci_command(
+                hci_packets.LeSetExtendedAdvertisingLegacyParametersBuilder(
+                    advertising_handle,
+                    hci_packets.LegacyAdvertisingProperties.ADV_IND,
+                    400,
+                    450,
+                    7,
+                    hci_packets.OwnAddressType.RANDOM_DEVICE_ADDRESS,
+                    hci_packets.PeerAddressType.
+                    PUBLIC_DEVICE_OR_IDENTITY_ADDRESS,
+                    '00:00:00:00:00:00',
+                    hci_packets.AdvertisingFilterPolicy.ALL_DEVICES,
+                    0xF8,
+                    1,  #SID
+                    hci_packets.Enable.DISABLED  # Scan request notification
+                ))
+
+            self.send_dut_hci_command(
+                hci_packets.LeSetExtendedAdvertisingRandomAddressBuilder(
+                    advertising_handle, '0D:05:04:03:02:01'))
+
+            gap_name = hci_packets.GapData()
+            gap_name.data_type = hci_packets.GapDataType.COMPLETE_LOCAL_NAME
+            gap_name.data = list(
+                bytes(b'Im_The_DUT!'))  # TODO: Fix and remove !
+
+            self.send_dut_hci_command(
+                hci_packets.LeSetExtendedAdvertisingDataBuilder(
+                    advertising_handle,
+                    hci_packets.Operation.COMPLETE_ADVERTISEMENT,
+                    hci_packets.FragmentPreference.CONTROLLER_SHOULD_NOT,
+                    [gap_name]))
+
+            gap_short_name = hci_packets.GapData()
+            gap_short_name.data_type = hci_packets.GapDataType.SHORTENED_LOCAL_NAME
+            gap_short_name.data = list(bytes(b'Im_The_D'))
+
+            self.send_dut_hci_command(
+                hci_packets.LeSetExtendedAdvertisingScanResponseBuilder(
+                    advertising_handle,
+                    hci_packets.Operation.COMPLETE_ADVERTISEMENT,
+                    hci_packets.FragmentPreference.CONTROLLER_SHOULD_NOT,
+                    [gap_short_name]))
+
+            enabled_set = hci_packets.EnabledSet()
+            enabled_set.advertising_handle = advertising_handle
+            enabled_set.duration = 0
+            enabled_set.max_extended_advertising_events = 0
+            self.send_dut_hci_command(
+                hci_packets.LeSetExtendedAdvertisingEnableBuilder(
+                    hci_packets.Enable.ENABLED, [enabled_set]))
+
+            conn_handle = 0xfff
+
+            def payload_handle(packet):
+                packet_bytes = packet.payload
+                if b'\x3e\x13\x01\x00' in packet_bytes:
+                    nonlocal conn_handle
+                    cc_view = hci_packets.LeConnectionCompleteView(
+                        hci_packets.LeMetaEventView(
+                            hci_packets.EventPacketView(
+                                bt_packets.PacketViewLittleEndian(
+                                    list(packet_bytes)))))
+                    conn_handle = cc_view.GetConnectionHandle()
+                    return True
+                return False
+
+            cert_hci_event_asserts.assert_event_occurs(payload_handle)
+            cert_handle = conn_handle
+            conn_handle = 0xfff
+            hci_event_asserts.assert_event_occurs(payload_handle)
+            dut_handle = conn_handle
+
+            # Send ACL Data
+            self.send_dut_acl_data(
+                dut_handle, hci_packets.PacketBoundaryFlag.
+                FIRST_NON_AUTOMATICALLY_FLUSHABLE,
+                hci_packets.BroadcastFlag.POINT_TO_POINT,
+                bytes(b'Just SomeAclData'))
+            self.send_cert_acl_data(
+                cert_handle, hci_packets.PacketBoundaryFlag.
+                FIRST_NON_AUTOMATICALLY_FLUSHABLE,
+                hci_packets.BroadcastFlag.POINT_TO_POINT,
+                bytes(b'Just SomeMoreAclData'))
+
+            cert_acl_data_asserts.assert_event_occurs(
+                lambda packet: b'SomeAclData' in packet.payload)
+            acl_data_asserts.assert_event_occurs(
+                lambda packet: b'SomeMoreAclData' in packet.payload)
+
+    def test_le_white_list_connection_cert_advertises(self):
+        with EventCallbackStream(self.device_under_test.hal.FetchHciEvent(empty_pb2.Empty())) as hci_event_stream, \
+            EventCallbackStream(self.cert_device.hal.FetchHciEvent(empty_pb2.Empty())) as cert_hci_event_stream:
+            hci_event_asserts = EventAsserts(hci_event_stream)
+            cert_hci_event_asserts = EventAsserts(cert_hci_event_stream)
+
+            # DUT Connects
+            self.send_dut_hci_command(
+                hci_packets.LeSetRandomAddressBuilder('0D:05:04:03:02:01'))
+            self.send_dut_hci_command(
+                hci_packets.LeAddDeviceToWhiteListBuilder(
+                    hci_packets.WhiteListAddressType.RANDOM,
+                    '0C:05:04:03:02:01'))
+            phy_scan_params = hci_packets.LeCreateConnPhyScanParameters()
+            phy_scan_params.scan_interval = 0x60
+            phy_scan_params.scan_window = 0x30
+            phy_scan_params.conn_interval_min = 0x18
+            phy_scan_params.conn_interval_max = 0x28
+            phy_scan_params.conn_latency = 0
+            phy_scan_params.supervision_timeout = 0x1f4
+            phy_scan_params.min_ce_length = 0
+            phy_scan_params.max_ce_length = 0
+            self.send_dut_hci_command(
+                hci_packets.LeExtendedCreateConnectionBuilder(
+                    hci_packets.InitiatorFilterPolicy.USE_WHITE_LIST,
+                    hci_packets.OwnAddressType.RANDOM_DEVICE_ADDRESS,
+                    hci_packets.AddressType.RANDOM_DEVICE_ADDRESS,
+                    'BA:D5:A4:A3:A2:A1', 1, [phy_scan_params]))
+
+            # CERT Advertises
+            advertising_handle = 1
+            self.send_cert_hci_command(
+                hci_packets.LeSetExtendedAdvertisingLegacyParametersBuilder(
+                    advertising_handle,
+                    hci_packets.LegacyAdvertisingProperties.ADV_IND,
+                    512,
+                    768,
+                    7,
+                    hci_packets.OwnAddressType.RANDOM_DEVICE_ADDRESS,
+                    hci_packets.PeerAddressType.
+                    PUBLIC_DEVICE_OR_IDENTITY_ADDRESS,
+                    'A6:A5:A4:A3:A2:A1',
+                    hci_packets.AdvertisingFilterPolicy.ALL_DEVICES,
+                    0x7F,
+                    0,  # SID
+                    hci_packets.Enable.DISABLED  # Scan request notification
+                ))
+
+            self.send_cert_hci_command(
+                hci_packets.LeSetExtendedAdvertisingRandomAddressBuilder(
+                    advertising_handle, '0C:05:04:03:02:01'))
+
+            gap_name = hci_packets.GapData()
+            gap_name.data_type = hci_packets.GapDataType.COMPLETE_LOCAL_NAME
+            gap_name.data = list(bytes(b'Im_A_Cert!'))  # TODO: Fix and remove !
+
+            self.send_cert_hci_command(
+                hci_packets.LeSetExtendedAdvertisingDataBuilder(
+                    advertising_handle,
+                    hci_packets.Operation.COMPLETE_ADVERTISEMENT,
+                    hci_packets.FragmentPreference.CONTROLLER_SHOULD_NOT,
+                    [gap_name]))
+            enabled_set = hci_packets.EnabledSet()
+            enabled_set.advertising_handle = 1
+            enabled_set.duration = 0
+            enabled_set.max_extended_advertising_events = 0
+            self.send_cert_hci_command(
+                hci_packets.LeSetExtendedAdvertisingEnableBuilder(
+                    hci_packets.Enable.ENABLED, [enabled_set]))
+
+            # LeConnectionComplete
+            cert_hci_event_asserts.assert_event_occurs(
+                lambda packet: b'\x3e\x13\x01\x00' in packet.payload,
+                timeout=timedelta(seconds=20))
+            hci_event_asserts.assert_event_occurs(
+                lambda packet: b'\x3e\x13\x01\x00' in packet.payload,
+                timeout=timedelta(seconds=20))
diff --git a/gd/hal/facade.cc b/gd/hal/facade.cc
new file mode 100644
index 0000000..3f41d62
--- /dev/null
+++ b/gd/hal/facade.cc
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "hal/facade.h"
+
+#include <memory>
+#include <mutex>
+
+#include "grpc/grpc_event_queue.h"
+#include "hal/facade.grpc.pb.h"
+#include "hal/hci_hal.h"
+
+using ::grpc::ServerAsyncResponseWriter;
+using ::grpc::ServerAsyncWriter;
+using ::grpc::ServerContext;
+
+namespace bluetooth {
+namespace hal {
+
+class HciHalFacadeService : public HciHalFacade::Service, public ::bluetooth::hal::HciHalCallbacks {
+ public:
+  explicit HciHalFacadeService(HciHal* hal) : hal_(hal) {
+    hal->registerIncomingPacketCallback(this);
+  }
+
+  ~HciHalFacadeService() override {
+    hal_->unregisterIncomingPacketCallback();
+  }
+
+  ::grpc::Status SendHciCommand(::grpc::ServerContext* context, const ::bluetooth::hal::HciCommandPacket* request,
+                                ::google::protobuf::Empty* response) override {
+    std::unique_lock<std::mutex> lock(mutex_);
+    can_send_hci_command_ = false;
+    std::string req_string = request->payload();
+    hal_->sendHciCommand(std::vector<uint8_t>(req_string.begin(), req_string.end()));
+    while (!can_send_hci_command_) {
+      cv_.wait(lock);
+    }
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status SendHciAcl(::grpc::ServerContext* context, const ::bluetooth::hal::HciAclPacket* request,
+                            ::google::protobuf::Empty* response) override {
+    std::string req_string = request->payload();
+    hal_->sendAclData(std::vector<uint8_t>(req_string.begin(), req_string.end()));
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status SendHciSco(::grpc::ServerContext* context, const ::bluetooth::hal::HciScoPacket* request,
+                            ::google::protobuf::Empty* response) override {
+    std::string req_string = request->payload();
+    hal_->sendScoData(std::vector<uint8_t>(req_string.begin(), req_string.end()));
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status FetchHciEvent(::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
+                               ::grpc::ServerWriter<HciEventPacket>* writer) override {
+    return pending_hci_events_.RunLoop(context, writer);
+  };
+
+  ::grpc::Status FetchHciAcl(::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
+                             ::grpc::ServerWriter<HciAclPacket>* writer) override {
+    return pending_acl_events_.RunLoop(context, writer);
+  };
+
+  ::grpc::Status FetchHciSco(::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
+                             ::grpc::ServerWriter<HciScoPacket>* writer) override {
+    return pending_sco_events_.RunLoop(context, writer);
+  };
+
+  void hciEventReceived(bluetooth::hal::HciPacket event) override {
+    {
+      HciEventPacket response;
+      response.set_payload(std::string(event.begin(), event.end()));
+      pending_hci_events_.OnIncomingEvent(std::move(response));
+    }
+    can_send_hci_command_ = true;
+    cv_.notify_one();
+  }
+
+  void aclDataReceived(bluetooth::hal::HciPacket data) override {
+    HciAclPacket response;
+    response.set_payload(std::string(data.begin(), data.end()));
+    pending_acl_events_.OnIncomingEvent(std::move(response));
+  }
+
+  void scoDataReceived(bluetooth::hal::HciPacket data) override {
+    HciScoPacket response;
+    response.set_payload(std::string(data.begin(), data.end()));
+    pending_sco_events_.OnIncomingEvent(std::move(response));
+  }
+
+ private:
+  HciHal* hal_;
+  bool can_send_hci_command_ = true;
+  mutable std::mutex mutex_;
+  std::condition_variable cv_;
+  ::bluetooth::grpc::GrpcEventQueue<HciEventPacket> pending_hci_events_{"FetchHciEvent"};
+  ::bluetooth::grpc::GrpcEventQueue<HciAclPacket> pending_acl_events_{"FetchHciAcl"};
+  ::bluetooth::grpc::GrpcEventQueue<HciScoPacket> pending_sco_events_{"FetchHciSco"};
+};
+
+void HciHalFacadeModule::ListDependencies(ModuleList* list) {
+  ::bluetooth::grpc::GrpcFacadeModule::ListDependencies(list);
+  list->add<HciHal>();
+}
+
+void HciHalFacadeModule::Start() {
+  ::bluetooth::grpc::GrpcFacadeModule::Start();
+  service_ = new HciHalFacadeService(GetDependency<HciHal>());
+}
+
+void HciHalFacadeModule::Stop() {
+  delete service_;
+  ::bluetooth::grpc::GrpcFacadeModule::Stop();
+}
+
+::grpc::Service* HciHalFacadeModule::GetService() const {
+  return service_;
+}
+
+const ModuleFactory HciHalFacadeModule::Factory = ::bluetooth::ModuleFactory([]() { return new HciHalFacadeModule(); });
+
+}  // namespace hal
+}  // namespace bluetooth
diff --git a/gd/hal/facade.h b/gd/hal/facade.h
new file mode 100644
index 0000000..fa28d10
--- /dev/null
+++ b/gd/hal/facade.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <list>
+#include <mutex>
+
+#include <grpc++/grpc++.h>
+
+#include "grpc/grpc_module.h"
+#include "hal/hci_hal.h"
+
+namespace bluetooth {
+namespace hal {
+
+class HciHalFacadeService;
+
+class HciHalFacadeModule : public ::bluetooth::grpc::GrpcFacadeModule {
+ public:
+  static const ModuleFactory Factory;
+
+  void ListDependencies(ModuleList* list) override;
+
+  void Start() override;
+  void Stop() override;
+
+  ::grpc::Service* GetService() const override;
+
+ private:
+  HciHalFacadeService* service_;
+};
+
+}  // namespace hal
+}  // namespace bluetooth
diff --git a/gd/hal/facade.proto b/gd/hal/facade.proto
new file mode 100644
index 0000000..539e810
--- /dev/null
+++ b/gd/hal/facade.proto
@@ -0,0 +1,31 @@
+syntax = "proto3";
+
+package bluetooth.hal;
+
+import "google/protobuf/empty.proto";
+
+service HciHalFacade {
+  rpc SendHciCommand(HciCommandPacket) returns (google.protobuf.Empty) {}
+  rpc SendHciAcl(HciAclPacket) returns (google.protobuf.Empty) {}
+  rpc SendHciSco(HciScoPacket) returns (google.protobuf.Empty) {}
+
+  rpc FetchHciEvent(google.protobuf.Empty) returns (stream HciEventPacket) {}
+  rpc FetchHciAcl(google.protobuf.Empty) returns (stream HciAclPacket) {}
+  rpc FetchHciSco(google.protobuf.Empty) returns (stream HciScoPacket) {}
+}
+
+message HciEventPacket {
+  bytes payload = 1;
+}
+
+message HciCommandPacket {
+  bytes payload = 1;
+}
+
+message HciAclPacket {
+  bytes payload = 1;
+}
+
+message HciScoPacket {
+  bytes payload = 1;
+}
diff --git a/gd/hal/hci_hal.h b/gd/hal/hci_hal.h
new file mode 100644
index 0000000..f95a4f9
--- /dev/null
+++ b/gd/hal/hci_hal.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <vector>
+
+#include "module.h"
+
+namespace bluetooth {
+namespace hal {
+
+using HciPacket = std::vector<uint8_t>;
+
+enum class Status : int32_t { SUCCESS, TRANSPORT_ERROR, INITIALIZATION_ERROR, UNKNOWN };
+
+// Mirrors hardware/interfaces/bluetooth/1.0/IBluetoothHciCallbacks.hal in Android, but moved initializationComplete
+// callback to BluetoothInitializationCompleteCallback
+
+// The interface from the Bluetooth Controller to the stack
+class HciHalCallbacks {
+ public:
+  virtual ~HciHalCallbacks() = default;
+
+  // This function is invoked when an HCI event is received from the
+  // Bluetooth controller to be forwarded to the Bluetooth stack
+  // @param event is the HCI event to be sent to the Bluetooth stack
+  virtual void hciEventReceived(HciPacket event) = 0;
+
+  // Send an ACL data packet form the controller to the host
+  // @param data the ACL HCI packet to be passed to the host stack
+  virtual void aclDataReceived(HciPacket data) = 0;
+
+  // Send a SCO data packet form the controller to the host
+  // @param data the SCO HCI packet to be passed to the host stack
+  virtual void scoDataReceived(HciPacket data) = 0;
+};
+
+// Mirrors hardware/interfaces/bluetooth/1.0/IBluetoothHci.hal in Android
+// The Host Controller Interface (HCI) is the layer defined by the Bluetooth
+// specification between the software that runs on the host and the Bluetooth
+// controller chip. This boundary is the natural choice for a Hardware
+// Abstraction Layer (HAL). Dealing only in HCI packets and events simplifies
+// the stack and abstracts away power management, initialization, and other
+// implementation-specific details related to the hardware.
+class HciHal : public ::bluetooth::Module {
+ public:
+  static const ModuleFactory Factory;
+
+  virtual ~HciHal() = default;
+
+  // Register the callback for incoming packets. All incoming packets are dropped before
+  // this callback is registered. Callback can only be registered once.
+  //
+  // @param callback implements BluetoothHciHalCallbacks which will
+  //    receive callbacks when incoming HCI packets are received
+  //    from the controller to be sent to the host.
+  virtual void registerIncomingPacketCallback(HciHalCallbacks* callback) = 0;
+
+  // Unregister the callback for incoming packets. Drop all further incoming packets.
+  virtual void unregisterIncomingPacketCallback() = 0;
+
+  // Send an HCI command (as specified in the Bluetooth Specification
+  // V4.2, Vol 2, Part 5, Section 5.4.1) to the Bluetooth controller.
+  // Commands must be executed in order.
+  virtual void sendHciCommand(HciPacket command) = 0;
+
+  // Send an HCI ACL data packet (as specified in the Bluetooth Specification
+  // V4.2, Vol 2, Part 5, Section 5.4.2) to the Bluetooth controller.
+  // Packets must be processed in order.
+  virtual void sendAclData(HciPacket data) = 0;
+
+  // Send an SCO data packet (as specified in the Bluetooth Specification
+  // V4.2, Vol 2, Part 5, Section 5.4.3) to the Bluetooth controller.
+  // Packets must be processed in order.
+  virtual void sendScoData(HciPacket data) = 0;
+};
+
+}  // namespace hal
+}  // namespace bluetooth
diff --git a/gd/hal/hci_hal_android_hidl.cc b/gd/hal/hci_hal_android_hidl.cc
new file mode 100644
index 0000000..7e45d89
--- /dev/null
+++ b/gd/hal/hci_hal_android_hidl.cc
@@ -0,0 +1,185 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "hal/hci_hal.h"
+
+#include <stdlib.h>
+#include <vector>
+#include <future>
+
+#include <android/hardware/bluetooth/1.0/IBluetoothHci.h>
+#include <android/hardware/bluetooth/1.0/IBluetoothHciCallbacks.h>
+#include <android/hardware/bluetooth/1.0/types.h>
+
+#include "hal/snoop_logger.h"
+#include "os/log.h"
+
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::bluetooth::V1_0::IBluetoothHci;
+using ::android::hardware::bluetooth::V1_0::IBluetoothHciCallbacks;
+using HidlStatus = ::android::hardware::bluetooth::V1_0::Status;
+
+namespace bluetooth {
+namespace hal {
+namespace {
+
+class HciDeathRecipient : public ::android::hardware::hidl_death_recipient {
+ public:
+  virtual void serviceDied(uint64_t /*cookie*/, const android::wp<::android::hidl::base::V1_0::IBase>& /*who*/) {
+    LOG_ERROR("Bluetooth HAL service died!");
+    abort();
+  }
+};
+
+android::sp<HciDeathRecipient> hci_death_recipient_ = new HciDeathRecipient();
+
+class InternalHciCallbacks : public IBluetoothHciCallbacks {
+ public:
+  InternalHciCallbacks(SnoopLogger* btsnoop_logger)
+      : btsnoop_logger_(btsnoop_logger) {
+    init_promise_ = new std::promise<void>();
+  }
+
+  void SetCallback(HciHalCallbacks* callback) {
+    ASSERT(callback_ == nullptr && callback != nullptr);
+    callback_ = callback;
+  }
+
+  void ResetCallback() {
+    callback_ = nullptr;
+  }
+
+  std::promise<void>* GetInitPromise() {
+    return init_promise_;
+  }
+
+  Return<void> initializationComplete(HidlStatus status) {
+    ASSERT(status == HidlStatus::SUCCESS);
+    init_promise_->set_value();
+    return Void();
+  }
+
+  Return<void> hciEventReceived(const hidl_vec<uint8_t>& event) {
+    std::vector<uint8_t> received_hci_packet(event.begin(), event.end());
+    btsnoop_logger_->capture(received_hci_packet, SnoopLogger::Direction::INCOMING,
+                             SnoopLogger::PacketType::EVT);
+    if (callback_ != nullptr) {
+      callback_->hciEventReceived(std::move(received_hci_packet));
+    }
+    return Void();
+  }
+
+  Return<void> aclDataReceived(const hidl_vec<uint8_t>& data) {
+    std::vector<uint8_t> received_hci_packet(data.begin(), data.end());
+    btsnoop_logger_->capture(received_hci_packet, SnoopLogger::Direction::INCOMING,
+                             SnoopLogger::PacketType::ACL);
+    if (callback_ != nullptr) {
+      callback_->aclDataReceived(std::move(received_hci_packet));
+    }
+    return Void();
+  }
+
+  Return<void> scoDataReceived(const hidl_vec<uint8_t>& data) {
+    std::vector<uint8_t> received_hci_packet(data.begin(), data.end());
+    btsnoop_logger_->capture(received_hci_packet, SnoopLogger::Direction::INCOMING,
+                             SnoopLogger::PacketType::SCO);
+    if (callback_ != nullptr) {
+      callback_->scoDataReceived(std::move(received_hci_packet));
+    }
+    return Void();
+  }
+
+ private:
+  std::promise<void>* init_promise_ = nullptr;
+  HciHalCallbacks* callback_ = nullptr;
+  SnoopLogger* btsnoop_logger_ = nullptr;
+};
+
+}  // namespace
+
+const std::string SnoopLogger::DefaultFilePath = "/data/misc/bluetooth/logs/btsnoop_hci.log";
+const bool SnoopLogger::AlwaysFlush = false;
+
+class HciHalHidl : public HciHal {
+ public:
+  void registerIncomingPacketCallback(HciHalCallbacks* callback) override {
+    callbacks_->SetCallback(callback);
+  }
+
+  void unregisterIncomingPacketCallback() override {
+    callbacks_->ResetCallback();
+  }
+
+  void sendHciCommand(HciPacket command) override {
+    btsnoop_logger_->capture(command, SnoopLogger::Direction::OUTGOING, SnoopLogger::PacketType::CMD);
+    bt_hci_->sendHciCommand(command);
+  }
+
+  void sendAclData(HciPacket packet) override {
+    btsnoop_logger_->capture(packet, SnoopLogger::Direction::OUTGOING, SnoopLogger::PacketType::ACL);
+    bt_hci_->sendAclData(packet);
+  }
+
+  void sendScoData(HciPacket packet) override {
+    btsnoop_logger_->capture(packet, SnoopLogger::Direction::OUTGOING, SnoopLogger::PacketType::SCO);
+    bt_hci_->sendScoData(packet);
+  }
+
+ protected:
+  void ListDependencies(ModuleList* list) override {
+    list->add<SnoopLogger>();
+  }
+
+  void Start() override {
+    btsnoop_logger_ = GetDependency<SnoopLogger>();
+    bt_hci_ = IBluetoothHci::getService();
+    ASSERT(bt_hci_ != nullptr);
+    auto death_link = bt_hci_->linkToDeath(hci_death_recipient_, 0);
+    ASSERT_LOG(death_link.isOk(), "Unable to set the death recipient for the Bluetooth HAL");
+    // Block allows allocation of a variable that might be bypassed by goto.
+    {
+      callbacks_ = new InternalHciCallbacks(btsnoop_logger_);
+      bt_hci_->initialize(callbacks_);
+      // Don't timeout here, time out at a higher layer
+      callbacks_->GetInitPromise()->get_future().wait();
+    }
+  }
+
+  void Stop() override {
+    ASSERT(bt_hci_ != nullptr);
+    auto death_unlink = bt_hci_->unlinkToDeath(hci_death_recipient_);
+    if (!death_unlink.isOk()) {
+      LOG_ERROR("Error unlinking death recipient from the Bluetooth HAL");
+    }
+    bt_hci_->close();
+    callbacks_->ResetCallback();
+    bt_hci_ = nullptr;
+  }
+
+ private:
+  android::sp<InternalHciCallbacks> callbacks_;
+  android::sp<IBluetoothHci> bt_hci_;
+  SnoopLogger* btsnoop_logger_;
+};
+
+const ModuleFactory HciHal::Factory = ModuleFactory([]() {
+  return new HciHalHidl();
+});
+
+}  // namespace hal
+}  // namespace bluetooth
diff --git a/gd/hal/hci_hal_android_hidl_test.cc b/gd/hal/hci_hal_android_hidl_test.cc
new file mode 100644
index 0000000..5b8b68d
--- /dev/null
+++ b/gd/hal/hci_hal_android_hidl_test.cc
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "hal/hci_hal.h"
+
+#include <chrono>
+#include <future>
+
+#include <gtest/gtest.h>
+
+#include "os/thread.h"
+
+using ::bluetooth::os::Thread;
+
+namespace bluetooth {
+namespace hal {
+namespace {
+
+class HciHalHidlTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    thread_ = new Thread("test_thread", Thread::Priority::NORMAL);
+  }
+
+  void TearDown() override {
+    delete thread_;
+  }
+
+  ModuleRegistry fake_registry_;
+  Thread* thread_;
+};
+
+TEST_F(HciHalHidlTest, init_and_close) {
+  fake_registry_.Start<HciHal>(thread_);
+  fake_registry_.StopAll();
+}
+}  // namespace
+}  // namespace hal
+}  // namespace bluetooth
diff --git a/gd/hal/hci_hal_host_rootcanal.cc b/gd/hal/hci_hal_host_rootcanal.cc
new file mode 100644
index 0000000..0d9bf22
--- /dev/null
+++ b/gd/hal/hci_hal_host_rootcanal.cc
@@ -0,0 +1,321 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "hal/hci_hal_host_rootcanal.h"
+#include "hal/hci_hal.h"
+
+#include <netdb.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <chrono>
+#include <csignal>
+#include <mutex>
+#include <queue>
+
+#include "hal/snoop_logger.h"
+#include "os/log.h"
+#include "os/reactor.h"
+#include "os/thread.h"
+
+namespace {
+constexpr int INVALID_FD = -1;
+
+constexpr uint8_t kH4Command = 0x01;
+constexpr uint8_t kH4Acl = 0x02;
+constexpr uint8_t kH4Sco = 0x03;
+constexpr uint8_t kH4Event = 0x04;
+
+constexpr uint8_t kH4HeaderSize = 1;
+constexpr uint8_t kHciAclHeaderSize = 4;
+constexpr uint8_t kHciScoHeaderSize = 3;
+constexpr uint8_t kHciEvtHeaderSize = 2;
+constexpr int kBufSize = 1024 + 4 + 1;  // DeviceProperties::acl_data_packet_size_ + ACL header + H4 header
+
+int ConnectToRootCanal(const std::string& server, int port) {
+  int socket_fd = socket(AF_INET, SOCK_STREAM, 0);
+  if (socket_fd < 1) {
+    LOG_ERROR("can't create socket: %s", strerror(errno));
+    return INVALID_FD;
+  }
+
+  struct hostent* host;
+  host = gethostbyname(server.c_str());
+  if (host == nullptr) {
+    LOG_ERROR("can't get server name");
+    return INVALID_FD;
+  }
+
+  struct sockaddr_in serv_addr;
+  memset((void*)&serv_addr, 0, sizeof(serv_addr));
+  serv_addr.sin_family = AF_INET;
+  serv_addr.sin_addr.s_addr = INADDR_ANY;
+  serv_addr.sin_port = htons(port);
+
+  int result = connect(socket_fd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
+  if (result < 0) {
+    LOG_ERROR("can't connect: %s", strerror(errno));
+    return INVALID_FD;
+  }
+
+  timeval socket_timeout{
+      .tv_sec = 3,
+      .tv_usec = 0,
+  };
+  int ret = setsockopt(socket_fd, SOL_SOCKET, SO_RCVTIMEO, &socket_timeout, sizeof(socket_timeout));
+  if (ret == -1) {
+    LOG_ERROR("can't control socket fd: %s", strerror(errno));
+    return INVALID_FD;
+  }
+  return socket_fd;
+}
+}  // namespace
+
+namespace bluetooth {
+namespace hal {
+
+const std::string SnoopLogger::DefaultFilePath = "/tmp/btsnoop_hci.log";
+const bool SnoopLogger::AlwaysFlush = true;
+
+class HciHalHostRootcanal : public HciHal {
+ public:
+  void registerIncomingPacketCallback(HciHalCallbacks* callback) override {
+    std::lock_guard<std::mutex> lock(api_mutex_);
+    LOG_INFO("%s before", __func__);
+    {
+      std::lock_guard<std::mutex> incoming_packet_callback_lock(incoming_packet_callback_mutex_);
+      ASSERT(incoming_packet_callback_ == nullptr && callback != nullptr);
+      incoming_packet_callback_ = callback;
+    }
+    LOG_INFO("%s after", __func__);
+  }
+
+  void unregisterIncomingPacketCallback() override {
+    std::lock_guard<std::mutex> lock(api_mutex_);
+    LOG_INFO("%s before", __func__);
+    {
+      std::lock_guard<std::mutex> incoming_packet_callback_lock(incoming_packet_callback_mutex_);
+      incoming_packet_callback_ = nullptr;
+    }
+    LOG_INFO("%s after", __func__);
+  }
+
+  void sendHciCommand(HciPacket command) override {
+    std::lock_guard<std::mutex> lock(api_mutex_);
+    ASSERT(sock_fd_ != INVALID_FD);
+    std::vector<uint8_t> packet = std::move(command);
+    btsnoop_logger_->capture(packet, SnoopLogger::Direction::OUTGOING, SnoopLogger::PacketType::CMD);
+    packet.insert(packet.cbegin(), kH4Command);
+    write_to_rootcanal_fd(packet);
+  }
+
+  void sendAclData(HciPacket data) override {
+    std::lock_guard<std::mutex> lock(api_mutex_);
+    ASSERT(sock_fd_ != INVALID_FD);
+    std::vector<uint8_t> packet = std::move(data);
+    btsnoop_logger_->capture(packet, SnoopLogger::Direction::OUTGOING, SnoopLogger::PacketType::ACL);
+    packet.insert(packet.cbegin(), kH4Acl);
+    write_to_rootcanal_fd(packet);
+  }
+
+  void sendScoData(HciPacket data) override {
+    std::lock_guard<std::mutex> lock(api_mutex_);
+    ASSERT(sock_fd_ != INVALID_FD);
+    std::vector<uint8_t> packet = std::move(data);
+    btsnoop_logger_->capture(packet, SnoopLogger::Direction::OUTGOING, SnoopLogger::PacketType::SCO);
+    packet.insert(packet.cbegin(), kH4Sco);
+    write_to_rootcanal_fd(packet);
+  }
+
+ protected:
+  void ListDependencies(ModuleList* list) override {
+    list->add<SnoopLogger>();
+  }
+
+  void Start() override {
+    std::lock_guard<std::mutex> lock(api_mutex_);
+    ASSERT(sock_fd_ == INVALID_FD);
+    sock_fd_ = ConnectToRootCanal(config_->GetServerAddress(), config_->GetPort());
+    ASSERT(sock_fd_ != INVALID_FD);
+    reactable_ = hci_incoming_thread_.GetReactor()->Register(
+        sock_fd_, common::Bind(&HciHalHostRootcanal::incoming_packet_received, common::Unretained(this)),
+        common::Closure());
+    btsnoop_logger_ = GetDependency<SnoopLogger>();
+    LOG_INFO("Rootcanal HAL opened successfully");
+  }
+
+  void Stop() override {
+    std::lock_guard<std::mutex> lock(api_mutex_);
+    LOG_INFO("Rootcanal HAL is closing");
+    if (reactable_ != nullptr) {
+      hci_incoming_thread_.GetReactor()->Unregister(reactable_);
+      LOG_INFO("Rootcanal HAL is stopping, start waiting for last callback");
+      // Wait up to 1 second for the last incoming packet callback to finish
+      hci_incoming_thread_.GetReactor()->WaitForUnregisteredReactable(std::chrono::milliseconds(1000));
+      LOG_INFO("Rootcanal HAL is stopping, finished waiting for last callback");
+      ASSERT(sock_fd_ != INVALID_FD);
+    }
+    reactable_ = nullptr;
+    {
+      std::lock_guard<std::mutex> incoming_packet_callback_lock(incoming_packet_callback_mutex_);
+      incoming_packet_callback_ = nullptr;
+    }
+    ::close(sock_fd_);
+    sock_fd_ = INVALID_FD;
+    LOG_INFO("Rootcanal HAL is closed");
+  }
+
+ private:
+  // Held when APIs are called, NOT to be held during callbacks
+  std::mutex api_mutex_;
+  HciHalHostRootcanalConfig* config_ = HciHalHostRootcanalConfig::Get();
+  HciHalCallbacks* incoming_packet_callback_ = nullptr;
+  std::mutex incoming_packet_callback_mutex_;
+  int sock_fd_ = INVALID_FD;
+  bluetooth::os::Thread hci_incoming_thread_ =
+      bluetooth::os::Thread("hci_incoming_thread", bluetooth::os::Thread::Priority::NORMAL);
+  bluetooth::os::Reactor::Reactable* reactable_ = nullptr;
+  std::queue<std::vector<uint8_t>> hci_outgoing_queue_;
+  SnoopLogger* btsnoop_logger_ = nullptr;
+
+  void write_to_rootcanal_fd(HciPacket packet) {
+    // TODO: replace this with new queue when it's ready
+    hci_outgoing_queue_.emplace(packet);
+    if (hci_outgoing_queue_.size() == 1) {
+      hci_incoming_thread_.GetReactor()->ModifyRegistration(
+          reactable_, common::Bind(&HciHalHostRootcanal::incoming_packet_received, common::Unretained(this)),
+          common::Bind(&HciHalHostRootcanal::send_packet_ready, common::Unretained(this)));
+    }
+  }
+
+  void send_packet_ready() {
+    std::lock_guard<std::mutex> lock(this->api_mutex_);
+    auto packet_to_send = this->hci_outgoing_queue_.front();
+    auto bytes_written = write(this->sock_fd_, (void*)packet_to_send.data(), packet_to_send.size());
+    this->hci_outgoing_queue_.pop();
+    if (bytes_written == -1) {
+      abort();
+    }
+    if (hci_outgoing_queue_.empty()) {
+      this->hci_incoming_thread_.GetReactor()->ModifyRegistration(
+          this->reactable_, common::Bind(&HciHalHostRootcanal::incoming_packet_received, common::Unretained(this)),
+          common::Closure());
+    }
+  }
+
+  void incoming_packet_received() {
+    {
+      std::lock_guard<std::mutex> incoming_packet_callback_lock(incoming_packet_callback_mutex_);
+      if (incoming_packet_callback_ == nullptr) {
+        LOG_INFO("Dropping a packet");
+        return;
+      }
+    }
+    uint8_t buf[kBufSize] = {};
+
+    ssize_t received_size;
+    RUN_NO_INTR(received_size = recv(sock_fd_, buf, kH4HeaderSize, 0));
+    ASSERT_LOG(received_size != -1, "Can't receive from socket: %s", strerror(errno));
+    if (received_size == 0) {
+      LOG_WARN("Can't read H4 header. EOF received");
+      raise(SIGINT);
+      return;
+    }
+
+    if (buf[0] == kH4Event) {
+      RUN_NO_INTR(received_size = recv(sock_fd_, buf + kH4HeaderSize, kHciEvtHeaderSize, 0));
+      ASSERT_LOG(received_size != -1, "Can't receive from socket: %s", strerror(errno));
+      ASSERT_LOG(received_size == kHciEvtHeaderSize, "malformed HCI event header received");
+
+      uint8_t hci_evt_parameter_total_length = buf[2];
+      ssize_t payload_size;
+      RUN_NO_INTR(payload_size =
+                      recv(sock_fd_, buf + kH4HeaderSize + kHciEvtHeaderSize, hci_evt_parameter_total_length, 0));
+      ASSERT_LOG(payload_size != -1, "Can't receive from socket: %s", strerror(errno));
+      ASSERT_LOG(payload_size == hci_evt_parameter_total_length,
+                 "malformed HCI event total parameter size received: %zu != %d", payload_size,
+                 hci_evt_parameter_total_length);
+
+      HciPacket receivedHciPacket;
+      receivedHciPacket.assign(buf + kH4HeaderSize, buf + kH4HeaderSize + kHciEvtHeaderSize + payload_size);
+      btsnoop_logger_->capture(receivedHciPacket, SnoopLogger::Direction::INCOMING, SnoopLogger::PacketType::EVT);
+      {
+        std::lock_guard<std::mutex> incoming_packet_callback_lock(incoming_packet_callback_mutex_);
+        if (incoming_packet_callback_ == nullptr) {
+          LOG_INFO("Dropping an event after processing");
+          return;
+        }
+        incoming_packet_callback_->hciEventReceived(receivedHciPacket);
+      }
+    }
+
+    if (buf[0] == kH4Acl) {
+      RUN_NO_INTR(received_size = recv(sock_fd_, buf + kH4HeaderSize, kHciAclHeaderSize, 0));
+      ASSERT_LOG(received_size != -1, "Can't receive from socket: %s", strerror(errno));
+      ASSERT_LOG(received_size == kHciAclHeaderSize, "malformed ACL header received");
+
+      uint16_t hci_acl_data_total_length = (buf[4] << 8) + buf[3];
+      int payload_size;
+      RUN_NO_INTR(payload_size = recv(sock_fd_, buf + kH4HeaderSize + kHciAclHeaderSize, hci_acl_data_total_length, 0));
+      ASSERT_LOG(payload_size != -1, "Can't receive from socket: %s", strerror(errno));
+      ASSERT_LOG(payload_size == hci_acl_data_total_length, "malformed ACL length received: %d != %d", payload_size,
+                 hci_acl_data_total_length);
+      ASSERT_LOG(hci_acl_data_total_length <= kBufSize - kH4HeaderSize - kHciAclHeaderSize, "packet too long");
+
+      HciPacket receivedHciPacket;
+      receivedHciPacket.assign(buf + kH4HeaderSize, buf + kH4HeaderSize + kHciAclHeaderSize + payload_size);
+      btsnoop_logger_->capture(receivedHciPacket, SnoopLogger::Direction::INCOMING, SnoopLogger::PacketType::ACL);
+      {
+        std::lock_guard<std::mutex> incoming_packet_callback_lock(incoming_packet_callback_mutex_);
+        if (incoming_packet_callback_ == nullptr) {
+          LOG_INFO("Dropping an ACL packet after processing");
+          return;
+        }
+        incoming_packet_callback_->aclDataReceived(receivedHciPacket);
+      }
+    }
+
+    if (buf[0] == kH4Sco) {
+      RUN_NO_INTR(received_size = recv(sock_fd_, buf + kH4HeaderSize, kHciScoHeaderSize, 0));
+      ASSERT_LOG(received_size != -1, "Can't receive from socket: %s", strerror(errno));
+      ASSERT_LOG(received_size == kHciScoHeaderSize, "malformed SCO header received");
+
+      uint8_t hci_sco_data_total_length = buf[3];
+      int payload_size;
+      RUN_NO_INTR(payload_size = recv(sock_fd_, buf + kH4HeaderSize + kHciScoHeaderSize, hci_sco_data_total_length, 0));
+      ASSERT_LOG(payload_size != -1, "Can't receive from socket: %s", strerror(errno));
+      ASSERT_LOG(payload_size == hci_sco_data_total_length, "malformed SCO packet received: size mismatch");
+
+      HciPacket receivedHciPacket;
+      receivedHciPacket.assign(buf + kH4HeaderSize, buf + kH4HeaderSize + kHciScoHeaderSize + payload_size);
+      btsnoop_logger_->capture(receivedHciPacket, SnoopLogger::Direction::INCOMING, SnoopLogger::PacketType::SCO);
+      {
+        std::lock_guard<std::mutex> incoming_packet_callback_lock(incoming_packet_callback_mutex_);
+        if (incoming_packet_callback_ == nullptr) {
+          LOG_INFO("Dropping a SCO packet after processing");
+          return;
+        }
+        incoming_packet_callback_->scoDataReceived(receivedHciPacket);
+      }
+    }
+    memset(buf, 0, kBufSize);
+  }
+};
+
+const ModuleFactory HciHal::Factory = ModuleFactory([]() { return new HciHalHostRootcanal(); });
+
+}  // namespace hal
+}  // namespace bluetooth
diff --git a/gd/hal/hci_hal_host_rootcanal.h b/gd/hal/hci_hal_host_rootcanal.h
new file mode 100644
index 0000000..9b73b15
--- /dev/null
+++ b/gd/hal/hci_hal_host_rootcanal.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+
+namespace bluetooth {
+namespace hal {
+
+// Singleton object to store runtime configuration for rootcanal
+class HciHalHostRootcanalConfig {
+ public:
+  static HciHalHostRootcanalConfig* Get() {
+    static HciHalHostRootcanalConfig instance;
+    return &instance;
+  }
+
+  // Get the listening TCP port for rootcanal HCI socket
+  uint16_t GetPort() {
+    return port_;
+  }
+
+  // Set the listening TCP port for rootcanal HCI socket
+  void SetPort(uint16_t port) {
+    port_ = port;
+  }
+
+  // Get the server address for rootcanal HCI socket
+  std::string GetServerAddress() {
+    return server_address_;
+  }
+
+  // Set the server address for rootcanal HCI socket
+  void SetServerAddress(const std::string& address) {
+    server_address_ = address;
+  }
+
+ private:
+  HciHalHostRootcanalConfig() = default;
+  uint16_t port_ = 6402;                      // Default server TCP port
+  std::string server_address_ = "127.0.0.1";  // Default server address
+};
+
+}  // namespace hal
+}  // namespace bluetooth
diff --git a/gd/hal/hci_hal_host_rootcanal_test.cc b/gd/hal/hci_hal_host_rootcanal_test.cc
new file mode 100644
index 0000000..aeff6a8
--- /dev/null
+++ b/gd/hal/hci_hal_host_rootcanal_test.cc
@@ -0,0 +1,399 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "hal/hci_hal_host_rootcanal.h"
+#include "hal/hci_hal.h"
+#include "hal/serialize_packet.h"
+
+#include <fcntl.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <cstring>
+#include <queue>
+#include <thread>
+#include <utility>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "os/log.h"
+#include "os/thread.h"
+#include "os/utils.h"
+#include "packet/raw_builder.h"
+
+using ::bluetooth::os::Thread;
+
+namespace bluetooth {
+namespace hal {
+namespace {
+
+uint16_t kTestPort = 6537;
+
+constexpr uint8_t kH4Command = 0x01;
+constexpr uint8_t kH4Acl = 0x02;
+constexpr uint8_t kH4Sco = 0x03;
+constexpr uint8_t kH4Event = 0x04;
+
+using H4Packet = std::vector<uint8_t>;
+
+std::queue<std::pair<uint8_t, HciPacket>> incoming_packets_queue_;
+
+class TestHciHalCallbacks : public HciHalCallbacks {
+ public:
+  void hciEventReceived(HciPacket packet) override {
+    incoming_packets_queue_.emplace(kH4Event, packet);
+  }
+
+  void aclDataReceived(HciPacket packet) override {
+    incoming_packets_queue_.emplace(kH4Acl, packet);
+  }
+
+  void scoDataReceived(HciPacket packet) override {
+    incoming_packets_queue_.emplace(kH4Sco, packet);
+  }
+};
+
+// An implementation of rootcanal desktop HCI server which listens on localhost:kListeningPort
+class FakeRootcanalDesktopHciServer {
+ public:
+  FakeRootcanalDesktopHciServer() {
+    struct sockaddr_in listen_address;
+    socklen_t sockaddr_in_size = sizeof(struct sockaddr_in);
+    memset(&listen_address, 0, sockaddr_in_size);
+
+    RUN_NO_INTR(listen_fd_ = socket(AF_INET, SOCK_STREAM, 0));
+    if (listen_fd_ < 0) {
+      LOG_WARN("Error creating socket for test channel.");
+      return;
+    }
+
+    listen_address.sin_family = AF_INET;
+    listen_address.sin_port = htons(HciHalHostRootcanalConfig::Get()->GetPort());
+    listen_address.sin_addr.s_addr = htonl(INADDR_ANY);
+
+    if (bind(listen_fd_, reinterpret_cast<sockaddr*>(&listen_address), sockaddr_in_size) < 0) {
+      LOG_WARN("Error binding test channel listener socket to address.");
+      close(listen_fd_);
+      return;
+    }
+
+    if (listen(listen_fd_, 1) < 0) {
+      LOG_WARN("Error listening for test channel.");
+      close(listen_fd_);
+      return;
+    }
+  }
+
+  ~FakeRootcanalDesktopHciServer() {
+    close(listen_fd_);
+  }
+
+  int Accept() {
+    int accept_fd;
+
+    RUN_NO_INTR(accept_fd = accept(listen_fd_, nullptr, nullptr));
+
+    int flags = fcntl(accept_fd, F_GETFL, NULL);
+    int ret = fcntl(accept_fd, F_SETFL, flags | O_NONBLOCK);
+    if (ret == -1) {
+      LOG_ERROR("Can't fcntl");
+      return -1;
+    }
+
+    if (accept_fd < 0) {
+      LOG_WARN("Error accepting test channel connection errno=%d (%s).", errno, strerror(errno));
+
+      if (errno != EAGAIN && errno != EWOULDBLOCK) {
+        LOG_ERROR("Closing listen_fd_ (won't try again).");
+        close(listen_fd_);
+        return -1;
+      }
+    }
+
+    return accept_fd;
+  }
+
+ private:
+  int listen_fd_ = -1;
+};
+
+class HciHalRootcanalTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    thread_ = new Thread("test_thread", Thread::Priority::NORMAL);
+
+    HciHalHostRootcanalConfig::Get()->SetPort(kTestPort);
+    fake_server_ = new FakeRootcanalDesktopHciServer;
+    hal_ = fake_registry_.Start<HciHal>(thread_);
+    hal_->registerIncomingPacketCallback(&callbacks_);
+    fake_server_socket_ = fake_server_->Accept();  // accept() after client is connected to avoid blocking
+    std::queue<std::pair<uint8_t, HciPacket>> empty;
+    std::swap(incoming_packets_queue_, empty);
+  }
+
+  void TearDown() override {
+    hal_->unregisterIncomingPacketCallback();
+    fake_registry_.StopAll();
+    close(fake_server_socket_);
+    delete fake_server_;
+    delete thread_;
+  }
+
+  void SetFakeServerSocketToBlocking() {
+    int flags = fcntl(fake_server_socket_, F_GETFL, NULL);
+    int ret = fcntl(fake_server_socket_, F_SETFL, flags & ~O_NONBLOCK);
+    EXPECT_NE(ret, -1) << "Can't set accept fd to blocking";
+  }
+
+  FakeRootcanalDesktopHciServer* fake_server_ = nullptr;
+  HciHal* hal_ = nullptr;
+  ModuleRegistry fake_registry_;
+  TestHciHalCallbacks callbacks_;
+  int fake_server_socket_ = -1;
+  Thread* thread_;
+};
+
+void check_packet_equal(std::pair<uint8_t, HciPacket> hci_packet1_type_data_pair, H4Packet h4_packet2) {
+  auto packet1_hci_size = hci_packet1_type_data_pair.second.size();
+  ASSERT_EQ(packet1_hci_size + 1, h4_packet2.size());
+  ASSERT_EQ(hci_packet1_type_data_pair.first, h4_packet2[0]);
+  ASSERT_EQ(memcmp(hci_packet1_type_data_pair.second.data(), h4_packet2.data() + 1, packet1_hci_size), 0);
+}
+
+HciPacket make_sample_hci_cmd_pkt(uint8_t parameter_total_length) {
+  HciPacket pkt;
+  pkt.assign(2 + 1 + parameter_total_length, 0x01);
+  pkt[2] = parameter_total_length;
+  return pkt;
+}
+
+HciPacket make_sample_hci_acl_pkt(uint8_t payload_size) {
+  HciPacket pkt;
+  pkt.assign(2 + 2 + payload_size, 0x01);
+  pkt[2] = payload_size;
+  return pkt;
+}
+
+HciPacket make_sample_hci_sco_pkt(uint8_t payload_size) {
+  HciPacket pkt;
+  pkt.assign(3 + payload_size, 0x01);
+  pkt[2] = payload_size;
+  return pkt;
+}
+
+H4Packet make_sample_h4_evt_pkt(uint8_t parameter_total_length) {
+  H4Packet pkt;
+  pkt.assign(1 + 1 + 1 + parameter_total_length, 0x01);
+  pkt[0] = kH4Event;
+  pkt[2] = parameter_total_length;
+  return pkt;
+}
+
+HciPacket make_sample_h4_acl_pkt(uint8_t payload_size) {
+  HciPacket pkt;
+  pkt.assign(1 + 2 + 2 + payload_size, 0x01);
+  pkt[0] = kH4Acl;
+  pkt[3] = payload_size;
+  pkt[4] = 0;
+  return pkt;
+}
+
+HciPacket make_sample_h4_sco_pkt(uint8_t payload_size) {
+  HciPacket pkt;
+  pkt.assign(1 + 3 + payload_size, 0x01);
+  pkt[0] = kH4Sco;
+  pkt[3] = payload_size;
+  return pkt;
+}
+
+size_t read_with_retry(int socket, uint8_t* data, size_t length) {
+  size_t bytes_read = 0;
+  ssize_t bytes_read_current = 0;
+  do {
+    bytes_read_current = read(socket, data + bytes_read, length - bytes_read);
+    bytes_read += bytes_read_current;
+  } while (length > bytes_read && bytes_read_current > 0);
+  return bytes_read;
+}
+
+TEST_F(HciHalRootcanalTest, init_and_close) {}
+
+TEST_F(HciHalRootcanalTest, receive_hci_evt) {
+  H4Packet incoming_packet = make_sample_h4_evt_pkt(3);
+  write(fake_server_socket_, incoming_packet.data(), incoming_packet.size());
+  while (incoming_packets_queue_.size() != 1) {
+  }
+  auto packet = incoming_packets_queue_.front();
+  incoming_packets_queue_.pop();
+  check_packet_equal(packet, incoming_packet);
+}
+
+TEST_F(HciHalRootcanalTest, receive_hci_acl) {
+  H4Packet incoming_packet = make_sample_h4_acl_pkt(3);
+  write(fake_server_socket_, incoming_packet.data(), incoming_packet.size());
+  while (incoming_packets_queue_.size() != 1) {
+  }
+  auto packet = incoming_packets_queue_.front();
+  incoming_packets_queue_.pop();
+  check_packet_equal(packet, incoming_packet);
+}
+
+TEST_F(HciHalRootcanalTest, receive_hci_sco) {
+  H4Packet incoming_packet = make_sample_h4_sco_pkt(3);
+  write(fake_server_socket_, incoming_packet.data(), incoming_packet.size());
+  while (incoming_packets_queue_.size() != 1) {
+  }
+  auto packet = incoming_packets_queue_.front();
+  incoming_packets_queue_.pop();
+  check_packet_equal(packet, incoming_packet);
+}
+
+TEST_F(HciHalRootcanalTest, receive_two_hci_evts) {
+  H4Packet incoming_packet = make_sample_h4_evt_pkt(3);
+  H4Packet incoming_packet2 = make_sample_h4_evt_pkt(5);
+  write(fake_server_socket_, incoming_packet.data(), incoming_packet.size());
+  write(fake_server_socket_, incoming_packet2.data(), incoming_packet2.size());
+  while (incoming_packets_queue_.size() != 2) {
+  }
+  auto packet = incoming_packets_queue_.front();
+  incoming_packets_queue_.pop();
+  check_packet_equal(packet, incoming_packet);
+  packet = incoming_packets_queue_.front();
+  incoming_packets_queue_.pop();
+  check_packet_equal(packet, incoming_packet2);
+}
+
+TEST_F(HciHalRootcanalTest, receive_evt_and_acl) {
+  H4Packet incoming_packet = make_sample_h4_evt_pkt(3);
+  H4Packet incoming_packet2 = make_sample_h4_acl_pkt(5);
+  write(fake_server_socket_, incoming_packet.data(), incoming_packet.size());
+  write(fake_server_socket_, incoming_packet2.data(), incoming_packet2.size());
+  while (incoming_packets_queue_.size() != 2) {
+  }
+  auto packet = incoming_packets_queue_.front();
+  incoming_packets_queue_.pop();
+  check_packet_equal(packet, incoming_packet);
+  packet = incoming_packets_queue_.front();
+  incoming_packets_queue_.pop();
+  check_packet_equal(packet, incoming_packet2);
+}
+
+TEST_F(HciHalRootcanalTest, receive_multiple_acl_batch) {
+  H4Packet incoming_packet = make_sample_h4_acl_pkt(5);
+  int num_packets = 1000;
+  for (int i = 0; i < num_packets; i++) {
+    write(fake_server_socket_, incoming_packet.data(), incoming_packet.size());
+  }
+  while (incoming_packets_queue_.size() != num_packets) {
+  }
+  for (int i = 0; i < num_packets; i++) {
+    auto packet = incoming_packets_queue_.front();
+    incoming_packets_queue_.pop();
+    check_packet_equal(packet, incoming_packet);
+  }
+}
+
+TEST_F(HciHalRootcanalTest, receive_multiple_acl_sequential) {
+  H4Packet incoming_packet = make_sample_h4_acl_pkt(5);
+  int num_packets = 1000;
+  for (int i = 0; i < num_packets; i++) {
+    write(fake_server_socket_, incoming_packet.data(), incoming_packet.size());
+    while (incoming_packets_queue_.empty()) {
+    }
+    auto packet = incoming_packets_queue_.front();
+    incoming_packets_queue_.pop();
+    check_packet_equal(packet, incoming_packet);
+  }
+}
+
+TEST_F(HciHalRootcanalTest, send_hci_cmd) {
+  uint8_t hci_cmd_param_size = 2;
+  HciPacket hci_data = make_sample_hci_cmd_pkt(hci_cmd_param_size);
+  hal_->sendHciCommand(hci_data);
+  H4Packet read_buf(1 + 2 + 1 + hci_cmd_param_size);
+  SetFakeServerSocketToBlocking();
+  auto size_read = read_with_retry(fake_server_socket_, read_buf.data(), read_buf.size());
+
+  ASSERT_EQ(size_read, 1 + hci_data.size());
+  check_packet_equal({kH4Command, hci_data}, read_buf);
+}
+
+TEST_F(HciHalRootcanalTest, send_acl) {
+  uint8_t acl_payload_size = 200;
+  HciPacket acl_packet = make_sample_hci_acl_pkt(acl_payload_size);
+  hal_->sendAclData(acl_packet);
+  H4Packet read_buf(1 + 2 + 2 + acl_payload_size);
+  SetFakeServerSocketToBlocking();
+  auto size_read = read_with_retry(fake_server_socket_, read_buf.data(), read_buf.size());
+
+  ASSERT_EQ(size_read, 1 + acl_packet.size());
+  check_packet_equal({kH4Acl, acl_packet}, read_buf);
+}
+
+TEST_F(HciHalRootcanalTest, send_sco) {
+  uint8_t sco_payload_size = 200;
+  HciPacket sco_packet = make_sample_hci_sco_pkt(sco_payload_size);
+  hal_->sendScoData(sco_packet);
+  H4Packet read_buf(1 + 3 + sco_payload_size);
+  SetFakeServerSocketToBlocking();
+  auto size_read = read_with_retry(fake_server_socket_, read_buf.data(), read_buf.size());
+
+  ASSERT_EQ(size_read, 1 + sco_packet.size());
+  check_packet_equal({kH4Sco, sco_packet}, read_buf);
+}
+
+TEST_F(HciHalRootcanalTest, send_multiple_acl_batch) {
+  uint8_t acl_payload_size = 200;
+  int num_packets = 1000;
+  HciPacket acl_packet = make_sample_hci_acl_pkt(acl_payload_size);
+  for (int i = 0; i < num_packets; i++) {
+    hal_->sendAclData(acl_packet);
+  }
+  H4Packet read_buf(1 + 2 + 2 + acl_payload_size);
+  SetFakeServerSocketToBlocking();
+  for (int i = 0; i < num_packets; i++) {
+    auto size_read = read_with_retry(fake_server_socket_, read_buf.data(), read_buf.size());
+    ASSERT_EQ(size_read, 1 + acl_packet.size());
+    check_packet_equal({kH4Acl, acl_packet}, read_buf);
+  }
+}
+
+TEST_F(HciHalRootcanalTest, send_multiple_acl_sequential) {
+  uint8_t acl_payload_size = 200;
+  int num_packets = 1000;
+  HciPacket acl_packet = make_sample_hci_acl_pkt(acl_payload_size);
+  SetFakeServerSocketToBlocking();
+  for (int i = 0; i < num_packets; i++) {
+    hal_->sendAclData(acl_packet);
+    H4Packet read_buf(1 + 2 + 2 + acl_payload_size);
+    auto size_read = read_with_retry(fake_server_socket_, read_buf.data(), read_buf.size());
+    ASSERT_EQ(size_read, 1 + acl_packet.size());
+    check_packet_equal({kH4Acl, acl_packet}, read_buf);
+  }
+}
+
+TEST(HciHalHidlTest, serialize) {
+  std::vector<uint8_t> bytes = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+  auto packet_bytes = hal::SerializePacket(std::unique_ptr<packet::BasePacketBuilder>(new packet::RawBuilder(bytes)));
+  ASSERT_EQ(bytes, packet_bytes);
+}
+}  // namespace
+}  // namespace hal
+}  // namespace bluetooth
diff --git a/gd/hal/serialize_packet.h b/gd/hal/serialize_packet.h
new file mode 100644
index 0000000..621dcec
--- /dev/null
+++ b/gd/hal/serialize_packet.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include "packet/base_packet_builder.h"
+
+namespace bluetooth {
+namespace hal {
+
+inline std::vector<uint8_t> SerializePacket(std::unique_ptr<packet::BasePacketBuilder> packet) {
+  std::vector<uint8_t> packet_bytes;
+  packet_bytes.reserve(packet->size());
+  packet::BitInserter it(packet_bytes);
+  packet->Serialize(it);
+  return packet_bytes;
+}
+
+}  // namespace hal
+}  // namespace bluetooth
diff --git a/gd/hal/snoop_logger.cc b/gd/hal/snoop_logger.cc
new file mode 100644
index 0000000..3574b17
--- /dev/null
+++ b/gd/hal/snoop_logger.cc
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "hal/snoop_logger.h"
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <bitset>
+#include <chrono>
+
+#include "os/log.h"
+
+namespace bluetooth {
+namespace hal {
+
+namespace {
+typedef struct {
+  uint32_t length_original;
+  uint32_t length_captured;
+  uint32_t flags;
+  uint32_t dropped_packets;
+  uint64_t timestamp;
+  uint8_t type;
+} __attribute__((__packed__)) btsnoop_packet_header_t;
+
+typedef struct {
+  uint8_t identification_pattern[8];
+  uint32_t version_number;
+  uint32_t datalink_type;
+} __attribute__((__packed__)) btsnoop_file_header_t;
+
+constexpr uint64_t BTSNOOP_EPOCH_DELTA = 0x00dcddb30f2f8000ULL;
+
+constexpr uint32_t kBytesToTest = 0x12345678;
+constexpr uint8_t kFirstByte = (const uint8_t&)kBytesToTest;
+constexpr bool isLittleEndian = kFirstByte == 0x78;
+constexpr bool isBigEndian = kFirstByte == 0x12;
+static_assert(isLittleEndian || isBigEndian && isLittleEndian != isBigEndian);
+
+constexpr uint32_t BTSNOOP_VERSION_NUMBER = isLittleEndian ? 0x01000000 : 1;
+constexpr uint32_t BTSNOOP_DATALINK_TYPE =
+    isLittleEndian ? 0xea030000 : 0x03ea;  // Datalink Type code for HCI UART (H4) is 1002
+uint64_t htonll(uint64_t ll) {
+  if constexpr (isLittleEndian) {
+    return static_cast<uint64_t>(htonl(ll & 0xffffffff)) << 32 | htonl(ll >> 32);
+  } else {
+    return ll;
+  }
+}
+
+constexpr btsnoop_file_header_t BTSNOOP_FILE_HEADER = {
+    .identification_pattern = {'b', 't', 's', 'n', 'o', 'o', 'p', 0x00},
+    .version_number = BTSNOOP_VERSION_NUMBER,
+    .datalink_type = BTSNOOP_DATALINK_TYPE};
+}  // namespace
+
+SnoopLogger::SnoopLogger() {
+  bool file_exists;
+  {
+    std::ifstream btsnoop_istream(file_path);
+    file_exists = btsnoop_istream.is_open();
+  }
+  btsnoop_ostream_.open(file_path, std::ios::binary | std::ios::app | std::ios::out);
+  if (!file_exists) {
+    LOG_INFO("Creating new BTSNOOP");
+    btsnoop_ostream_.write(reinterpret_cast<const char*>(&BTSNOOP_FILE_HEADER), sizeof(btsnoop_file_header_t));
+  } else {
+    LOG_INFO("Appending to old BTSNOOP");
+  }
+}
+
+void SnoopLogger::SetFilePath(const std::string& filename) {
+  file_path = filename;
+}
+
+void SnoopLogger::capture(const HciPacket& packet, Direction direction, PacketType type) {
+  uint64_t timestamp_us =
+      std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch())
+          .count();
+  std::lock_guard<std::mutex> lock(file_mutex_);
+  std::bitset<32> flags = 0;
+  switch (type) {
+    case PacketType::CMD:
+      flags.set(0, false);
+      flags.set(1, true);
+      break;
+    case PacketType::ACL:
+      flags.set(0, direction == Direction::INCOMING);
+      flags.set(1, false);
+      break;
+    case PacketType::SCO:
+      flags.set(0, direction == Direction::INCOMING);
+      flags.set(1, false);
+      break;
+    case PacketType::EVT:
+      flags.set(0, true);
+      flags.set(1, true);
+      break;
+  }
+  uint32_t length = packet.size() + /* type byte */ 1;
+  btsnoop_packet_header_t header = {.length_original = htonl(length),
+                                    .length_captured = htonl(length),
+                                    .flags = htonl(static_cast<uint32_t>(flags.to_ulong())),
+                                    .dropped_packets = 0,
+                                    .timestamp = htonll(timestamp_us + BTSNOOP_EPOCH_DELTA),
+                                    .type = static_cast<uint8_t>(type)};
+  btsnoop_ostream_.write(reinterpret_cast<const char*>(&header), sizeof(btsnoop_packet_header_t));
+  btsnoop_ostream_.write(reinterpret_cast<const char*>(packet.data()), packet.size());
+  if (AlwaysFlush) btsnoop_ostream_.flush();
+}
+
+void SnoopLogger::ListDependencies(ModuleList* list) {
+  // We have no dependencies
+}
+
+void SnoopLogger::Start() {}
+
+void SnoopLogger::Stop() {}
+
+std::string SnoopLogger::file_path = SnoopLogger::DefaultFilePath;
+
+const ModuleFactory SnoopLogger::Factory = ModuleFactory([]() {
+  return new SnoopLogger();
+});
+
+}  // namespace hal
+}  // namespace bluetooth
diff --git a/gd/hal/snoop_logger.h b/gd/hal/snoop_logger.h
new file mode 100644
index 0000000..8021c2b
--- /dev/null
+++ b/gd/hal/snoop_logger.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <fstream>
+#include <iostream>
+#include <mutex>
+#include <string>
+
+#include "hal/hci_hal.h"
+#include "module.h"
+
+namespace bluetooth {
+namespace hal {
+
+class SnoopLogger : public ::bluetooth::Module {
+ public:
+  static const ModuleFactory Factory;
+
+  // Each transport using SnoopLogger should define its own DefaultFilepath
+  static const std::string DefaultFilePath;
+  // Set File Path before module is started to ensure all packets are written to the right file
+  static void SetFilePath(const std::string& filename);
+  // Flag to allow flush into persistent memory on every packet captured. This is enabled on host for debugging.
+  static const bool AlwaysFlush;
+
+  enum class PacketType {
+    CMD = 1,
+    ACL = 2,
+    SCO = 3,
+    EVT = 4,
+  };
+
+  enum class Direction {
+    INCOMING,
+    OUTGOING,
+  };
+
+  void capture(const HciPacket& packet, Direction direction, PacketType type);
+
+ protected:
+  void ListDependencies(ModuleList* list) override;
+  void Start() override;
+  void Stop() override;
+
+ private:
+  SnoopLogger();
+  static std::string file_path;
+  std::ofstream btsnoop_ostream_;
+  std::mutex file_mutex_;
+};
+
+}  // namespace hal
+}  // namespace bluetooth
diff --git a/gd/hci/Android.bp b/gd/hci/Android.bp
new file mode 100644
index 0000000..98d8d41
--- /dev/null
+++ b/gd/hci/Android.bp
@@ -0,0 +1,53 @@
+filegroup {
+    name: "BluetoothHciSources",
+    srcs: [
+        "acl_manager.cc",
+        "acl_fragmenter.cc",
+        "address.cc",
+        "class_of_device.cc",
+        "controller.cc",
+        "device.cc",
+        "device_database.cc",
+        "hci_layer.cc",
+        "le_advertising_manager.cc",
+        "le_scanning_manager.cc",
+    ],
+}
+
+filegroup {
+    name: "BluetoothHciTestSources",
+    srcs: [
+        "acl_builder_test.cc",
+        "acl_manager_test.cc",
+        "address_unittest.cc",
+        "address_with_type_test.cc",
+        "class_of_device_unittest.cc",
+        "controller_test.cc",
+        "device_test.cc",
+        "device_database_test.cc",
+        "dual_device_test.cc",
+        "hci_layer_test.cc",
+        "hci_packets_test.cc",
+        "le_advertising_manager_test.cc",
+        "le_scanning_manager_test.cc",
+    ],
+}
+
+filegroup {
+    name: "BluetoothFacade_hci_layer",
+    srcs: [
+        "facade/facade.cc",
+        "facade/acl_manager_facade.cc",
+        "facade/controller_facade.cc",
+        "facade/le_acl_manager_facade.cc",
+        "facade/le_advertising_manager_facade.cc",
+        "facade/le_scanning_manager_facade.cc",
+    ],
+}
+
+filegroup {
+    name: "BluetoothHciFuzzTestSources",
+    srcs: [
+        "hci_packets_fuzz_test.cc",
+    ],
+}
diff --git a/gd/hci/acl_builder_test.cc b/gd/hci/acl_builder_test.cc
new file mode 100644
index 0000000..7fc59c6
--- /dev/null
+++ b/gd/hci/acl_builder_test.cc
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "hci/hci_packets.h"
+
+#include <gtest/gtest.h>
+#include <memory>
+
+#include "os/log.h"
+#include "packet/bit_inserter.h"
+#include "packet/raw_builder.h"
+
+using bluetooth::packet::BitInserter;
+using bluetooth::packet::RawBuilder;
+using std::vector;
+
+namespace {
+vector<uint8_t> information_request = {
+    0xfe, 0x2e, 0x0a, 0x00, 0x06, 0x00, 0x01, 0x00, 0x0a, 0x02, 0x02, 0x00, 0x02, 0x00,
+};
+// 0x00, 0x01, 0x02, 0x03, ...
+vector<uint8_t> counting_bytes;
+// 0xFF, 0xFE, 0xFD, 0xFC, ...
+vector<uint8_t> counting_down_bytes;
+const size_t count_size = 0x8;
+
+}  // namespace
+
+namespace bluetooth {
+namespace hci {
+
+class AclBuilderTest : public ::testing::Test {
+ public:
+  AclBuilderTest() {
+    counting_bytes.reserve(count_size);
+    counting_down_bytes.reserve(count_size);
+    for (size_t i = 0; i < count_size; i++) {
+      counting_bytes.push_back(i);
+      counting_down_bytes.push_back(~i);
+    }
+  }
+  ~AclBuilderTest() = default;
+};
+
+TEST(AclBuilderTest, buildAclCount) {
+  uint16_t handle = 0x0314;
+  PacketBoundaryFlag packet_boundary_flag = PacketBoundaryFlag::FIRST_AUTOMATICALLY_FLUSHABLE;
+  BroadcastFlag broadcast_flag = BroadcastFlag::ACTIVE_SLAVE_BROADCAST;
+
+  std::unique_ptr<RawBuilder> count_payload = std::make_unique<RawBuilder>();
+  count_payload->AddOctets(counting_bytes);
+  ASSERT_EQ(counting_bytes.size(), count_payload->size());
+
+  std::unique_ptr<AclPacketBuilder> count_packet =
+      AclPacketBuilder::Create(handle, packet_boundary_flag, broadcast_flag, std::move(count_payload));
+
+  ASSERT_EQ(counting_bytes.size() + 4, count_packet->size());
+
+  std::shared_ptr<std::vector<uint8_t>> count_packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*count_packet_bytes);
+  count_packet->Serialize(it);
+
+  PacketView<true> count_packet_bytes_view(count_packet_bytes);
+  AclPacketView count_packet_view = AclPacketView::Create(count_packet_bytes_view);
+  ASSERT_TRUE(count_packet_view.IsValid());
+
+  ASSERT_EQ(handle, count_packet_view.GetHandle());
+  ASSERT_EQ(packet_boundary_flag, count_packet_view.GetPacketBoundaryFlag());
+  ASSERT_EQ(broadcast_flag, count_packet_view.GetBroadcastFlag());
+  PacketView<true> count_view = count_packet_view.GetPayload();
+
+  ASSERT_EQ(count_view.size(), counting_bytes.size());
+  for (size_t i = 0; i < count_view.size(); i++) {
+    ASSERT_EQ(count_view[i], counting_bytes[i]);
+  }
+}
+
+TEST(AclBuilderTest, buildAclCountInverted) {
+  uint16_t handle = 0x0304;
+  PacketBoundaryFlag packet_boundary_flag = PacketBoundaryFlag::CONTINUING_FRAGMENT;
+  BroadcastFlag broadcast_flag = BroadcastFlag::POINT_TO_POINT;
+
+  std::unique_ptr<RawBuilder> counting_down_bytes_payload = std::make_unique<RawBuilder>();
+  counting_down_bytes_payload->AddOctets(counting_down_bytes);
+  ASSERT_EQ(counting_down_bytes.size(), counting_down_bytes_payload->size());
+
+  std::unique_ptr<AclPacketBuilder> counting_down_bytes_packet =
+      AclPacketBuilder::Create(handle, packet_boundary_flag, broadcast_flag, std::move(counting_down_bytes_payload));
+
+  ASSERT_EQ(counting_down_bytes.size() + 4, counting_down_bytes_packet->size());
+
+  std::shared_ptr<std::vector<uint8_t>> counting_down_bytes_packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*counting_down_bytes_packet_bytes);
+  counting_down_bytes_packet->Serialize(it);
+  PacketView<true> counting_down_bytes_packet_bytes_view(counting_down_bytes_packet_bytes);
+  AclPacketView counting_down_bytes_packet_view = AclPacketView::Create(counting_down_bytes_packet_bytes_view);
+  ASSERT_TRUE(counting_down_bytes_packet_view.IsValid());
+
+  ASSERT_EQ(handle, counting_down_bytes_packet_view.GetHandle());
+  ASSERT_EQ(packet_boundary_flag, counting_down_bytes_packet_view.GetPacketBoundaryFlag());
+  ASSERT_EQ(broadcast_flag, counting_down_bytes_packet_view.GetBroadcastFlag());
+  PacketView<true> counting_down_bytes_view = counting_down_bytes_packet_view.GetPayload();
+
+  ASSERT_EQ(counting_down_bytes_view.size(), counting_down_bytes.size());
+  for (size_t i = 0; i < counting_down_bytes_view.size(); i++) {
+    ASSERT_EQ(counting_down_bytes_view[i], counting_down_bytes[i]);
+  }
+}
+
+TEST(AclBuilderTest, buildInformationRequest) {
+  uint16_t handle = 0x0efe;
+  PacketBoundaryFlag packet_boundary_flag = PacketBoundaryFlag::FIRST_AUTOMATICALLY_FLUSHABLE;
+  BroadcastFlag broadcast_flag = BroadcastFlag::POINT_TO_POINT;
+
+  std::vector<uint8_t> payload_bytes(information_request.begin() + 4, information_request.end());
+  std::unique_ptr<RawBuilder> payload = std::make_unique<RawBuilder>();
+  payload->AddOctets(payload_bytes);
+  ASSERT_EQ(payload_bytes.size(), payload->size());
+
+  std::unique_ptr<AclPacketBuilder> packet =
+      AclPacketBuilder::Create(handle, packet_boundary_flag, broadcast_flag, std::move(payload));
+
+  ASSERT_EQ(information_request.size(), packet->size());
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  packet->Serialize(it);
+  PacketView<true> packet_bytes_view(packet_bytes);
+  AclPacketView packet_view = AclPacketView::Create(packet_bytes_view);
+  ASSERT_TRUE(packet_view.IsValid());
+
+  ASSERT_EQ(packet_bytes->size(), information_request.size());
+  for (size_t i = 0; i < packet_bytes->size(); i++) {
+    ASSERT_EQ((*packet_bytes)[i], information_request[i]);
+  }
+
+  ASSERT_EQ(handle, packet_view.GetHandle());
+  ASSERT_EQ(packet_boundary_flag, packet_view.GetPacketBoundaryFlag());
+  ASSERT_EQ(broadcast_flag, packet_view.GetBroadcastFlag());
+  PacketView<true> payload_view = packet_view.GetPayload();
+
+  ASSERT_EQ(payload_view.size(), payload_bytes.size());
+  for (size_t i = 0; i < payload_view.size(); i++) {
+    ASSERT_EQ(payload_view[i], payload_bytes[i]);
+  }
+
+  ASSERT_EQ(packet_view.size(), information_request.size());
+  for (size_t i = 0; i < packet_view.size(); i++) {
+    ASSERT_EQ(packet_view[i], information_request[i]);
+  }
+}
+
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/acl_fragmenter.cc b/gd/hci/acl_fragmenter.cc
new file mode 100644
index 0000000..fa3b31d
--- /dev/null
+++ b/gd/hci/acl_fragmenter.cc
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "hci/acl_fragmenter.h"
+
+#include "os/log.h"
+#include "packet/fragmenting_inserter.h"
+
+namespace bluetooth {
+namespace hci {
+
+AclFragmenter::AclFragmenter(size_t mtu, std::unique_ptr<packet::BasePacketBuilder> packet)
+    : mtu_(mtu), packet_(std::move(packet)) {}
+
+std::vector<std::unique_ptr<packet::RawBuilder>> AclFragmenter::GetFragments() {
+  std::vector<std::unique_ptr<packet::RawBuilder>> to_return;
+  packet::FragmentingInserter fragmenting_inserter(mtu_, std::back_insert_iterator(to_return));
+  packet_->Serialize(fragmenting_inserter);
+  fragmenting_inserter.finalize();
+  return to_return;
+}
+
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/acl_fragmenter.h b/gd/hci/acl_fragmenter.h
new file mode 100644
index 0000000..4bc3d06
--- /dev/null
+++ b/gd/hci/acl_fragmenter.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <forward_list>
+#include <iterator>
+#include <memory>
+#include <vector>
+
+#include "packet/base_packet_builder.h"
+#include "packet/raw_builder.h"
+
+namespace bluetooth {
+namespace hci {
+
+class AclFragmenter {
+ public:
+  AclFragmenter(size_t mtu, std::unique_ptr<packet::BasePacketBuilder> input);
+  virtual ~AclFragmenter() = default;
+
+  std::vector<std::unique_ptr<packet::RawBuilder>> GetFragments();
+
+ private:
+  size_t mtu_;
+  std::unique_ptr<packet::BasePacketBuilder> packet_;
+};
+
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/acl_manager.cc b/gd/hci/acl_manager.cc
new file mode 100644
index 0000000..0271854
--- /dev/null
+++ b/gd/hci/acl_manager.cc
@@ -0,0 +1,2148 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "hci/acl_manager.h"
+
+#include <future>
+#include <queue>
+#include <set>
+#include <utility>
+
+#include "acl_fragmenter.h"
+#include "acl_manager.h"
+#include "common/bidi_queue.h"
+#include "hci/controller.h"
+#include "hci/hci_layer.h"
+
+namespace bluetooth {
+namespace hci {
+
+constexpr uint16_t kQualcommDebugHandle = 0xedc;
+constexpr size_t kMaxQueuedPacketsPerConnection = 10;
+
+using common::Bind;
+using common::BindOnce;
+
+namespace {
+class PacketViewForRecombination : public packet::PacketView<kLittleEndian> {
+ public:
+  PacketViewForRecombination(const PacketView& packetView) : PacketView(packetView) {}
+  void AppendPacketView(packet::PacketView<kLittleEndian> to_append) {
+    Append(to_append);
+  }
+};
+
+constexpr int kL2capBasicFrameHeaderSize = 4;
+
+// Per spec 5.1 Vol 2 Part B 5.3, ACL link shall carry L2CAP data. Therefore, an ACL packet shall contain L2CAP PDU.
+// This function returns the PDU size of the L2CAP data if it's a starting packet. Returns 0 if it's invalid.
+uint16_t GetL2capPduSize(AclPacketView packet) {
+  auto l2cap_payload = packet.GetPayload();
+  if (l2cap_payload.size() < kL2capBasicFrameHeaderSize) {
+    LOG_ERROR("Controller sent an invalid L2CAP starting packet!");
+    return 0;
+  }
+  return (l2cap_payload.at(1) << 8) + l2cap_payload.at(0);
+}
+
+}  // namespace
+
+struct AclManager::acl_connection {
+  acl_connection(AddressWithType address_with_type, os::Handler* handler)
+      : address_with_type_(address_with_type), handler_(handler) {}
+  friend AclConnection;
+  AddressWithType address_with_type_;
+  os::Handler* handler_;
+  std::unique_ptr<AclConnection::Queue> queue_ = std::make_unique<AclConnection::Queue>(10);
+  bool is_disconnected_ = false;
+  ErrorCode disconnect_reason_;
+  os::Handler* command_complete_handler_ = nullptr;
+  os::Handler* disconnect_handler_ = nullptr;
+  ConnectionManagementCallbacks* command_complete_callbacks_ = nullptr;
+  common::OnceCallback<void(ErrorCode)> on_disconnect_callback_;
+  // For LE Connection parameter update from L2CAP
+  common::OnceCallback<void(ErrorCode)> on_connection_update_complete_callback_;
+  os::Handler* on_connection_update_complete_callback_handler_ = nullptr;
+  // Round-robin: Track if dequeue is registered for this connection
+  bool is_registered_ = false;
+  // Credits: Track the number of packets which have been sent to the controller
+  uint16_t number_of_sent_packets_ = 0;
+  PacketViewForRecombination recombination_stage_{std::make_shared<std::vector<uint8_t>>()};
+  int remaining_sdu_continuation_packet_size_ = 0;
+  bool enqueue_registered_ = false;
+  std::queue<packet::PacketView<kLittleEndian>> incoming_queue_;
+
+  std::unique_ptr<packet::PacketView<kLittleEndian>> on_incoming_data_ready() {
+    auto packet = incoming_queue_.front();
+    incoming_queue_.pop();
+    if (incoming_queue_.empty()) {
+      auto queue_end = queue_->GetDownEnd();
+      queue_end->UnregisterEnqueue();
+      enqueue_registered_ = false;
+    }
+    return std::make_unique<PacketView<kLittleEndian>>(packet);
+  }
+
+  void on_incoming_packet(AclPacketView packet) {
+    // TODO: What happens if the connection is stalled and fills up?
+    PacketView<kLittleEndian> payload = packet.GetPayload();
+    auto payload_size = payload.size();
+    auto packet_boundary_flag = packet.GetPacketBoundaryFlag();
+    if (packet_boundary_flag == PacketBoundaryFlag::FIRST_NON_AUTOMATICALLY_FLUSHABLE) {
+      LOG_ERROR("Controller is not allowed to send FIRST_NON_AUTOMATICALLY_FLUSHABLE to host except loopback mode");
+      return;
+    }
+    if (packet_boundary_flag == PacketBoundaryFlag::CONTINUING_FRAGMENT) {
+      if (remaining_sdu_continuation_packet_size_ < payload_size) {
+        LOG_WARN("Remote sent unexpected L2CAP PDU. Drop the entire L2CAP PDU");
+        recombination_stage_ = PacketViewForRecombination(std::make_shared<std::vector<uint8_t>>());
+        remaining_sdu_continuation_packet_size_ = 0;
+        return;
+      }
+      remaining_sdu_continuation_packet_size_ -= payload_size;
+      recombination_stage_.AppendPacketView(payload);
+      if (remaining_sdu_continuation_packet_size_ != 0) {
+        return;
+      } else {
+        payload = recombination_stage_;
+        recombination_stage_ = PacketViewForRecombination(std::make_shared<std::vector<uint8_t>>());
+      }
+    } else if (packet_boundary_flag == PacketBoundaryFlag::FIRST_AUTOMATICALLY_FLUSHABLE) {
+      if (recombination_stage_.size() > 0) {
+        LOG_ERROR("Controller sent a starting packet without finishing previous packet. Drop previous one.");
+      }
+      auto l2cap_pdu_size = GetL2capPduSize(packet);
+      remaining_sdu_continuation_packet_size_ = l2cap_pdu_size - (payload_size - kL2capBasicFrameHeaderSize);
+      if (remaining_sdu_continuation_packet_size_ > 0) {
+        recombination_stage_ = payload;
+        return;
+      }
+    }
+    if (incoming_queue_.size() > kMaxQueuedPacketsPerConnection) {
+      LOG_ERROR("Dropping packet due to congestion from remote:%s", address_with_type_.ToString().c_str());
+      return;
+    }
+
+    incoming_queue_.push(payload);
+    if (!enqueue_registered_) {
+      enqueue_registered_ = true;
+      auto queue_end = queue_->GetDownEnd();
+      queue_end->RegisterEnqueue(
+          handler_, common::Bind(&AclManager::acl_connection::on_incoming_data_ready, common::Unretained(this)));
+    }
+  }
+
+  void call_disconnect_callback() {
+    disconnect_handler_->Post(BindOnce(std::move(on_disconnect_callback_), disconnect_reason_));
+  }
+};
+
+struct AclManager::impl {
+  impl(const AclManager& acl_manager) : acl_manager_(acl_manager) {}
+
+  void Start() {
+    hci_layer_ = acl_manager_.GetDependency<HciLayer>();
+    handler_ = acl_manager_.GetHandler();
+    controller_ = acl_manager_.GetDependency<Controller>();
+    max_acl_packet_credits_ = controller_->GetControllerNumAclPacketBuffers();
+    acl_packet_credits_ = max_acl_packet_credits_;
+    acl_buffer_length_ = controller_->GetControllerAclPacketLength();
+    controller_->RegisterCompletedAclPacketsCallback(
+        common::Bind(&impl::incoming_acl_credits, common::Unretained(this)), handler_);
+
+    // TODO: determine when we should reject connection
+    should_accept_connection_ = common::Bind([](Address, ClassOfDevice) { return true; });
+    hci_queue_end_ = hci_layer_->GetAclQueueEnd();
+    hci_queue_end_->RegisterDequeue(
+        handler_, common::Bind(&impl::dequeue_and_route_acl_packet_to_connection, common::Unretained(this)));
+    hci_layer_->RegisterEventHandler(EventCode::CONNECTION_COMPLETE,
+                                     Bind(&impl::on_connection_complete, common::Unretained(this)), handler_);
+    hci_layer_->RegisterEventHandler(EventCode::DISCONNECTION_COMPLETE,
+                                     Bind(&impl::on_disconnection_complete, common::Unretained(this)), handler_);
+    hci_layer_->RegisterEventHandler(EventCode::CONNECTION_REQUEST,
+                                     Bind(&impl::on_incoming_connection, common::Unretained(this)), handler_);
+    hci_layer_->RegisterLeEventHandler(SubeventCode::CONNECTION_COMPLETE,
+                                       Bind(&impl::on_le_connection_complete, common::Unretained(this)), handler_);
+    hci_layer_->RegisterLeEventHandler(SubeventCode::ENHANCED_CONNECTION_COMPLETE,
+                                       Bind(&impl::on_le_enhanced_connection_complete, common::Unretained(this)),
+                                       handler_);
+    hci_layer_->RegisterLeEventHandler(SubeventCode::CONNECTION_UPDATE_COMPLETE,
+                                       Bind(&impl::on_le_connection_update_complete, common::Unretained(this)),
+                                       handler_);
+    hci_layer_->RegisterEventHandler(EventCode::CONNECTION_PACKET_TYPE_CHANGED,
+                                     Bind(&impl::on_connection_packet_type_changed, common::Unretained(this)),
+                                     handler_);
+    hci_layer_->RegisterEventHandler(EventCode::AUTHENTICATION_COMPLETE,
+                                     Bind(&impl::on_authentication_complete, common::Unretained(this)), handler_);
+    hci_layer_->RegisterEventHandler(EventCode::READ_CLOCK_OFFSET_COMPLETE,
+                                     Bind(&impl::on_read_clock_offset_complete, common::Unretained(this)), handler_);
+    hci_layer_->RegisterEventHandler(EventCode::MODE_CHANGE, Bind(&impl::on_mode_change, common::Unretained(this)),
+                                     handler_);
+    hci_layer_->RegisterEventHandler(EventCode::QOS_SETUP_COMPLETE,
+                                     Bind(&impl::on_qos_setup_complete, common::Unretained(this)), handler_);
+    hci_layer_->RegisterEventHandler(EventCode::ROLE_CHANGE, Bind(&impl::on_role_change, common::Unretained(this)),
+                                     handler_);
+    hci_layer_->RegisterEventHandler(EventCode::FLOW_SPECIFICATION_COMPLETE,
+                                     Bind(&impl::on_flow_specification_complete, common::Unretained(this)), handler_);
+    hci_layer_->RegisterEventHandler(EventCode::FLUSH_OCCURRED,
+                                     Bind(&impl::on_flush_occurred, common::Unretained(this)), handler_);
+    hci_layer_->RegisterEventHandler(EventCode::READ_REMOTE_SUPPORTED_FEATURES_COMPLETE,
+                                     Bind(&impl::on_read_remote_supported_features_complete, common::Unretained(this)),
+                                     handler_);
+    hci_layer_->RegisterEventHandler(EventCode::READ_REMOTE_EXTENDED_FEATURES_COMPLETE,
+                                     Bind(&impl::on_read_remote_extended_features_complete, common::Unretained(this)),
+                                     handler_);
+    hci_layer_->RegisterEventHandler(EventCode::READ_REMOTE_VERSION_INFORMATION_COMPLETE,
+                                     Bind(&impl::on_read_remote_version_information_complete, common::Unretained(this)),
+                                     handler_);
+    hci_layer_->RegisterEventHandler(EventCode::ENCRYPTION_CHANGE,
+                                     Bind(&impl::on_encryption_change, common::Unretained(this)), handler_);
+    hci_layer_->RegisterEventHandler(EventCode::LINK_SUPERVISION_TIMEOUT_CHANGED,
+                                     Bind(&impl::on_link_supervision_timeout_changed, common::Unretained(this)),
+                                     handler_);
+    hci_mtu_ = controller_->GetControllerAclPacketLength();
+  }
+
+  void Stop() {
+    hci_layer_->UnregisterEventHandler(EventCode::DISCONNECTION_COMPLETE);
+    hci_layer_->UnregisterEventHandler(EventCode::CONNECTION_COMPLETE);
+    hci_layer_->UnregisterEventHandler(EventCode::CONNECTION_REQUEST);
+    hci_layer_->UnregisterEventHandler(EventCode::AUTHENTICATION_COMPLETE);
+    hci_layer_->UnregisterEventHandler(EventCode::READ_REMOTE_SUPPORTED_FEATURES_COMPLETE);
+    hci_layer_->UnregisterEventHandler(EventCode::READ_REMOTE_EXTENDED_FEATURES_COMPLETE);
+    hci_queue_end_->UnregisterDequeue();
+    unregister_all_connections();
+    acl_connections_.clear();
+    hci_queue_end_ = nullptr;
+    handler_ = nullptr;
+    hci_layer_ = nullptr;
+  }
+
+  void incoming_acl_credits(uint16_t handle, uint16_t credits) {
+    auto connection_pair = acl_connections_.find(handle);
+    if (connection_pair == acl_connections_.end()) {
+      LOG_INFO("Dropping %hx received credits to unknown connection 0x%0hx", credits, handle);
+      return;
+    }
+    if (connection_pair->second.is_disconnected_) {
+      LOG_INFO("Dropping %hx received credits to disconnected connection 0x%0hx", credits, handle);
+      return;
+    }
+    connection_pair->second.number_of_sent_packets_ -= credits;
+    acl_packet_credits_ += credits;
+    ASSERT(acl_packet_credits_ <= max_acl_packet_credits_);
+    start_round_robin();
+  }
+
+  // Round-robin scheduler
+  void start_round_robin() {
+    if (acl_packet_credits_ == 0) {
+      return;
+    }
+    if (!fragments_to_send_.empty()) {
+      send_next_fragment();
+      return;
+    }
+    for (auto connection_pair = acl_connections_.begin(); connection_pair != acl_connections_.end();
+         connection_pair = std::next(connection_pair)) {
+      if (connection_pair->second.is_registered_) {
+        continue;
+      }
+      connection_pair->second.is_registered_ = true;
+      connection_pair->second.queue_->GetDownEnd()->RegisterDequeue(
+          handler_, common::Bind(&impl::handle_dequeue_from_upper, common::Unretained(this), connection_pair));
+    }
+  }
+
+  void handle_dequeue_from_upper(std::map<uint16_t, acl_connection>::iterator connection_pair) {
+    current_connection_pair_ = connection_pair;
+    buffer_packet();
+  }
+
+  void unregister_all_connections() {
+    for (auto connection_pair = acl_connections_.begin(); connection_pair != acl_connections_.end();
+         connection_pair = std::next(connection_pair)) {
+      if (connection_pair->second.is_registered_) {
+        connection_pair->second.is_registered_ = false;
+        connection_pair->second.queue_->GetDownEnd()->UnregisterDequeue();
+      }
+    }
+  }
+
+  void buffer_packet() {
+    unregister_all_connections();
+    BroadcastFlag broadcast_flag = BroadcastFlag::POINT_TO_POINT;
+    //   Wrap packet and enqueue it
+    uint16_t handle = current_connection_pair_->first;
+
+    auto packet = current_connection_pair_->second.queue_->GetDownEnd()->TryDequeue();
+    ASSERT(packet != nullptr);
+
+    if (packet->size() <= hci_mtu_) {
+      fragments_to_send_.push_front(AclPacketBuilder::Create(handle, PacketBoundaryFlag::FIRST_AUTOMATICALLY_FLUSHABLE,
+                                                             broadcast_flag, std::move(packet)));
+    } else {
+      auto fragments = AclFragmenter(hci_mtu_, std::move(packet)).GetFragments();
+      PacketBoundaryFlag packet_boundary_flag = PacketBoundaryFlag::FIRST_AUTOMATICALLY_FLUSHABLE;
+      for (size_t i = 0; i < fragments.size(); i++) {
+        fragments_to_send_.push_back(
+            AclPacketBuilder::Create(handle, packet_boundary_flag, broadcast_flag, std::move(fragments[i])));
+        packet_boundary_flag = PacketBoundaryFlag::CONTINUING_FRAGMENT;
+      }
+    }
+    ASSERT(fragments_to_send_.size() > 0);
+
+    current_connection_pair_->second.number_of_sent_packets_ += fragments_to_send_.size();
+    send_next_fragment();
+  }
+
+  void send_next_fragment() {
+    hci_queue_end_->RegisterEnqueue(handler_,
+                                    common::Bind(&impl::handle_enqueue_next_fragment, common::Unretained(this)));
+  }
+
+  std::unique_ptr<AclPacketBuilder> handle_enqueue_next_fragment() {
+    ASSERT(acl_packet_credits_ > 0);
+    if (acl_packet_credits_ == 1 || fragments_to_send_.size() == 1) {
+      hci_queue_end_->UnregisterEnqueue();
+      if (fragments_to_send_.size() == 1) {
+        handler_->Post(common::BindOnce(&impl::start_round_robin, common::Unretained(this)));
+      }
+    }
+    ASSERT(fragments_to_send_.size() > 0);
+    auto raw_pointer = fragments_to_send_.front().release();
+    acl_packet_credits_ -= 1;
+    fragments_to_send_.pop_front();
+    return std::unique_ptr<AclPacketBuilder>(raw_pointer);
+  }
+
+  void dequeue_and_route_acl_packet_to_connection() {
+    auto packet = hci_queue_end_->TryDequeue();
+    ASSERT(packet != nullptr);
+    if (!packet->IsValid()) {
+      LOG_INFO("Dropping invalid packet of size %zu", packet->size());
+      return;
+    }
+    uint16_t handle = packet->GetHandle();
+    if (handle == kQualcommDebugHandle) {
+      return;
+    }
+    auto connection_pair = acl_connections_.find(handle);
+    if (connection_pair == acl_connections_.end()) {
+      LOG_INFO("Dropping packet of size %zu to unknown connection 0x%0hx", packet->size(), handle);
+      return;
+    }
+
+    connection_pair->second.on_incoming_packet(*packet);
+  }
+
+  void on_incoming_connection(EventPacketView packet) {
+    ConnectionRequestView request = ConnectionRequestView::Create(packet);
+    ASSERT(request.IsValid());
+    Address address = request.GetBdAddr();
+    if (client_callbacks_ == nullptr) {
+      LOG_ERROR("No callbacks to call");
+      auto reason = RejectConnectionReason::LIMITED_RESOURCES;
+      this->reject_connection(RejectConnectionRequestBuilder::Create(address, reason));
+      return;
+    }
+    connecting_.insert(address);
+    if (is_classic_link_already_connected(address)) {
+      auto reason = RejectConnectionReason::UNACCEPTABLE_BD_ADDR;
+      this->reject_connection(RejectConnectionRequestBuilder::Create(address, reason));
+    } else if (should_accept_connection_.Run(address, request.GetClassOfDevice())) {
+      this->accept_connection(address);
+    } else {
+      auto reason = RejectConnectionReason::LIMITED_RESOURCES;  // TODO: determine reason
+      this->reject_connection(RejectConnectionRequestBuilder::Create(address, reason));
+    }
+  }
+
+  void on_classic_connection_complete(Address address) {
+    auto connecting_addr = connecting_.find(address);
+    if (connecting_addr == connecting_.end()) {
+      LOG_WARN("No prior connection request for %s", address.ToString().c_str());
+    } else {
+      connecting_.erase(connecting_addr);
+    }
+  }
+
+  void on_common_le_connection_complete(AddressWithType address_with_type) {
+    auto connecting_addr_with_type = connecting_le_.find(address_with_type);
+    if (connecting_addr_with_type == connecting_le_.end()) {
+      LOG_WARN("No prior connection request for %s", address_with_type.ToString().c_str());
+    } else {
+      connecting_le_.erase(connecting_addr_with_type);
+    }
+  }
+
+  void on_le_connection_complete(LeMetaEventView packet) {
+    LeConnectionCompleteView connection_complete = LeConnectionCompleteView::Create(packet);
+    ASSERT(connection_complete.IsValid());
+    auto status = connection_complete.GetStatus();
+    auto address = connection_complete.GetPeerAddress();
+    auto peer_address_type = connection_complete.GetPeerAddressType();
+    // TODO: find out which address and type was used to initiate the connection
+    AddressWithType address_with_type(address, peer_address_type);
+    on_common_le_connection_complete(address_with_type);
+    if (status != ErrorCode::SUCCESS) {
+      le_client_handler_->Post(common::BindOnce(&LeConnectionCallbacks::OnLeConnectFail,
+                                                common::Unretained(le_client_callbacks_), address_with_type, status));
+      return;
+    }
+    // TODO: Check and save other connection parameters
+    uint16_t handle = connection_complete.GetConnectionHandle();
+    ASSERT(acl_connections_.count(handle) == 0);
+    acl_connections_.emplace(std::piecewise_construct, std::forward_as_tuple(handle),
+                             std::forward_as_tuple(address_with_type, handler_));
+    if (acl_connections_.size() == 1 && fragments_to_send_.size() == 0) {
+      start_round_robin();
+    }
+    auto role = connection_complete.GetRole();
+    std::unique_ptr<AclConnection> connection_proxy(
+        new AclConnection(&acl_manager_, handle, address, peer_address_type, role));
+    le_client_handler_->Post(common::BindOnce(&LeConnectionCallbacks::OnLeConnectSuccess,
+                                              common::Unretained(le_client_callbacks_), address_with_type,
+                                              std::move(connection_proxy)));
+  }
+
+  void on_le_enhanced_connection_complete(LeMetaEventView packet) {
+    LeEnhancedConnectionCompleteView connection_complete = LeEnhancedConnectionCompleteView::Create(packet);
+    ASSERT(connection_complete.IsValid());
+    auto status = connection_complete.GetStatus();
+    auto address = connection_complete.GetPeerAddress();
+    auto peer_address_type = connection_complete.GetPeerAddressType();
+    auto peer_resolvable_address = connection_complete.GetPeerResolvablePrivateAddress();
+    AddressWithType reporting_address_with_type(address, peer_address_type);
+    if (!peer_resolvable_address.IsEmpty()) {
+      reporting_address_with_type = AddressWithType(peer_resolvable_address, AddressType::RANDOM_DEVICE_ADDRESS);
+    }
+    on_common_le_connection_complete(reporting_address_with_type);
+    if (status != ErrorCode::SUCCESS) {
+      le_client_handler_->Post(common::BindOnce(&LeConnectionCallbacks::OnLeConnectFail,
+                                                common::Unretained(le_client_callbacks_), reporting_address_with_type,
+                                                status));
+      return;
+    }
+    // TODO: Check and save other connection parameters
+    uint16_t handle = connection_complete.GetConnectionHandle();
+    ASSERT(acl_connections_.count(handle) == 0);
+    acl_connections_.emplace(std::piecewise_construct, std::forward_as_tuple(handle),
+                             std::forward_as_tuple(reporting_address_with_type, handler_));
+    if (acl_connections_.size() == 1 && fragments_to_send_.size() == 0) {
+      start_round_robin();
+    }
+    auto role = connection_complete.GetRole();
+    std::unique_ptr<AclConnection> connection_proxy(
+        new AclConnection(&acl_manager_, handle, address, peer_address_type, role));
+    le_client_handler_->Post(common::BindOnce(&LeConnectionCallbacks::OnLeConnectSuccess,
+                                              common::Unretained(le_client_callbacks_), reporting_address_with_type,
+                                              std::move(connection_proxy)));
+  }
+
+  void on_connection_complete(EventPacketView packet) {
+    ConnectionCompleteView connection_complete = ConnectionCompleteView::Create(packet);
+    ASSERT(connection_complete.IsValid());
+    auto status = connection_complete.GetStatus();
+    auto address = connection_complete.GetBdAddr();
+    on_classic_connection_complete(address);
+    if (status != ErrorCode::SUCCESS) {
+      client_handler_->Post(common::BindOnce(&ConnectionCallbacks::OnConnectFail, common::Unretained(client_callbacks_),
+                                             address, status));
+      return;
+    }
+    uint16_t handle = connection_complete.GetConnectionHandle();
+    ASSERT(acl_connections_.count(handle) == 0);
+    acl_connections_.emplace(
+        std::piecewise_construct, std::forward_as_tuple(handle),
+        std::forward_as_tuple(AddressWithType{address, AddressType::PUBLIC_DEVICE_ADDRESS}, handler_));
+    if (acl_connections_.size() == 1 && fragments_to_send_.size() == 0) {
+      start_round_robin();
+    }
+    std::unique_ptr<AclConnection> connection_proxy(new AclConnection(&acl_manager_, handle, address));
+    client_handler_->Post(common::BindOnce(&ConnectionCallbacks::OnConnectSuccess,
+                                           common::Unretained(client_callbacks_), std::move(connection_proxy)));
+    while (!pending_outgoing_connections_.empty()) {
+      auto create_connection_packet_and_address = std::move(pending_outgoing_connections_.front());
+      pending_outgoing_connections_.pop();
+      if (!is_classic_link_already_connected(create_connection_packet_and_address.first)) {
+        connecting_.insert(create_connection_packet_and_address.first);
+        hci_layer_->EnqueueCommand(std::move(create_connection_packet_and_address.second),
+                                   common::BindOnce([](CommandStatusView status) {
+                                     ASSERT(status.IsValid());
+                                     ASSERT(status.GetCommandOpCode() == OpCode::CREATE_CONNECTION);
+                                   }),
+                                   handler_);
+        break;
+      }
+    }
+  }
+
+  void on_disconnection_complete(EventPacketView packet) {
+    DisconnectionCompleteView disconnection_complete = DisconnectionCompleteView::Create(packet);
+    ASSERT(disconnection_complete.IsValid());
+    uint16_t handle = disconnection_complete.GetConnectionHandle();
+    auto status = disconnection_complete.GetStatus();
+    if (status == ErrorCode::SUCCESS) {
+      ASSERT(acl_connections_.count(handle) == 1);
+      auto& acl_connection = acl_connections_.find(handle)->second;
+      acl_connection.is_disconnected_ = true;
+      acl_connection.disconnect_reason_ = disconnection_complete.GetReason();
+      acl_connection.call_disconnect_callback();
+      // Reclaim outstanding packets
+      acl_packet_credits_ += acl_connection.number_of_sent_packets_;
+      acl_connection.number_of_sent_packets_ = 0;
+    } else {
+      std::string error_code = ErrorCodeText(status);
+      LOG_ERROR("Received disconnection complete with error code %s, handle 0x%02hx", error_code.c_str(), handle);
+    }
+  }
+
+  void on_connection_packet_type_changed(EventPacketView packet) {
+    ConnectionPacketTypeChangedView packet_type_changed = ConnectionPacketTypeChangedView::Create(packet);
+    if (!packet_type_changed.IsValid()) {
+      LOG_ERROR("Received on_connection_packet_type_changed with invalid packet");
+      return;
+    } else if (packet_type_changed.GetStatus() != ErrorCode::SUCCESS) {
+      auto status = packet_type_changed.GetStatus();
+      std::string error_code = ErrorCodeText(status);
+      LOG_ERROR("Received on_connection_packet_type_changed with error code %s", error_code.c_str());
+      return;
+    }
+    uint16_t handle = packet_type_changed.GetConnectionHandle();
+    auto& acl_connection = acl_connections_.find(handle)->second;
+    if (acl_connection.command_complete_handler_ != nullptr) {
+      uint16_t packet_type = packet_type_changed.GetPacketType();
+      acl_connection.command_complete_handler_->Post(
+          common::BindOnce(&ConnectionManagementCallbacks::OnConnectionPacketTypeChanged,
+                           common::Unretained(acl_connection.command_complete_callbacks_), packet_type));
+    }
+  }
+
+  void on_master_link_key_complete(EventPacketView packet) {
+    MasterLinkKeyCompleteView complete_view = MasterLinkKeyCompleteView::Create(packet);
+    if (!complete_view.IsValid()) {
+      LOG_ERROR("Received on_master_link_key_complete with invalid packet");
+      return;
+    } else if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
+      auto status = complete_view.GetStatus();
+      std::string error_code = ErrorCodeText(status);
+      LOG_ERROR("Received on_master_link_key_complete with error code %s", error_code.c_str());
+      return;
+    }
+    if (acl_manager_client_callbacks_ != nullptr) {
+      uint16_t connection_handle = complete_view.GetConnectionHandle();
+      KeyFlag key_flag = complete_view.GetKeyFlag();
+      acl_manager_client_handler_->Post(common::BindOnce(&AclManagerCallbacks::OnMasterLinkKeyComplete,
+                                                         common::Unretained(acl_manager_client_callbacks_),
+                                                         connection_handle, key_flag));
+    }
+  }
+
+  void on_authentication_complete(EventPacketView packet) {
+    AuthenticationCompleteView authentication_complete = AuthenticationCompleteView::Create(packet);
+    if (!authentication_complete.IsValid()) {
+      LOG_ERROR("Received on_authentication_complete with invalid packet");
+      return;
+    } else if (authentication_complete.GetStatus() != ErrorCode::SUCCESS) {
+      auto status = authentication_complete.GetStatus();
+      std::string error_code = ErrorCodeText(status);
+      LOG_ERROR("Received on_authentication_complete with error code %s", error_code.c_str());
+      return;
+    }
+    uint16_t handle = authentication_complete.GetConnectionHandle();
+    auto& acl_connection = acl_connections_.find(handle)->second;
+    if (acl_connection.command_complete_handler_ != nullptr) {
+      acl_connection.command_complete_handler_->Post(
+          common::BindOnce(&ConnectionManagementCallbacks::OnAuthenticationComplete,
+                           common::Unretained(acl_connection.command_complete_callbacks_)));
+    }
+  }
+
+  void on_encryption_change(EventPacketView packet) {
+    EncryptionChangeView encryption_change_view = EncryptionChangeView::Create(packet);
+    if (!encryption_change_view.IsValid()) {
+      LOG_ERROR("Received on_encryption_change with invalid packet");
+      return;
+    } else if (encryption_change_view.GetStatus() != ErrorCode::SUCCESS) {
+      auto status = encryption_change_view.GetStatus();
+      std::string error_code = ErrorCodeText(status);
+      LOG_ERROR("Received on_change_connection_link_key_complete with error code %s", error_code.c_str());
+      return;
+    }
+    uint16_t handle = encryption_change_view.GetConnectionHandle();
+    auto& acl_connection = acl_connections_.find(handle)->second;
+    if (acl_connection.command_complete_handler_ != nullptr) {
+      EncryptionEnabled enabled = encryption_change_view.GetEncryptionEnabled();
+      acl_connection.command_complete_handler_->Post(
+          common::BindOnce(&ConnectionManagementCallbacks::OnEncryptionChange,
+                           common::Unretained(acl_connection.command_complete_callbacks_), enabled));
+    }
+  }
+
+  void on_change_connection_link_key_complete(EventPacketView packet) {
+    ChangeConnectionLinkKeyCompleteView complete_view = ChangeConnectionLinkKeyCompleteView::Create(packet);
+    if (!complete_view.IsValid()) {
+      LOG_ERROR("Received on_change_connection_link_key_complete with invalid packet");
+      return;
+    } else if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
+      auto status = complete_view.GetStatus();
+      std::string error_code = ErrorCodeText(status);
+      LOG_ERROR("Received on_change_connection_link_key_complete with error code %s", error_code.c_str());
+      return;
+    }
+    uint16_t handle = complete_view.GetConnectionHandle();
+    auto& acl_connection = acl_connections_.find(handle)->second;
+    if (acl_connection.command_complete_handler_ != nullptr) {
+      acl_connection.command_complete_handler_->Post(
+          common::BindOnce(&ConnectionManagementCallbacks::OnChangeConnectionLinkKeyComplete,
+                           common::Unretained(acl_connection.command_complete_callbacks_)));
+    }
+  }
+
+  void on_read_clock_offset_complete(EventPacketView packet) {
+    ReadClockOffsetCompleteView complete_view = ReadClockOffsetCompleteView::Create(packet);
+    if (!complete_view.IsValid()) {
+      LOG_ERROR("Received on_read_clock_offset_complete with invalid packet");
+      return;
+    } else if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
+      auto status = complete_view.GetStatus();
+      std::string error_code = ErrorCodeText(status);
+      LOG_ERROR("Received on_read_clock_offset_complete with error code %s", error_code.c_str());
+      return;
+    }
+    uint16_t handle = complete_view.GetConnectionHandle();
+    auto& acl_connection = acl_connections_.find(handle)->second;
+    if (acl_connection.command_complete_handler_ != nullptr) {
+      uint16_t clock_offset = complete_view.GetClockOffset();
+      acl_connection.command_complete_handler_->Post(
+          common::BindOnce(&ConnectionManagementCallbacks::OnReadClockOffsetComplete,
+                           common::Unretained(acl_connection.command_complete_callbacks_), clock_offset));
+    }
+  }
+
+  void on_mode_change(EventPacketView packet) {
+    ModeChangeView mode_change_view = ModeChangeView::Create(packet);
+    if (!mode_change_view.IsValid()) {
+      LOG_ERROR("Received on_mode_change with invalid packet");
+      return;
+    } else if (mode_change_view.GetStatus() != ErrorCode::SUCCESS) {
+      auto status = mode_change_view.GetStatus();
+      std::string error_code = ErrorCodeText(status);
+      LOG_ERROR("Received on_mode_change with error code %s", error_code.c_str());
+      return;
+    }
+    uint16_t handle = mode_change_view.GetConnectionHandle();
+    auto& acl_connection = acl_connections_.find(handle)->second;
+    if (acl_connection.command_complete_handler_ != nullptr) {
+      Mode current_mode = mode_change_view.GetCurrentMode();
+      uint16_t interval = mode_change_view.GetInterval();
+      acl_connection.command_complete_handler_->Post(
+          common::BindOnce(&ConnectionManagementCallbacks::OnModeChange,
+                           common::Unretained(acl_connection.command_complete_callbacks_), current_mode, interval));
+    }
+  }
+
+  void on_qos_setup_complete(EventPacketView packet) {
+    QosSetupCompleteView complete_view = QosSetupCompleteView::Create(packet);
+    if (!complete_view.IsValid()) {
+      LOG_ERROR("Received on_qos_setup_complete with invalid packet");
+      return;
+    } else if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
+      auto status = complete_view.GetStatus();
+      std::string error_code = ErrorCodeText(status);
+      LOG_ERROR("Received on_qos_setup_complete with error code %s", error_code.c_str());
+      return;
+    }
+    uint16_t handle = complete_view.GetConnectionHandle();
+    auto& acl_connection = acl_connections_.find(handle)->second;
+    if (acl_connection.command_complete_handler_ != nullptr) {
+      ServiceType service_type = complete_view.GetServiceType();
+      uint32_t token_rate = complete_view.GetTokenRate();
+      uint32_t peak_bandwidth = complete_view.GetPeakBandwidth();
+      uint32_t latency = complete_view.GetLatency();
+      uint32_t delay_variation = complete_view.GetDelayVariation();
+      acl_connection.command_complete_handler_->Post(
+          common::BindOnce(&ConnectionManagementCallbacks::OnQosSetupComplete,
+                           common::Unretained(acl_connection.command_complete_callbacks_), service_type, token_rate,
+                           peak_bandwidth, latency, delay_variation));
+    }
+  }
+
+  void on_role_change(EventPacketView packet) {
+    RoleChangeView role_change_view = RoleChangeView::Create(packet);
+    if (!role_change_view.IsValid()) {
+      LOG_ERROR("Received on_role_change with invalid packet");
+      return;
+    } else if (role_change_view.GetStatus() != ErrorCode::SUCCESS) {
+      auto status = role_change_view.GetStatus();
+      std::string error_code = ErrorCodeText(status);
+      LOG_ERROR("Received on_role_change with error code %s", error_code.c_str());
+      return;
+    }
+    if (acl_manager_client_callbacks_ != nullptr) {
+      Address bd_addr = role_change_view.GetBdAddr();
+      Role new_role = role_change_view.GetNewRole();
+      acl_manager_client_handler_->Post(common::BindOnce(
+          &AclManagerCallbacks::OnRoleChange, common::Unretained(acl_manager_client_callbacks_), bd_addr, new_role));
+    }
+  }
+
+  void on_flow_specification_complete(EventPacketView packet) {
+    FlowSpecificationCompleteView complete_view = FlowSpecificationCompleteView::Create(packet);
+    if (!complete_view.IsValid()) {
+      LOG_ERROR("Received on_flow_specification_complete with invalid packet");
+      return;
+    } else if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
+      auto status = complete_view.GetStatus();
+      std::string error_code = ErrorCodeText(status);
+      LOG_ERROR("Received on_flow_specification_complete with error code %s", error_code.c_str());
+      return;
+    }
+    uint16_t handle = complete_view.GetConnectionHandle();
+    auto& acl_connection = acl_connections_.find(handle)->second;
+    if (acl_connection.command_complete_handler_ != nullptr) {
+      FlowDirection flow_direction = complete_view.GetFlowDirection();
+      ServiceType service_type = complete_view.GetServiceType();
+      uint32_t token_rate = complete_view.GetTokenRate();
+      uint32_t token_bucket_size = complete_view.GetTokenBucketSize();
+      uint32_t peak_bandwidth = complete_view.GetPeakBandwidth();
+      uint32_t access_latency = complete_view.GetAccessLatency();
+      acl_connection.command_complete_handler_->Post(
+          common::BindOnce(&ConnectionManagementCallbacks::OnFlowSpecificationComplete,
+                           common::Unretained(acl_connection.command_complete_callbacks_), flow_direction, service_type,
+                           token_rate, token_bucket_size, peak_bandwidth, access_latency));
+    }
+  }
+
+  void on_flush_occurred(EventPacketView packet) {
+    FlushOccurredView flush_occurred_view = FlushOccurredView::Create(packet);
+    if (!flush_occurred_view.IsValid()) {
+      LOG_ERROR("Received on_flush_occurred with invalid packet");
+      return;
+    }
+    uint16_t handle = flush_occurred_view.GetConnectionHandle();
+    auto& acl_connection = acl_connections_.find(handle)->second;
+    if (acl_connection.command_complete_handler_ != nullptr) {
+      acl_connection.command_complete_handler_->Post(
+          common::BindOnce(&ConnectionManagementCallbacks::OnFlushOccurred,
+                           common::Unretained(acl_connection.command_complete_callbacks_)));
+    }
+  }
+
+  void on_read_remote_version_information_complete(EventPacketView packet) {
+    auto view = ReadRemoteVersionInformationCompleteView::Create(packet);
+    ASSERT_LOG(view.IsValid(), "Read remote version information packet invalid");
+    LOG_INFO("UNIMPLEMENTED called");
+  }
+
+  void on_read_remote_supported_features_complete(EventPacketView packet) {
+    auto view = ReadRemoteSupportedFeaturesCompleteView::Create(packet);
+    ASSERT_LOG(view.IsValid(), "Read remote supported features packet invalid");
+    LOG_INFO("UNIMPLEMENTED called");
+  }
+
+  void on_read_remote_extended_features_complete(EventPacketView packet) {
+    auto view = ReadRemoteExtendedFeaturesCompleteView::Create(packet);
+    ASSERT_LOG(view.IsValid(), "Read remote extended features packet invalid");
+    LOG_INFO("UNIMPLEMENTED called");
+  }
+
+  void on_link_supervision_timeout_changed(EventPacketView packet) {
+    auto view = LinkSupervisionTimeoutChangedView::Create(packet);
+    ASSERT_LOG(view.IsValid(), "Link supervision timeout changed packet invalid");
+    LOG_INFO("UNIMPLEMENTED called");
+  }
+
+  void on_role_discovery_complete(CommandCompleteView view) {
+    auto complete_view = RoleDiscoveryCompleteView::Create(view);
+    if (!complete_view.IsValid()) {
+      LOG_ERROR("Received on_role_discovery_complete with invalid packet");
+      return;
+    } else if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
+      auto status = complete_view.GetStatus();
+      std::string error_code = ErrorCodeText(status);
+      LOG_ERROR("Received on_role_discovery_complete with error code %s", error_code.c_str());
+      return;
+    }
+    uint16_t handle = complete_view.GetConnectionHandle();
+    auto& acl_connection = acl_connections_.find(handle)->second;
+    if (acl_connection.command_complete_handler_ != nullptr) {
+      Role role = complete_view.GetCurrentRole();
+      acl_connection.command_complete_handler_->Post(
+          common::BindOnce(&ConnectionManagementCallbacks::OnRoleDiscoveryComplete,
+                           common::Unretained(acl_connection.command_complete_callbacks_), role));
+    }
+  }
+
+  void on_read_link_policy_settings_complete(CommandCompleteView view) {
+    auto complete_view = ReadLinkPolicySettingsCompleteView::Create(view);
+    if (!complete_view.IsValid()) {
+      LOG_ERROR("Received on_read_link_policy_settings_complete with invalid packet");
+      return;
+    } else if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
+      auto status = complete_view.GetStatus();
+      std::string error_code = ErrorCodeText(status);
+      LOG_ERROR("Received on_read_link_policy_settings_complete with error code %s", error_code.c_str());
+      return;
+    }
+    uint16_t handle = complete_view.GetConnectionHandle();
+    auto& acl_connection = acl_connections_.find(handle)->second;
+    if (acl_connection.command_complete_handler_ != nullptr) {
+      uint16_t link_policy_settings = complete_view.GetLinkPolicySettings();
+      acl_connection.command_complete_handler_->Post(
+          common::BindOnce(&ConnectionManagementCallbacks::OnReadLinkPolicySettingsComplete,
+                           common::Unretained(acl_connection.command_complete_callbacks_), link_policy_settings));
+    }
+  }
+
+  void on_read_default_link_policy_settings_complete(CommandCompleteView view) {
+    auto complete_view = ReadDefaultLinkPolicySettingsCompleteView::Create(view);
+    if (!complete_view.IsValid()) {
+      LOG_ERROR("Received on_read_link_policy_settings_complete with invalid packet");
+      return;
+    } else if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
+      auto status = complete_view.GetStatus();
+      std::string error_code = ErrorCodeText(status);
+      LOG_ERROR("Received on_read_link_policy_settings_complete with error code %s", error_code.c_str());
+      return;
+    }
+    if (acl_manager_client_callbacks_ != nullptr) {
+      uint16_t default_link_policy_settings = complete_view.GetDefaultLinkPolicySettings();
+      acl_manager_client_handler_->Post(common::BindOnce(&AclManagerCallbacks::OnReadDefaultLinkPolicySettingsComplete,
+                                                         common::Unretained(acl_manager_client_callbacks_),
+                                                         default_link_policy_settings));
+    }
+  }
+
+  void on_read_automatic_flush_timeout_complete(CommandCompleteView view) {
+    auto complete_view = ReadAutomaticFlushTimeoutCompleteView::Create(view);
+    if (!complete_view.IsValid()) {
+      LOG_ERROR("Received on_read_automatic_flush_timeout_complete with invalid packet");
+      return;
+    } else if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
+      auto status = complete_view.GetStatus();
+      std::string error_code = ErrorCodeText(status);
+      LOG_ERROR("Received on_read_automatic_flush_timeout_complete with error code %s", error_code.c_str());
+      return;
+    }
+    uint16_t handle = complete_view.GetConnectionHandle();
+    auto& acl_connection = acl_connections_.find(handle)->second;
+    if (acl_connection.command_complete_handler_ != nullptr) {
+      uint16_t flush_timeout = complete_view.GetFlushTimeout();
+      acl_connection.command_complete_handler_->Post(
+          common::BindOnce(&ConnectionManagementCallbacks::OnReadAutomaticFlushTimeoutComplete,
+                           common::Unretained(acl_connection.command_complete_callbacks_), flush_timeout));
+    }
+  }
+
+  void on_read_transmit_power_level_complete(CommandCompleteView view) {
+    auto complete_view = ReadTransmitPowerLevelCompleteView::Create(view);
+    if (!complete_view.IsValid()) {
+      LOG_ERROR("Received on_read_transmit_power_level_complete with invalid packet");
+      return;
+    } else if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
+      auto status = complete_view.GetStatus();
+      std::string error_code = ErrorCodeText(status);
+      LOG_ERROR("Received on_read_transmit_power_level_complete with error code %s", error_code.c_str());
+      return;
+    }
+    uint16_t handle = complete_view.GetConnectionHandle();
+    auto& acl_connection = acl_connections_.find(handle)->second;
+    if (acl_connection.command_complete_handler_ != nullptr) {
+      uint8_t transmit_power_level = complete_view.GetTransmitPowerLevel();
+      acl_connection.command_complete_handler_->Post(
+          common::BindOnce(&ConnectionManagementCallbacks::OnReadTransmitPowerLevelComplete,
+                           common::Unretained(acl_connection.command_complete_callbacks_), transmit_power_level));
+    }
+  }
+
+  void on_read_link_supervision_timeout_complete(CommandCompleteView view) {
+    auto complete_view = ReadLinkSupervisionTimeoutCompleteView::Create(view);
+    if (!complete_view.IsValid()) {
+      LOG_ERROR("Received on_read_link_supervision_timeout_complete with invalid packet");
+      return;
+    } else if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
+      auto status = complete_view.GetStatus();
+      std::string error_code = ErrorCodeText(status);
+      LOG_ERROR("Received on_read_link_supervision_timeout_complete with error code %s", error_code.c_str());
+      return;
+    }
+    uint16_t handle = complete_view.GetConnectionHandle();
+    auto& acl_connection = acl_connections_.find(handle)->second;
+    if (acl_connection.command_complete_handler_ != nullptr) {
+      uint16_t link_supervision_timeout = complete_view.GetLinkSupervisionTimeout();
+      acl_connection.command_complete_handler_->Post(
+          common::BindOnce(&ConnectionManagementCallbacks::OnReadLinkSupervisionTimeoutComplete,
+                           common::Unretained(acl_connection.command_complete_callbacks_), link_supervision_timeout));
+    }
+  }
+
+  void on_read_failed_contact_counter_complete(CommandCompleteView view) {
+    auto complete_view = ReadFailedContactCounterCompleteView::Create(view);
+    if (!complete_view.IsValid()) {
+      LOG_ERROR("Received on_read_failed_contact_counter_complete with invalid packet");
+      return;
+    } else if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
+      auto status = complete_view.GetStatus();
+      std::string error_code = ErrorCodeText(status);
+      LOG_ERROR("Received on_read_failed_contact_counter_complete with error code %s", error_code.c_str());
+      return;
+    }
+    uint16_t handle = complete_view.GetConnectionHandle();
+    auto& acl_connection = acl_connections_.find(handle)->second;
+    if (acl_connection.command_complete_handler_ != nullptr) {
+      uint16_t failed_contact_counter = complete_view.GetFailedContactCounter();
+      acl_connection.command_complete_handler_->Post(
+          common::BindOnce(&ConnectionManagementCallbacks::OnReadFailedContactCounterComplete,
+                           common::Unretained(acl_connection.command_complete_callbacks_), failed_contact_counter));
+    }
+  }
+
+  void on_read_link_quality_complete(CommandCompleteView view) {
+    auto complete_view = ReadLinkQualityCompleteView::Create(view);
+    if (!complete_view.IsValid()) {
+      LOG_ERROR("Received on_read_link_quality_complete with invalid packet");
+      return;
+    } else if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
+      auto status = complete_view.GetStatus();
+      std::string error_code = ErrorCodeText(status);
+      LOG_ERROR("Received on_read_link_quality_complete with error code %s", error_code.c_str());
+      return;
+    }
+    uint16_t handle = complete_view.GetConnectionHandle();
+    auto& acl_connection = acl_connections_.find(handle)->second;
+    if (acl_connection.command_complete_handler_ != nullptr) {
+      uint8_t link_quality = complete_view.GetLinkQuality();
+      acl_connection.command_complete_handler_->Post(
+          common::BindOnce(&ConnectionManagementCallbacks::OnReadLinkQualityComplete,
+                           common::Unretained(acl_connection.command_complete_callbacks_), link_quality));
+    }
+  }
+
+  void on_read_afh_channel_map_complete(CommandCompleteView view) {
+    auto complete_view = ReadAfhChannelMapCompleteView::Create(view);
+    if (!complete_view.IsValid()) {
+      LOG_ERROR("Received on_read_afh_channel_map_complete with invalid packet");
+      return;
+    } else if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
+      auto status = complete_view.GetStatus();
+      std::string error_code = ErrorCodeText(status);
+      LOG_ERROR("Received on_read_afh_channel_map_complete with error code %s", error_code.c_str());
+      return;
+    }
+    uint16_t handle = complete_view.GetConnectionHandle();
+    auto& acl_connection = acl_connections_.find(handle)->second;
+    if (acl_connection.command_complete_handler_ != nullptr) {
+      AfhMode afh_mode = complete_view.GetAfhMode();
+      std::array<uint8_t, 10> afh_channel_map = complete_view.GetAfhChannelMap();
+      acl_connection.command_complete_handler_->Post(
+          common::BindOnce(&ConnectionManagementCallbacks::OnReadAfhChannelMapComplete,
+                           common::Unretained(acl_connection.command_complete_callbacks_), afh_mode, afh_channel_map));
+    }
+  }
+
+  void on_read_rssi_complete(CommandCompleteView view) {
+    auto complete_view = ReadRssiCompleteView::Create(view);
+    if (!complete_view.IsValid()) {
+      LOG_ERROR("Received on_read_rssi_complete with invalid packet");
+      return;
+    } else if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
+      auto status = complete_view.GetStatus();
+      std::string error_code = ErrorCodeText(status);
+      LOG_ERROR("Received on_read_rssi_complete with error code %s", error_code.c_str());
+      return;
+    }
+    uint16_t handle = complete_view.GetConnectionHandle();
+    auto& acl_connection = acl_connections_.find(handle)->second;
+    if (acl_connection.command_complete_handler_ != nullptr) {
+      uint8_t rssi = complete_view.GetRssi();
+      acl_connection.command_complete_handler_->Post(
+          common::BindOnce(&ConnectionManagementCallbacks::OnReadRssiComplete,
+                           common::Unretained(acl_connection.command_complete_callbacks_), rssi));
+    }
+  }
+
+  void on_read_remote_version_information_status(CommandStatusView view) {
+    ASSERT_LOG(view.IsValid(), "Bad status packet!");
+    LOG_INFO("UNIMPLEMENTED called: %s", hci::ErrorCodeText(view.GetStatus()).c_str());
+  }
+
+  void on_read_remote_supported_features_status(CommandStatusView view) {
+    ASSERT_LOG(view.IsValid(), "Bad status packet!");
+    LOG_INFO("UNIMPLEMENTED called: %s", hci::ErrorCodeText(view.GetStatus()).c_str());
+  }
+
+  void on_read_remote_extended_features_status(CommandStatusView view) {
+    ASSERT_LOG(view.IsValid(), "Broken");
+    LOG_INFO("UNIMPLEMENTED called: %s", hci::ErrorCodeText(view.GetStatus()).c_str());
+  }
+
+  void on_read_clock_complete(CommandCompleteView view) {
+    auto complete_view = ReadClockCompleteView::Create(view);
+    if (!complete_view.IsValid()) {
+      LOG_ERROR("Received on_read_clock_complete with invalid packet");
+      return;
+    } else if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
+      auto status = complete_view.GetStatus();
+      std::string error_code = ErrorCodeText(status);
+      LOG_ERROR("Received on_read_clock_complete with error code %s", error_code.c_str());
+      return;
+    }
+    uint16_t handle = complete_view.GetConnectionHandle();
+    auto& acl_connection = acl_connections_.find(handle)->second;
+    if (acl_connection.command_complete_handler_ != nullptr) {
+      uint32_t clock = complete_view.GetClock();
+      uint16_t accuracy = complete_view.GetAccuracy();
+      acl_connection.command_complete_handler_->Post(
+          common::BindOnce(&ConnectionManagementCallbacks::OnReadClockComplete,
+                           common::Unretained(acl_connection.command_complete_callbacks_), clock, accuracy));
+    }
+  }
+
+  void on_le_connection_update_complete(LeMetaEventView view) {
+    auto complete_view = LeConnectionUpdateCompleteView::Create(view);
+    if (!complete_view.IsValid()) {
+      LOG_ERROR("Received on_le_connection_update_complete with invalid packet");
+      return;
+    } else if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
+      auto status = complete_view.GetStatus();
+      std::string error_code = ErrorCodeText(status);
+      LOG_ERROR("Received on_le_connection_update_complete with error code %s", error_code.c_str());
+      return;
+    }
+    auto handle = complete_view.GetConnectionHandle();
+    if (acl_connections_.find(handle) == acl_connections_.end()) {
+      LOG_WARN("Can't find connection");
+      return;
+    }
+    auto& connection = acl_connections_.find(handle)->second;
+    if (connection.is_disconnected_) {
+      LOG_INFO("Already disconnected");
+      return;
+    }
+    if (!connection.on_connection_update_complete_callback_.is_null()) {
+      connection.on_connection_update_complete_callback_handler_->Post(
+          common::BindOnce(std::move(connection.on_connection_update_complete_callback_), complete_view.GetStatus()));
+      connection.on_connection_update_complete_callback_handler_ = nullptr;
+    }
+  }
+
+  bool is_classic_link_already_connected(Address address) {
+    for (const auto& connection : acl_connections_) {
+      if (connection.second.address_with_type_.GetAddress() == address) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  void create_connection(Address address) {
+    // TODO: Configure default connection parameters?
+    uint16_t packet_type = 0x4408 /* DM 1,3,5 */ | 0x8810 /*DH 1,3,5 */;
+    PageScanRepetitionMode page_scan_repetition_mode = PageScanRepetitionMode::R1;
+    uint16_t clock_offset = 0;
+    ClockOffsetValid clock_offset_valid = ClockOffsetValid::INVALID;
+    CreateConnectionRoleSwitch allow_role_switch = CreateConnectionRoleSwitch::ALLOW_ROLE_SWITCH;
+    ASSERT(client_callbacks_ != nullptr);
+    std::unique_ptr<CreateConnectionBuilder> packet = CreateConnectionBuilder::Create(
+        address, packet_type, page_scan_repetition_mode, clock_offset, clock_offset_valid, allow_role_switch);
+
+    if (connecting_.empty()) {
+      if (is_classic_link_already_connected(address)) {
+        LOG_WARN("already connected: %s", address.ToString().c_str());
+        return;
+      }
+      connecting_.insert(address);
+      hci_layer_->EnqueueCommand(std::move(packet), common::BindOnce([](CommandStatusView status) {
+                                   ASSERT(status.IsValid());
+                                   ASSERT(status.GetCommandOpCode() == OpCode::CREATE_CONNECTION);
+                                 }),
+                                 handler_);
+    } else {
+      pending_outgoing_connections_.emplace(address, std::move(packet));
+    }
+  }
+
+  void create_le_connection(AddressWithType address_with_type) {
+    // TODO: Add white list handling.
+    // TODO: Configure default LE connection parameters?
+    uint16_t le_scan_interval = 0x0060;
+    uint16_t le_scan_window = 0x0030;
+    InitiatorFilterPolicy initiator_filter_policy = InitiatorFilterPolicy::USE_PEER_ADDRESS;
+    OwnAddressType own_address_type = OwnAddressType::RANDOM_DEVICE_ADDRESS;
+    uint16_t conn_interval_min = 0x0018;
+    uint16_t conn_interval_max = 0x0028;
+    uint16_t conn_latency = 0x0000;
+    uint16_t supervision_timeout = 0x001f4;
+    ASSERT(le_client_callbacks_ != nullptr);
+
+    connecting_le_.insert(address_with_type);
+
+    // TODO: make features check nicer, like HCI_LE_EXTENDED_ADVERTISING_SUPPORTED
+    if (controller_->GetControllerLeLocalSupportedFeatures() & 0x0010) {
+      LeCreateConnPhyScanParameters tmp;
+      tmp.scan_interval_ = le_scan_interval;
+      tmp.scan_window_ = le_scan_window;
+      tmp.conn_interval_min_ = conn_interval_min;
+      tmp.conn_interval_max_ = conn_interval_max;
+      tmp.conn_latency_ = conn_latency;
+      tmp.supervision_timeout_ = supervision_timeout;
+      tmp.min_ce_length_ = 0x00;
+      tmp.max_ce_length_ = 0x00;
+
+      // With real controllers, we must set random address before using it to establish connection
+      // TODO: have separate state machine generate new address when needed, consider using auto-generation in
+      // controller
+      hci_layer_->EnqueueCommand(hci::LeSetRandomAddressBuilder::Create(Address{{0x00, 0x11, 0xFF, 0xFF, 0x33, 0x22}}),
+                                 common::BindOnce([](CommandCompleteView status) {}), handler_);
+
+      hci_layer_->EnqueueCommand(LeExtendedCreateConnectionBuilder::Create(
+                                     initiator_filter_policy, own_address_type, address_with_type.GetAddressType(),
+                                     address_with_type.GetAddress(), 0x01 /* 1M PHY ONLY */, {tmp}),
+                                 common::BindOnce([](CommandStatusView status) {
+                                   ASSERT(status.IsValid());
+                                   ASSERT(status.GetCommandOpCode() == OpCode::LE_EXTENDED_CREATE_CONNECTION);
+                                 }),
+                                 handler_);
+    } else {
+      hci_layer_->EnqueueCommand(
+          LeCreateConnectionBuilder::Create(le_scan_interval, le_scan_window, initiator_filter_policy,
+                                            address_with_type.GetAddressType(), address_with_type.GetAddress(),
+                                            own_address_type, conn_interval_min, conn_interval_max, conn_latency,
+                                            supervision_timeout, kMinimumCeLength, kMaximumCeLength),
+          common::BindOnce([](CommandStatusView status) {
+            ASSERT(status.IsValid());
+            ASSERT(status.GetCommandOpCode() == OpCode::LE_CREATE_CONNECTION);
+          }),
+          handler_);
+    }
+  }
+
+  void cancel_connect(Address address) {
+    auto connecting_addr = connecting_.find(address);
+    if (connecting_addr == connecting_.end()) {
+      LOG_INFO("Cannot cancel non-existent connection to %s", address.ToString().c_str());
+      return;
+    }
+    std::unique_ptr<CreateConnectionCancelBuilder> packet = CreateConnectionCancelBuilder::Create(address);
+    hci_layer_->EnqueueCommand(std::move(packet), common::BindOnce([](CommandCompleteView complete) { /* TODO */ }),
+                               handler_);
+  }
+
+  void master_link_key(KeyFlag key_flag) {
+    std::unique_ptr<MasterLinkKeyBuilder> packet = MasterLinkKeyBuilder::Create(key_flag);
+    hci_layer_->EnqueueCommand(
+        std::move(packet),
+        common::BindOnce(&impl::check_command_status<MasterLinkKeyStatusView>, common::Unretained(this)), handler_);
+  }
+
+  void switch_role(Address address, Role role) {
+    std::unique_ptr<SwitchRoleBuilder> packet = SwitchRoleBuilder::Create(address, role);
+    hci_layer_->EnqueueCommand(
+        std::move(packet),
+        common::BindOnce(&impl::check_command_status<SwitchRoleStatusView>, common::Unretained(this)), handler_);
+  }
+
+  void read_default_link_policy_settings() {
+    std::unique_ptr<ReadDefaultLinkPolicySettingsBuilder> packet = ReadDefaultLinkPolicySettingsBuilder::Create();
+    hci_layer_->EnqueueCommand(
+        std::move(packet),
+        common::BindOnce(&impl::on_read_default_link_policy_settings_complete, common::Unretained(this)), handler_);
+  }
+
+  void write_default_link_policy_settings(uint16_t default_link_policy_settings) {
+    std::unique_ptr<WriteDefaultLinkPolicySettingsBuilder> packet =
+        WriteDefaultLinkPolicySettingsBuilder::Create(default_link_policy_settings);
+    hci_layer_->EnqueueCommand(
+        std::move(packet),
+        BindOnce(&AclManager::impl::check_command_complete<WriteDefaultLinkPolicySettingsCompleteView>,
+                 common::Unretained(this)),
+        handler_);
+  }
+
+  void accept_connection(Address address) {
+    auto role = AcceptConnectionRequestRole::BECOME_MASTER;  // We prefer to be master
+    hci_layer_->EnqueueCommand(AcceptConnectionRequestBuilder::Create(address, role),
+                               common::BindOnce(&impl::on_accept_connection_status, common::Unretained(this), address),
+                               handler_);
+  }
+
+  void handle_disconnect(uint16_t handle, DisconnectReason reason) {
+    ASSERT(acl_connections_.count(handle) == 1);
+    std::unique_ptr<DisconnectBuilder> packet = DisconnectBuilder::Create(handle, reason);
+    hci_layer_->EnqueueCommand(std::move(packet), BindOnce([](CommandStatusView status) { /* TODO: check? */ }),
+                               handler_);
+  }
+
+  void handle_change_connection_packet_type(uint16_t handle, uint16_t packet_type) {
+    ASSERT(acl_connections_.count(handle) == 1);
+    std::unique_ptr<ChangeConnectionPacketTypeBuilder> packet =
+        ChangeConnectionPacketTypeBuilder::Create(handle, packet_type);
+    hci_layer_->EnqueueCommand(std::move(packet),
+                               BindOnce(&AclManager::impl::check_command_status<ChangeConnectionPacketTypeStatusView>,
+                                        common::Unretained(this)),
+                               handler_);
+  }
+
+  void handle_authentication_requested(uint16_t handle) {
+    std::unique_ptr<AuthenticationRequestedBuilder> packet = AuthenticationRequestedBuilder::Create(handle);
+    hci_layer_->EnqueueCommand(
+        std::move(packet),
+        BindOnce(&AclManager::impl::check_command_status<AuthenticationRequestedStatusView>, common::Unretained(this)),
+        handler_);
+  }
+
+  void handle_set_connection_encryption(uint16_t handle, Enable enable) {
+    std::unique_ptr<SetConnectionEncryptionBuilder> packet = SetConnectionEncryptionBuilder::Create(handle, enable);
+    hci_layer_->EnqueueCommand(
+        std::move(packet),
+        BindOnce(&AclManager::impl::check_command_status<SetConnectionEncryptionStatusView>, common::Unretained(this)),
+        handler_);
+  }
+
+  void handle_change_connection_link_key(uint16_t handle) {
+    std::unique_ptr<ChangeConnectionLinkKeyBuilder> packet = ChangeConnectionLinkKeyBuilder::Create(handle);
+    hci_layer_->EnqueueCommand(
+        std::move(packet),
+        BindOnce(&AclManager::impl::check_command_status<ChangeConnectionLinkKeyStatusView>, common::Unretained(this)),
+        handler_);
+  }
+
+  void handle_read_clock_offset(uint16_t handle) {
+    std::unique_ptr<ReadClockOffsetBuilder> packet = ReadClockOffsetBuilder::Create(handle);
+    hci_layer_->EnqueueCommand(
+        std::move(packet),
+        BindOnce(&AclManager::impl::check_command_status<ReadClockOffsetStatusView>, common::Unretained(this)),
+        handler_);
+  }
+
+  void handle_hold_mode(uint16_t handle, uint16_t max_interval, uint16_t min_interval) {
+    std::unique_ptr<HoldModeBuilder> packet = HoldModeBuilder::Create(handle, max_interval, min_interval);
+    hci_layer_->EnqueueCommand(
+        std::move(packet),
+        BindOnce(&AclManager::impl::check_command_status<HoldModeStatusView>, common::Unretained(this)), handler_);
+  }
+
+  void handle_sniff_mode(uint16_t handle, uint16_t max_interval, uint16_t min_interval, int16_t attempt,
+                         uint16_t timeout) {
+    std::unique_ptr<SniffModeBuilder> packet =
+        SniffModeBuilder::Create(handle, max_interval, min_interval, attempt, timeout);
+    hci_layer_->EnqueueCommand(
+        std::move(packet),
+        BindOnce(&AclManager::impl::check_command_status<SniffModeStatusView>, common::Unretained(this)), handler_);
+  }
+
+  void handle_exit_sniff_mode(uint16_t handle) {
+    std::unique_ptr<ExitSniffModeBuilder> packet = ExitSniffModeBuilder::Create(handle);
+    hci_layer_->EnqueueCommand(
+        std::move(packet),
+        BindOnce(&AclManager::impl::check_command_status<ExitSniffModeStatusView>, common::Unretained(this)), handler_);
+  }
+
+  void handle_qos_setup_mode(uint16_t handle, ServiceType service_type, uint32_t token_rate, uint32_t peak_bandwidth,
+                             uint32_t latency, uint32_t delay_variation) {
+    std::unique_ptr<QosSetupBuilder> packet =
+        QosSetupBuilder::Create(handle, service_type, token_rate, peak_bandwidth, latency, delay_variation);
+    hci_layer_->EnqueueCommand(
+        std::move(packet),
+        BindOnce(&AclManager::impl::check_command_status<QosSetupStatusView>, common::Unretained(this)), handler_);
+  }
+
+  void handle_role_discovery(uint16_t handle) {
+    std::unique_ptr<RoleDiscoveryBuilder> packet = RoleDiscoveryBuilder::Create(handle);
+    hci_layer_->EnqueueCommand(std::move(packet),
+                               common::BindOnce(&impl::on_role_discovery_complete, common::Unretained(this)), handler_);
+  }
+
+  void handle_read_link_policy_settings(uint16_t handle) {
+    std::unique_ptr<ReadLinkPolicySettingsBuilder> packet = ReadLinkPolicySettingsBuilder::Create(handle);
+    hci_layer_->EnqueueCommand(std::move(packet),
+                               common::BindOnce(&impl::on_read_link_policy_settings_complete, common::Unretained(this)),
+                               handler_);
+  }
+
+  void handle_write_link_policy_settings(uint16_t handle, uint16_t link_policy_settings) {
+    std::unique_ptr<WriteLinkPolicySettingsBuilder> packet =
+        WriteLinkPolicySettingsBuilder::Create(handle, link_policy_settings);
+    hci_layer_->EnqueueCommand(std::move(packet),
+                               BindOnce(&AclManager::impl::check_command_complete<WriteLinkPolicySettingsCompleteView>,
+                                        common::Unretained(this)),
+                               handler_);
+  }
+
+  void handle_flow_specification(uint16_t handle, FlowDirection flow_direction, ServiceType service_type,
+                                 uint32_t token_rate, uint32_t token_bucket_size, uint32_t peak_bandwidth,
+                                 uint32_t access_latency) {
+    std::unique_ptr<FlowSpecificationBuilder> packet = FlowSpecificationBuilder::Create(
+        handle, flow_direction, service_type, token_rate, token_bucket_size, peak_bandwidth, access_latency);
+    hci_layer_->EnqueueCommand(
+        std::move(packet),
+        BindOnce(&AclManager::impl::check_command_status<FlowSpecificationStatusView>, common::Unretained(this)),
+        handler_);
+  }
+
+  void handle_sniff_subrating(uint16_t handle, uint16_t maximum_latency, uint16_t minimum_remote_timeout,
+                              uint16_t minimum_local_timeout) {
+    std::unique_ptr<SniffSubratingBuilder> packet =
+        SniffSubratingBuilder::Create(handle, maximum_latency, minimum_remote_timeout, minimum_local_timeout);
+    hci_layer_->EnqueueCommand(
+        std::move(packet),
+        BindOnce(&AclManager::impl::check_command_complete<SniffSubratingCompleteView>, common::Unretained(this)),
+        handler_);
+  }
+
+  void handle_flush(uint16_t handle) {
+    std::unique_ptr<FlushBuilder> packet = FlushBuilder::Create(handle);
+    hci_layer_->EnqueueCommand(
+        std::move(packet),
+        BindOnce(&AclManager::impl::check_command_complete<FlushCompleteView>, common::Unretained(this)), handler_);
+  }
+
+  void handle_read_automatic_flush_timeout(uint16_t handle) {
+    std::unique_ptr<ReadAutomaticFlushTimeoutBuilder> packet = ReadAutomaticFlushTimeoutBuilder::Create(handle);
+    hci_layer_->EnqueueCommand(
+        std::move(packet), common::BindOnce(&impl::on_read_automatic_flush_timeout_complete, common::Unretained(this)),
+        handler_);
+  }
+
+  void handle_write_automatic_flush_timeout(uint16_t handle, uint16_t flush_timeout) {
+    std::unique_ptr<WriteAutomaticFlushTimeoutBuilder> packet =
+        WriteAutomaticFlushTimeoutBuilder::Create(handle, flush_timeout);
+    hci_layer_->EnqueueCommand(
+        std::move(packet),
+        BindOnce(&AclManager::impl::check_command_complete<WriteAutomaticFlushTimeoutCompleteView>,
+                 common::Unretained(this)),
+        handler_);
+  }
+
+  void handle_read_transmit_power_level(uint16_t handle, TransmitPowerLevelType type) {
+    std::unique_ptr<ReadTransmitPowerLevelBuilder> packet = ReadTransmitPowerLevelBuilder::Create(handle, type);
+    hci_layer_->EnqueueCommand(std::move(packet),
+                               common::BindOnce(&impl::on_read_transmit_power_level_complete, common::Unretained(this)),
+                               handler_);
+  }
+
+  void handle_read_link_supervision_timeout(uint16_t handle) {
+    std::unique_ptr<ReadLinkSupervisionTimeoutBuilder> packet = ReadLinkSupervisionTimeoutBuilder::Create(handle);
+    hci_layer_->EnqueueCommand(
+        std::move(packet), common::BindOnce(&impl::on_read_link_supervision_timeout_complete, common::Unretained(this)),
+        handler_);
+  }
+
+  void handle_write_link_supervision_timeout(uint16_t handle, uint16_t link_supervision_timeout) {
+    std::unique_ptr<WriteLinkSupervisionTimeoutBuilder> packet =
+        WriteLinkSupervisionTimeoutBuilder::Create(handle, link_supervision_timeout);
+    hci_layer_->EnqueueCommand(
+        std::move(packet),
+        BindOnce(&AclManager::impl::check_command_complete<WriteLinkSupervisionTimeoutCompleteView>,
+                 common::Unretained(this)),
+        handler_);
+  }
+
+  void handle_read_failed_contact_counter(uint16_t handle) {
+    std::unique_ptr<ReadFailedContactCounterBuilder> packet = ReadFailedContactCounterBuilder::Create(handle);
+    hci_layer_->EnqueueCommand(
+        std::move(packet), common::BindOnce(&impl::on_read_failed_contact_counter_complete, common::Unretained(this)),
+        handler_);
+  }
+
+  void handle_reset_failed_contact_counter(uint16_t handle) {
+    std::unique_ptr<ResetFailedContactCounterBuilder> packet = ResetFailedContactCounterBuilder::Create(handle);
+    hci_layer_->EnqueueCommand(std::move(packet), BindOnce([](CommandCompleteView view) { /* TODO: check? */ }),
+                               handler_);
+  }
+
+  void handle_read_link_quality(uint16_t handle) {
+    std::unique_ptr<ReadLinkQualityBuilder> packet = ReadLinkQualityBuilder::Create(handle);
+    hci_layer_->EnqueueCommand(
+        std::move(packet), common::BindOnce(&impl::on_read_link_quality_complete, common::Unretained(this)), handler_);
+  }
+
+  void handle_afh_channel_map(uint16_t handle) {
+    std::unique_ptr<ReadAfhChannelMapBuilder> packet = ReadAfhChannelMapBuilder::Create(handle);
+    hci_layer_->EnqueueCommand(std::move(packet),
+                               common::BindOnce(&impl::on_read_afh_channel_map_complete, common::Unretained(this)),
+                               handler_);
+  }
+
+  void handle_read_rssi(uint16_t handle) {
+    std::unique_ptr<ReadRssiBuilder> packet = ReadRssiBuilder::Create(handle);
+    hci_layer_->EnqueueCommand(std::move(packet),
+                               common::BindOnce(&impl::on_read_rssi_complete, common::Unretained(this)), handler_);
+  }
+
+  void handle_read_remote_version_information(uint16_t handle) {
+    hci_layer_->EnqueueCommand(
+        ReadRemoteVersionInformationBuilder::Create(handle),
+        common::BindOnce(&impl::on_read_remote_version_information_status, common::Unretained(this)), handler_);
+  }
+
+  void handle_read_remote_supported_features(uint16_t handle) {
+    hci_layer_->EnqueueCommand(
+        ReadRemoteSupportedFeaturesBuilder::Create(handle),
+        common::BindOnce(&impl::on_read_remote_supported_features_status, common::Unretained(this)), handler_);
+  }
+
+  void handle_read_remote_extended_features(uint16_t handle) {
+    // TODO(optedoblivion): Read the other pages until max pages
+    hci_layer_->EnqueueCommand(
+        ReadRemoteExtendedFeaturesBuilder::Create(handle, 1),
+        common::BindOnce(&impl::on_read_remote_extended_features_status, common::Unretained(this)), handler_);
+  }
+
+  void handle_read_clock(uint16_t handle, WhichClock which_clock) {
+    std::unique_ptr<ReadClockBuilder> packet = ReadClockBuilder::Create(handle, which_clock);
+    hci_layer_->EnqueueCommand(std::move(packet),
+                               common::BindOnce(&impl::on_read_clock_complete, common::Unretained(this)), handler_);
+  }
+
+  void handle_le_connection_update(uint16_t handle, uint16_t conn_interval_min, uint16_t conn_interval_max,
+                                   uint16_t conn_latency, uint16_t supervision_timeout) {
+    auto packet = LeConnectionUpdateBuilder::Create(handle, conn_interval_min, conn_interval_max, conn_latency,
+                                                    supervision_timeout, kMinimumCeLength, kMaximumCeLength);
+    hci_layer_->EnqueueCommand(std::move(packet), common::BindOnce([](CommandStatusView status) {
+                                 ASSERT(status.IsValid());
+                                 ASSERT(status.GetCommandOpCode() == OpCode::LE_CREATE_CONNECTION);
+                               }),
+                               handler_);
+  }
+
+  template <class T>
+  void check_command_complete(CommandCompleteView view) {
+    ASSERT(view.IsValid());
+    auto status_view = T::Create(view);
+    if (!status_view.IsValid()) {
+      LOG_ERROR("Received command complete with invalid packet, opcode 0x%02hx", view.GetCommandOpCode());
+      return;
+    }
+    ErrorCode status = status_view.GetStatus();
+    OpCode op_code = status_view.GetCommandOpCode();
+    if (status != ErrorCode::SUCCESS) {
+      std::string error_code = ErrorCodeText(status);
+      LOG_ERROR("Received command complete with error code %s, opcode 0x%02hx", error_code.c_str(), op_code);
+      return;
+    }
+  }
+
+  template <class T>
+  void check_command_status(CommandStatusView view) {
+    ASSERT(view.IsValid());
+    auto status_view = T::Create(view);
+    if (!status_view.IsValid()) {
+      LOG_ERROR("Received command status with invalid packet, opcode 0x%02hx", view.GetCommandOpCode());
+      return;
+    }
+    ErrorCode status = status_view.GetStatus();
+    OpCode op_code = status_view.GetCommandOpCode();
+    if (status != ErrorCode::SUCCESS) {
+      std::string error_code = ErrorCodeText(status);
+      LOG_ERROR("Received command status with error code %s, opcode 0x%02hx", error_code.c_str(), op_code);
+      return;
+    }
+  }
+
+  void cleanup(uint16_t handle) {
+    ASSERT(acl_connections_.count(handle) == 1);
+    auto& acl_connection = acl_connections_.find(handle)->second;
+    if (acl_connection.is_registered_) {
+      acl_connection.is_registered_ = false;
+      acl_connection.queue_->GetDownEnd()->UnregisterDequeue();
+    }
+    acl_connections_.erase(handle);
+  }
+
+  void on_accept_connection_status(Address address, CommandStatusView status) {
+    auto accept_status = AcceptConnectionRequestStatusView::Create(status);
+    ASSERT(accept_status.IsValid());
+    if (status.GetStatus() != ErrorCode::SUCCESS) {
+      cancel_connect(address);
+    }
+  }
+
+  void reject_connection(std::unique_ptr<RejectConnectionRequestBuilder> builder) {
+    hci_layer_->EnqueueCommand(std::move(builder), BindOnce([](CommandStatusView status) { /* TODO: check? */ }),
+                               handler_);
+  }
+
+  void handle_register_callbacks(ConnectionCallbacks* callbacks, os::Handler* handler) {
+    ASSERT(client_callbacks_ == nullptr);
+    ASSERT(client_handler_ == nullptr);
+    client_callbacks_ = callbacks;
+    client_handler_ = handler;
+  }
+
+  void handle_register_le_callbacks(LeConnectionCallbacks* callbacks, os::Handler* handler) {
+    ASSERT(le_client_callbacks_ == nullptr);
+    ASSERT(le_client_handler_ == nullptr);
+    le_client_callbacks_ = callbacks;
+    le_client_handler_ = handler;
+  }
+
+  void handle_register_acl_manager_callbacks(AclManagerCallbacks* callbacks, os::Handler* handler) {
+    ASSERT(acl_manager_client_callbacks_ == nullptr);
+    ASSERT(acl_manager_client_handler_ == nullptr);
+    acl_manager_client_callbacks_ = callbacks;
+    acl_manager_client_handler_ = handler;
+  }
+
+  void handle_register_le_acl_manager_callbacks(AclManagerCallbacks* callbacks, os::Handler* handler) {
+    ASSERT(le_acl_manager_client_callbacks_ == nullptr);
+    ASSERT(le_acl_manager_client_handler_ == nullptr);
+    le_acl_manager_client_callbacks_ = callbacks;
+    le_acl_manager_client_handler_ = handler;
+  }
+
+  acl_connection& check_and_get_connection(uint16_t handle) {
+    auto connection = acl_connections_.find(handle);
+    ASSERT(connection != acl_connections_.end());
+    return connection->second;
+  }
+
+  AclConnection::QueueUpEnd* get_acl_queue_end(uint16_t handle) {
+    auto& connection = check_and_get_connection(handle);
+    return connection.queue_->GetUpEnd();
+  }
+
+  void RegisterCallbacks(uint16_t handle, ConnectionManagementCallbacks* callbacks, os::Handler* handler) {
+    auto& connection = check_and_get_connection(handle);
+    ASSERT(connection.command_complete_callbacks_ == nullptr);
+    connection.command_complete_callbacks_ = callbacks;
+    connection.command_complete_handler_ = handler;
+  }
+
+  void UnregisterCallbacks(uint16_t handle, ConnectionManagementCallbacks* callbacks) {
+    auto& connection = check_and_get_connection(handle);
+    ASSERT(connection.command_complete_callbacks_ == callbacks);
+    connection.command_complete_callbacks_ = nullptr;
+  }
+
+  void RegisterDisconnectCallback(uint16_t handle, common::OnceCallback<void(ErrorCode)> on_disconnect,
+                                  os::Handler* handler) {
+    auto& connection = check_and_get_connection(handle);
+    connection.on_disconnect_callback_ = std::move(on_disconnect);
+    connection.disconnect_handler_ = handler;
+    if (connection.is_disconnected_) {
+      connection.call_disconnect_callback();
+    }
+  }
+
+  bool Disconnect(uint16_t handle, DisconnectReason reason) {
+    auto& connection = check_and_get_connection(handle);
+    if (connection.is_disconnected_) {
+      LOG_INFO("Already disconnected");
+      return false;
+    }
+    handler_->Post(BindOnce(&impl::handle_disconnect, common::Unretained(this), handle, reason));
+    return true;
+  }
+
+  bool ChangeConnectionPacketType(uint16_t handle, uint16_t packet_type) {
+    auto& connection = check_and_get_connection(handle);
+    if (connection.is_disconnected_) {
+      LOG_INFO("Already disconnected");
+      return false;
+    }
+    handler_->Post(
+        BindOnce(&impl::handle_change_connection_packet_type, common::Unretained(this), handle, packet_type));
+    return true;
+  }
+
+  bool AuthenticationRequested(uint16_t handle) {
+    LOG_INFO("Auth reqiuest");
+    auto& connection = check_and_get_connection(handle);
+    if (connection.is_disconnected_) {
+      LOG_INFO("Already disconnected");
+      return false;
+    }
+    handler_->Post(BindOnce(&impl::handle_authentication_requested, common::Unretained(this), handle));
+    return true;
+  }
+
+  bool SetConnectionEncryption(uint16_t handle, Enable enable) {
+    auto& connection = check_and_get_connection(handle);
+    if (connection.is_disconnected_) {
+      LOG_INFO("Already disconnected");
+      return false;
+    }
+    handler_->Post(BindOnce(&impl::handle_set_connection_encryption, common::Unretained(this), handle, enable));
+    return true;
+  }
+
+  bool ChangeConnectionLinkKey(uint16_t handle) {
+    auto& connection = check_and_get_connection(handle);
+    if (connection.is_disconnected_) {
+      LOG_INFO("Already disconnected");
+      return false;
+    }
+    handler_->Post(BindOnce(&impl::handle_change_connection_link_key, common::Unretained(this), handle));
+    return true;
+  }
+
+  bool ReadClockOffset(uint16_t handle) {
+    auto& connection = check_and_get_connection(handle);
+    if (connection.is_disconnected_) {
+      LOG_INFO("Already disconnected");
+      return false;
+    }
+    handler_->Post(BindOnce(&impl::handle_read_clock_offset, common::Unretained(this), handle));
+    return true;
+  }
+
+  bool HoldMode(uint16_t handle, uint16_t max_interval, uint16_t min_interval) {
+    auto& connection = check_and_get_connection(handle);
+    if (connection.is_disconnected_) {
+      LOG_INFO("Already disconnected");
+      return false;
+    }
+    handler_->Post(BindOnce(&impl::handle_hold_mode, common::Unretained(this), handle, max_interval, min_interval));
+    return true;
+  }
+
+  bool SniffMode(uint16_t handle, uint16_t max_interval, uint16_t min_interval, int16_t attempt, uint16_t timeout) {
+    auto& connection = check_and_get_connection(handle);
+    if (connection.is_disconnected_) {
+      LOG_INFO("Already disconnected");
+      return false;
+    }
+    handler_->Post(BindOnce(&impl::handle_sniff_mode, common::Unretained(this), handle, max_interval, min_interval,
+                            attempt, timeout));
+    return true;
+  }
+
+  bool ExitSniffMode(uint16_t handle) {
+    auto& connection = check_and_get_connection(handle);
+    if (connection.is_disconnected_) {
+      LOG_INFO("Already disconnected");
+      return false;
+    }
+    handler_->Post(BindOnce(&impl::handle_exit_sniff_mode, common::Unretained(this), handle));
+    return true;
+  }
+
+  bool QosSetup(uint16_t handle, ServiceType service_type, uint32_t token_rate, uint32_t peak_bandwidth,
+                uint32_t latency, uint32_t delay_variation) {
+    auto& connection = check_and_get_connection(handle);
+    if (connection.is_disconnected_) {
+      LOG_INFO("Already disconnected");
+      return false;
+    }
+    handler_->Post(BindOnce(&impl::handle_qos_setup_mode, common::Unretained(this), handle, service_type, token_rate,
+                            peak_bandwidth, latency, delay_variation));
+    return true;
+  }
+
+  bool RoleDiscovery(uint16_t handle) {
+    auto& connection = check_and_get_connection(handle);
+    if (connection.is_disconnected_) {
+      LOG_INFO("Already disconnected");
+      return false;
+    }
+    handler_->Post(BindOnce(&impl::handle_role_discovery, common::Unretained(this), handle));
+    return true;
+  }
+
+  bool ReadLinkPolicySettings(uint16_t handle) {
+    auto& connection = check_and_get_connection(handle);
+    if (connection.is_disconnected_) {
+      LOG_INFO("Already disconnected");
+      return false;
+    }
+    handler_->Post(BindOnce(&impl::handle_read_link_policy_settings, common::Unretained(this), handle));
+    return true;
+  }
+
+  bool WriteLinkPolicySettings(uint16_t handle, uint16_t link_policy_settings) {
+    auto& connection = check_and_get_connection(handle);
+    if (connection.is_disconnected_) {
+      LOG_INFO("Already disconnected");
+      return false;
+    }
+    handler_->Post(
+        BindOnce(&impl::handle_write_link_policy_settings, common::Unretained(this), handle, link_policy_settings));
+    return true;
+  }
+
+  bool FlowSpecification(uint16_t handle, FlowDirection flow_direction, ServiceType service_type, uint32_t token_rate,
+                         uint32_t token_bucket_size, uint32_t peak_bandwidth, uint32_t access_latency) {
+    auto& connection = check_and_get_connection(handle);
+    if (connection.is_disconnected_) {
+      LOG_INFO("Already disconnected");
+      return false;
+    }
+    handler_->Post(BindOnce(&impl::handle_flow_specification, common::Unretained(this), handle, flow_direction,
+                            service_type, token_rate, token_bucket_size, peak_bandwidth, access_latency));
+    return true;
+  }
+
+  bool SniffSubrating(uint16_t handle, uint16_t maximum_latency, uint16_t minimum_remote_timeout,
+                      uint16_t minimum_local_timeout) {
+    auto& connection = check_and_get_connection(handle);
+    if (connection.is_disconnected_) {
+      LOG_INFO("Already disconnected");
+      return false;
+    }
+    handler_->Post(BindOnce(&impl::handle_sniff_subrating, common::Unretained(this), handle, maximum_latency,
+                            minimum_remote_timeout, minimum_local_timeout));
+    return true;
+  }
+
+  bool Flush(uint16_t handle) {
+    auto& connection = check_and_get_connection(handle);
+    if (connection.is_disconnected_) {
+      LOG_INFO("Already disconnected");
+      return false;
+    }
+    handler_->Post(BindOnce(&impl::handle_flush, common::Unretained(this), handle));
+    return true;
+  }
+
+  bool ReadAutomaticFlushTimeout(uint16_t handle) {
+    auto& connection = check_and_get_connection(handle);
+    if (connection.is_disconnected_) {
+      LOG_INFO("Already disconnected");
+      return false;
+    }
+    handler_->Post(BindOnce(&impl::handle_read_automatic_flush_timeout, common::Unretained(this), handle));
+    return true;
+  }
+
+  bool WriteAutomaticFlushTimeout(uint16_t handle, uint16_t flush_timeout) {
+    auto& connection = check_and_get_connection(handle);
+    if (connection.is_disconnected_) {
+      LOG_INFO("Already disconnected");
+      return false;
+    }
+    handler_->Post(
+        BindOnce(&impl::handle_write_automatic_flush_timeout, common::Unretained(this), handle, flush_timeout));
+    return true;
+  }
+
+  bool ReadTransmitPowerLevel(uint16_t handle, TransmitPowerLevelType type) {
+    auto& connection = check_and_get_connection(handle);
+    if (connection.is_disconnected_) {
+      LOG_INFO("Already disconnected");
+      return false;
+    }
+    handler_->Post(BindOnce(&impl::handle_read_transmit_power_level, common::Unretained(this), handle, type));
+    return true;
+  }
+
+  bool ReadLinkSupervisionTimeout(uint16_t handle) {
+    auto& connection = check_and_get_connection(handle);
+    if (connection.is_disconnected_) {
+      LOG_INFO("Already disconnected");
+      return false;
+    }
+    handler_->Post(BindOnce(&impl::handle_read_link_supervision_timeout, common::Unretained(this), handle));
+    return true;
+  }
+
+  bool WriteLinkSupervisionTimeout(uint16_t handle, uint16_t link_supervision_timeout) {
+    auto& connection = check_and_get_connection(handle);
+    if (connection.is_disconnected_) {
+      LOG_INFO("Already disconnected");
+      return false;
+    }
+    handler_->Post(BindOnce(&impl::handle_write_link_supervision_timeout, common::Unretained(this), handle,
+                            link_supervision_timeout));
+    return true;
+  }
+
+  bool ReadFailedContactCounter(uint16_t handle) {
+    auto& connection = check_and_get_connection(handle);
+    if (connection.is_disconnected_) {
+      LOG_INFO("Already disconnected");
+      return false;
+    }
+    handler_->Post(BindOnce(&impl::handle_read_failed_contact_counter, common::Unretained(this), handle));
+    return true;
+  }
+
+  bool ResetFailedContactCounter(uint16_t handle) {
+    auto& connection = check_and_get_connection(handle);
+    if (connection.is_disconnected_) {
+      LOG_INFO("Already disconnected");
+      return false;
+    }
+    handler_->Post(BindOnce(&impl::handle_reset_failed_contact_counter, common::Unretained(this), handle));
+    return true;
+  }
+
+  bool ReadLinkQuality(uint16_t handle) {
+    auto& connection = check_and_get_connection(handle);
+    if (connection.is_disconnected_) {
+      LOG_INFO("Already disconnected");
+      return false;
+    }
+    handler_->Post(BindOnce(&impl::handle_read_link_quality, common::Unretained(this), handle));
+    return true;
+  }
+
+  bool ReadAfhChannelMap(uint16_t handle) {
+    auto& connection = check_and_get_connection(handle);
+    if (connection.is_disconnected_) {
+      LOG_INFO("Already disconnected");
+      return false;
+    }
+    handler_->Post(BindOnce(&impl::handle_afh_channel_map, common::Unretained(this), handle));
+    return true;
+  }
+
+  bool ReadRssi(uint16_t handle) {
+    auto& connection = check_and_get_connection(handle);
+    if (connection.is_disconnected_) {
+      LOG_INFO("Already disconnected");
+      return false;
+    }
+    handler_->Post(BindOnce(&impl::handle_read_rssi, common::Unretained(this), handle));
+    return true;
+  }
+
+  bool ReadRemoteVersionInformation(uint16_t handle) {
+    auto& connection = check_and_get_connection(handle);
+    if (connection.is_disconnected_) {
+      LOG_INFO("Already disconnected");
+      return false;
+    }
+    handler_->Post(BindOnce(&impl::handle_read_remote_version_information, common::Unretained(this), handle));
+    return true;
+  }
+
+  bool ReadRemoteSupportedFeatures(uint16_t handle) {
+    auto& connection = check_and_get_connection(handle);
+    if (connection.is_disconnected_) {
+      LOG_INFO("Already disconnected");
+      return false;
+    }
+    handler_->Post(BindOnce(&impl::handle_read_remote_supported_features, common::Unretained(this), handle));
+    return true;
+  }
+
+  bool ReadRemoteExtendedFeatures(uint16_t handle) {
+    auto& connection = check_and_get_connection(handle);
+    if (connection.is_disconnected_) {
+      LOG_INFO("Already disconnected");
+      return false;
+    }
+    handler_->Post(BindOnce(&impl::handle_read_remote_extended_features, common::Unretained(this), handle));
+    return true;
+  }
+
+  bool ReadClock(uint16_t handle, WhichClock which_clock) {
+    auto& connection = check_and_get_connection(handle);
+    if (connection.is_disconnected_) {
+      LOG_INFO("Already disconnected");
+      return false;
+    }
+    handler_->Post(BindOnce(&impl::handle_read_clock, common::Unretained(this), handle, which_clock));
+    return true;
+  }
+
+  bool LeConnectionUpdate(uint16_t handle, uint16_t conn_interval_min, uint16_t conn_interval_max,
+                          uint16_t conn_latency, uint16_t supervision_timeout,
+                          common::OnceCallback<void(ErrorCode)> done_callback, os::Handler* handler) {
+    auto& connection = check_and_get_connection(handle);
+    if (connection.is_disconnected_) {
+      LOG_INFO("Already disconnected");
+      return false;
+    }
+    if (!connection.on_connection_update_complete_callback_.is_null()) {
+      LOG_INFO("There is another pending connection update");
+      return false;
+    }
+    connection.on_connection_update_complete_callback_ = std::move(done_callback);
+    connection.on_connection_update_complete_callback_handler_ = handler;
+    if (conn_interval_min < 0x0006 || conn_interval_min > 0x0C80 || conn_interval_max < 0x0006 ||
+        conn_interval_max > 0x0C80 || conn_latency > 0x01F3 || supervision_timeout < 0x000A ||
+        supervision_timeout > 0x0C80) {
+      LOG_ERROR("Invalid parameter");
+      return false;
+    }
+    handler_->Post(BindOnce(&impl::handle_le_connection_update, common::Unretained(this), handle, conn_interval_min,
+                            conn_interval_max, conn_latency, supervision_timeout));
+    return true;
+  }
+
+  void Finish(uint16_t handle) {
+    auto& connection = check_and_get_connection(handle);
+    ASSERT_LOG(connection.is_disconnected_, "Finish must be invoked after disconnection (handle 0x%04hx)", handle);
+    handler_->Post(BindOnce(&impl::cleanup, common::Unretained(this), handle));
+  }
+
+  const AclManager& acl_manager_;
+
+  static constexpr uint16_t kMinimumCeLength = 0x0002;
+  static constexpr uint16_t kMaximumCeLength = 0x0C00;
+
+  Controller* controller_ = nullptr;
+  uint16_t max_acl_packet_credits_ = 0;
+  uint16_t acl_packet_credits_ = 0;
+  uint16_t acl_buffer_length_ = 0;
+
+  std::list<std::unique_ptr<AclPacketBuilder>> fragments_to_send_;
+  std::map<uint16_t, acl_connection>::iterator current_connection_pair_;
+
+  HciLayer* hci_layer_ = nullptr;
+  os::Handler* handler_ = nullptr;
+  ConnectionCallbacks* client_callbacks_ = nullptr;
+  os::Handler* client_handler_ = nullptr;
+  LeConnectionCallbacks* le_client_callbacks_ = nullptr;
+  os::Handler* le_client_handler_ = nullptr;
+  AclManagerCallbacks* acl_manager_client_callbacks_ = nullptr;
+  os::Handler* acl_manager_client_handler_ = nullptr;
+  AclManagerCallbacks* le_acl_manager_client_callbacks_ = nullptr;
+  os::Handler* le_acl_manager_client_handler_ = nullptr;
+  common::BidiQueueEnd<AclPacketBuilder, AclPacketView>* hci_queue_end_ = nullptr;
+  std::map<uint16_t, AclManager::acl_connection> acl_connections_;
+  std::set<Address> connecting_;
+  std::set<AddressWithType> connecting_le_;
+  common::Callback<bool(Address, ClassOfDevice)> should_accept_connection_;
+  std::queue<std::pair<Address, std::unique_ptr<CreateConnectionBuilder>>> pending_outgoing_connections_;
+  size_t hci_mtu_{0};
+};
+
+AclConnection::QueueUpEnd* AclConnection::GetAclQueueEnd() const {
+  return manager_->pimpl_->get_acl_queue_end(handle_);
+}
+
+void AclConnection::RegisterCallbacks(ConnectionManagementCallbacks* callbacks, os::Handler* handler) {
+  return manager_->pimpl_->RegisterCallbacks(handle_, callbacks, handler);
+}
+
+void AclConnection::UnregisterCallbacks(ConnectionManagementCallbacks* callbacks) {
+  return manager_->pimpl_->UnregisterCallbacks(handle_, callbacks);
+}
+
+void AclConnection::RegisterDisconnectCallback(common::OnceCallback<void(ErrorCode)> on_disconnect,
+                                               os::Handler* handler) {
+  return manager_->pimpl_->RegisterDisconnectCallback(handle_, std::move(on_disconnect), handler);
+}
+
+bool AclConnection::Disconnect(DisconnectReason reason) {
+  return manager_->pimpl_->Disconnect(handle_, reason);
+}
+
+bool AclConnection::ChangeConnectionPacketType(uint16_t packet_type) {
+  return manager_->pimpl_->ChangeConnectionPacketType(handle_, packet_type);
+}
+
+bool AclConnection::AuthenticationRequested() {
+  return manager_->pimpl_->AuthenticationRequested(handle_);
+}
+
+bool AclConnection::SetConnectionEncryption(Enable enable) {
+  return manager_->pimpl_->SetConnectionEncryption(handle_, enable);
+}
+
+bool AclConnection::ChangeConnectionLinkKey() {
+  return manager_->pimpl_->ChangeConnectionLinkKey(handle_);
+}
+
+bool AclConnection::ReadClockOffset() {
+  return manager_->pimpl_->ReadClockOffset(handle_);
+}
+
+bool AclConnection::HoldMode(uint16_t max_interval, uint16_t min_interval) {
+  return manager_->pimpl_->HoldMode(handle_, max_interval, min_interval);
+}
+
+bool AclConnection::SniffMode(uint16_t max_interval, uint16_t min_interval, uint16_t attempt, uint16_t timeout) {
+  return manager_->pimpl_->SniffMode(handle_, max_interval, min_interval, attempt, timeout);
+}
+
+bool AclConnection::ExitSniffMode() {
+  return manager_->pimpl_->ExitSniffMode(handle_);
+}
+
+bool AclConnection::QosSetup(ServiceType service_type, uint32_t token_rate, uint32_t peak_bandwidth, uint32_t latency,
+                             uint32_t delay_variation) {
+  return manager_->pimpl_->QosSetup(handle_, service_type, token_rate, peak_bandwidth, latency, delay_variation);
+}
+
+bool AclConnection::RoleDiscovery() {
+  return manager_->pimpl_->RoleDiscovery(handle_);
+}
+
+bool AclConnection::ReadLinkPolicySettings() {
+  return manager_->pimpl_->ReadLinkPolicySettings(handle_);
+}
+
+bool AclConnection::WriteLinkPolicySettings(uint16_t link_policy_settings) {
+  return manager_->pimpl_->WriteLinkPolicySettings(handle_, link_policy_settings);
+}
+
+bool AclConnection::FlowSpecification(FlowDirection flow_direction, ServiceType service_type, uint32_t token_rate,
+                                      uint32_t token_bucket_size, uint32_t peak_bandwidth, uint32_t access_latency) {
+  return manager_->pimpl_->FlowSpecification(handle_, flow_direction, service_type, token_rate, token_bucket_size,
+                                             peak_bandwidth, access_latency);
+}
+
+bool AclConnection::SniffSubrating(uint16_t maximum_latency, uint16_t minimum_remote_timeout,
+                                   uint16_t minimum_local_timeout) {
+  return manager_->pimpl_->SniffSubrating(handle_, maximum_latency, minimum_remote_timeout, minimum_local_timeout);
+}
+
+bool AclConnection::Flush() {
+  return manager_->pimpl_->Flush(handle_);
+}
+
+bool AclConnection::ReadAutomaticFlushTimeout() {
+  return manager_->pimpl_->ReadAutomaticFlushTimeout(handle_);
+}
+
+bool AclConnection::WriteAutomaticFlushTimeout(uint16_t flush_timeout) {
+  return manager_->pimpl_->WriteAutomaticFlushTimeout(handle_, flush_timeout);
+}
+
+bool AclConnection::ReadTransmitPowerLevel(TransmitPowerLevelType type) {
+  return manager_->pimpl_->ReadTransmitPowerLevel(handle_, type);
+}
+
+bool AclConnection::ReadLinkSupervisionTimeout() {
+  return manager_->pimpl_->ReadLinkSupervisionTimeout(handle_);
+}
+
+bool AclConnection::WriteLinkSupervisionTimeout(uint16_t link_supervision_timeout) {
+  return manager_->pimpl_->WriteLinkSupervisionTimeout(handle_, link_supervision_timeout);
+}
+
+bool AclConnection::ReadFailedContactCounter() {
+  return manager_->pimpl_->ReadFailedContactCounter(handle_);
+}
+
+bool AclConnection::ResetFailedContactCounter() {
+  return manager_->pimpl_->ResetFailedContactCounter(handle_);
+}
+
+bool AclConnection::ReadLinkQuality() {
+  return manager_->pimpl_->ReadLinkQuality(handle_);
+}
+
+bool AclConnection::ReadAfhChannelMap() {
+  return manager_->pimpl_->ReadAfhChannelMap(handle_);
+}
+
+bool AclConnection::ReadRssi() {
+  return manager_->pimpl_->ReadRssi(handle_);
+}
+
+bool AclConnection::ReadRemoteVersionInformation() {
+  return manager_->pimpl_->ReadRemoteVersionInformation(handle_);
+}
+
+bool AclConnection::ReadRemoteSupportedFeatures() {
+  return manager_->pimpl_->ReadRemoteSupportedFeatures(handle_);
+}
+
+bool AclConnection::ReadRemoteExtendedFeatures() {
+  return manager_->pimpl_->ReadRemoteExtendedFeatures(handle_);
+}
+
+bool AclConnection::ReadClock(WhichClock which_clock) {
+  return manager_->pimpl_->ReadClock(handle_, which_clock);
+}
+
+bool AclConnection::LeConnectionUpdate(uint16_t conn_interval_min, uint16_t conn_interval_max, uint16_t conn_latency,
+                                       uint16_t supervision_timeout,
+                                       common::OnceCallback<void(ErrorCode)> done_callback, os::Handler* handler) {
+  return manager_->pimpl_->LeConnectionUpdate(handle_, conn_interval_min, conn_interval_max, conn_latency,
+                                              supervision_timeout, std::move(done_callback), handler);
+}
+
+void AclConnection::Finish() {
+  return manager_->pimpl_->Finish(handle_);
+}
+
+AclManager::AclManager() : pimpl_(std::make_unique<impl>(*this)) {}
+
+void AclManager::RegisterCallbacks(ConnectionCallbacks* callbacks, os::Handler* handler) {
+  ASSERT(callbacks != nullptr && handler != nullptr);
+  GetHandler()->Post(common::BindOnce(&impl::handle_register_callbacks, common::Unretained(pimpl_.get()),
+                                      common::Unretained(callbacks), common::Unretained(handler)));
+}
+
+void AclManager::RegisterLeCallbacks(LeConnectionCallbacks* callbacks, os::Handler* handler) {
+  ASSERT(callbacks != nullptr && handler != nullptr);
+  GetHandler()->Post(common::BindOnce(&impl::handle_register_le_callbacks, common::Unretained(pimpl_.get()),
+                                      common::Unretained(callbacks), common::Unretained(handler)));
+}
+
+void AclManager::RegisterAclManagerCallbacks(AclManagerCallbacks* callbacks, os::Handler* handler) {
+  ASSERT(callbacks != nullptr && handler != nullptr);
+  GetHandler()->Post(common::BindOnce(&impl::handle_register_acl_manager_callbacks, common::Unretained(pimpl_.get()),
+                                      common::Unretained(callbacks), common::Unretained(handler)));
+}
+
+void AclManager::RegisterLeAclManagerCallbacks(AclManagerCallbacks* callbacks, os::Handler* handler) {
+  ASSERT(callbacks != nullptr && handler != nullptr);
+  GetHandler()->Post(common::BindOnce(&impl::handle_register_le_acl_manager_callbacks, common::Unretained(pimpl_.get()),
+                                      common::Unretained(callbacks), common::Unretained(handler)));
+}
+
+void AclManager::CreateConnection(Address address) {
+  GetHandler()->Post(common::BindOnce(&impl::create_connection, common::Unretained(pimpl_.get()), address));
+}
+
+void AclManager::CreateLeConnection(AddressWithType address_with_type) {
+  GetHandler()->Post(
+      common::BindOnce(&impl::create_le_connection, common::Unretained(pimpl_.get()), address_with_type));
+}
+
+void AclManager::CancelConnect(Address address) {
+  GetHandler()->Post(BindOnce(&impl::cancel_connect, common::Unretained(pimpl_.get()), address));
+}
+
+void AclManager::MasterLinkKey(KeyFlag key_flag) {
+  GetHandler()->Post(BindOnce(&impl::master_link_key, common::Unretained(pimpl_.get()), key_flag));
+}
+
+void AclManager::SwitchRole(Address address, Role role) {
+  GetHandler()->Post(BindOnce(&impl::switch_role, common::Unretained(pimpl_.get()), address, role));
+}
+
+void AclManager::ReadDefaultLinkPolicySettings() {
+  GetHandler()->Post(BindOnce(&impl::read_default_link_policy_settings, common::Unretained(pimpl_.get())));
+}
+
+void AclManager::WriteDefaultLinkPolicySettings(uint16_t default_link_policy_settings) {
+  GetHandler()->Post(BindOnce(&impl::write_default_link_policy_settings, common::Unretained(pimpl_.get()),
+                              default_link_policy_settings));
+}
+
+void AclManager::ListDependencies(ModuleList* list) {
+  list->add<HciLayer>();
+  list->add<Controller>();
+}
+
+void AclManager::Start() {
+  pimpl_->Start();
+}
+
+void AclManager::Stop() {
+  pimpl_->Stop();
+}
+
+std::string AclManager::ToString() const {
+  return "Acl Manager";
+}
+
+const ModuleFactory AclManager::Factory = ModuleFactory([]() { return new AclManager(); });
+
+AclManager::~AclManager() = default;
+
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/acl_manager.h b/gd/hci/acl_manager.h
new file mode 100644
index 0000000..9d24835
--- /dev/null
+++ b/gd/hci/acl_manager.h
@@ -0,0 +1,264 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include "common/bidi_queue.h"
+#include "common/callback.h"
+#include "hci/address.h"
+#include "hci/address_with_type.h"
+#include "hci/hci_layer.h"
+#include "hci/hci_packets.h"
+#include "module.h"
+#include "os/handler.h"
+
+namespace bluetooth {
+namespace hci {
+
+class AclManager;
+
+class ConnectionManagementCallbacks {
+ public:
+  virtual ~ConnectionManagementCallbacks() = default;
+  // Invoked when controller sends Connection Packet Type Changed event with Success error code
+  virtual void OnConnectionPacketTypeChanged(uint16_t packet_type) = 0;
+  // Invoked when controller sends Authentication Complete event with Success error code
+  virtual void OnAuthenticationComplete() = 0;
+  // Invoked when controller sends Encryption Change event with Success error code
+  virtual void OnEncryptionChange(EncryptionEnabled enabled) = 0;
+  // Invoked when controller sends Change Connection Link Key Complete event with Success error code
+  virtual void OnChangeConnectionLinkKeyComplete() = 0;
+  // Invoked when controller sends Read Clock Offset Complete event with Success error code
+  virtual void OnReadClockOffsetComplete(uint16_t clock_offset) = 0;
+  // Invoked when controller sends Mode Change event with Success error code
+  virtual void OnModeChange(Mode current_mode, uint16_t interval) = 0;
+  // Invoked when controller sends QoS Setup Complete event with Success error code
+  virtual void OnQosSetupComplete(ServiceType service_type, uint32_t token_rate, uint32_t peak_bandwidth,
+                                  uint32_t latency, uint32_t delay_variation) = 0;
+  // Invoked when controller sends Flow Specification Complete event with Success error code
+  virtual void OnFlowSpecificationComplete(FlowDirection flow_direction, ServiceType service_type, uint32_t token_rate,
+                                           uint32_t token_bucket_size, uint32_t peak_bandwidth,
+                                           uint32_t access_latency) = 0;
+  // Invoked when controller sends Flush Occurred event
+  virtual void OnFlushOccurred() = 0;
+  // Invoked when controller sends Command Complete event for Role Discovery command with Success error code
+  virtual void OnRoleDiscoveryComplete(Role current_role) = 0;
+  // Invoked when controller sends Command Complete event for Read Link Policy Settings command with Success error code
+  virtual void OnReadLinkPolicySettingsComplete(uint16_t link_policy_settings) = 0;
+  // Invoked when controller sends Command Complete event for Read Automatic Flush Timeout command with Success error
+  // code
+  virtual void OnReadAutomaticFlushTimeoutComplete(uint16_t flush_timeout) = 0;
+  // Invoked when controller sends Command Complete event for Read Transmit Power Level command with Success error code
+  virtual void OnReadTransmitPowerLevelComplete(uint8_t transmit_power_level) = 0;
+  // Invoked when controller sends Command Complete event for Read Link Supervision Time out command with Success error
+  // code
+  virtual void OnReadLinkSupervisionTimeoutComplete(uint16_t link_supervision_timeout) = 0;
+  // Invoked when controller sends Command Complete event for Read Failed Contact Counter command with Success error
+  // code
+  virtual void OnReadFailedContactCounterComplete(uint16_t failed_contact_counter) = 0;
+  // Invoked when controller sends Command Complete event for Read Link Quality command with Success error code
+  virtual void OnReadLinkQualityComplete(uint8_t link_quality) = 0;
+  // Invoked when controller sends Command Complete event for Read AFH Channel Map command with Success error code
+  virtual void OnReadAfhChannelMapComplete(AfhMode afh_mode, std::array<uint8_t, 10> afh_channel_map) = 0;
+  // Invoked when controller sends Command Complete event for Read RSSI command with Success error code
+  virtual void OnReadRssiComplete(uint8_t rssi) = 0;
+  // Invoked when controller sends Command Complete event for Read Clock command with Success error code
+  virtual void OnReadClockComplete(uint32_t clock, uint16_t accuracy) = 0;
+};
+
+class AclConnection {
+ public:
+  AclConnection()
+      : manager_(nullptr), handle_(0), address_(Address::kEmpty), address_type_(AddressType::PUBLIC_DEVICE_ADDRESS){};
+  virtual ~AclConnection() = default;
+
+  virtual Address GetAddress() const {
+    return address_;
+  }
+
+  virtual AddressType GetAddressType() const {
+    return address_type_;
+  }
+
+  uint16_t GetHandle() const {
+    return handle_;
+  }
+
+  /* This return role for LE devices only, for Classic, please see |RoleDiscovery| method.
+   * TODO: split AclConnection for LE and Classic
+   */
+  Role GetRole() const {
+    return role_;
+  }
+
+  using Queue = common::BidiQueue<PacketView<kLittleEndian>, BasePacketBuilder>;
+  using QueueUpEnd = common::BidiQueueEnd<BasePacketBuilder, PacketView<kLittleEndian>>;
+  using QueueDownEnd = common::BidiQueueEnd<PacketView<kLittleEndian>, BasePacketBuilder>;
+  virtual QueueUpEnd* GetAclQueueEnd() const;
+  virtual void RegisterCallbacks(ConnectionManagementCallbacks* callbacks, os::Handler* handler);
+  virtual void UnregisterCallbacks(ConnectionManagementCallbacks* callbacks);
+  virtual void RegisterDisconnectCallback(common::OnceCallback<void(ErrorCode)> on_disconnect, os::Handler* handler);
+  virtual bool Disconnect(DisconnectReason reason);
+  virtual bool ChangeConnectionPacketType(uint16_t packet_type);
+  virtual bool AuthenticationRequested();
+  virtual bool SetConnectionEncryption(Enable enable);
+  virtual bool ChangeConnectionLinkKey();
+  virtual bool ReadClockOffset();
+  virtual bool HoldMode(uint16_t max_interval, uint16_t min_interval);
+  virtual bool SniffMode(uint16_t max_interval, uint16_t min_interval, uint16_t attempt, uint16_t timeout);
+  virtual bool ExitSniffMode();
+  virtual bool QosSetup(ServiceType service_type, uint32_t token_rate, uint32_t peak_bandwidth, uint32_t latency,
+                        uint32_t delay_variation);
+  virtual bool RoleDiscovery();
+  virtual bool ReadLinkPolicySettings();
+  virtual bool WriteLinkPolicySettings(uint16_t link_policy_settings);
+  virtual bool FlowSpecification(FlowDirection flow_direction, ServiceType service_type, uint32_t token_rate,
+                                 uint32_t token_bucket_size, uint32_t peak_bandwidth, uint32_t access_latency);
+  virtual bool SniffSubrating(uint16_t maximum_latency, uint16_t minimum_remote_timeout,
+                              uint16_t minimum_local_timeout);
+  virtual bool Flush();
+  virtual bool ReadAutomaticFlushTimeout();
+  virtual bool WriteAutomaticFlushTimeout(uint16_t flush_timeout);
+  virtual bool ReadTransmitPowerLevel(TransmitPowerLevelType type);
+  virtual bool ReadLinkSupervisionTimeout();
+  virtual bool WriteLinkSupervisionTimeout(uint16_t link_supervision_timeout);
+  virtual bool ReadFailedContactCounter();
+  virtual bool ResetFailedContactCounter();
+  virtual bool ReadLinkQuality();
+  virtual bool ReadAfhChannelMap();
+  virtual bool ReadRssi();
+  virtual bool ReadClock(WhichClock which_clock);
+  virtual bool ReadRemoteVersionInformation();
+  virtual bool ReadRemoteSupportedFeatures();
+  virtual bool ReadRemoteExtendedFeatures();
+
+  // LE ACL Method
+  virtual bool LeConnectionUpdate(uint16_t conn_interval_min, uint16_t conn_interval_max, uint16_t conn_latency,
+                                  uint16_t supervision_timeout, common::OnceCallback<void(ErrorCode)> done_callback,
+                                  os::Handler* handler);
+
+  // Ask AclManager to clean me up. Must invoke after on_disconnect is called
+  virtual void Finish();
+
+  // TODO: API to change link settings ... ?
+
+ private:
+  friend AclManager;
+  AclConnection(const AclManager* manager, uint16_t handle, Address address)
+      : manager_(manager), handle_(handle), address_(address), address_type_(AddressType::PUBLIC_DEVICE_ADDRESS) {}
+  AclConnection(const AclManager* manager, uint16_t handle, Address address, AddressType address_type, Role role)
+      : manager_(manager), handle_(handle), address_(address), address_type_(address_type), role_(role) {}
+  const AclManager* manager_;
+  uint16_t handle_;
+  Address address_;
+  AddressType address_type_;
+  Role role_;
+  DISALLOW_COPY_AND_ASSIGN(AclConnection);
+};
+
+class ConnectionCallbacks {
+ public:
+  virtual ~ConnectionCallbacks() = default;
+  // Invoked when controller sends Connection Complete event with Success error code
+  virtual void OnConnectSuccess(std::unique_ptr<AclConnection> /* , initiated_by_local ? */) = 0;
+  // Invoked when controller sends Connection Complete event with non-Success error code
+  virtual void OnConnectFail(Address, ErrorCode reason) = 0;
+};
+
+class LeConnectionCallbacks {
+ public:
+  virtual ~LeConnectionCallbacks() = default;
+  // Invoked when controller sends Connection Complete event with Success error code
+  // AddressWithType is always equal to the object used in AclManager#CreateLeConnection
+  virtual void OnLeConnectSuccess(AddressWithType, std::unique_ptr<AclConnection> /* , initiated_by_local ? */) = 0;
+  // Invoked when controller sends Connection Complete event with non-Success error code
+  virtual void OnLeConnectFail(AddressWithType, ErrorCode reason) = 0;
+};
+
+class AclManagerCallbacks {
+ public:
+  virtual ~AclManagerCallbacks() = default;
+  // Invoked when controller sends Master Link Key Complete event with Success error code
+  virtual void OnMasterLinkKeyComplete(uint16_t connection_handle, KeyFlag key_flag) = 0;
+  // Invoked when controller sends Role Change event with Success error code
+  virtual void OnRoleChange(Address bd_addr, Role new_role) = 0;
+  // Invoked when controller sends Command Complete event for Read Default Link Policy Settings command with Success
+  // error code
+  virtual void OnReadDefaultLinkPolicySettingsComplete(uint16_t default_link_policy_settings) = 0;
+};
+
+class AclManager : public Module {
+ public:
+  AclManager();
+  // NOTE: It is necessary to forward declare a default destructor that overrides the base class one, because
+  // "struct impl" is forwarded declared in .cc and compiler needs a concrete definition of "struct impl" when
+  // compiling AclManager's destructor. Hence we need to forward declare the destructor for AclManager to delay
+  // compiling AclManager's destructor until it starts linking the .cc file.
+  ~AclManager() override;
+
+  // Should register only once when user module starts.
+  // Generates OnConnectSuccess when an incoming connection is established.
+  virtual void RegisterCallbacks(ConnectionCallbacks* callbacks, os::Handler* handler);
+
+  // Should register only once when user module starts.
+  virtual void RegisterLeCallbacks(LeConnectionCallbacks* callbacks, os::Handler* handler);
+
+  // Should register only once when user module starts.
+  virtual void RegisterAclManagerCallbacks(AclManagerCallbacks* callbacks, os::Handler* handler);
+
+  // Should register only once when user module starts.
+  virtual void RegisterLeAclManagerCallbacks(AclManagerCallbacks* callbacks, os::Handler* handler);
+
+  // Generates OnConnectSuccess if connected, or OnConnectFail otherwise
+  virtual void CreateConnection(Address address);
+
+  // Generates OnLeConnectSuccess if connected, or OnLeConnectFail otherwise
+  virtual void CreateLeConnection(AddressWithType address_with_type);
+
+  // Generates OnConnectFail with error code "terminated by local host 0x16" if cancelled, or OnConnectSuccess if not
+  // successfully cancelled and already connected
+  virtual void CancelConnect(Address address);
+
+  virtual void MasterLinkKey(KeyFlag key_flag);
+  virtual void SwitchRole(Address address, Role role);
+  virtual void ReadDefaultLinkPolicySettings();
+  virtual void WriteDefaultLinkPolicySettings(uint16_t default_link_policy_settings);
+
+  static const ModuleFactory Factory;
+
+ protected:
+  void ListDependencies(ModuleList* list) override;
+
+  void Start() override;
+
+  void Stop() override;
+
+  std::string ToString() const override;
+
+ private:
+  friend AclConnection;
+
+  struct impl;
+  std::unique_ptr<impl> pimpl_;
+
+  struct acl_connection;
+  DISALLOW_COPY_AND_ASSIGN(AclManager);
+};
+
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/acl_manager_mock.h b/gd/hci/acl_manager_mock.h
new file mode 100644
index 0000000..d34b3ef
--- /dev/null
+++ b/gd/hci/acl_manager_mock.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include "hci/acl_manager.h"
+
+#include <gmock/gmock.h>
+
+// Unit test interfaces
+namespace bluetooth {
+namespace hci {
+namespace testing {
+
+class MockAclConnection : public AclConnection {
+ public:
+  MOCK_METHOD(Address, GetAddress, (), (const, override));
+  MOCK_METHOD(AddressType, GetAddressType, (), (const, override));
+  MOCK_METHOD(void, RegisterDisconnectCallback,
+              (common::OnceCallback<void(ErrorCode)> on_disconnect, os::Handler* handler), (override));
+  MOCK_METHOD(bool, Disconnect, (DisconnectReason reason), (override));
+  MOCK_METHOD(void, Finish, (), (override));
+  MOCK_METHOD(void, RegisterCallbacks, (ConnectionManagementCallbacks * callbacks, os::Handler* handler), (override));
+  MOCK_METHOD(void, UnregisterCallbacks, (ConnectionManagementCallbacks * callbacks), (override));
+
+  QueueUpEnd* GetAclQueueEnd() const override {
+    return acl_queue_.GetUpEnd();
+  }
+  mutable common::BidiQueue<PacketView<kLittleEndian>, BasePacketBuilder> acl_queue_{10};
+};
+
+class MockAclManager : public AclManager {
+ public:
+  MOCK_METHOD(void, RegisterCallbacks, (ConnectionCallbacks * callbacks, os::Handler* handler), (override));
+  MOCK_METHOD(void, RegisterLeCallbacks, (LeConnectionCallbacks * callbacks, os::Handler* handler), (override));
+  MOCK_METHOD(void, CreateConnection, (Address address), (override));
+  MOCK_METHOD(void, CreateLeConnection, (AddressWithType address_with_type), (override));
+  MOCK_METHOD(void, CancelConnect, (Address address), (override));
+};
+
+}  // namespace testing
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/acl_manager_test.cc b/gd/hci/acl_manager_test.cc
new file mode 100644
index 0000000..c741d45
--- /dev/null
+++ b/gd/hci/acl_manager_test.cc
@@ -0,0 +1,1134 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "hci/acl_manager.h"
+
+#include <algorithm>
+#include <chrono>
+#include <future>
+#include <map>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "common/bind.h"
+#include "hci/address.h"
+#include "hci/controller.h"
+#include "hci/hci_layer.h"
+#include "os/thread.h"
+#include "packet/raw_builder.h"
+
+namespace bluetooth {
+namespace hci {
+namespace {
+
+using common::BidiQueue;
+using common::BidiQueueEnd;
+using packet::kLittleEndian;
+using packet::PacketView;
+using packet::RawBuilder;
+
+constexpr std::chrono::seconds kTimeout = std::chrono::seconds(2);
+
+PacketView<kLittleEndian> GetPacketView(std::unique_ptr<packet::BasePacketBuilder> packet) {
+  auto bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter i(*bytes);
+  bytes->reserve(packet->size());
+  packet->Serialize(i);
+  return packet::PacketView<packet::kLittleEndian>(bytes);
+}
+
+std::unique_ptr<BasePacketBuilder> NextPayload(uint16_t handle) {
+  static uint32_t packet_number = 1;
+  auto payload = std::make_unique<RawBuilder>();
+  payload->AddOctets2(6);  // L2CAP PDU size
+  payload->AddOctets2(2);  // L2CAP CID
+  payload->AddOctets2(handle);
+  payload->AddOctets4(packet_number++);
+  return std::move(payload);
+}
+
+std::unique_ptr<AclPacketBuilder> NextAclPacket(uint16_t handle) {
+  PacketBoundaryFlag packet_boundary_flag = PacketBoundaryFlag::FIRST_AUTOMATICALLY_FLUSHABLE;
+  BroadcastFlag broadcast_flag = BroadcastFlag::ACTIVE_SLAVE_BROADCAST;
+  return AclPacketBuilder::Create(handle, packet_boundary_flag, broadcast_flag, NextPayload(handle));
+}
+
+class TestController : public Controller {
+ public:
+  void RegisterCompletedAclPacketsCallback(common::Callback<void(uint16_t /* handle */, uint16_t /* packets */)> cb,
+                                           os::Handler* handler) override {
+    acl_cb_ = cb;
+    acl_cb_handler_ = handler;
+  }
+
+  uint16_t GetControllerAclPacketLength() const override {
+    return acl_buffer_length_;
+  }
+
+  uint16_t GetControllerNumAclPacketBuffers() const override {
+    return total_acl_buffers_;
+  }
+
+  uint64_t GetControllerLeLocalSupportedFeatures() const override {
+    return le_local_supported_features_;
+  }
+
+  void CompletePackets(uint16_t handle, uint16_t packets) {
+    acl_cb_handler_->Post(common::BindOnce(acl_cb_, handle, packets));
+  }
+
+  uint16_t acl_buffer_length_ = 1024;
+  uint16_t total_acl_buffers_ = 2;
+  uint64_t le_local_supported_features_ = 0;
+  common::Callback<void(uint16_t /* handle */, uint16_t /* packets */)> acl_cb_;
+  os::Handler* acl_cb_handler_ = nullptr;
+
+ protected:
+  void Start() override {}
+  void Stop() override {}
+  void ListDependencies(ModuleList* list) override {}
+};
+
+class TestHciLayer : public HciLayer {
+ public:
+  void EnqueueCommand(std::unique_ptr<CommandPacketBuilder> command,
+                      common::OnceCallback<void(CommandStatusView)> on_status, os::Handler* handler) override {
+    command_queue_.push(std::move(command));
+    command_status_callbacks.push_front(std::move(on_status));
+    if (command_promise_ != nullptr) {
+      command_promise_->set_value();
+      command_promise_.reset();
+    }
+  }
+
+  void EnqueueCommand(std::unique_ptr<CommandPacketBuilder> command,
+                      common::OnceCallback<void(CommandCompleteView)> on_complete, os::Handler* handler) override {
+    command_queue_.push(std::move(command));
+    command_complete_callbacks.push_front(std::move(on_complete));
+    if (command_promise_ != nullptr) {
+      command_promise_->set_value();
+      command_promise_.reset();
+    }
+  }
+
+  void SetCommandFuture() {
+    ASSERT_LOG(command_promise_ == nullptr, "Promises, Promises, ... Only one at a time.");
+    command_promise_ = std::make_unique<std::promise<void>>();
+    command_future_ = std::make_unique<std::future<void>>(command_promise_->get_future());
+  }
+
+  std::unique_ptr<CommandPacketBuilder> GetLastCommand() {
+    if (command_queue_.size() == 0) {
+      return nullptr;
+    }
+    auto last = std::move(command_queue_.front());
+    command_queue_.pop();
+    return last;
+  }
+
+  ConnectionManagementCommandView GetCommandPacket(OpCode op_code) {
+    if (command_future_ != nullptr) {
+      auto result = command_future_->wait_for(std::chrono::milliseconds(1000));
+      EXPECT_NE(std::future_status::timeout, result);
+    }
+    ASSERT(command_queue_.size() > 0);
+    auto packet_view = GetPacketView(GetLastCommand());
+    CommandPacketView command_packet_view = CommandPacketView::Create(packet_view);
+    ConnectionManagementCommandView command = ConnectionManagementCommandView::Create(command_packet_view);
+    ASSERT(command.IsValid());
+    EXPECT_EQ(command.GetOpCode(), op_code);
+
+    return command;
+  }
+
+  void RegisterEventHandler(EventCode event_code, common::Callback<void(EventPacketView)> event_handler,
+                            os::Handler* handler) override {
+    registered_events_[event_code] = event_handler;
+  }
+
+  void UnregisterEventHandler(EventCode event_code) override {
+    registered_events_.erase(event_code);
+  }
+
+  void RegisterLeEventHandler(SubeventCode subevent_code, common::Callback<void(LeMetaEventView)> event_handler,
+                              os::Handler* handler) override {
+    registered_le_events_[subevent_code] = event_handler;
+  }
+
+  void UnregisterLeEventHandler(SubeventCode subevent_code) {
+    registered_le_events_.erase(subevent_code);
+  }
+
+  void IncomingEvent(std::unique_ptr<EventPacketBuilder> event_builder) {
+    auto packet = GetPacketView(std::move(event_builder));
+    EventPacketView event = EventPacketView::Create(packet);
+    ASSERT_TRUE(event.IsValid());
+    EventCode event_code = event.GetEventCode();
+    ASSERT_TRUE(registered_events_.find(event_code) != registered_events_.end()) << EventCodeText(event_code);
+    registered_events_[event_code].Run(event);
+  }
+
+  void IncomingLeMetaEvent(std::unique_ptr<LeMetaEventBuilder> event_builder) {
+    auto packet = GetPacketView(std::move(event_builder));
+    EventPacketView event = EventPacketView::Create(packet);
+    LeMetaEventView meta_event_view = LeMetaEventView::Create(event);
+    EXPECT_TRUE(meta_event_view.IsValid());
+    SubeventCode subevent_code = meta_event_view.GetSubeventCode();
+    EXPECT_TRUE(registered_le_events_.find(subevent_code) != registered_le_events_.end());
+    registered_le_events_[subevent_code].Run(meta_event_view);
+  }
+
+  void IncomingAclData(uint16_t handle) {
+    os::Handler* hci_handler = GetHandler();
+    auto* queue_end = acl_queue_.GetDownEnd();
+    std::promise<void> promise;
+    auto future = promise.get_future();
+    queue_end->RegisterEnqueue(hci_handler,
+                               common::Bind(
+                                   [](decltype(queue_end) queue_end, uint16_t handle, std::promise<void> promise) {
+                                     auto packet = GetPacketView(NextAclPacket(handle));
+                                     AclPacketView acl2 = AclPacketView::Create(packet);
+                                     queue_end->UnregisterEnqueue();
+                                     promise.set_value();
+                                     return std::make_unique<AclPacketView>(acl2);
+                                   },
+                                   queue_end, handle, common::Passed(std::move(promise))));
+    auto status = future.wait_for(kTimeout);
+    ASSERT_EQ(status, std::future_status::ready);
+  }
+
+  void AssertNoOutgoingAclData() {
+    auto queue_end = acl_queue_.GetDownEnd();
+    EXPECT_EQ(queue_end->TryDequeue(), nullptr);
+  }
+
+  void CommandCompleteCallback(EventPacketView event) {
+    CommandCompleteView complete_view = CommandCompleteView::Create(event);
+    ASSERT(complete_view.IsValid());
+    std::move(command_complete_callbacks.front()).Run(complete_view);
+    command_complete_callbacks.pop_front();
+  }
+
+  void CommandStatusCallback(EventPacketView event) {
+    CommandStatusView status_view = CommandStatusView::Create(event);
+    ASSERT(status_view.IsValid());
+    std::move(command_status_callbacks.front()).Run(status_view);
+    command_status_callbacks.pop_front();
+  }
+
+  PacketView<kLittleEndian> OutgoingAclData() {
+    auto queue_end = acl_queue_.GetDownEnd();
+    std::unique_ptr<AclPacketBuilder> received;
+    do {
+      received = queue_end->TryDequeue();
+    } while (received == nullptr);
+
+    return GetPacketView(std::move(received));
+  }
+
+  BidiQueueEnd<AclPacketBuilder, AclPacketView>* GetAclQueueEnd() override {
+    return acl_queue_.GetUpEnd();
+  }
+
+  void ListDependencies(ModuleList* list) override {}
+  void Start() override {
+    RegisterEventHandler(EventCode::COMMAND_COMPLETE,
+                         base::Bind(&TestHciLayer::CommandCompleteCallback, common::Unretained(this)), nullptr);
+    RegisterEventHandler(EventCode::COMMAND_STATUS,
+                         base::Bind(&TestHciLayer::CommandStatusCallback, common::Unretained(this)), nullptr);
+  }
+  void Stop() override {}
+
+ private:
+  std::map<EventCode, common::Callback<void(EventPacketView)>> registered_events_;
+  std::map<SubeventCode, common::Callback<void(LeMetaEventView)>> registered_le_events_;
+  std::list<base::OnceCallback<void(CommandCompleteView)>> command_complete_callbacks;
+  std::list<base::OnceCallback<void(CommandStatusView)>> command_status_callbacks;
+  BidiQueue<AclPacketView, AclPacketBuilder> acl_queue_{3 /* TODO: Set queue depth */};
+
+  std::queue<std::unique_ptr<CommandPacketBuilder>> command_queue_;
+  std::unique_ptr<std::promise<void>> command_promise_;
+  std::unique_ptr<std::future<void>> command_future_;
+};
+
+class AclManagerNoCallbacksTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    test_hci_layer_ = new TestHciLayer;  // Ownership is transferred to registry
+    test_hci_layer_->Start();
+    test_controller_ = new TestController;
+    fake_registry_.InjectTestModule(&HciLayer::Factory, test_hci_layer_);
+    fake_registry_.InjectTestModule(&Controller::Factory, test_controller_);
+    client_handler_ = fake_registry_.GetTestModuleHandler(&HciLayer::Factory);
+    EXPECT_NE(client_handler_, nullptr);
+    fake_registry_.Start<AclManager>(&thread_);
+    acl_manager_ = static_cast<AclManager*>(fake_registry_.GetModuleUnderTest(&AclManager::Factory));
+    Address::FromString("A1:A2:A3:A4:A5:A6", remote);
+  }
+
+  void TearDown() override {
+    fake_registry_.SynchronizeModuleHandler(&AclManager::Factory, std::chrono::milliseconds(20));
+    fake_registry_.StopAll();
+  }
+
+  TestModuleRegistry fake_registry_;
+  TestHciLayer* test_hci_layer_ = nullptr;
+  TestController* test_controller_ = nullptr;
+  os::Thread& thread_ = fake_registry_.GetTestThread();
+  AclManager* acl_manager_ = nullptr;
+  os::Handler* client_handler_ = nullptr;
+  Address remote;
+
+  std::future<void> GetConnectionFuture() {
+    ASSERT_LOG(mock_connection_callback_.connection_promise_ == nullptr, "Promises promises ... Only one at a time");
+    mock_connection_callback_.connection_promise_ = std::make_unique<std::promise<void>>();
+    return mock_connection_callback_.connection_promise_->get_future();
+  }
+
+  std::future<void> GetLeConnectionFuture() {
+    ASSERT_LOG(mock_le_connection_callbacks_.le_connection_promise_ == nullptr,
+               "Promises promises ... Only one at a time");
+    mock_le_connection_callbacks_.le_connection_promise_ = std::make_unique<std::promise<void>>();
+    return mock_le_connection_callbacks_.le_connection_promise_->get_future();
+  }
+
+  std::shared_ptr<AclConnection> GetLastConnection() {
+    return mock_connection_callback_.connections_.back();
+  }
+
+  std::shared_ptr<AclConnection> GetLastLeConnection() {
+    return mock_le_connection_callbacks_.le_connections_.back();
+  }
+
+  void SendAclData(uint16_t handle, std::shared_ptr<AclConnection> connection) {
+    auto queue_end = connection->GetAclQueueEnd();
+    std::promise<void> promise;
+    auto future = promise.get_future();
+    queue_end->RegisterEnqueue(client_handler_,
+                               common::Bind(
+                                   [](decltype(queue_end) queue_end, uint16_t handle, std::promise<void> promise) {
+                                     queue_end->UnregisterEnqueue();
+                                     promise.set_value();
+                                     return NextPayload(handle);
+                                   },
+                                   queue_end, handle, common::Passed(std::move(promise))));
+    auto status = future.wait_for(kTimeout);
+    ASSERT_EQ(status, std::future_status::ready);
+  }
+
+  class MockConnectionCallback : public ConnectionCallbacks {
+   public:
+    void OnConnectSuccess(std::unique_ptr<AclConnection> connection) override {
+      // Convert to std::shared_ptr during push_back()
+      connections_.push_back(std::move(connection));
+      if (connection_promise_ != nullptr) {
+        connection_promise_->set_value();
+        connection_promise_.reset();
+      }
+    }
+    MOCK_METHOD(void, OnConnectFail, (Address, ErrorCode reason), (override));
+
+    std::list<std::shared_ptr<AclConnection>> connections_;
+    std::unique_ptr<std::promise<void>> connection_promise_;
+  } mock_connection_callback_;
+
+  class MockLeConnectionCallbacks : public LeConnectionCallbacks {
+   public:
+    void OnLeConnectSuccess(AddressWithType address_with_type, std::unique_ptr<AclConnection> connection) override {
+      le_connections_.push_back(std::move(connection));
+      if (le_connection_promise_ != nullptr) {
+        le_connection_promise_->set_value();
+        le_connection_promise_.reset();
+      }
+    }
+    MOCK_METHOD(void, OnLeConnectFail, (AddressWithType, ErrorCode reason), (override));
+
+    std::list<std::shared_ptr<AclConnection>> le_connections_;
+    std::unique_ptr<std::promise<void>> le_connection_promise_;
+  } mock_le_connection_callbacks_;
+
+  class MockAclManagerCallbacks : public AclManagerCallbacks {
+   public:
+    MOCK_METHOD(void, OnMasterLinkKeyComplete, (uint16_t connection_handle, KeyFlag key_flag), (override));
+    MOCK_METHOD(void, OnRoleChange, (Address bd_addr, Role new_role), (override));
+    MOCK_METHOD(void, OnReadDefaultLinkPolicySettingsComplete, (uint16_t default_link_policy_settings), (override));
+  } mock_acl_manager_callbacks_;
+};
+
+class AclManagerTest : public AclManagerNoCallbacksTest {
+ protected:
+  void SetUp() override {
+    AclManagerNoCallbacksTest::SetUp();
+    acl_manager_->RegisterCallbacks(&mock_connection_callback_, client_handler_);
+    acl_manager_->RegisterLeCallbacks(&mock_le_connection_callbacks_, client_handler_);
+    acl_manager_->RegisterAclManagerCallbacks(&mock_acl_manager_callbacks_, client_handler_);
+  }
+};
+
+class AclManagerWithConnectionTest : public AclManagerTest {
+ protected:
+  void SetUp() override {
+    AclManagerTest::SetUp();
+
+    handle_ = 0x123;
+    acl_manager_->CreateConnection(remote);
+
+    // Wait for the connection request
+    std::unique_ptr<CommandPacketBuilder> last_command;
+    do {
+      last_command = test_hci_layer_->GetLastCommand();
+    } while (last_command == nullptr);
+
+    auto first_connection = GetConnectionFuture();
+    test_hci_layer_->IncomingEvent(
+        ConnectionCompleteBuilder::Create(ErrorCode::SUCCESS, handle_, remote, LinkType::ACL, Enable::DISABLED));
+
+    auto first_connection_status = first_connection.wait_for(kTimeout);
+    ASSERT_EQ(first_connection_status, std::future_status::ready);
+
+    connection_ = GetLastConnection();
+    connection_->RegisterCallbacks(&mock_connection_management_callbacks_, client_handler_);
+  }
+
+  void sync_client_handler() {
+    std::promise<void> promise;
+    auto future = promise.get_future();
+    client_handler_->Post(common::BindOnce(&std::promise<void>::set_value, common::Unretained(&promise)));
+    auto future_status = future.wait_for(std::chrono::seconds(1));
+    EXPECT_EQ(future_status, std::future_status::ready);
+  }
+
+  uint16_t handle_;
+  std::shared_ptr<AclConnection> connection_;
+
+  class MockConnectionManagementCallbacks : public ConnectionManagementCallbacks {
+   public:
+    MOCK_METHOD1(OnConnectionPacketTypeChanged, void(uint16_t packet_type));
+    MOCK_METHOD0(OnAuthenticationComplete, void());
+    MOCK_METHOD1(OnEncryptionChange, void(EncryptionEnabled enabled));
+    MOCK_METHOD0(OnChangeConnectionLinkKeyComplete, void());
+    MOCK_METHOD1(OnReadClockOffsetComplete, void(uint16_t clock_offse));
+    MOCK_METHOD2(OnModeChange, void(Mode current_mode, uint16_t interval));
+    MOCK_METHOD5(OnQosSetupComplete, void(ServiceType service_type, uint32_t token_rate, uint32_t peak_bandwidth,
+                                          uint32_t latency, uint32_t delay_variation));
+    MOCK_METHOD6(OnFlowSpecificationComplete,
+                 void(FlowDirection flow_direction, ServiceType service_type, uint32_t token_rate,
+                      uint32_t token_bucket_size, uint32_t peak_bandwidth, uint32_t access_latency));
+    MOCK_METHOD0(OnFlushOccurred, void());
+    MOCK_METHOD1(OnRoleDiscoveryComplete, void(Role current_role));
+    MOCK_METHOD1(OnReadLinkPolicySettingsComplete, void(uint16_t link_policy_settings));
+    MOCK_METHOD1(OnReadAutomaticFlushTimeoutComplete, void(uint16_t flush_timeout));
+    MOCK_METHOD1(OnReadTransmitPowerLevelComplete, void(uint8_t transmit_power_level));
+    MOCK_METHOD1(OnReadLinkSupervisionTimeoutComplete, void(uint16_t link_supervision_timeout));
+    MOCK_METHOD1(OnReadFailedContactCounterComplete, void(uint16_t failed_contact_counter));
+    MOCK_METHOD1(OnReadLinkQualityComplete, void(uint8_t link_quality));
+    MOCK_METHOD2(OnReadAfhChannelMapComplete, void(AfhMode afh_mode, std::array<uint8_t, 10> afh_channel_map));
+    MOCK_METHOD1(OnReadRssiComplete, void(uint8_t rssi));
+    MOCK_METHOD2(OnReadClockComplete, void(uint32_t clock, uint16_t accuracy));
+  } mock_connection_management_callbacks_;
+};
+
+TEST_F(AclManagerTest, startup_teardown) {}
+
+TEST_F(AclManagerNoCallbacksTest, acl_connection_before_registered_callbacks) {
+  ClassOfDevice class_of_device;
+
+  test_hci_layer_->IncomingEvent(
+      ConnectionRequestBuilder::Create(remote, class_of_device, ConnectionRequestLinkType::ACL));
+  fake_registry_.SynchronizeModuleHandler(&HciLayer::Factory, std::chrono::milliseconds(20));
+  fake_registry_.SynchronizeModuleHandler(&AclManager::Factory, std::chrono::milliseconds(20));
+  fake_registry_.SynchronizeModuleHandler(&HciLayer::Factory, std::chrono::milliseconds(20));
+  auto last_command = test_hci_layer_->GetLastCommand();
+  auto packet = GetPacketView(std::move(last_command));
+  CommandPacketView command = CommandPacketView::Create(packet);
+  EXPECT_TRUE(command.IsValid());
+  OpCode op_code = command.GetOpCode();
+  EXPECT_EQ(op_code, OpCode::REJECT_CONNECTION_REQUEST);
+}
+
+TEST_F(AclManagerTest, invoke_registered_callback_connection_complete_success) {
+  uint16_t handle = 1;
+
+  test_hci_layer_->SetCommandFuture();
+  acl_manager_->CreateConnection(remote);
+
+  // Wait for the connection request
+  std::unique_ptr<CommandPacketBuilder> last_command;
+  do {
+    last_command = test_hci_layer_->GetLastCommand();
+  } while (last_command == nullptr);
+
+  auto first_connection = GetConnectionFuture();
+
+  test_hci_layer_->IncomingEvent(
+      ConnectionCompleteBuilder::Create(ErrorCode::SUCCESS, handle, remote, LinkType::ACL, Enable::DISABLED));
+
+  auto first_connection_status = first_connection.wait_for(kTimeout);
+  ASSERT_EQ(first_connection_status, std::future_status::ready);
+
+  std::shared_ptr<AclConnection> connection = GetLastConnection();
+  ASSERT_EQ(connection->GetAddress(), remote);
+}
+
+TEST_F(AclManagerTest, invoke_registered_callback_connection_complete_fail) {
+  uint16_t handle = 0x123;
+
+  test_hci_layer_->SetCommandFuture();
+  acl_manager_->CreateConnection(remote);
+
+  // Wait for the connection request
+  std::unique_ptr<CommandPacketBuilder> last_command;
+  do {
+    last_command = test_hci_layer_->GetLastCommand();
+  } while (last_command == nullptr);
+
+  EXPECT_CALL(mock_connection_callback_, OnConnectFail(remote, ErrorCode::PAGE_TIMEOUT));
+  test_hci_layer_->IncomingEvent(
+      ConnectionCompleteBuilder::Create(ErrorCode::PAGE_TIMEOUT, handle, remote, LinkType::ACL, Enable::DISABLED));
+  fake_registry_.SynchronizeModuleHandler(&HciLayer::Factory, std::chrono::milliseconds(20));
+  fake_registry_.SynchronizeModuleHandler(&AclManager::Factory, std::chrono::milliseconds(20));
+  fake_registry_.SynchronizeModuleHandler(&HciLayer::Factory, std::chrono::milliseconds(20));
+}
+
+// TODO: implement version of this test where controller supports Extended Advertising Feature in
+// GetControllerLeLocalSupportedFeatures, and LE Extended Create Connection is used
+TEST_F(AclManagerTest, invoke_registered_callback_le_connection_complete_success) {
+  AddressWithType remote_with_type(remote, AddressType::PUBLIC_DEVICE_ADDRESS);
+  test_hci_layer_->SetCommandFuture();
+  acl_manager_->CreateLeConnection(remote_with_type);
+
+  auto packet = test_hci_layer_->GetCommandPacket(OpCode::LE_CREATE_CONNECTION);
+  auto le_connection_management_command_view = LeConnectionManagementCommandView::Create(packet);
+  auto command_view = LeCreateConnectionView::Create(le_connection_management_command_view);
+  ASSERT(command_view.IsValid());
+  EXPECT_EQ(command_view.GetPeerAddress(), remote);
+  EXPECT_EQ(command_view.GetPeerAddressType(), AddressType::PUBLIC_DEVICE_ADDRESS);
+
+  test_hci_layer_->IncomingEvent(LeCreateConnectionStatusBuilder::Create(ErrorCode::SUCCESS, 0x01));
+
+  auto first_connection = GetLeConnectionFuture();
+
+  test_hci_layer_->IncomingLeMetaEvent(
+      LeConnectionCompleteBuilder::Create(ErrorCode::SUCCESS, 0x123, Role::SLAVE, AddressType::PUBLIC_DEVICE_ADDRESS,
+                                          remote, 0x0100, 0x0010, 0x0011, MasterClockAccuracy::PPM_30));
+
+  auto first_connection_status = first_connection.wait_for(kTimeout);
+  ASSERT_EQ(first_connection_status, std::future_status::ready);
+
+  std::shared_ptr<AclConnection> connection = GetLastLeConnection();
+  ASSERT_EQ(connection->GetAddress(), remote);
+}
+
+TEST_F(AclManagerTest, invoke_registered_callback_le_connection_complete_fail) {
+  AddressWithType remote_with_type(remote, AddressType::PUBLIC_DEVICE_ADDRESS);
+  test_hci_layer_->SetCommandFuture();
+  acl_manager_->CreateLeConnection(remote_with_type);
+
+  auto packet = test_hci_layer_->GetCommandPacket(OpCode::LE_CREATE_CONNECTION);
+  auto le_connection_management_command_view = LeConnectionManagementCommandView::Create(packet);
+  auto command_view = LeCreateConnectionView::Create(le_connection_management_command_view);
+  ASSERT(command_view.IsValid());
+  EXPECT_EQ(command_view.GetPeerAddress(), remote);
+  EXPECT_EQ(command_view.GetPeerAddressType(), AddressType::PUBLIC_DEVICE_ADDRESS);
+
+  test_hci_layer_->IncomingEvent(LeCreateConnectionStatusBuilder::Create(ErrorCode::SUCCESS, 0x01));
+
+  EXPECT_CALL(mock_le_connection_callbacks_,
+              OnLeConnectFail(remote_with_type, ErrorCode::CONNECTION_REJECTED_LIMITED_RESOURCES));
+  test_hci_layer_->IncomingLeMetaEvent(LeConnectionCompleteBuilder::Create(
+      ErrorCode::CONNECTION_REJECTED_LIMITED_RESOURCES, 0x123, Role::SLAVE, AddressType::PUBLIC_DEVICE_ADDRESS, remote,
+      0x0100, 0x0010, 0x0011, MasterClockAccuracy::PPM_30));
+}
+
+TEST_F(AclManagerTest, invoke_registered_callback_le_connection_update_success) {
+  AddressWithType remote_with_type(remote, AddressType::PUBLIC_DEVICE_ADDRESS);
+  test_hci_layer_->SetCommandFuture();
+  acl_manager_->CreateLeConnection(remote_with_type);
+
+  auto packet = test_hci_layer_->GetCommandPacket(OpCode::LE_CREATE_CONNECTION);
+  auto le_connection_management_command_view = LeConnectionManagementCommandView::Create(packet);
+  auto command_view = LeCreateConnectionView::Create(le_connection_management_command_view);
+  ASSERT(command_view.IsValid());
+  EXPECT_EQ(command_view.GetPeerAddress(), remote);
+  EXPECT_EQ(command_view.GetPeerAddressType(), AddressType::PUBLIC_DEVICE_ADDRESS);
+
+  test_hci_layer_->IncomingEvent(LeCreateConnectionStatusBuilder::Create(ErrorCode::SUCCESS, 0x01));
+
+  auto first_connection = GetLeConnectionFuture();
+
+  test_hci_layer_->IncomingLeMetaEvent(
+      LeConnectionCompleteBuilder::Create(ErrorCode::SUCCESS, 0x123, Role::SLAVE, AddressType::PUBLIC_DEVICE_ADDRESS,
+                                          remote, 0x0100, 0x0010, 0x0011, MasterClockAccuracy::PPM_30));
+
+  auto first_connection_status = first_connection.wait_for(kTimeout);
+  ASSERT_EQ(first_connection_status, std::future_status::ready);
+
+  std::shared_ptr<AclConnection> connection = GetLastLeConnection();
+  ASSERT_EQ(connection->GetAddress(), remote);
+
+  std::promise<ErrorCode> promise;
+  auto future = promise.get_future();
+  connection->LeConnectionUpdate(
+      0x0006, 0x0C80, 0x0000, 0x000A,
+      common::BindOnce([](std::promise<ErrorCode> promise, ErrorCode code) { promise.set_value(code); },
+                       std::move(promise)),
+      client_handler_);
+  test_hci_layer_->IncomingLeMetaEvent(
+      LeConnectionUpdateCompleteBuilder::Create(ErrorCode::SUCCESS, 0x123, 0x0006, 0x0000, 0x000A));
+  EXPECT_EQ(future.wait_for(std::chrono::milliseconds(3)), std::future_status::ready);
+  EXPECT_EQ(future.get(), ErrorCode::SUCCESS);
+}
+
+TEST_F(AclManagerTest, invoke_registered_callback_disconnection_complete) {
+  uint16_t handle = 0x123;
+
+  test_hci_layer_->SetCommandFuture();
+  acl_manager_->CreateConnection(remote);
+
+  // Wait for the connection request
+  std::unique_ptr<CommandPacketBuilder> last_command;
+  do {
+    last_command = test_hci_layer_->GetLastCommand();
+  } while (last_command == nullptr);
+
+  auto first_connection = GetConnectionFuture();
+
+  test_hci_layer_->IncomingEvent(
+      ConnectionCompleteBuilder::Create(ErrorCode::SUCCESS, handle, remote, LinkType::ACL, Enable::DISABLED));
+
+  auto first_connection_status = first_connection.wait_for(kTimeout);
+  ASSERT_EQ(first_connection_status, std::future_status::ready);
+
+  std::shared_ptr<AclConnection> connection = GetLastConnection();
+
+  // Register the disconnect handler
+  std::promise<ErrorCode> promise;
+  auto future = promise.get_future();
+  connection->RegisterDisconnectCallback(
+      common::BindOnce([](std::promise<ErrorCode> promise, ErrorCode reason) { promise.set_value(reason); },
+                       std::move(promise)),
+      client_handler_);
+
+  test_hci_layer_->IncomingEvent(
+      DisconnectionCompleteBuilder::Create(ErrorCode::SUCCESS, handle, ErrorCode::REMOTE_USER_TERMINATED_CONNECTION));
+
+  auto disconnection_status = future.wait_for(kTimeout);
+  ASSERT_EQ(disconnection_status, std::future_status::ready);
+  ASSERT_EQ(ErrorCode::REMOTE_USER_TERMINATED_CONNECTION, future.get());
+
+  fake_registry_.SynchronizeModuleHandler(&HciLayer::Factory, std::chrono::milliseconds(20));
+}
+
+TEST_F(AclManagerTest, acl_connection_finish_after_disconnected) {
+  uint16_t handle = 0x123;
+
+  test_hci_layer_->SetCommandFuture();
+  acl_manager_->CreateConnection(remote);
+
+  // Wait for the connection request
+  std::unique_ptr<CommandPacketBuilder> last_command;
+  do {
+    last_command = test_hci_layer_->GetLastCommand();
+  } while (last_command == nullptr);
+
+  auto first_connection = GetConnectionFuture();
+
+  test_hci_layer_->IncomingEvent(
+      ConnectionCompleteBuilder::Create(ErrorCode::SUCCESS, handle, remote, LinkType::ACL, Enable::DISABLED));
+
+  auto first_connection_status = first_connection.wait_for(kTimeout);
+  ASSERT_EQ(first_connection_status, std::future_status::ready);
+
+  std::shared_ptr<AclConnection> connection = GetLastConnection();
+
+  // Register the disconnect handler
+  std::promise<ErrorCode> promise;
+  auto future = promise.get_future();
+  connection->RegisterDisconnectCallback(
+      common::BindOnce([](std::promise<ErrorCode> promise, ErrorCode reason) { promise.set_value(reason); },
+                       std::move(promise)),
+      client_handler_);
+
+  test_hci_layer_->IncomingEvent(DisconnectionCompleteBuilder::Create(
+      ErrorCode::SUCCESS, handle, ErrorCode::REMOTE_DEVICE_TERMINATED_CONNECTION_POWER_OFF));
+
+  auto disconnection_status = future.wait_for(kTimeout);
+  ASSERT_EQ(disconnection_status, std::future_status::ready);
+  ASSERT_EQ(ErrorCode::REMOTE_DEVICE_TERMINATED_CONNECTION_POWER_OFF, future.get());
+
+  connection->Finish();
+}
+
+TEST_F(AclManagerTest, acl_send_data_one_connection) {
+  uint16_t handle = 0x123;
+
+  acl_manager_->CreateConnection(remote);
+
+  // Wait for the connection request
+  std::unique_ptr<CommandPacketBuilder> last_command;
+  do {
+    last_command = test_hci_layer_->GetLastCommand();
+  } while (last_command == nullptr);
+
+  auto first_connection = GetConnectionFuture();
+
+  test_hci_layer_->IncomingEvent(
+      ConnectionCompleteBuilder::Create(ErrorCode::SUCCESS, handle, remote, LinkType::ACL, Enable::DISABLED));
+
+  auto first_connection_status = first_connection.wait_for(kTimeout);
+  ASSERT_EQ(first_connection_status, std::future_status::ready);
+
+  std::shared_ptr<AclConnection> connection = GetLastConnection();
+
+  // Register the disconnect handler
+  connection->RegisterDisconnectCallback(
+      common::Bind([](std::shared_ptr<AclConnection> conn, ErrorCode) { conn->Finish(); }, connection),
+      client_handler_);
+
+  // Send a packet from HCI
+  test_hci_layer_->IncomingAclData(handle);
+  auto queue_end = connection->GetAclQueueEnd();
+
+  std::unique_ptr<PacketView<kLittleEndian>> received;
+  do {
+    received = queue_end->TryDequeue();
+  } while (received == nullptr);
+
+  PacketView<kLittleEndian> received_packet = *received;
+
+  // Send a packet from the connection
+  SendAclData(handle, connection);
+
+  auto sent_packet = test_hci_layer_->OutgoingAclData();
+
+  // Send another packet from the connection
+  SendAclData(handle, connection);
+
+  sent_packet = test_hci_layer_->OutgoingAclData();
+  connection->Disconnect(DisconnectReason::AUTHENTICATION_FAILURE);
+}
+
+TEST_F(AclManagerTest, acl_send_data_credits) {
+  uint16_t handle = 0x123;
+
+  acl_manager_->CreateConnection(remote);
+
+  // Wait for the connection request
+  std::unique_ptr<CommandPacketBuilder> last_command;
+  do {
+    last_command = test_hci_layer_->GetLastCommand();
+  } while (last_command == nullptr);
+
+  auto first_connection = GetConnectionFuture();
+  test_hci_layer_->IncomingEvent(
+      ConnectionCompleteBuilder::Create(ErrorCode::SUCCESS, handle, remote, LinkType::ACL, Enable::DISABLED));
+
+  auto first_connection_status = first_connection.wait_for(kTimeout);
+  ASSERT_EQ(first_connection_status, std::future_status::ready);
+
+  std::shared_ptr<AclConnection> connection = GetLastConnection();
+
+  // Register the disconnect handler
+  connection->RegisterDisconnectCallback(
+      common::BindOnce([](std::shared_ptr<AclConnection> conn, ErrorCode) { conn->Finish(); }, connection),
+      client_handler_);
+
+  // Use all the credits
+  for (uint16_t credits = 0; credits < test_controller_->total_acl_buffers_; credits++) {
+    // Send a packet from the connection
+    SendAclData(handle, connection);
+
+    auto sent_packet = test_hci_layer_->OutgoingAclData();
+  }
+
+  // Send another packet from the connection
+  SendAclData(handle, connection);
+
+  test_hci_layer_->AssertNoOutgoingAclData();
+
+  test_controller_->CompletePackets(handle, 1);
+
+  auto after_credits_sent_packet = test_hci_layer_->OutgoingAclData();
+
+  connection->Disconnect(DisconnectReason::AUTHENTICATION_FAILURE);
+}
+
+TEST_F(AclManagerWithConnectionTest, send_switch_role) {
+  test_hci_layer_->SetCommandFuture();
+  acl_manager_->SwitchRole(connection_->GetAddress(), Role::SLAVE);
+  auto packet = test_hci_layer_->GetCommandPacket(OpCode::SWITCH_ROLE);
+  auto command_view = SwitchRoleView::Create(packet);
+  ASSERT(command_view.IsValid());
+  EXPECT_EQ(command_view.GetBdAddr(), connection_->GetAddress());
+  EXPECT_EQ(command_view.GetRole(), Role::SLAVE);
+
+  EXPECT_CALL(mock_acl_manager_callbacks_, OnRoleChange(connection_->GetAddress(), Role::SLAVE));
+  test_hci_layer_->IncomingEvent(RoleChangeBuilder::Create(ErrorCode::SUCCESS, connection_->GetAddress(), Role::SLAVE));
+}
+
+TEST_F(AclManagerWithConnectionTest, send_read_default_link_policy_settings) {
+  test_hci_layer_->SetCommandFuture();
+  acl_manager_->ReadDefaultLinkPolicySettings();
+  auto packet = test_hci_layer_->GetCommandPacket(OpCode::READ_DEFAULT_LINK_POLICY_SETTINGS);
+  auto command_view = ReadDefaultLinkPolicySettingsView::Create(packet);
+  ASSERT(command_view.IsValid());
+
+  test_hci_layer_->SetCommandFuture();
+  EXPECT_CALL(mock_acl_manager_callbacks_, OnReadDefaultLinkPolicySettingsComplete(0x07));
+  uint8_t num_packets = 1;
+  test_hci_layer_->IncomingEvent(
+      ReadDefaultLinkPolicySettingsCompleteBuilder::Create(num_packets, ErrorCode::SUCCESS, 0x07));
+}
+
+TEST_F(AclManagerWithConnectionTest, send_write_default_link_policy_settings) {
+  test_hci_layer_->SetCommandFuture();
+  acl_manager_->WriteDefaultLinkPolicySettings(0x05);
+  auto packet = test_hci_layer_->GetCommandPacket(OpCode::WRITE_DEFAULT_LINK_POLICY_SETTINGS);
+  auto command_view = WriteDefaultLinkPolicySettingsView::Create(packet);
+  ASSERT(command_view.IsValid());
+  EXPECT_EQ(command_view.GetDefaultLinkPolicySettings(), 0x05);
+
+  uint8_t num_packets = 1;
+  test_hci_layer_->IncomingEvent(
+      WriteDefaultLinkPolicySettingsCompleteBuilder::Create(num_packets, ErrorCode::SUCCESS));
+}
+
+TEST_F(AclManagerWithConnectionTest, send_change_connection_packet_type) {
+  test_hci_layer_->SetCommandFuture();
+  connection_->ChangeConnectionPacketType(0xEE1C);
+  auto packet = test_hci_layer_->GetCommandPacket(OpCode::CHANGE_CONNECTION_PACKET_TYPE);
+  auto command_view = ChangeConnectionPacketTypeView::Create(packet);
+  ASSERT(command_view.IsValid());
+  EXPECT_EQ(command_view.GetPacketType(), 0xEE1C);
+
+  EXPECT_CALL(mock_connection_management_callbacks_, OnConnectionPacketTypeChanged(0xEE1C));
+  test_hci_layer_->IncomingEvent(ConnectionPacketTypeChangedBuilder::Create(ErrorCode::SUCCESS, handle_, 0xEE1C));
+}
+
+TEST_F(AclManagerWithConnectionTest, send_authentication_requested) {
+  test_hci_layer_->SetCommandFuture();
+  connection_->AuthenticationRequested();
+  auto packet = test_hci_layer_->GetCommandPacket(OpCode::AUTHENTICATION_REQUESTED);
+  auto command_view = AuthenticationRequestedView::Create(packet);
+  ASSERT(command_view.IsValid());
+
+  EXPECT_CALL(mock_connection_management_callbacks_, OnAuthenticationComplete);
+  test_hci_layer_->IncomingEvent(AuthenticationCompleteBuilder::Create(ErrorCode::SUCCESS, handle_));
+}
+
+TEST_F(AclManagerWithConnectionTest, send_read_clock_offset) {
+  test_hci_layer_->SetCommandFuture();
+  connection_->ReadClockOffset();
+  auto packet = test_hci_layer_->GetCommandPacket(OpCode::READ_CLOCK_OFFSET);
+  auto command_view = ReadClockOffsetView::Create(packet);
+  ASSERT(command_view.IsValid());
+
+  EXPECT_CALL(mock_connection_management_callbacks_, OnReadClockOffsetComplete(0x0123));
+  test_hci_layer_->IncomingEvent(ReadClockOffsetCompleteBuilder::Create(ErrorCode::SUCCESS, handle_, 0x0123));
+}
+
+TEST_F(AclManagerWithConnectionTest, send_hold_mode) {
+  test_hci_layer_->SetCommandFuture();
+  connection_->HoldMode(0x0500, 0x0020);
+  auto packet = test_hci_layer_->GetCommandPacket(OpCode::HOLD_MODE);
+  auto command_view = HoldModeView::Create(packet);
+  ASSERT(command_view.IsValid());
+  EXPECT_EQ(command_view.GetHoldModeMaxInterval(), 0x0500);
+  EXPECT_EQ(command_view.GetHoldModeMinInterval(), 0x0020);
+
+  EXPECT_CALL(mock_connection_management_callbacks_, OnModeChange(Mode::HOLD, 0x0020));
+  test_hci_layer_->IncomingEvent(ModeChangeBuilder::Create(ErrorCode::SUCCESS, handle_, Mode::HOLD, 0x0020));
+}
+
+TEST_F(AclManagerWithConnectionTest, send_sniff_mode) {
+  test_hci_layer_->SetCommandFuture();
+  connection_->SniffMode(0x0500, 0x0020, 0x0040, 0x0014);
+  auto packet = test_hci_layer_->GetCommandPacket(OpCode::SNIFF_MODE);
+  auto command_view = SniffModeView::Create(packet);
+  ASSERT(command_view.IsValid());
+  EXPECT_EQ(command_view.GetSniffMaxInterval(), 0x0500);
+  EXPECT_EQ(command_view.GetSniffMinInterval(), 0x0020);
+  EXPECT_EQ(command_view.GetSniffAttempt(), 0x0040);
+  EXPECT_EQ(command_view.GetSniffTimeout(), 0x0014);
+
+  EXPECT_CALL(mock_connection_management_callbacks_, OnModeChange(Mode::SNIFF, 0x0028));
+  test_hci_layer_->IncomingEvent(ModeChangeBuilder::Create(ErrorCode::SUCCESS, handle_, Mode::SNIFF, 0x0028));
+}
+
+TEST_F(AclManagerWithConnectionTest, send_exit_sniff_mode) {
+  test_hci_layer_->SetCommandFuture();
+  connection_->ExitSniffMode();
+  auto packet = test_hci_layer_->GetCommandPacket(OpCode::EXIT_SNIFF_MODE);
+  auto command_view = ExitSniffModeView::Create(packet);
+  ASSERT(command_view.IsValid());
+
+  EXPECT_CALL(mock_connection_management_callbacks_, OnModeChange(Mode::ACTIVE, 0x00));
+  test_hci_layer_->IncomingEvent(ModeChangeBuilder::Create(ErrorCode::SUCCESS, handle_, Mode::ACTIVE, 0x00));
+}
+
+TEST_F(AclManagerWithConnectionTest, send_qos_setup) {
+  test_hci_layer_->SetCommandFuture();
+  connection_->QosSetup(ServiceType::BEST_EFFORT, 0x1234, 0x1233, 0x1232, 0x1231);
+  auto packet = test_hci_layer_->GetCommandPacket(OpCode::QOS_SETUP);
+  auto command_view = QosSetupView::Create(packet);
+  ASSERT(command_view.IsValid());
+  EXPECT_EQ(command_view.GetServiceType(), ServiceType::BEST_EFFORT);
+  EXPECT_EQ(command_view.GetTokenRate(), 0x1234);
+  EXPECT_EQ(command_view.GetPeakBandwidth(), 0x1233);
+  EXPECT_EQ(command_view.GetLatency(), 0x1232);
+  EXPECT_EQ(command_view.GetDelayVariation(), 0x1231);
+
+  EXPECT_CALL(mock_connection_management_callbacks_,
+              OnQosSetupComplete(ServiceType::BEST_EFFORT, 0x1234, 0x1233, 0x1232, 0x1231));
+  test_hci_layer_->IncomingEvent(QosSetupCompleteBuilder::Create(ErrorCode::SUCCESS, handle_, ServiceType::BEST_EFFORT,
+                                                                 0x1234, 0x1233, 0x1232, 0x1231));
+}
+
+TEST_F(AclManagerWithConnectionTest, send_flow_specification) {
+  test_hci_layer_->SetCommandFuture();
+  connection_->FlowSpecification(FlowDirection::OUTGOING_FLOW, ServiceType::BEST_EFFORT, 0x1234, 0x1233, 0x1232,
+                                 0x1231);
+  auto packet = test_hci_layer_->GetCommandPacket(OpCode::FLOW_SPECIFICATION);
+  auto command_view = FlowSpecificationView::Create(packet);
+  ASSERT(command_view.IsValid());
+  EXPECT_EQ(command_view.GetFlowDirection(), FlowDirection::OUTGOING_FLOW);
+  EXPECT_EQ(command_view.GetServiceType(), ServiceType::BEST_EFFORT);
+  EXPECT_EQ(command_view.GetTokenRate(), 0x1234);
+  EXPECT_EQ(command_view.GetTokenBucketSize(), 0x1233);
+  EXPECT_EQ(command_view.GetPeakBandwidth(), 0x1232);
+  EXPECT_EQ(command_view.GetAccessLatency(), 0x1231);
+
+  EXPECT_CALL(mock_connection_management_callbacks_,
+              OnFlowSpecificationComplete(FlowDirection::OUTGOING_FLOW, ServiceType::BEST_EFFORT, 0x1234, 0x1233,
+                                          0x1232, 0x1231));
+  test_hci_layer_->IncomingEvent(
+      FlowSpecificationCompleteBuilder::Create(ErrorCode::SUCCESS, handle_, FlowDirection::OUTGOING_FLOW,
+                                               ServiceType::BEST_EFFORT, 0x1234, 0x1233, 0x1232, 0x1231));
+}
+
+TEST_F(AclManagerWithConnectionTest, send_flush) {
+  test_hci_layer_->SetCommandFuture();
+  connection_->Flush();
+  auto packet = test_hci_layer_->GetCommandPacket(OpCode::FLUSH);
+  auto command_view = FlushView::Create(packet);
+  ASSERT(command_view.IsValid());
+
+  EXPECT_CALL(mock_connection_management_callbacks_, OnFlushOccurred());
+  test_hci_layer_->IncomingEvent(FlushOccurredBuilder::Create(handle_));
+}
+
+TEST_F(AclManagerWithConnectionTest, send_role_discovery) {
+  test_hci_layer_->SetCommandFuture();
+  connection_->RoleDiscovery();
+  auto packet = test_hci_layer_->GetCommandPacket(OpCode::ROLE_DISCOVERY);
+  auto command_view = RoleDiscoveryView::Create(packet);
+  ASSERT(command_view.IsValid());
+
+  EXPECT_CALL(mock_connection_management_callbacks_, OnRoleDiscoveryComplete(Role::MASTER));
+  uint8_t num_packets = 1;
+  test_hci_layer_->IncomingEvent(
+      RoleDiscoveryCompleteBuilder::Create(num_packets, ErrorCode::SUCCESS, handle_, Role::MASTER));
+}
+
+TEST_F(AclManagerWithConnectionTest, send_read_link_policy_settings) {
+  test_hci_layer_->SetCommandFuture();
+  connection_->ReadLinkPolicySettings();
+  auto packet = test_hci_layer_->GetCommandPacket(OpCode::READ_LINK_POLICY_SETTINGS);
+  auto command_view = ReadLinkPolicySettingsView::Create(packet);
+  ASSERT(command_view.IsValid());
+
+  EXPECT_CALL(mock_connection_management_callbacks_, OnReadLinkPolicySettingsComplete(0x07));
+  uint8_t num_packets = 1;
+  test_hci_layer_->IncomingEvent(
+      ReadLinkPolicySettingsCompleteBuilder::Create(num_packets, ErrorCode::SUCCESS, handle_, 0x07));
+}
+
+TEST_F(AclManagerWithConnectionTest, send_write_link_policy_settings) {
+  test_hci_layer_->SetCommandFuture();
+  connection_->WriteLinkPolicySettings(0x05);
+  auto packet = test_hci_layer_->GetCommandPacket(OpCode::WRITE_LINK_POLICY_SETTINGS);
+  auto command_view = WriteLinkPolicySettingsView::Create(packet);
+  ASSERT(command_view.IsValid());
+  EXPECT_EQ(command_view.GetLinkPolicySettings(), 0x05);
+
+  uint8_t num_packets = 1;
+  test_hci_layer_->IncomingEvent(
+      WriteLinkPolicySettingsCompleteBuilder::Create(num_packets, ErrorCode::SUCCESS, handle_));
+}
+
+TEST_F(AclManagerWithConnectionTest, send_sniff_subrating) {
+  test_hci_layer_->SetCommandFuture();
+  connection_->SniffSubrating(0x1234, 0x1235, 0x1236);
+  auto packet = test_hci_layer_->GetCommandPacket(OpCode::SNIFF_SUBRATING);
+  auto command_view = SniffSubratingView::Create(packet);
+  ASSERT(command_view.IsValid());
+  EXPECT_EQ(command_view.GetMaximumLatency(), 0x1234);
+  EXPECT_EQ(command_view.GetMinimumRemoteTimeout(), 0x1235);
+  EXPECT_EQ(command_view.GetMinimumLocalTimeout(), 0x1236);
+
+  uint8_t num_packets = 1;
+  test_hci_layer_->IncomingEvent(SniffSubratingCompleteBuilder::Create(num_packets, ErrorCode::SUCCESS, handle_));
+}
+
+TEST_F(AclManagerWithConnectionTest, send_read_automatic_flush_timeout) {
+  test_hci_layer_->SetCommandFuture();
+  connection_->ReadAutomaticFlushTimeout();
+  auto packet = test_hci_layer_->GetCommandPacket(OpCode::READ_AUTOMATIC_FLUSH_TIMEOUT);
+  auto command_view = ReadAutomaticFlushTimeoutView::Create(packet);
+  ASSERT(command_view.IsValid());
+
+  EXPECT_CALL(mock_connection_management_callbacks_, OnReadAutomaticFlushTimeoutComplete(0x07ff));
+  uint8_t num_packets = 1;
+  test_hci_layer_->IncomingEvent(
+      ReadAutomaticFlushTimeoutCompleteBuilder::Create(num_packets, ErrorCode::SUCCESS, handle_, 0x07ff));
+}
+
+TEST_F(AclManagerWithConnectionTest, send_write_automatic_flush_timeout) {
+  test_hci_layer_->SetCommandFuture();
+  connection_->WriteAutomaticFlushTimeout(0x07FF);
+  auto packet = test_hci_layer_->GetCommandPacket(OpCode::WRITE_AUTOMATIC_FLUSH_TIMEOUT);
+  auto command_view = WriteAutomaticFlushTimeoutView::Create(packet);
+  ASSERT(command_view.IsValid());
+  EXPECT_EQ(command_view.GetFlushTimeout(), 0x07FF);
+
+  uint8_t num_packets = 1;
+  test_hci_layer_->IncomingEvent(
+      WriteAutomaticFlushTimeoutCompleteBuilder::Create(num_packets, ErrorCode::SUCCESS, handle_));
+}
+
+TEST_F(AclManagerWithConnectionTest, send_read_transmit_power_level) {
+  test_hci_layer_->SetCommandFuture();
+  connection_->ReadTransmitPowerLevel(TransmitPowerLevelType::CURRENT);
+  auto packet = test_hci_layer_->GetCommandPacket(OpCode::READ_TRANSMIT_POWER_LEVEL);
+  auto command_view = ReadTransmitPowerLevelView::Create(packet);
+  ASSERT(command_view.IsValid());
+  EXPECT_EQ(command_view.GetType(), TransmitPowerLevelType::CURRENT);
+
+  EXPECT_CALL(mock_connection_management_callbacks_, OnReadTransmitPowerLevelComplete(0x07));
+  uint8_t num_packets = 1;
+  test_hci_layer_->IncomingEvent(
+      ReadTransmitPowerLevelCompleteBuilder::Create(num_packets, ErrorCode::SUCCESS, handle_, 0x07));
+}
+
+TEST_F(AclManagerWithConnectionTest, send_read_link_supervision_timeout) {
+  test_hci_layer_->SetCommandFuture();
+  connection_->ReadLinkSupervisionTimeout();
+  auto packet = test_hci_layer_->GetCommandPacket(OpCode::READ_LINK_SUPERVISION_TIMEOUT);
+  auto command_view = ReadLinkSupervisionTimeoutView::Create(packet);
+  ASSERT(command_view.IsValid());
+
+  EXPECT_CALL(mock_connection_management_callbacks_, OnReadLinkSupervisionTimeoutComplete(0x5677));
+  uint8_t num_packets = 1;
+  test_hci_layer_->IncomingEvent(
+      ReadLinkSupervisionTimeoutCompleteBuilder::Create(num_packets, ErrorCode::SUCCESS, handle_, 0x5677));
+}
+
+TEST_F(AclManagerWithConnectionTest, send_write_link_supervision_timeout) {
+  test_hci_layer_->SetCommandFuture();
+  connection_->WriteLinkSupervisionTimeout(0x5678);
+  auto packet = test_hci_layer_->GetCommandPacket(OpCode::WRITE_LINK_SUPERVISION_TIMEOUT);
+  auto command_view = WriteLinkSupervisionTimeoutView::Create(packet);
+  ASSERT(command_view.IsValid());
+  EXPECT_EQ(command_view.GetLinkSupervisionTimeout(), 0x5678);
+
+  uint8_t num_packets = 1;
+  test_hci_layer_->IncomingEvent(
+      WriteLinkSupervisionTimeoutCompleteBuilder::Create(num_packets, ErrorCode::SUCCESS, handle_));
+}
+
+TEST_F(AclManagerWithConnectionTest, send_read_failed_contact_counter) {
+  test_hci_layer_->SetCommandFuture();
+  connection_->ReadFailedContactCounter();
+  auto packet = test_hci_layer_->GetCommandPacket(OpCode::READ_FAILED_CONTACT_COUNTER);
+  auto command_view = ReadFailedContactCounterView::Create(packet);
+  ASSERT(command_view.IsValid());
+
+  EXPECT_CALL(mock_connection_management_callbacks_, OnReadFailedContactCounterComplete(0x00));
+  uint8_t num_packets = 1;
+  test_hci_layer_->IncomingEvent(
+      ReadFailedContactCounterCompleteBuilder::Create(num_packets, ErrorCode::SUCCESS, handle_, 0x00));
+}
+
+TEST_F(AclManagerWithConnectionTest, send_reset_failed_contact_counter) {
+  test_hci_layer_->SetCommandFuture();
+  connection_->ResetFailedContactCounter();
+  auto packet = test_hci_layer_->GetCommandPacket(OpCode::RESET_FAILED_CONTACT_COUNTER);
+  auto command_view = ResetFailedContactCounterView::Create(packet);
+  ASSERT(command_view.IsValid());
+
+  uint8_t num_packets = 1;
+  test_hci_layer_->IncomingEvent(
+      ResetFailedContactCounterCompleteBuilder::Create(num_packets, ErrorCode::SUCCESS, handle_));
+}
+
+TEST_F(AclManagerWithConnectionTest, send_read_link_quality) {
+  test_hci_layer_->SetCommandFuture();
+  connection_->ReadLinkQuality();
+  auto packet = test_hci_layer_->GetCommandPacket(OpCode::READ_LINK_QUALITY);
+  auto command_view = ReadLinkQualityView::Create(packet);
+  ASSERT(command_view.IsValid());
+
+  EXPECT_CALL(mock_connection_management_callbacks_, OnReadLinkQualityComplete(0xa9));
+  uint8_t num_packets = 1;
+  test_hci_layer_->IncomingEvent(
+      ReadLinkQualityCompleteBuilder::Create(num_packets, ErrorCode::SUCCESS, handle_, 0xa9));
+}
+
+TEST_F(AclManagerWithConnectionTest, send_read_afh_channel_map) {
+  test_hci_layer_->SetCommandFuture();
+  connection_->ReadAfhChannelMap();
+  auto packet = test_hci_layer_->GetCommandPacket(OpCode::READ_AFH_CHANNEL_MAP);
+  auto command_view = ReadAfhChannelMapView::Create(packet);
+  ASSERT(command_view.IsValid());
+  std::array<uint8_t, 10> afh_channel_map = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09};
+
+  EXPECT_CALL(mock_connection_management_callbacks_,
+              OnReadAfhChannelMapComplete(AfhMode::AFH_ENABLED, afh_channel_map));
+  uint8_t num_packets = 1;
+  test_hci_layer_->IncomingEvent(ReadAfhChannelMapCompleteBuilder::Create(num_packets, ErrorCode::SUCCESS, handle_,
+                                                                          AfhMode::AFH_ENABLED, afh_channel_map));
+}
+
+TEST_F(AclManagerWithConnectionTest, send_read_rssi) {
+  test_hci_layer_->SetCommandFuture();
+  connection_->ReadRssi();
+  auto packet = test_hci_layer_->GetCommandPacket(OpCode::READ_RSSI);
+  auto command_view = ReadRssiView::Create(packet);
+  ASSERT(command_view.IsValid());
+  sync_client_handler();
+  EXPECT_CALL(mock_connection_management_callbacks_, OnReadRssiComplete(0x00));
+  uint8_t num_packets = 1;
+  test_hci_layer_->IncomingEvent(ReadRssiCompleteBuilder::Create(num_packets, ErrorCode::SUCCESS, handle_, 0x00));
+}
+
+TEST_F(AclManagerWithConnectionTest, send_read_clock) {
+  test_hci_layer_->SetCommandFuture();
+  connection_->ReadClock(WhichClock::LOCAL);
+  auto packet = test_hci_layer_->GetCommandPacket(OpCode::READ_CLOCK);
+  auto command_view = ReadClockView::Create(packet);
+  ASSERT(command_view.IsValid());
+  EXPECT_EQ(command_view.GetWhichClock(), WhichClock::LOCAL);
+
+  EXPECT_CALL(mock_connection_management_callbacks_, OnReadClockComplete(0x00002e6a, 0x0000));
+  uint8_t num_packets = 1;
+  test_hci_layer_->IncomingEvent(
+      ReadClockCompleteBuilder::Create(num_packets, ErrorCode::SUCCESS, handle_, 0x00002e6a, 0x0000));
+}
+
+}  // namespace
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/address.cc b/gd/hci/address.cc
new file mode 100644
index 0000000..8a407fb
--- /dev/null
+++ b/gd/hci/address.cc
@@ -0,0 +1,94 @@
+/******************************************************************************
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#include "hci/address.h"
+
+#include <stdint.h>
+#include <algorithm>
+#include <sstream>
+#include <vector>
+
+#include "os/log.h"
+
+namespace bluetooth {
+namespace hci {
+
+static_assert(sizeof(Address) == 6, "Address must be 6 bytes long!");
+
+const Address Address::kAny{{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}};
+const Address Address::kEmpty{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
+
+Address::Address(const uint8_t (&addr)[6]) {
+  std::copy(addr, addr + kLength, address);
+};
+
+std::string Address::ToString() const {
+  char buffer[] = "00:00:00:00:00:00";
+  std::snprintf(&buffer[0], sizeof(buffer), "%02x:%02x:%02x:%02x:%02x:%02x", address[5], address[4], address[3],
+                address[2], address[1], address[0]);
+  std::string str(buffer);
+  return str;
+}
+
+bool Address::FromString(const std::string& from, Address& to) {
+  Address new_addr;
+  if (from.length() != 17) {
+    return false;
+  }
+
+  std::istringstream stream(from);
+  std::string token;
+  int index = 0;
+  while (getline(stream, token, ':')) {
+    if (index >= 6) {
+      return false;
+    }
+
+    if (token.length() != 2) {
+      return false;
+    }
+
+    char* temp = nullptr;
+    new_addr.address[5 - index] = strtol(token.c_str(), &temp, 16);
+    if (*temp != '\0') {
+      return false;
+    }
+
+    index++;
+  }
+
+  if (index != 6) {
+    return false;
+  }
+
+  to = new_addr;
+  return true;
+}
+
+size_t Address::FromOctets(const uint8_t* from) {
+  std::copy(from, from + kLength, address);
+  return kLength;
+};
+
+bool Address::IsValidAddress(const std::string& address) {
+  Address tmp;
+  return Address::FromString(address, tmp);
+}
+
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/address.h b/gd/hci/address.h
new file mode 100644
index 0000000..3bc507f
--- /dev/null
+++ b/gd/hci/address.h
@@ -0,0 +1,94 @@
+/******************************************************************************
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#pragma once
+
+#include <cstring>
+#include <string>
+
+namespace bluetooth {
+namespace hci {
+
+class Address final {
+ public:
+  static constexpr unsigned int kLength = 6;
+
+  uint8_t address[kLength];
+
+  Address() = default;
+  Address(const uint8_t (&addr)[6]);
+
+  bool operator<(const Address& rhs) const {
+    return (std::memcmp(address, rhs.address, sizeof(address)) < 0);
+  }
+  bool operator==(const Address& rhs) const {
+    return (std::memcmp(address, rhs.address, sizeof(address)) == 0);
+  }
+  bool operator>(const Address& rhs) const {
+    return (rhs < *this);
+  }
+  bool operator<=(const Address& rhs) const {
+    return !(*this > rhs);
+  }
+  bool operator>=(const Address& rhs) const {
+    return !(*this < rhs);
+  }
+  bool operator!=(const Address& rhs) const {
+    return !(*this == rhs);
+  }
+
+  bool IsEmpty() const {
+    return *this == kEmpty;
+  }
+
+  std::string ToString() const;
+
+  // Converts |string| to Address and places it in |to|. If |from| does
+  // not represent a Bluetooth address, |to| is not modified and this function
+  // returns false. Otherwise, it returns true.
+  static bool FromString(const std::string& from, Address& to);
+
+  // Copies |from| raw Bluetooth address octets to the local object.
+  // Returns the number of copied octets - should be always Address::kLength
+  size_t FromOctets(const uint8_t* from);
+
+  static bool IsValidAddress(const std::string& address);
+
+  static const Address kEmpty;  // 00:00:00:00:00:00
+  static const Address kAny;    // FF:FF:FF:FF:FF:FF
+};
+
+inline std::ostream& operator<<(std::ostream& os, const Address& a) {
+  os << a.ToString();
+  return os;
+}
+
+}  // namespace hci
+}  // namespace bluetooth
+
+namespace std {
+template <>
+struct hash<bluetooth::hci::Address> {
+  std::size_t operator()(const bluetooth::hci::Address& val) const {
+    static_assert(sizeof(uint64_t) >= bluetooth::hci::Address::kLength);
+    uint64_t int_addr = 0;
+    memcpy(reinterpret_cast<uint8_t*>(&int_addr), val.address, bluetooth::hci::Address::kLength);
+    return std::hash<uint64_t>{}(int_addr);
+  }
+};
+}  // namespace std
\ No newline at end of file
diff --git a/gd/hci/address_pybind11_type_caster.h b/gd/hci/address_pybind11_type_caster.h
new file mode 100644
index 0000000..18151c3
--- /dev/null
+++ b/gd/hci/address_pybind11_type_caster.h
@@ -0,0 +1,55 @@
+/******************************************************************************
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#pragma once
+
+#include <cstring>
+#include <string>
+
+#include <pybind11/pybind11.h>
+#include <pybind11/stl.h>
+
+#include "hci/address.h"
+
+namespace py = pybind11;
+
+namespace pybind11 {
+namespace detail {
+template <>
+struct type_caster<::bluetooth::hci::Address> {
+ public:
+  // Set the Python name of Address and declare "value"
+  PYBIND11_TYPE_CASTER(::bluetooth::hci::Address, _("Address"));
+
+  // Convert from Python->C++
+  bool load(handle src, bool) {
+    PyObject* source = src.ptr();
+    if (py::isinstance<py::str>(src)) {
+      bool conversion_successful = bluetooth::hci::Address::FromString(PyUnicode_AsUTF8(source), value);
+      return conversion_successful && !PyErr_Occurred();
+    }
+    return false;
+  }
+
+  // Convert from C++->Python
+  static handle cast(bluetooth::hci::Address src, return_value_policy, handle) {
+    return PyUnicode_FromString(src.ToString().c_str());
+  }
+};
+}  // namespace detail
+}  // namespace pybind11
diff --git a/gd/hci/address_unittest.cc b/gd/hci/address_unittest.cc
new file mode 100644
index 0000000..17ecd3a
--- /dev/null
+++ b/gd/hci/address_unittest.cc
@@ -0,0 +1,232 @@
+/******************************************************************************
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#include <unordered_map>
+
+#include <gtest/gtest.h>
+
+#include "hci/address.h"
+
+using bluetooth::hci::Address;
+
+static const char* test_addr = "bc:9a:78:56:34:12";
+static const char* test_addr2 = "21:43:65:87:a9:cb";
+
+TEST(AddressUnittest, test_constructor_array) {
+  Address bdaddr({0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc});
+
+  ASSERT_EQ(0x12, bdaddr.address[0]);
+  ASSERT_EQ(0x34, bdaddr.address[1]);
+  ASSERT_EQ(0x56, bdaddr.address[2]);
+  ASSERT_EQ(0x78, bdaddr.address[3]);
+  ASSERT_EQ(0x9A, bdaddr.address[4]);
+  ASSERT_EQ(0xBC, bdaddr.address[5]);
+
+  std::string ret = bdaddr.ToString();
+
+  ASSERT_STREQ(test_addr, ret.c_str());
+}
+
+TEST(AddressUnittest, test_is_empty) {
+  Address empty;
+  Address::FromString("00:00:00:00:00:00", empty);
+  ASSERT_TRUE(empty.IsEmpty());
+
+  Address not_empty;
+  Address::FromString("00:00:00:00:00:01", not_empty);
+  ASSERT_FALSE(not_empty.IsEmpty());
+}
+
+TEST(AddressUnittest, test_to_from_str) {
+  Address bdaddr;
+  Address::FromString(test_addr, bdaddr);
+
+  ASSERT_EQ(0x12, bdaddr.address[0]);
+  ASSERT_EQ(0x34, bdaddr.address[1]);
+  ASSERT_EQ(0x56, bdaddr.address[2]);
+  ASSERT_EQ(0x78, bdaddr.address[3]);
+  ASSERT_EQ(0x9A, bdaddr.address[4]);
+  ASSERT_EQ(0xBC, bdaddr.address[5]);
+
+  std::string ret = bdaddr.ToString();
+
+  ASSERT_STREQ(test_addr, ret.c_str());
+}
+
+TEST(AddressUnittest, test_from_octets) {
+  static const uint8_t test_addr_array[] = {0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc};
+
+  Address bdaddr;
+  size_t expected_result = Address::kLength;
+  ASSERT_EQ(expected_result, bdaddr.FromOctets(test_addr_array));
+
+  ASSERT_EQ(0x12, bdaddr.address[0]);
+  ASSERT_EQ(0x34, bdaddr.address[1]);
+  ASSERT_EQ(0x56, bdaddr.address[2]);
+  ASSERT_EQ(0x78, bdaddr.address[3]);
+  ASSERT_EQ(0x9A, bdaddr.address[4]);
+  ASSERT_EQ(0xBC, bdaddr.address[5]);
+
+  std::string ret = bdaddr.ToString();
+
+  ASSERT_STREQ(test_addr, ret.c_str());
+}
+
+TEST(AddressTest, test_equals) {
+  Address bdaddr1;
+  Address bdaddr2;
+  Address bdaddr3;
+  Address::FromString(test_addr, bdaddr1);
+  Address::FromString(test_addr, bdaddr2);
+  EXPECT_TRUE(bdaddr1 == bdaddr2);
+  EXPECT_FALSE(bdaddr1 != bdaddr2);
+  EXPECT_TRUE(bdaddr1 == bdaddr1);
+  EXPECT_FALSE(bdaddr1 != bdaddr1);
+
+  Address::FromString(test_addr2, bdaddr3);
+  EXPECT_FALSE(bdaddr2 == bdaddr3);
+  EXPECT_TRUE(bdaddr2 != bdaddr3);
+}
+
+TEST(AddressTest, test_less_than) {
+  Address bdaddr1;
+  Address bdaddr2;
+  Address bdaddr3;
+  Address::FromString(test_addr, bdaddr1);
+  Address::FromString(test_addr, bdaddr2);
+  EXPECT_FALSE(bdaddr1 < bdaddr2);
+  EXPECT_FALSE(bdaddr1 < bdaddr1);
+
+  Address::FromString(test_addr2, bdaddr3);
+  EXPECT_TRUE(bdaddr2 < bdaddr3);
+  EXPECT_FALSE(bdaddr3 < bdaddr2);
+}
+
+TEST(AddressTest, test_more_than) {
+  Address bdaddr1;
+  Address bdaddr2;
+  Address bdaddr3;
+  Address::FromString(test_addr, bdaddr1);
+  Address::FromString(test_addr, bdaddr2);
+  EXPECT_FALSE(bdaddr1 > bdaddr2);
+  EXPECT_FALSE(bdaddr1 > bdaddr1);
+
+  Address::FromString(test_addr2, bdaddr3);
+  EXPECT_FALSE(bdaddr2 > bdaddr3);
+  EXPECT_TRUE(bdaddr3 > bdaddr2);
+}
+
+TEST(AddressTest, test_less_than_or_equal) {
+  Address bdaddr1;
+  Address bdaddr2;
+  Address bdaddr3;
+  Address::FromString(test_addr, bdaddr1);
+  Address::FromString(test_addr, bdaddr2);
+  EXPECT_TRUE(bdaddr1 <= bdaddr2);
+  EXPECT_TRUE(bdaddr1 <= bdaddr1);
+
+  Address::FromString(test_addr2, bdaddr3);
+  EXPECT_TRUE(bdaddr2 <= bdaddr3);
+  EXPECT_FALSE(bdaddr3 <= bdaddr2);
+}
+
+TEST(AddressTest, test_more_than_or_equal) {
+  Address bdaddr1;
+  Address bdaddr2;
+  Address bdaddr3;
+  Address::FromString(test_addr, bdaddr1);
+  Address::FromString(test_addr, bdaddr2);
+  EXPECT_TRUE(bdaddr1 >= bdaddr2);
+  EXPECT_TRUE(bdaddr1 >= bdaddr1);
+
+  Address::FromString(test_addr2, bdaddr3);
+  EXPECT_FALSE(bdaddr2 >= bdaddr3);
+  EXPECT_TRUE(bdaddr3 >= bdaddr2);
+}
+
+TEST(AddressTest, test_copy) {
+  Address bdaddr1;
+  Address bdaddr2;
+  Address::FromString(test_addr, bdaddr1);
+  bdaddr2 = bdaddr1;
+
+  EXPECT_TRUE(bdaddr1 == bdaddr2);
+}
+
+TEST(AddressTest, IsValidAddress) {
+  EXPECT_FALSE(Address::IsValidAddress(""));
+  EXPECT_FALSE(Address::IsValidAddress("000000000000"));
+  EXPECT_FALSE(Address::IsValidAddress("00:00:00:00:0000"));
+  EXPECT_FALSE(Address::IsValidAddress("00:00:00:00:00:0"));
+  EXPECT_FALSE(Address::IsValidAddress("00:00:00:00:00:0;"));
+  EXPECT_TRUE(Address::IsValidAddress("00:00:00:00:00:00"));
+  EXPECT_TRUE(Address::IsValidAddress("AB:cd:00:00:00:00"));
+  EXPECT_FALSE(Address::IsValidAddress("aB:cD:eF:Gh:iJ:Kl"));
+}
+
+TEST(AddressTest, BdAddrFromString) {
+  Address addr;
+  memset(&addr, 0, sizeof(addr));
+
+  EXPECT_TRUE(Address::FromString("00:00:00:00:00:00", addr));
+  const Address result0 = {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
+  EXPECT_EQ(0, memcmp(&addr, &result0, sizeof(addr)));
+
+  EXPECT_TRUE(Address::FromString("ab:01:4C:d5:21:9f", addr));
+  const Address result1 = {{0x9f, 0x21, 0xd5, 0x4c, 0x01, 0xab}};
+  EXPECT_EQ(0, memcmp(&addr, &result1, sizeof(addr)));
+}
+
+TEST(AddressTest, BdAddrFromStringToStringEquivalent) {
+  std::string address = "c1:c2:c3:d1:d2:d3";
+  Address addr;
+
+  EXPECT_TRUE(Address::FromString(address, addr));
+  EXPECT_EQ(addr.ToString(), address);
+}
+
+TEST(AddressTest, BdAddrSameValueSameOrder) {
+  Address addr1{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}};
+  Address addr2{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}};
+  // Test if two addresses with same byte value have the same hash
+  struct std::hash<bluetooth::hci::Address> hasher;
+  EXPECT_EQ(hasher(addr1), hasher(addr2));
+  // Test if two addresses with the same hash and the same value, they will
+  // still map to the same value
+  std::unordered_map<Address, int> data = {};
+  data[addr1] = 5;
+  data[addr2] = 8;
+  EXPECT_EQ(data[addr1], data[addr2]);
+}
+
+TEST(AddressTest, BdAddrHashDifferentForDifferentAddressesZeroAddr) {
+  Address addr1{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}};
+  struct std::hash<Address> hasher;
+  EXPECT_NE(hasher(addr1), hasher(Address::kEmpty));
+}
+
+TEST(AddressTest, BdAddrHashDifferentForDifferentAddressesFullAddr) {
+  Address addr1{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}};
+  struct std::hash<Address> hasher;
+  EXPECT_NE(hasher(addr1), hasher(Address::kAny));
+}
+
+TEST(AddressTest, BdAddrHashDifferentForDifferentAddressesZeroAndFullAddr) {
+  struct std::hash<Address> hasher;
+  EXPECT_NE(hasher(Address::kEmpty), hasher(Address::kAny));
+}
diff --git a/gd/hci/address_with_type.h b/gd/hci/address_with_type.h
new file mode 100644
index 0000000..ca77e2d
--- /dev/null
+++ b/gd/hci/address_with_type.h
@@ -0,0 +1,125 @@
+/******************************************************************************
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#pragma once
+
+#include <sstream>
+#include <string>
+#include <utility>
+
+#include "crypto_toolbox/crypto_toolbox.h"
+#include "hci/address.h"
+#include "hci/hci_packets.h"
+
+namespace bluetooth {
+namespace hci {
+
+class AddressWithType final {
+ public:
+  AddressWithType(Address address, AddressType address_type) : address_(address), address_type_(address_type) {}
+
+  explicit AddressWithType() : address_(Address::kEmpty), address_type_(AddressType::PUBLIC_DEVICE_ADDRESS) {}
+
+  inline Address GetAddress() const {
+    return address_;
+  }
+
+  inline AddressType GetAddressType() const {
+    return address_type_;
+  }
+
+  /* Is this an Resolvable Private Address ? */
+  inline bool IsRpa() const {
+    return address_type_ == hci::AddressType::RANDOM_DEVICE_ADDRESS && ((address_.address)[0] & 0xc0) == 0x40;
+  }
+
+  /* Is this an Resolvable Private Address, that was generated from given irk ? */
+  bool IsRpaThatMatchesIrk(const crypto_toolbox::Octet16& irk) const {
+    if (!IsRpa()) return false;
+
+    /* use the 3 MSB of bd address as prand */
+    uint8_t prand[3];
+    prand[0] = address_.address[2];
+    prand[1] = address_.address[1];
+    prand[2] = address_.address[0];
+    /* generate X = E irk(R0, R1, R2) and R is random address 3 LSO */
+    crypto_toolbox::Octet16 computed_hash = crypto_toolbox::aes_128(irk, &prand[0], 3);
+    uint8_t hash[3];
+    hash[0] = address_.address[5];
+    hash[1] = address_.address[4];
+    hash[2] = address_.address[3];
+    if (memcmp(computed_hash.data(), &hash[0], 3) == 0) {
+      // match
+      return true;
+    }
+    // not a match
+    return false;
+  }
+
+  bool operator<(const AddressWithType& rhs) const {
+    return address_ < rhs.address_ && address_type_ < rhs.address_type_;
+  }
+  bool operator==(const AddressWithType& rhs) const {
+    return address_ == rhs.address_ && address_type_ == rhs.address_type_;
+  }
+  bool operator>(const AddressWithType& rhs) const {
+    return (rhs < *this);
+  }
+  bool operator<=(const AddressWithType& rhs) const {
+    return !(*this > rhs);
+  }
+  bool operator>=(const AddressWithType& rhs) const {
+    return !(*this < rhs);
+  }
+  bool operator!=(const AddressWithType& rhs) const {
+    return !(*this == rhs);
+  }
+
+  std::string ToString() const {
+    std::stringstream ss;
+    ss << address_ << "[" << AddressTypeText(address_type_) << "]";
+    return ss.str();
+  }
+
+ private:
+  Address address_;
+  AddressType address_type_;
+};
+
+inline std::ostream& operator<<(std::ostream& os, const AddressWithType& a) {
+  os << a.ToString();
+  return os;
+}
+
+}  // namespace hci
+}  // namespace bluetooth
+
+namespace std {
+template <>
+struct hash<bluetooth::hci::AddressWithType> {
+  std::size_t operator()(const bluetooth::hci::AddressWithType& val) const {
+    static_assert(sizeof(uint64_t) >= (sizeof(bluetooth::hci::Address) + sizeof(bluetooth::hci::AddressType)));
+    uint64_t int_addr = 0;
+    memcpy(reinterpret_cast<uint8_t*>(&int_addr), val.GetAddress().address, sizeof(bluetooth::hci::Address));
+    bluetooth::hci::AddressType address_type = val.GetAddressType();
+    memcpy(reinterpret_cast<uint8_t*>(&int_addr) + sizeof(bluetooth::hci::Address), &address_type,
+           sizeof(address_type));
+    return std::hash<uint64_t>{}(int_addr);
+  }
+};
+}  // namespace std
\ No newline at end of file
diff --git a/gd/hci/address_with_type_test.cc b/gd/hci/address_with_type_test.cc
new file mode 100644
index 0000000..37b4ab6
--- /dev/null
+++ b/gd/hci/address_with_type_test.cc
@@ -0,0 +1,101 @@
+/******************************************************************************
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#include <unordered_map>
+
+#include <gtest/gtest.h>
+
+#include "hci/address.h"
+#include "hci/address_with_type.h"
+#include "hci/hci_packets.h"
+
+namespace bluetooth {
+namespace hci {
+
+TEST(AddressWithTypeTest, AddressWithTypeSameValueSameOrder) {
+  Address addr1{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}};
+  AddressType type1 = AddressType::PUBLIC_DEVICE_ADDRESS;
+  AddressWithType address_with_type_1(addr1, type1);
+  Address addr2{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}};
+  AddressType type2 = AddressType::PUBLIC_DEVICE_ADDRESS;
+  AddressWithType address_with_type_2(addr2, type2);
+  // Test if two address with type with same byte value have the same hash
+  struct std::hash<bluetooth::hci::AddressWithType> hasher;
+  EXPECT_EQ(hasher(address_with_type_1), hasher(address_with_type_2));
+  // Test if two address with type with the same hash and the same value, they will
+  // still map to the same value
+  std::unordered_map<AddressWithType, int> data = {};
+  data[address_with_type_1] = 5;
+  data[address_with_type_2] = 8;
+  EXPECT_EQ(data[address_with_type_1], data[address_with_type_2]);
+}
+
+TEST(AddressWithTypeTest, HashDifferentDiffAddrSameType) {
+  Address addr{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}};
+  AddressType type = AddressType::PUBLIC_IDENTITY_ADDRESS;
+  AddressWithType address_with_type(addr, type);
+  struct std::hash<AddressWithType> hasher;
+  EXPECT_NE(hasher(address_with_type), hasher(AddressWithType(Address::kEmpty, AddressType::PUBLIC_IDENTITY_ADDRESS)));
+}
+
+TEST(AddressWithTypeTest, HashDifferentSameAddressDiffType) {
+  Address addr1{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}};
+  AddressType type1 = AddressType::PUBLIC_DEVICE_ADDRESS;
+  AddressWithType address_with_type_1(addr1, type1);
+  Address addr2{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}};
+  AddressType type2 = AddressType::PUBLIC_IDENTITY_ADDRESS;
+  AddressWithType address_with_type_2(addr2, type2);
+  struct std::hash<bluetooth::hci::AddressWithType> hasher;
+  EXPECT_NE(hasher(address_with_type_1), hasher(address_with_type_2));
+}
+
+TEST(AddressWithTypeTest, IsRpa) {
+  // Public address can't be RPA
+  EXPECT_FALSE(
+      AddressWithType(Address{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}}, AddressType::PUBLIC_IDENTITY_ADDRESS).IsRpa());
+
+  // Must have proper Most Significant Bit configuration
+  EXPECT_FALSE(
+      AddressWithType(Address{{0x30, 0x02, 0x03, 0x04, 0x05, 0x06}}, AddressType::RANDOM_DEVICE_ADDRESS).IsRpa());
+  EXPECT_TRUE(
+      AddressWithType(Address{{0x40, 0x02, 0x03, 0x04, 0x05, 0x03}}, AddressType::RANDOM_DEVICE_ADDRESS).IsRpa());
+  EXPECT_TRUE(
+      AddressWithType(Address{{0x50, 0x02, 0x03, 0x04, 0x05, 0x06}}, AddressType::RANDOM_DEVICE_ADDRESS).IsRpa());
+  EXPECT_TRUE(
+      AddressWithType(Address{{0x60, 0x02, 0x03, 0x04, 0x05, 0x06}}, AddressType::RANDOM_DEVICE_ADDRESS).IsRpa());
+  EXPECT_TRUE(
+      AddressWithType(Address{{0x70, 0x02, 0x03, 0x04, 0x05, 0x06}}, AddressType::RANDOM_DEVICE_ADDRESS).IsRpa());
+  EXPECT_FALSE(
+      AddressWithType(Address{{0x80, 0x02, 0x03, 0x04, 0x05, 0x06}}, AddressType::RANDOM_DEVICE_ADDRESS).IsRpa());
+}
+
+TEST(AddressWithTypeTest, IsRpaThatMatchesIrk) {
+  // Public address can't be RPA
+  AddressWithType address_1 =
+      AddressWithType(Address{{0x50, 0x02, 0x03, 0xC9, 0x12, 0xDE}}, AddressType::RANDOM_DEVICE_ADDRESS);
+  AddressWithType address_2 =
+      AddressWithType(Address{{0x50, 0x02, 0x03, 0xC9, 0x12, 0xDD}}, AddressType::RANDOM_DEVICE_ADDRESS);
+  crypto_toolbox::Octet16 irk_1{0x90, 0x5e, 0x60, 0x59, 0xc9, 0x11, 0x43, 0x7b,
+                                0x04, 0x09, 0x6a, 0x53, 0x28, 0xe6, 0x59, 0x6d};
+
+  EXPECT_TRUE(address_1.IsRpaThatMatchesIrk(irk_1));
+  EXPECT_FALSE(address_2.IsRpaThatMatchesIrk(irk_1));
+}
+
+}  // namespace hci
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/hci/cert/acl_manager_test.py b/gd/hci/cert/acl_manager_test.py
new file mode 100644
index 0000000..1f25c7d
--- /dev/null
+++ b/gd/hci/cert/acl_manager_test.py
@@ -0,0 +1,364 @@
+#!/usr/bin/env python3
+#
+#   Copyright 2020 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import os
+import sys
+import logging
+
+from cert.gd_base_test_facade_only import GdFacadeOnlyBaseTestClass
+from cert.event_callback_stream import EventCallbackStream
+from cert.event_asserts import EventAsserts
+from google.protobuf import empty_pb2 as empty_proto
+from facade import rootservice_pb2 as facade_rootservice
+from hci.facade import acl_manager_facade_pb2 as acl_manager_facade
+from neighbor.facade import facade_pb2 as neighbor_facade
+from hci.facade import controller_facade_pb2 as controller_facade
+from hci.facade import facade_pb2 as hci_facade
+import bluetooth_packets_python3 as bt_packets
+from bluetooth_packets_python3 import hci_packets
+
+
+class AclManagerTest(GdFacadeOnlyBaseTestClass):
+
+    def setup_test(self):
+        self.device_under_test.rootservice.StartStack(
+            facade_rootservice.StartStackRequest(
+                module_under_test=facade_rootservice.BluetoothModule.Value(
+                    'HCI_INTERFACES'),))
+        self.cert_device.rootservice.StartStack(
+            facade_rootservice.StartStackRequest(
+                module_under_test=facade_rootservice.BluetoothModule.Value(
+                    'HCI'),))
+
+        self.device_under_test.wait_channel_ready()
+        self.cert_device.wait_channel_ready()
+
+    def teardown_test(self):
+        self.device_under_test.rootservice.StopStack(
+            facade_rootservice.StopStackRequest())
+        self.cert_device.rootservice.StopStack(
+            facade_rootservice.StopStackRequest())
+
+    def register_for_event(self, event_code):
+        msg = hci_facade.EventCodeMsg(code=int(event_code))
+        self.cert_device.hci.RegisterEventHandler(msg)
+
+    def enqueue_hci_command(self, command, expect_complete):
+        cmd_bytes = bytes(command.Serialize())
+        cmd = hci_facade.CommandMsg(command=cmd_bytes)
+        if (expect_complete):
+            self.cert_device.hci.EnqueueCommandWithComplete(cmd)
+        else:
+            self.cert_device.hci.EnqueueCommandWithStatus(cmd)
+
+    def enqueue_acl_data(self, handle, pb_flag, b_flag, acl):
+        acl_msg = hci_facade.AclMsg(
+            handle=int(handle),
+            packet_boundary_flag=int(pb_flag),
+            broadcast_flag=int(b_flag),
+            data=acl)
+        self.cert_device.hci.SendAclData(acl_msg)
+
+    def test_dut_connects(self):
+        self.register_for_event(hci_packets.EventCode.CONNECTION_REQUEST)
+        self.register_for_event(hci_packets.EventCode.CONNECTION_COMPLETE)
+        self.register_for_event(
+            hci_packets.EventCode.CONNECTION_PACKET_TYPE_CHANGED)
+        with EventCallbackStream(self.cert_device.hci.FetchEvents(empty_proto.Empty())) as cert_hci_event_stream, \
+            EventCallbackStream(self.cert_device.hci.FetchAclPackets(empty_proto.Empty())) as cert_acl_data_stream, \
+            EventCallbackStream(self.device_under_test.hci_acl_manager.FetchAclData(empty_proto.Empty())) as acl_data_stream:
+
+            cert_hci_event_asserts = EventAsserts(cert_hci_event_stream)
+            acl_data_asserts = EventAsserts(acl_data_stream)
+            cert_acl_data_asserts = EventAsserts(cert_acl_data_stream)
+
+            # CERT Enables scans and gets its address
+            self.enqueue_hci_command(
+                hci_packets.WriteScanEnableBuilder(
+                    hci_packets.ScanEnable.INQUIRY_AND_PAGE_SCAN), True)
+
+            cert_address = None
+
+            def get_address_from_complete(packet):
+                packet_bytes = packet.event
+                if b'\x0e\x0a\x01\x09\x10' in packet_bytes:
+                    nonlocal cert_address
+                    addr_view = hci_packets.ReadBdAddrCompleteView(
+                        hci_packets.CommandCompleteView(
+                            hci_packets.EventPacketView(
+                                bt_packets.PacketViewLittleEndian(
+                                    list(packet_bytes)))))
+                    cert_address = addr_view.GetBdAddr()
+                    return True
+                return False
+
+            self.enqueue_hci_command(hci_packets.ReadBdAddrBuilder(), True)
+
+            cert_hci_event_asserts.assert_event_occurs(
+                get_address_from_complete)
+
+            with EventCallbackStream(
+                    self.device_under_test.hci_acl_manager.CreateConnection(
+                        acl_manager_facade.ConnectionMsg(
+                            address_type=int(
+                                hci_packets.AddressType.PUBLIC_DEVICE_ADDRESS),
+                            address=bytes(cert_address,
+                                          'utf8')))) as connection_event_stream:
+
+                connection_event_asserts = EventAsserts(connection_event_stream)
+                connection_request = None
+
+                def get_connect_request(packet):
+                    if b'\x04\x0a' in packet.event:
+                        nonlocal connection_request
+                        connection_request = hci_packets.ConnectionRequestView(
+                            hci_packets.EventPacketView(
+                                bt_packets.PacketViewLittleEndian(
+                                    list(packet.event))))
+                        return True
+                    return False
+
+                # Cert Accepts
+                cert_hci_event_asserts.assert_event_occurs(get_connect_request)
+                self.enqueue_hci_command(
+                    hci_packets.AcceptConnectionRequestBuilder(
+                        connection_request.GetBdAddr(),
+                        hci_packets.AcceptConnectionRequestRole.REMAIN_SLAVE),
+                    False)
+
+                # Cert gets ConnectionComplete with a handle and sends ACL data
+                handle = 0xfff
+
+                def get_handle(packet):
+                    packet_bytes = packet.event
+                    if b'\x03\x0b\x00' in packet_bytes:
+                        nonlocal handle
+                        cc_view = hci_packets.ConnectionCompleteView(
+                            hci_packets.EventPacketView(
+                                bt_packets.PacketViewLittleEndian(
+                                    list(packet_bytes))))
+                        handle = cc_view.GetConnectionHandle()
+                        return True
+                    return False
+
+                cert_hci_event_asserts.assert_event_occurs(get_handle)
+                cert_handle = handle
+
+                self.enqueue_acl_data(
+                    cert_handle, hci_packets.PacketBoundaryFlag.
+                    FIRST_AUTOMATICALLY_FLUSHABLE,
+                    hci_packets.BroadcastFlag.POINT_TO_POINT,
+                    bytes(
+                        b'\x26\x00\x07\x00This is just SomeAclData from the Cert'
+                    ))
+
+                # DUT gets a connection complete event and sends and receives
+                handle = 0xfff
+                connection_event_asserts.assert_event_occurs(get_handle)
+
+                self.device_under_test.hci_acl_manager.SendAclData(
+                    acl_manager_facade.AclData(
+                        handle=handle,
+                        payload=bytes(
+                            b'\x29\x00\x07\x00This is just SomeMoreAclData from the DUT'
+                        )))
+
+                cert_acl_data_asserts.assert_event_occurs(
+                    lambda packet: b'SomeMoreAclData' in packet.data)
+                acl_data_asserts.assert_event_occurs(
+                    lambda packet: b'SomeAclData' in packet.payload)
+
+    def test_cert_connects(self):
+        self.register_for_event(hci_packets.EventCode.CONNECTION_COMPLETE)
+        self.register_for_event(hci_packets.EventCode.ROLE_CHANGE)
+        self.register_for_event(
+            hci_packets.EventCode.CONNECTION_PACKET_TYPE_CHANGED)
+        with EventCallbackStream(self.cert_device.hci.FetchEvents(empty_proto.Empty())) as cert_hci_event_stream, \
+            EventCallbackStream(self.cert_device.hci.FetchAclPackets(empty_proto.Empty())) as cert_acl_data_stream, \
+            EventCallbackStream(self.device_under_test.hci_acl_manager.FetchIncomingConnection(empty_proto.Empty())) as incoming_connection_stream, \
+            EventCallbackStream(self.device_under_test.hci_acl_manager.FetchAclData(empty_proto.Empty())) as acl_data_stream:
+
+            cert_hci_event_asserts = EventAsserts(cert_hci_event_stream)
+            incoming_connection_asserts = EventAsserts(
+                incoming_connection_stream)
+            cert_acl_data_asserts = EventAsserts(cert_acl_data_stream)
+            acl_data_asserts = EventAsserts(acl_data_stream)
+
+            # DUT Enables scans and gets its address
+            dut_address = self.device_under_test.hci_controller.GetMacAddress(
+                empty_proto.Empty()).address
+
+            self.device_under_test.neighbor.EnablePageScan(
+                neighbor_facade.EnableMsg(enabled=True))
+
+            # Cert connects
+            self.enqueue_hci_command(
+                hci_packets.CreateConnectionBuilder(
+                    dut_address.decode('utf-8'),
+                    0xcc18,  # Packet Type
+                    hci_packets.PageScanRepetitionMode.R1,
+                    0x0,
+                    hci_packets.ClockOffsetValid.INVALID,
+                    hci_packets.CreateConnectionRoleSwitch.ALLOW_ROLE_SWITCH),
+                False)
+
+            conn_handle = 0xfff
+
+            def get_handle(packet):
+                packet_bytes = packet.event
+                if b'\x03\x0b\x00' in packet_bytes:
+                    nonlocal conn_handle
+                    cc_view = hci_packets.ConnectionCompleteView(
+                        hci_packets.EventPacketView(
+                            bt_packets.PacketViewLittleEndian(
+                                list(packet_bytes))))
+                    conn_handle = cc_view.GetConnectionHandle()
+                    return True
+                return False
+
+            # DUT gets a connection request
+            incoming_connection_asserts.assert_event_occurs(get_handle)
+
+            self.device_under_test.hci_acl_manager.SendAclData(
+                acl_manager_facade.AclData(
+                    handle=conn_handle,
+                    payload=bytes(
+                        b'\x29\x00\x07\x00This is just SomeMoreAclData from the DUT'
+                    )))
+
+            conn_handle = 0xfff
+
+            cert_hci_event_asserts.assert_event_occurs(get_handle)
+            cert_handle = conn_handle
+
+            self.enqueue_acl_data(
+                cert_handle,
+                hci_packets.PacketBoundaryFlag.FIRST_AUTOMATICALLY_FLUSHABLE,
+                hci_packets.BroadcastFlag.POINT_TO_POINT,
+                bytes(
+                    b'\x26\x00\x07\x00This is just SomeAclData from the Cert'))
+
+            cert_acl_data_asserts.assert_event_occurs(
+                lambda packet: b'SomeMoreAclData' in packet.data)
+            acl_data_asserts.assert_event_occurs(
+                lambda packet: b'SomeAclData' in packet.payload)
+
+    def test_recombination_l2cap_packet(self):
+        self.register_for_event(hci_packets.EventCode.CONNECTION_REQUEST)
+        self.register_for_event(hci_packets.EventCode.CONNECTION_COMPLETE)
+        self.register_for_event(
+            hci_packets.EventCode.CONNECTION_PACKET_TYPE_CHANGED)
+        with EventCallbackStream(self.cert_device.hci.FetchEvents(empty_proto.Empty())) as cert_hci_event_stream, \
+            EventCallbackStream(self.cert_device.hci.FetchAclPackets(empty_proto.Empty())) as cert_acl_data_stream, \
+            EventCallbackStream(self.device_under_test.hci_acl_manager.FetchAclData(empty_proto.Empty())) as acl_data_stream:
+
+            cert_hci_event_asserts = EventAsserts(cert_hci_event_stream)
+            acl_data_asserts = EventAsserts(acl_data_stream)
+
+            # CERT Enables scans and gets its address
+            self.enqueue_hci_command(
+                hci_packets.WriteScanEnableBuilder(
+                    hci_packets.ScanEnable.INQUIRY_AND_PAGE_SCAN), True)
+
+            cert_address = None
+
+            def get_address_from_complete(packet):
+                packet_bytes = packet.event
+                if b'\x0e\x0a\x01\x09\x10' in packet_bytes:
+                    nonlocal cert_address
+                    addr_view = hci_packets.ReadBdAddrCompleteView(
+                        hci_packets.CommandCompleteView(
+                            hci_packets.EventPacketView(
+                                bt_packets.PacketViewLittleEndian(
+                                    list(packet_bytes)))))
+                    cert_address = addr_view.GetBdAddr()
+                    return True
+                return False
+
+            self.enqueue_hci_command(hci_packets.ReadBdAddrBuilder(), True)
+
+            cert_hci_event_asserts.assert_event_occurs(
+                get_address_from_complete)
+
+            with EventCallbackStream(
+                    self.device_under_test.hci_acl_manager.CreateConnection(
+                        acl_manager_facade.ConnectionMsg(
+                            address_type=int(
+                                hci_packets.AddressType.PUBLIC_DEVICE_ADDRESS),
+                            address=bytes(cert_address,
+                                          'utf8')))) as connection_event_stream:
+
+                connection_event_asserts = EventAsserts(connection_event_stream)
+                connection_request = None
+
+                def get_connect_request(packet):
+                    if b'\x04\x0a' in packet.event:
+                        nonlocal connection_request
+                        connection_request = hci_packets.ConnectionRequestView(
+                            hci_packets.EventPacketView(
+                                bt_packets.PacketViewLittleEndian(
+                                    list(packet.event))))
+                        return True
+                    return False
+
+                # Cert Accepts
+                cert_hci_event_asserts.assert_event_occurs(get_connect_request)
+                self.enqueue_hci_command(
+                    hci_packets.AcceptConnectionRequestBuilder(
+                        connection_request.GetBdAddr(),
+                        hci_packets.AcceptConnectionRequestRole.REMAIN_SLAVE),
+                    False)
+
+                # Cert gets ConnectionComplete with a handle and sends ACL data
+                handle = 0xfff
+
+                def get_handle(packet):
+                    packet_bytes = packet.event
+                    if b'\x03\x0b\x00' in packet_bytes:
+                        nonlocal handle
+                        cc_view = hci_packets.ConnectionCompleteView(
+                            hci_packets.EventPacketView(
+                                bt_packets.PacketViewLittleEndian(
+                                    list(packet_bytes))))
+                        handle = cc_view.GetConnectionHandle()
+                        return True
+                    return False
+
+                cert_hci_event_asserts.assert_event_occurs(get_handle)
+                cert_handle = handle
+
+                self.enqueue_acl_data(
+                    cert_handle, hci_packets.PacketBoundaryFlag.
+                    FIRST_AUTOMATICALLY_FLUSHABLE,
+                    hci_packets.BroadcastFlag.POINT_TO_POINT,
+                    bytes(b'\x06\x00\x07\x00Hello'))
+                self.enqueue_acl_data(
+                    cert_handle,
+                    hci_packets.PacketBoundaryFlag.CONTINUING_FRAGMENT,
+                    hci_packets.BroadcastFlag.POINT_TO_POINT, bytes(b'!'))
+                self.enqueue_acl_data(
+                    cert_handle, hci_packets.PacketBoundaryFlag.
+                    FIRST_AUTOMATICALLY_FLUSHABLE,
+                    hci_packets.BroadcastFlag.POINT_TO_POINT,
+                    bytes(b'\xe8\x03\x07\x00' + b'Hello' * 200))
+
+                # DUT gets a connection complete event and sends and receives
+                connection_event_asserts.assert_event_occurs(get_handle)
+
+                acl_data_asserts.assert_event_occurs(
+                    lambda packet: b'Hello!' in packet.payload)
+                acl_data_asserts.assert_event_occurs(
+                    lambda packet: b'Hello' * 200 in packet.payload)
diff --git a/gd/hci/cert/api.proto b/gd/hci/cert/api.proto
new file mode 100644
index 0000000..30b3591
--- /dev/null
+++ b/gd/hci/cert/api.proto
@@ -0,0 +1,46 @@
+syntax = "proto3";
+
+package bluetooth.hci.cert;
+
+import "google/protobuf/empty.proto";
+import "facade/common.proto";
+
+service AclManagerCert {
+  rpc SetPageScanMode(PageScanMode) returns (google.protobuf.Empty) {}
+  rpc SetIncomingConnectionPolicy(IncomingConnectionPolicy) returns (google.protobuf.Empty) {}
+  rpc Connect(facade.BluetoothAddress) returns (google.protobuf.Empty) {}
+  rpc Disconnect(facade.BluetoothAddress) returns (google.protobuf.Empty) {}
+  rpc FetchConnectionComplete(google.protobuf.Empty) returns (stream ConnectionEvent) {}
+  rpc FetchDisconnection(google.protobuf.Empty) returns (stream DisconnectionEvent) {}
+  rpc FetchConnectionFailed(google.protobuf.Empty) returns (stream ConnectionFailedEvent) {}
+  rpc SendAclData(AclData) returns (google.protobuf.Empty) {}
+  rpc FetchAclData(google.protobuf.Empty) returns (stream AclData) {}
+}
+
+message PageScanMode {
+  bool enabled = 1;
+}
+
+message IncomingConnectionPolicy {
+  facade.BluetoothAddress remote = 1;
+  bool accepted = 2;
+}
+
+message ConnectionEvent {
+  facade.BluetoothAddress remote = 1;
+}
+
+message DisconnectionEvent {
+  facade.BluetoothAddress remote = 1;
+  uint32 reason = 2;
+}
+
+message ConnectionFailedEvent {
+  facade.BluetoothAddress remote = 1;
+  uint32 reason = 2;
+}
+
+message AclData {
+  facade.BluetoothAddress remote = 1;
+  bytes payload = 2;
+}
diff --git a/gd/hci/cert/cert.cc b/gd/hci/cert/cert.cc
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gd/hci/cert/cert.cc
diff --git a/gd/hci/cert/controller_test.py b/gd/hci/cert/controller_test.py
new file mode 100644
index 0000000..6dc23c4
--- /dev/null
+++ b/gd/hci/cert/controller_test.py
@@ -0,0 +1,86 @@
+#!/usr/bin/env python3
+#
+#   Copyright 2020 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import time
+
+from acts import asserts
+from cert.gd_base_test_facade_only import GdFacadeOnlyBaseTestClass
+from google.protobuf import empty_pb2 as empty_proto
+from facade import rootservice_pb2 as facade_rootservice
+from hci.facade import controller_facade_pb2 as controller_facade
+
+
+class ControllerTest(GdFacadeOnlyBaseTestClass):
+
+    def setup_test(self):
+        self.device_under_test.rootservice.StartStack(
+            facade_rootservice.StartStackRequest(
+                module_under_test=facade_rootservice.BluetoothModule.Value(
+                    'HCI_INTERFACES'),))
+        self.cert_device.rootservice.StartStack(
+            facade_rootservice.StartStackRequest(
+                module_under_test=facade_rootservice.BluetoothModule.Value(
+                    'HCI_INTERFACES'),))
+
+        self.device_under_test.wait_channel_ready()
+        self.cert_device.wait_channel_ready()
+
+    def teardown_test(self):
+        self.device_under_test.rootservice.StopStack(
+            facade_rootservice.StopStackRequest())
+        self.cert_device.rootservice.StopStack(
+            facade_rootservice.StopStackRequest())
+
+    def test_get_addresses(self):
+        cert_address_response = self.cert_device.hci_controller.GetMacAddress(
+            empty_proto.Empty())
+        dut_address_response = self.device_under_test.hci_controller.GetMacAddress(
+            empty_proto.Empty())
+        asserts.assert_true(
+            cert_address_response.address != dut_address_response.address,
+            msg="Expected cert and dut address to be different %s" %
+            cert_address_response.address)
+        time.sleep(1)  # This shouldn't be needed b/149120542
+
+    def test_get_local_extended_features(self):
+        request = controller_facade.PageNumberMsg()
+        request.page_number = 1
+        dut_feature_response1 = self.device_under_test.hci_controller.GetLocalExtendedFeatures(
+            request)
+        request0 = controller_facade.PageNumberMsg()
+        request0.page_number = 0
+        dut_feature_response0 = self.device_under_test.hci_controller.GetLocalExtendedFeatures(
+            request0)
+        asserts.assert_true(
+            dut_feature_response1.page != dut_feature_response0.page,
+            msg="Expected cert dut feature pages to be different %d" %
+            dut_feature_response1.page)
+
+    def test_write_local_name(self):
+        self.device_under_test.hci_controller.WriteLocalName(
+            controller_facade.NameMsg(name=b'ImTheDUT'))
+        self.cert_device.hci_controller.WriteLocalName(
+            controller_facade.NameMsg(name=b'ImTheCert'))
+        cert_name_msg = self.cert_device.hci_controller.GetLocalName(
+            empty_proto.Empty()).name
+        dut_name_msg = self.device_under_test.hci_controller.GetLocalName(
+            empty_proto.Empty()).name
+        asserts.assert_true(
+            dut_name_msg == b'ImTheDUT',
+            msg="unexpected dut name %s" % dut_name_msg)
+        asserts.assert_true(
+            cert_name_msg == b'ImTheCert',
+            msg="unexpected cert name %s" % cert_name_msg)
diff --git a/gd/hci/cert/direct_hci_test.py b/gd/hci/cert/direct_hci_test.py
new file mode 100644
index 0000000..b9d8642
--- /dev/null
+++ b/gd/hci/cert/direct_hci_test.py
@@ -0,0 +1,702 @@
+#!/usr/bin/env python3
+#
+#   Copyright 2019 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+from datetime import timedelta
+import os
+import sys
+import logging
+
+from cert.gd_base_test_facade_only import GdFacadeOnlyBaseTestClass
+from cert.event_callback_stream import EventCallbackStream
+from cert.event_asserts import EventAsserts
+from google.protobuf import empty_pb2 as empty_proto
+from facade import rootservice_pb2 as facade_rootservice
+from hal import facade_pb2 as hal_facade
+from hci.facade import facade_pb2 as hci_facade
+from bluetooth_packets_python3 import hci_packets
+import bluetooth_packets_python3 as bt_packets
+
+
+class DirectHciTest(GdFacadeOnlyBaseTestClass):
+
+    def setup_test(self):
+        self.device_under_test.rootservice.StartStack(
+            facade_rootservice.StartStackRequest(
+                module_under_test=facade_rootservice.BluetoothModule.Value(
+                    'HCI'),))
+        self.cert_device.rootservice.StartStack(
+            facade_rootservice.StartStackRequest(
+                module_under_test=facade_rootservice.BluetoothModule.Value(
+                    'HAL'),))
+
+        self.device_under_test.wait_channel_ready()
+        self.cert_device.wait_channel_ready()
+
+        self.cert_device.hal.SendHciCommand(
+            hal_facade.HciCommandPacket(
+                payload=bytes(hci_packets.ResetBuilder().Serialize())))
+
+    def teardown_test(self):
+        self.device_under_test.rootservice.StopStack(
+            facade_rootservice.StopStackRequest())
+        self.cert_device.rootservice.StopStack(
+            facade_rootservice.StopStackRequest())
+
+    def register_for_event(self, event_code):
+        msg = hci_facade.EventCodeMsg(code=int(event_code))
+        self.device_under_test.hci.RegisterEventHandler(msg)
+
+    def register_for_le_event(self, event_code):
+        msg = hci_facade.LeSubeventCodeMsg(code=int(event_code))
+        self.device_under_test.hci.RegisterLeEventHandler(msg)
+
+    def enqueue_hci_command(self, command, expect_complete):
+        cmd_bytes = bytes(command.Serialize())
+        cmd = hci_facade.CommandMsg(command=cmd_bytes)
+        if (expect_complete):
+            self.device_under_test.hci.EnqueueCommandWithComplete(cmd)
+        else:
+            self.device_under_test.hci.EnqueueCommandWithStatus(cmd)
+
+    def send_hal_hci_command(self, command):
+        self.cert_device.hal.SendHciCommand(
+            hal_facade.HciCommandPacket(payload=bytes(command.Serialize())))
+
+    def enqueue_acl_data(self, handle, pb_flag, b_flag, acl):
+        acl_msg = hci_facade.AclMsg(
+            handle=int(handle),
+            packet_boundary_flag=int(pb_flag),
+            broadcast_flag=int(b_flag),
+            data=acl)
+        self.device_under_test.hci.SendAclData(acl_msg)
+
+    def send_hal_acl_data(self, handle, pb_flag, b_flag, acl):
+        lower = handle & 0xff
+        upper = (handle >> 8) & 0xf
+        upper = upper | int(pb_flag) & 0x3
+        upper = upper | ((int(b_flag) & 0x3) << 2)
+        lower_length = len(acl) & 0xff
+        upper_length = (len(acl) & 0xff00) >> 8
+        concatenated = bytes([lower, upper, lower_length, upper_length] +
+                             list(acl))
+        self.cert_device.hal.SendHciAcl(
+            hal_facade.HciAclPacket(payload=concatenated))
+
+    def test_local_hci_cmd_and_event(self):
+        # Loopback mode responds with ACL and SCO connection complete
+        self.register_for_event(hci_packets.EventCode.CONNECTION_COMPLETE)
+        self.register_for_event(hci_packets.EventCode.LOOPBACK_COMMAND)
+        self.register_for_event(
+            hci_packets.EventCode.CONNECTION_PACKET_TYPE_CHANGED)
+        with EventCallbackStream(
+                self.device_under_test.hci.FetchEvents(
+                    empty_proto.Empty())) as hci_event_stream:
+            hci_event_asserts = EventAsserts(hci_event_stream)
+
+            self.enqueue_hci_command(
+                hci_packets.WriteLoopbackModeBuilder(
+                    hci_packets.LoopbackMode.ENABLE_LOCAL), True)
+
+            cmd2loop = hci_packets.ReadLocalNameBuilder()
+            self.enqueue_hci_command(cmd2loop, True)
+
+            looped_bytes = bytes(cmd2loop.Serialize())
+            hci_event_asserts.assert_event_occurs(
+                lambda packet: looped_bytes in packet.event)
+
+    def test_inquiry_from_dut(self):
+        self.register_for_event(hci_packets.EventCode.INQUIRY_RESULT)
+        with EventCallbackStream(
+                self.device_under_test.hci.FetchEvents(
+                    empty_proto.Empty())) as hci_event_stream:
+
+            hci_event_asserts = EventAsserts(hci_event_stream)
+
+            self.send_hal_hci_command(
+                hci_packets.WriteScanEnableBuilder(
+                    hci_packets.ScanEnable.INQUIRY_AND_PAGE_SCAN))
+            lap = hci_packets.Lap()
+            lap.lap = 0x33
+            self.enqueue_hci_command(
+                hci_packets.InquiryBuilder(lap, 0x30, 0xff), False)
+            hci_event_asserts.assert_event_occurs(
+                lambda packet: b'\x02\x0f' in packet.event
+                # Expecting an HCI Event (code 0x02, length 0x0f)
+            )
+
+    def test_le_ad_scan_cert_advertises(self):
+        self.register_for_le_event(
+            hci_packets.SubeventCode.EXTENDED_ADVERTISING_REPORT)
+        self.register_for_le_event(hci_packets.SubeventCode.ADVERTISING_REPORT)
+        with EventCallbackStream(
+                self.device_under_test.hci.FetchLeSubevents(
+                    empty_proto.Empty())) as hci_le_event_stream:
+
+            hci_event_asserts = EventAsserts(hci_le_event_stream)
+
+            # DUT Scans
+            self.enqueue_hci_command(
+                hci_packets.LeSetRandomAddressBuilder('0D:05:04:03:02:01'),
+                True)
+            phy_scan_params = hci_packets.PhyScanParameters()
+            phy_scan_params.le_scan_interval = 6553
+            phy_scan_params.le_scan_window = 6553
+            phy_scan_params.le_scan_type = hci_packets.LeScanType.ACTIVE
+
+            self.enqueue_hci_command(
+                hci_packets.LeSetExtendedScanParametersBuilder(
+                    hci_packets.AddressType.RANDOM_DEVICE_ADDRESS,
+                    hci_packets.LeSetScanningFilterPolicy.ACCEPT_ALL, 1,
+                    [phy_scan_params]), True)
+            self.enqueue_hci_command(
+                hci_packets.LeSetExtendedScanEnableBuilder(
+                    hci_packets.Enable.ENABLED,
+                    hci_packets.FilterDuplicates.DISABLED, 0, 0), True)
+
+            # CERT Advertises
+            advertising_handle = 0
+            self.send_hal_hci_command(
+                hci_packets.LeSetExtendedAdvertisingLegacyParametersBuilder(
+                    advertising_handle,
+                    hci_packets.LegacyAdvertisingProperties.ADV_IND,
+                    512,
+                    768,
+                    7,
+                    hci_packets.OwnAddressType.RANDOM_DEVICE_ADDRESS,
+                    hci_packets.PeerAddressType.
+                    PUBLIC_DEVICE_OR_IDENTITY_ADDRESS,
+                    'A6:A5:A4:A3:A2:A1',
+                    hci_packets.AdvertisingFilterPolicy.ALL_DEVICES,
+                    0xF7,
+                    1,  # SID
+                    hci_packets.Enable.DISABLED  # Scan request notification
+                ))
+
+            self.send_hal_hci_command(
+                hci_packets.LeSetExtendedAdvertisingRandomAddressBuilder(
+                    advertising_handle, '0C:05:04:03:02:01'))
+            gap_name = hci_packets.GapData()
+            gap_name.data_type = hci_packets.GapDataType.COMPLETE_LOCAL_NAME
+            gap_name.data = list(bytes(b'Im_A_Cert!'))  # TODO: Fix and remove !
+
+            self.send_hal_hci_command(
+                hci_packets.LeSetExtendedAdvertisingDataBuilder(
+                    advertising_handle,
+                    hci_packets.Operation.COMPLETE_ADVERTISEMENT,
+                    hci_packets.FragmentPreference.CONTROLLER_SHOULD_NOT,
+                    [gap_name]))
+
+            gap_short_name = hci_packets.GapData()
+            gap_short_name.data_type = hci_packets.GapDataType.SHORTENED_LOCAL_NAME
+            gap_short_name.data = list(bytes(b'Im_A_C'))
+
+            self.send_hal_hci_command(
+                hci_packets.LeSetExtendedAdvertisingScanResponseBuilder(
+                    advertising_handle,
+                    hci_packets.Operation.COMPLETE_ADVERTISEMENT,
+                    hci_packets.FragmentPreference.CONTROLLER_SHOULD_NOT,
+                    [gap_short_name]))
+
+            enabled_set = hci_packets.EnabledSet()
+            enabled_set.advertising_handle = 0
+            enabled_set.duration = 0
+            enabled_set.max_extended_advertising_events = 0
+            self.send_hal_hci_command(
+                hci_packets.LeSetExtendedAdvertisingEnableBuilder(
+                    hci_packets.Enable.ENABLED, [enabled_set]))
+
+            hci_event_asserts.assert_event_occurs(
+                lambda packet: b'Im_A_Cert' in packet.event)
+
+            self.send_hal_hci_command(
+                hci_packets.LeSetExtendedAdvertisingEnableBuilder(
+                    hci_packets.Enable.DISABLED, [enabled_set]))
+            self.enqueue_hci_command(
+                hci_packets.LeSetExtendedScanEnableBuilder(
+                    hci_packets.Enable.DISABLED,
+                    hci_packets.FilterDuplicates.DISABLED, 0, 0), True)
+
+    def test_le_connection_dut_advertises(self):
+        self.register_for_le_event(hci_packets.SubeventCode.CONNECTION_COMPLETE)
+        with EventCallbackStream(self.device_under_test.hci.FetchLeSubevents(empty_proto.Empty())) as le_event_stream, \
+            EventCallbackStream(self.device_under_test.hci.FetchEvents(empty_proto.Empty())) as event_stream, \
+            EventCallbackStream(self.device_under_test.hci.FetchAclPackets(empty_proto.Empty())) as acl_data_stream, \
+            EventCallbackStream(self.cert_device.hal.FetchHciEvent(empty_proto.Empty())) as cert_hci_event_stream, \
+            EventCallbackStream(self.cert_device.hal.FetchHciAcl(empty_proto.Empty())) as cert_acl_data_stream:
+
+            le_event_asserts = EventAsserts(le_event_stream)
+            event_asserts = EventAsserts(event_stream)
+            cert_hci_event_asserts = EventAsserts(cert_hci_event_stream)
+            acl_data_asserts = EventAsserts(acl_data_stream)
+            cert_acl_data_asserts = EventAsserts(cert_acl_data_stream)
+
+            # Cert Connects
+            self.send_hal_hci_command(
+                hci_packets.LeSetRandomAddressBuilder('0C:05:04:03:02:01'))
+            phy_scan_params = hci_packets.LeCreateConnPhyScanParameters()
+            phy_scan_params.scan_interval = 0x60
+            phy_scan_params.scan_window = 0x30
+            phy_scan_params.conn_interval_min = 0x18
+            phy_scan_params.conn_interval_max = 0x28
+            phy_scan_params.conn_latency = 0
+            phy_scan_params.supervision_timeout = 0x1f4
+            phy_scan_params.min_ce_length = 0
+            phy_scan_params.max_ce_length = 0
+            self.send_hal_hci_command(
+                hci_packets.LeExtendedCreateConnectionBuilder(
+                    hci_packets.InitiatorFilterPolicy.USE_PEER_ADDRESS,
+                    hci_packets.OwnAddressType.RANDOM_DEVICE_ADDRESS,
+                    hci_packets.AddressType.RANDOM_DEVICE_ADDRESS,
+                    '0D:05:04:03:02:01', 1, [phy_scan_params]))
+
+            # DUT Advertises
+            advertising_handle = 0
+            self.enqueue_hci_command(
+                hci_packets.LeSetExtendedAdvertisingLegacyParametersBuilder(
+                    advertising_handle,
+                    hci_packets.LegacyAdvertisingProperties.ADV_IND,
+                    400,
+                    450,
+                    7,
+                    hci_packets.OwnAddressType.RANDOM_DEVICE_ADDRESS,
+                    hci_packets.PeerAddressType.
+                    PUBLIC_DEVICE_OR_IDENTITY_ADDRESS,
+                    '00:00:00:00:00:00',
+                    hci_packets.AdvertisingFilterPolicy.ALL_DEVICES,
+                    0xF8,
+                    1,  #SID
+                    hci_packets.Enable.DISABLED  # Scan request notification
+                ),
+                True)
+
+            self.enqueue_hci_command(
+                hci_packets.LeSetExtendedAdvertisingRandomAddressBuilder(
+                    advertising_handle, '0D:05:04:03:02:01'), True)
+
+            gap_name = hci_packets.GapData()
+            gap_name.data_type = hci_packets.GapDataType.COMPLETE_LOCAL_NAME
+            gap_name.data = list(
+                bytes(b'Im_The_DUT!'))  # TODO: Fix and remove !
+
+            self.enqueue_hci_command(
+                hci_packets.LeSetExtendedAdvertisingDataBuilder(
+                    advertising_handle,
+                    hci_packets.Operation.COMPLETE_ADVERTISEMENT,
+                    hci_packets.FragmentPreference.CONTROLLER_SHOULD_NOT,
+                    [gap_name]), True)
+
+            gap_short_name = hci_packets.GapData()
+            gap_short_name.data_type = hci_packets.GapDataType.SHORTENED_LOCAL_NAME
+            gap_short_name.data = list(bytes(b'Im_The_D'))
+
+            self.enqueue_hci_command(
+                hci_packets.LeSetExtendedAdvertisingScanResponseBuilder(
+                    advertising_handle,
+                    hci_packets.Operation.COMPLETE_ADVERTISEMENT,
+                    hci_packets.FragmentPreference.CONTROLLER_SHOULD_NOT,
+                    [gap_short_name]), True)
+
+            enabled_set = hci_packets.EnabledSet()
+            enabled_set.advertising_handle = advertising_handle
+            enabled_set.duration = 0
+            enabled_set.max_extended_advertising_events = 0
+            self.enqueue_hci_command(
+                hci_packets.LeSetExtendedAdvertisingEnableBuilder(
+                    hci_packets.Enable.ENABLED, [enabled_set]), True)
+
+            # Check for success of Enable
+            event_asserts.assert_event_occurs(
+                lambda packet: b'\x0e\x04\x01\x39\x20\x00' in packet.event)
+
+            conn_handle = 0xfff
+
+            def event_handle(packet):
+                packet_bytes = packet.event
+                if b'\x3e\x13\x01\x00' in packet_bytes:
+                    nonlocal conn_handle
+                    cc_view = hci_packets.LeConnectionCompleteView(
+                        hci_packets.LeMetaEventView(
+                            hci_packets.EventPacketView(
+                                bt_packets.PacketViewLittleEndian(
+                                    list(packet_bytes)))))
+                    conn_handle = cc_view.GetConnectionHandle()
+                    return True
+                return False
+
+            def payload_handle(packet):
+                packet_bytes = packet.payload
+                if b'\x3e\x13\x01\x00' in packet_bytes:
+                    nonlocal conn_handle
+                    cc_view = hci_packets.LeConnectionCompleteView(
+                        hci_packets.LeMetaEventView(
+                            hci_packets.EventPacketView(
+                                bt_packets.PacketViewLittleEndian(
+                                    list(packet_bytes)))))
+                    conn_handle = cc_view.GetConnectionHandle()
+                    return True
+                return False
+
+            cert_hci_event_asserts.assert_event_occurs(payload_handle)
+            cert_handle = conn_handle
+            conn_handle = 0xfff
+            le_event_asserts.assert_event_occurs(event_handle)
+            dut_handle = conn_handle
+            if dut_handle == 0xfff:
+                logging.warning("Failed to get the DUT handle")
+                return False
+            if cert_handle == 0xfff:
+                logging.warning("Failed to get the CERT handle")
+                return False
+
+            # Send ACL Data
+            self.enqueue_acl_data(
+                dut_handle, hci_packets.PacketBoundaryFlag.
+                FIRST_NON_AUTOMATICALLY_FLUSHABLE,
+                hci_packets.BroadcastFlag.POINT_TO_POINT,
+                bytes(b'Just SomeAclData'))
+            self.send_hal_acl_data(
+                cert_handle, hci_packets.PacketBoundaryFlag.
+                FIRST_NON_AUTOMATICALLY_FLUSHABLE,
+                hci_packets.BroadcastFlag.POINT_TO_POINT,
+                bytes(b'Just SomeMoreAclData'))
+
+            cert_acl_data_asserts.assert_event_occurs(
+                lambda packet: logging.debug(packet.payload) or b'SomeAclData' in packet.payload
+            )
+            acl_data_asserts.assert_event_occurs(
+                lambda packet: logging.debug(packet.data) or b'SomeMoreAclData' in packet.data
+            )
+
+    def test_le_white_list_connection_cert_advertises(self):
+        self.register_for_le_event(hci_packets.SubeventCode.CONNECTION_COMPLETE)
+        with EventCallbackStream(self.device_under_test.hci.FetchLeSubevents(empty_proto.Empty())) as le_event_stream, \
+                EventCallbackStream(self.cert_device.hal.FetchHciEvent(empty_proto.Empty())) as cert_hci_event_stream:
+            le_event_asserts = EventAsserts(le_event_stream)
+            cert_hci_event_asserts = EventAsserts(cert_hci_event_stream)
+
+            # DUT Connects
+            self.enqueue_hci_command(
+                hci_packets.LeSetRandomAddressBuilder('0D:05:04:03:02:01'),
+                True)
+            self.enqueue_hci_command(
+                hci_packets.LeAddDeviceToWhiteListBuilder(
+                    hci_packets.WhiteListAddressType.RANDOM,
+                    '0C:05:04:03:02:01'), True)
+            phy_scan_params = hci_packets.LeCreateConnPhyScanParameters()
+            phy_scan_params.scan_interval = 0x60
+            phy_scan_params.scan_window = 0x30
+            phy_scan_params.conn_interval_min = 0x18
+            phy_scan_params.conn_interval_max = 0x28
+            phy_scan_params.conn_latency = 0
+            phy_scan_params.supervision_timeout = 0x1f4
+            phy_scan_params.min_ce_length = 0
+            phy_scan_params.max_ce_length = 0
+            self.enqueue_hci_command(
+                hci_packets.LeExtendedCreateConnectionBuilder(
+                    hci_packets.InitiatorFilterPolicy.USE_WHITE_LIST,
+                    hci_packets.OwnAddressType.RANDOM_DEVICE_ADDRESS,
+                    hci_packets.AddressType.RANDOM_DEVICE_ADDRESS,
+                    'BA:D5:A4:A3:A2:A1', 1, [phy_scan_params]), False)
+
+            # CERT Advertises
+            advertising_handle = 1
+            self.send_hal_hci_command(
+                hci_packets.LeSetExtendedAdvertisingLegacyParametersBuilder(
+                    advertising_handle,
+                    hci_packets.LegacyAdvertisingProperties.ADV_IND,
+                    512,
+                    768,
+                    7,
+                    hci_packets.OwnAddressType.RANDOM_DEVICE_ADDRESS,
+                    hci_packets.PeerAddressType.
+                    PUBLIC_DEVICE_OR_IDENTITY_ADDRESS,
+                    'A6:A5:A4:A3:A2:A1',
+                    hci_packets.AdvertisingFilterPolicy.ALL_DEVICES,
+                    0x7F,
+                    0,  # SID
+                    hci_packets.Enable.DISABLED  # Scan request notification
+                ))
+
+            self.send_hal_hci_command(
+                hci_packets.LeSetExtendedAdvertisingRandomAddressBuilder(
+                    advertising_handle, '0C:05:04:03:02:01'))
+
+            gap_name = hci_packets.GapData()
+            gap_name.data_type = hci_packets.GapDataType.COMPLETE_LOCAL_NAME
+            gap_name.data = list(bytes(b'Im_A_Cert!'))  # TODO: Fix and remove !
+
+            self.send_hal_hci_command(
+                hci_packets.LeSetExtendedAdvertisingDataBuilder(
+                    advertising_handle,
+                    hci_packets.Operation.COMPLETE_ADVERTISEMENT,
+                    hci_packets.FragmentPreference.CONTROLLER_SHOULD_NOT,
+                    [gap_name]))
+            enabled_set = hci_packets.EnabledSet()
+            enabled_set.advertising_handle = 1
+            enabled_set.duration = 0
+            enabled_set.max_extended_advertising_events = 0
+            self.send_hal_hci_command(
+                hci_packets.LeSetExtendedAdvertisingEnableBuilder(
+                    hci_packets.Enable.ENABLED, [enabled_set]))
+
+            # LeConnectionComplete
+            cert_hci_event_asserts.assert_event_occurs(
+                lambda packet: b'\x3e\x13\x01\x00' in packet.payload,
+                timeout=timedelta(seconds=20))
+            le_event_asserts.assert_event_occurs(
+                lambda packet: b'\x3e\x13\x01\x00' in packet.event)
+
+    def test_connection_dut_connects(self):
+        self.register_for_event(hci_packets.EventCode.CONNECTION_COMPLETE)
+        self.register_for_event(
+            hci_packets.EventCode.CONNECTION_PACKET_TYPE_CHANGED)
+        self.enqueue_hci_command(
+            hci_packets.WritePageTimeoutBuilder(0x4000), True)
+        with EventCallbackStream(self.device_under_test.hci.FetchEvents(empty_proto.Empty())) as hci_event_stream, \
+            EventCallbackStream(self.device_under_test.hci.FetchAclPackets(empty_proto.Empty())) as acl_data_stream, \
+            EventCallbackStream(self.cert_device.hal.FetchHciEvent(empty_proto.Empty())) as cert_hci_event_stream, \
+            EventCallbackStream(self.cert_device.hal.FetchHciAcl(empty_proto.Empty())) as cert_acl_data_stream:
+
+            cert_hci_event_asserts = EventAsserts(cert_hci_event_stream)
+            hci_event_asserts = EventAsserts(hci_event_stream)
+            acl_data_asserts = EventAsserts(acl_data_stream)
+            cert_acl_data_asserts = EventAsserts(cert_acl_data_stream)
+
+            address = hci_packets.Address()
+
+            def get_address_from_complete(packet):
+                packet_bytes = packet.payload
+                if b'\x0e\x0a\x01\x09\x10' in packet_bytes:
+                    nonlocal address
+                    addr_view = hci_packets.ReadBdAddrCompleteView(
+                        hci_packets.CommandCompleteView(
+                            hci_packets.EventPacketView(
+                                bt_packets.PacketViewLittleEndian(
+                                    list(packet_bytes)))))
+                    address = addr_view.GetBdAddr()
+                    return True
+                return False
+
+            # CERT Enables scans and gets its address
+            self.send_hal_hci_command(hci_packets.ReadBdAddrBuilder())
+
+            cert_hci_event_asserts.assert_event_occurs(
+                get_address_from_complete)
+            self.send_hal_hci_command(
+                hci_packets.WriteScanEnableBuilder(
+                    hci_packets.ScanEnable.INQUIRY_AND_PAGE_SCAN))
+
+            # DUT Connects
+            self.enqueue_hci_command(
+                hci_packets.CreateConnectionBuilder(
+                    address,
+                    0xcc18,  # Packet Type
+                    hci_packets.PageScanRepetitionMode.R0,
+                    0,
+                    hci_packets.ClockOffsetValid.INVALID,
+                    hci_packets.CreateConnectionRoleSwitch.ALLOW_ROLE_SWITCH),
+                False)
+
+            # Cert Accepts
+            connection_request = None
+
+            def get_connect_request(packet):
+                if b'\x04\x0a' in packet.payload:
+                    nonlocal connection_request
+                    connection_request = hci_packets.ConnectionRequestView(
+                        hci_packets.EventPacketView(
+                            bt_packets.PacketViewLittleEndian(
+                                list(packet.payload))))
+                    return True
+                return False
+
+            # Cert Accepts
+            cert_hci_event_asserts.assert_event_occurs(
+                get_connect_request, timeout=timedelta(seconds=20))
+            self.send_hal_hci_command(
+                hci_packets.AcceptConnectionRequestBuilder(
+                    connection_request.GetBdAddr(),
+                    hci_packets.AcceptConnectionRequestRole.REMAIN_SLAVE))
+
+            conn_handle = 0xfff
+
+            def get_handle(packet_bytes):
+                if b'\x03\x0b\x00' in packet_bytes:
+                    nonlocal conn_handle
+                    cc_view = hci_packets.ConnectionCompleteView(
+                        hci_packets.EventPacketView(
+                            bt_packets.PacketViewLittleEndian(
+                                list(packet_bytes))))
+                    conn_handle = cc_view.GetConnectionHandle()
+                    return True
+                return False
+
+            def event_handle(packet):
+                packet_bytes = packet.event
+                return get_handle(packet_bytes)
+
+            def payload_handle(packet):
+                packet_bytes = packet.payload
+                return get_handle(packet_bytes)
+
+            cert_hci_event_asserts.assert_event_occurs(payload_handle)
+            cert_handle = conn_handle
+            conn_handle = 0xfff
+            hci_event_asserts.assert_event_occurs(event_handle)
+            dut_handle = conn_handle
+            if dut_handle == 0xfff:
+                logging.warning("Failed to get the DUT handle")
+                return False
+            if cert_handle == 0xfff:
+                logging.warning("Failed to get the CERT handle")
+                return False
+
+            # Send ACL Data
+            self.enqueue_acl_data(
+                dut_handle, hci_packets.PacketBoundaryFlag.
+                FIRST_NON_AUTOMATICALLY_FLUSHABLE,
+                hci_packets.BroadcastFlag.POINT_TO_POINT,
+                bytes(b'Just SomeAclData'))
+            self.send_hal_acl_data(
+                cert_handle, hci_packets.PacketBoundaryFlag.
+                FIRST_NON_AUTOMATICALLY_FLUSHABLE,
+                hci_packets.BroadcastFlag.POINT_TO_POINT,
+                bytes(b'Just SomeMoreAclData'))
+
+            cert_acl_data_asserts.assert_event_occurs(
+                lambda packet: b'SomeAclData' in packet.payload)
+            acl_data_asserts.assert_event_occurs(
+                lambda packet: b'SomeMoreAclData' in packet.data)
+
+    def test_connection_cert_connects(self):
+        self.register_for_event(hci_packets.EventCode.CONNECTION_COMPLETE)
+        self.register_for_event(
+            hci_packets.EventCode.CONNECTION_PACKET_TYPE_CHANGED)
+        self.register_for_event(hci_packets.EventCode.CONNECTION_REQUEST)
+        self.send_hal_hci_command(hci_packets.WritePageTimeoutBuilder(0x4000))
+        with EventCallbackStream(self.device_under_test.hci.FetchEvents(empty_proto.Empty())) as hci_event_stream, \
+            EventCallbackStream(self.device_under_test.hci.FetchAclPackets(empty_proto.Empty())) as acl_data_stream, \
+            EventCallbackStream(self.cert_device.hal.FetchHciEvent(empty_proto.Empty())) as cert_hci_event_stream, \
+            EventCallbackStream(self.cert_device.hal.FetchHciAcl(empty_proto.Empty())) as cert_acl_data_stream:
+
+            hci_event_asserts = EventAsserts(hci_event_stream)
+            cert_hci_event_asserts = EventAsserts(cert_hci_event_stream)
+            acl_data_asserts = EventAsserts(acl_data_stream)
+            cert_acl_data_asserts = EventAsserts(cert_acl_data_stream)
+
+            address = hci_packets.Address()
+
+            def get_address_from_complete(packet):
+                packet_bytes = packet.event
+                if b'\x0e\x0a\x01\x09\x10' in packet_bytes:
+                    nonlocal address
+                    addr_view = hci_packets.ReadBdAddrCompleteView(
+                        hci_packets.CommandCompleteView(
+                            hci_packets.EventPacketView(
+                                bt_packets.PacketViewLittleEndian(
+                                    list(packet_bytes)))))
+                    address = addr_view.GetBdAddr()
+                    return True
+                return False
+
+            # DUT Enables scans and gets its address
+            self.enqueue_hci_command(
+                hci_packets.WriteScanEnableBuilder(
+                    hci_packets.ScanEnable.INQUIRY_AND_PAGE_SCAN), True)
+            self.enqueue_hci_command(hci_packets.ReadBdAddrBuilder(), True)
+
+            hci_event_asserts.assert_event_occurs(get_address_from_complete)
+
+            # Cert Connects
+            self.send_hal_hci_command(
+                hci_packets.CreateConnectionBuilder(
+                    address,
+                    0xcc18,  # Packet Type
+                    hci_packets.PageScanRepetitionMode.R0,
+                    0,
+                    hci_packets.ClockOffsetValid.INVALID,
+                    hci_packets.CreateConnectionRoleSwitch.ALLOW_ROLE_SWITCH))
+
+            # DUT Accepts
+            connection_request = None
+
+            def get_connect_request(packet):
+                if b'\x04\x0a' in packet.event:
+                    nonlocal connection_request
+                    connection_request = hci_packets.ConnectionRequestView(
+                        hci_packets.EventPacketView(
+                            bt_packets.PacketViewLittleEndian(
+                                list(packet.event))))
+                    return True
+                return False
+
+            hci_event_asserts.assert_event_occurs(
+                get_connect_request, timeout=timedelta(seconds=20))
+            self.enqueue_hci_command(
+                hci_packets.AcceptConnectionRequestBuilder(
+                    connection_request.GetBdAddr(),
+                    hci_packets.AcceptConnectionRequestRole.REMAIN_SLAVE),
+                False)
+
+            conn_handle = 0xfff
+
+            def get_handle(packet_bytes):
+                if b'\x03\x0b\x00' in packet_bytes:
+                    nonlocal conn_handle
+                    cc_view = hci_packets.ConnectionCompleteView(
+                        hci_packets.EventPacketView(
+                            bt_packets.PacketViewLittleEndian(
+                                list(packet_bytes))))
+                    conn_handle = cc_view.GetConnectionHandle()
+                    return True
+                return False
+
+            def event_handle(packet):
+                packet_bytes = packet.event
+                return get_handle(packet_bytes)
+
+            def payload_handle(packet):
+                packet_bytes = packet.payload
+                return get_handle(packet_bytes)
+
+            cert_hci_event_asserts.assert_event_occurs(payload_handle)
+            cert_handle = conn_handle
+            conn_handle = 0xfff
+            hci_event_asserts.assert_event_occurs(event_handle)
+            dut_handle = conn_handle
+            if dut_handle == 0xfff:
+                logging.warning("Failed to get the DUT handle")
+                return False
+            if cert_handle == 0xfff:
+                logging.warning("Failed to get the CERT handle")
+                return False
+
+            # Send ACL Data
+            self.enqueue_acl_data(
+                dut_handle, hci_packets.PacketBoundaryFlag.
+                FIRST_NON_AUTOMATICALLY_FLUSHABLE,
+                hci_packets.BroadcastFlag.POINT_TO_POINT,
+                bytes(b'This is just SomeAclData'))
+            self.send_hal_acl_data(
+                cert_handle, hci_packets.PacketBoundaryFlag.
+                FIRST_NON_AUTOMATICALLY_FLUSHABLE,
+                hci_packets.BroadcastFlag.POINT_TO_POINT,
+                bytes(b'This is just SomeMoreAclData'))
+
+            cert_acl_data_asserts.assert_event_occurs(
+                lambda packet: b'SomeAclData' in packet.payload)
+            acl_data_asserts.assert_event_occurs(
+                lambda packet: b'SomeMoreAclData' in packet.data)
diff --git a/gd/hci/cert/le_acl_manager_test.py b/gd/hci/cert/le_acl_manager_test.py
new file mode 100644
index 0000000..1abf0a2
--- /dev/null
+++ b/gd/hci/cert/le_acl_manager_test.py
@@ -0,0 +1,419 @@
+#!/usr/bin/env python3
+#
+#   Copyright 2020 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+from cert.gd_base_test_facade_only import GdFacadeOnlyBaseTestClass
+from cert.event_callback_stream import EventCallbackStream
+from cert.event_asserts import EventAsserts
+from google.protobuf import empty_pb2 as empty_proto
+from facade import rootservice_pb2 as facade_rootservice
+from facade import common_pb2 as common
+from hci.facade import le_acl_manager_facade_pb2 as le_acl_manager_facade
+from hci.facade import le_advertising_manager_facade_pb2 as le_advertising_facade
+from hci.facade import facade_pb2 as hci_facade
+import bluetooth_packets_python3 as bt_packets
+from bluetooth_packets_python3 import hci_packets
+
+
+class LeAclManagerTest(GdFacadeOnlyBaseTestClass):
+
+    def setup_test(self):
+        self.device_under_test.rootservice.StartStack(
+            facade_rootservice.StartStackRequest(
+                module_under_test=facade_rootservice.BluetoothModule.Value(
+                    'HCI_INTERFACES'),))
+        self.cert_device.rootservice.StartStack(
+            facade_rootservice.StartStackRequest(
+                module_under_test=facade_rootservice.BluetoothModule.Value(
+                    'HCI'),))
+
+        self.device_under_test.wait_channel_ready()
+        self.cert_device.wait_channel_ready()
+
+    def teardown_test(self):
+        self.device_under_test.rootservice.StopStack(
+            facade_rootservice.StopStackRequest())
+        self.cert_device.rootservice.StopStack(
+            facade_rootservice.StopStackRequest())
+
+    def register_for_event(self, event_code):
+        msg = hci_facade.EventCodeMsg(code=int(event_code))
+        self.cert_device.hci.RegisterEventHandler(msg)
+
+    def register_for_le_event(self, event_code):
+        msg = hci_facade.LeSubeventCodeMsg(code=int(event_code))
+        self.cert_device.hci.RegisterLeEventHandler(msg)
+
+    def enqueue_hci_command(self, command, expect_complete):
+        cmd_bytes = bytes(command.Serialize())
+        cmd = hci_facade.CommandMsg(command=cmd_bytes)
+        if (expect_complete):
+            self.cert_device.hci.EnqueueCommandWithComplete(cmd)
+        else:
+            self.cert_device.hci.EnqueueCommandWithStatus(cmd)
+
+    def enqueue_acl_data(self, handle, pb_flag, b_flag, acl):
+        acl_msg = hci_facade.AclMsg(
+            handle=int(handle),
+            packet_boundary_flag=int(pb_flag),
+            broadcast_flag=int(b_flag),
+            data=acl)
+        self.cert_device.hci.SendAclData(acl_msg)
+
+    def test_dut_connects(self):
+        self.register_for_le_event(hci_packets.SubeventCode.CONNECTION_COMPLETE)
+        with EventCallbackStream(self.cert_device.hci.FetchLeSubevents(empty_proto.Empty())) as cert_hci_le_event_stream, \
+            EventCallbackStream(self.cert_device.hci.FetchAclPackets(empty_proto.Empty())) as cert_acl_data_stream, \
+            EventCallbackStream(self.device_under_test.hci_le_acl_manager.FetchAclData(empty_proto.Empty())) as acl_data_stream:
+
+            cert_hci_le_event_asserts = EventAsserts(cert_hci_le_event_stream)
+            acl_data_asserts = EventAsserts(acl_data_stream)
+            cert_acl_data_asserts = EventAsserts(cert_acl_data_stream)
+
+            # Cert Advertises
+            advertising_handle = 0
+            self.enqueue_hci_command(
+                hci_packets.LeSetExtendedAdvertisingLegacyParametersBuilder(
+                    advertising_handle,
+                    hci_packets.LegacyAdvertisingProperties.ADV_IND,
+                    400,
+                    450,
+                    7,
+                    hci_packets.OwnAddressType.RANDOM_DEVICE_ADDRESS,
+                    hci_packets.PeerAddressType.
+                    PUBLIC_DEVICE_OR_IDENTITY_ADDRESS,
+                    '00:00:00:00:00:00',
+                    hci_packets.AdvertisingFilterPolicy.ALL_DEVICES,
+                    0xF8,
+                    1,  #SID
+                    hci_packets.Enable.DISABLED  # Scan request notification
+                ),
+                True)
+
+            self.enqueue_hci_command(
+                hci_packets.LeSetExtendedAdvertisingRandomAddressBuilder(
+                    advertising_handle, '0C:05:04:03:02:01'), True)
+
+            gap_name = hci_packets.GapData()
+            gap_name.data_type = hci_packets.GapDataType.COMPLETE_LOCAL_NAME
+            gap_name.data = list(bytes(b'Im_A_Cert'))
+
+            self.enqueue_hci_command(
+                hci_packets.LeSetExtendedAdvertisingDataBuilder(
+                    advertising_handle,
+                    hci_packets.Operation.COMPLETE_ADVERTISEMENT,
+                    hci_packets.FragmentPreference.CONTROLLER_SHOULD_NOT,
+                    [gap_name]), True)
+
+            gap_short_name = hci_packets.GapData()
+            gap_short_name.data_type = hci_packets.GapDataType.SHORTENED_LOCAL_NAME
+            gap_short_name.data = list(bytes(b'Im_A_C'))
+
+            self.enqueue_hci_command(
+                hci_packets.LeSetExtendedAdvertisingScanResponseBuilder(
+                    advertising_handle,
+                    hci_packets.Operation.COMPLETE_ADVERTISEMENT,
+                    hci_packets.FragmentPreference.CONTROLLER_SHOULD_NOT,
+                    [gap_short_name]), True)
+
+            enabled_set = hci_packets.EnabledSet()
+            enabled_set.advertising_handle = advertising_handle
+            enabled_set.duration = 0
+            enabled_set.max_extended_advertising_events = 0
+            self.enqueue_hci_command(
+                hci_packets.LeSetExtendedAdvertisingEnableBuilder(
+                    hci_packets.Enable.ENABLED, [enabled_set]), True)
+
+            with EventCallbackStream(
+                    self.device_under_test.hci_le_acl_manager.CreateConnection(
+                        le_acl_manager_facade.LeConnectionMsg(
+                            address_type=int(
+                                hci_packets.AddressType.RANDOM_DEVICE_ADDRESS),
+                            address=bytes('0C:05:04:03:02:01',
+                                          'utf8')))) as connection_event_stream:
+
+                connection_event_asserts = EventAsserts(connection_event_stream)
+
+                # Cert gets ConnectionComplete with a handle and sends ACL data
+                handle = 0xfff
+
+                def get_handle(packet):
+                    packet_bytes = packet.event
+                    nonlocal handle
+                    if b'\x3e\x13\x01\x00' in packet_bytes:
+                        cc_view = hci_packets.LeConnectionCompleteView(
+                            hci_packets.LeMetaEventView(
+                                hci_packets.EventPacketView(
+                                    bt_packets.PacketViewLittleEndian(
+                                        list(packet_bytes)))))
+                        handle = cc_view.GetConnectionHandle()
+                        return True
+                    if b'\x3e\x13\x0A\x00' in packet_bytes:
+                        cc_view = hci_packets.LeEnhancedConnectionCompleteView(
+                            hci_packets.LeMetaEventView(
+                                hci_packets.EventPacketView(
+                                    bt_packets.PacketViewLittleEndian(
+                                        list(packet_bytes)))))
+                        handle = cc_view.GetConnectionHandle()
+                        return True
+                    return False
+
+                cert_hci_le_event_asserts.assert_event_occurs(get_handle)
+                cert_handle = handle
+
+                self.enqueue_acl_data(
+                    cert_handle, hci_packets.PacketBoundaryFlag.
+                    FIRST_AUTOMATICALLY_FLUSHABLE,
+                    hci_packets.BroadcastFlag.POINT_TO_POINT,
+                    bytes(b'\x19\x00\x07\x00SomeAclData from the Cert'))
+
+                # DUT gets a connection complete event and sends and receives
+                handle = 0xfff
+                connection_event_asserts.assert_event_occurs(get_handle)
+
+                self.device_under_test.hci_le_acl_manager.SendAclData(
+                    le_acl_manager_facade.LeAclData(
+                        handle=handle,
+                        payload=bytes(
+                            b'\x1C\x00\x07\x00SomeMoreAclData from the DUT')))
+
+                cert_acl_data_asserts.assert_event_occurs(
+                    lambda packet: b'SomeMoreAclData' in packet.data)
+                acl_data_asserts.assert_event_occurs(
+                    lambda packet: b'SomeAclData' in packet.payload)
+
+    def test_cert_connects(self):
+        self.register_for_le_event(hci_packets.SubeventCode.CONNECTION_COMPLETE)
+        with EventCallbackStream(self.cert_device.hci.FetchLeSubevents(empty_proto.Empty())) as cert_hci_le_event_stream, \
+                EventCallbackStream(self.cert_device.hci.FetchAclPackets(empty_proto.Empty())) as cert_acl_data_stream, \
+                EventCallbackStream(self.device_under_test.hci_le_acl_manager.FetchIncomingConnection(empty_proto.Empty())) as incoming_connection_stream, \
+                EventCallbackStream(self.device_under_test.hci_le_acl_manager.FetchAclData(empty_proto.Empty())) as acl_data_stream:
+
+            cert_hci_le_event_asserts = EventAsserts(cert_hci_le_event_stream)
+            incoming_connection_asserts = EventAsserts(
+                incoming_connection_stream)
+            acl_data_asserts = EventAsserts(acl_data_stream)
+            cert_acl_data_asserts = EventAsserts(cert_acl_data_stream)
+
+            # DUT Advertises
+            gap_name = hci_packets.GapData()
+            gap_name.data_type = hci_packets.GapDataType.COMPLETE_LOCAL_NAME
+            gap_name.data = list(bytes(b'Im_The_DUT'))
+            gap_data = le_advertising_facade.GapDataMsg(
+                data=bytes(gap_name.Serialize()))
+            config = le_advertising_facade.AdvertisingConfig(
+                advertisement=[gap_data],
+                random_address=common.BluetoothAddress(
+                    address=bytes(b'0D:05:04:03:02:01')),
+                interval_min=512,
+                interval_max=768,
+                event_type=le_advertising_facade.AdvertisingEventType.ADV_IND,
+                address_type=common.RANDOM_DEVICE_ADDRESS,
+                peer_address_type=common.PUBLIC_DEVICE_OR_IDENTITY_ADDRESS,
+                peer_address=common.BluetoothAddress(
+                    address=bytes(b'A6:A5:A4:A3:A2:A1')),
+                channel_map=7,
+                filter_policy=le_advertising_facade.AdvertisingFilterPolicy.
+                ALL_DEVICES)
+            request = le_advertising_facade.CreateAdvertiserRequest(
+                config=config)
+
+            create_response = self.device_under_test.hci_le_advertising_manager.CreateAdvertiser(
+                request)
+
+            # Cert Connects
+            self.enqueue_hci_command(
+                hci_packets.LeSetRandomAddressBuilder('0C:05:04:03:02:01'),
+                True)
+            phy_scan_params = hci_packets.LeCreateConnPhyScanParameters()
+            phy_scan_params.scan_interval = 0x60
+            phy_scan_params.scan_window = 0x30
+            phy_scan_params.conn_interval_min = 0x18
+            phy_scan_params.conn_interval_max = 0x28
+            phy_scan_params.conn_latency = 0
+            phy_scan_params.supervision_timeout = 0x1f4
+            phy_scan_params.min_ce_length = 0
+            phy_scan_params.max_ce_length = 0
+            self.enqueue_hci_command(
+                hci_packets.LeExtendedCreateConnectionBuilder(
+                    hci_packets.InitiatorFilterPolicy.USE_PEER_ADDRESS,
+                    hci_packets.OwnAddressType.RANDOM_DEVICE_ADDRESS,
+                    hci_packets.AddressType.RANDOM_DEVICE_ADDRESS,
+                    '0D:05:04:03:02:01', 1, [phy_scan_params]), False)
+
+            # Cert gets ConnectionComplete with a handle and sends ACL data
+            handle = 0xfff
+
+            def get_handle(packet):
+                packet_bytes = packet.event
+                nonlocal handle
+                if b'\x3e\x13\x01\x00' in packet_bytes:
+                    cc_view = hci_packets.LeConnectionCompleteView(
+                        hci_packets.LeMetaEventView(
+                            hci_packets.EventPacketView(
+                                bt_packets.PacketViewLittleEndian(
+                                    list(packet_bytes)))))
+                    handle = cc_view.GetConnectionHandle()
+                    return True
+                if b'\x3e\x13\x0A\x00' in packet_bytes:
+                    cc_view = hci_packets.LeEnhancedConnectionCompleteView(
+                        hci_packets.LeMetaEventView(
+                            hci_packets.EventPacketView(
+                                bt_packets.PacketViewLittleEndian(
+                                    list(packet_bytes)))))
+                    handle = cc_view.GetConnectionHandle()
+                    return True
+                return False
+
+            cert_hci_le_event_asserts.assert_event_occurs(get_handle)
+            cert_handle = handle
+
+            self.enqueue_acl_data(
+                cert_handle,
+                hci_packets.PacketBoundaryFlag.FIRST_AUTOMATICALLY_FLUSHABLE,
+                hci_packets.BroadcastFlag.POINT_TO_POINT,
+                bytes(b'\x19\x00\x07\x00SomeAclData from the Cert'))
+
+            # DUT gets a connection complete event and sends and receives
+            handle = 0xfff
+            incoming_connection_asserts.assert_event_occurs(get_handle)
+
+            self.device_under_test.hci_le_acl_manager.SendAclData(
+                le_acl_manager_facade.LeAclData(
+                    handle=handle,
+                    payload=bytes(
+                        b'\x1C\x00\x07\x00SomeMoreAclData from the DUT')))
+
+            cert_acl_data_asserts.assert_event_occurs(
+                lambda packet: b'SomeMoreAclData' in packet.data)
+            acl_data_asserts.assert_event_occurs(
+                lambda packet: b'SomeAclData' in packet.payload)
+
+    def test_recombination_l2cap_packet(self):
+        self.register_for_le_event(hci_packets.SubeventCode.CONNECTION_COMPLETE)
+        with EventCallbackStream(self.cert_device.hci.FetchLeSubevents(empty_proto.Empty())) as cert_hci_le_event_stream, \
+                EventCallbackStream(self.cert_device.hci.FetchAclPackets(empty_proto.Empty())) as cert_acl_data_stream, \
+                EventCallbackStream(self.device_under_test.hci_le_acl_manager.FetchAclData(empty_proto.Empty())) as acl_data_stream:
+
+            cert_hci_le_event_asserts = EventAsserts(cert_hci_le_event_stream)
+            acl_data_asserts = EventAsserts(acl_data_stream)
+            cert_acl_data_asserts = EventAsserts(cert_acl_data_stream)
+
+            # Cert Advertises
+            advertising_handle = 0
+            self.enqueue_hci_command(
+                hci_packets.LeSetExtendedAdvertisingLegacyParametersBuilder(
+                    advertising_handle,
+                    hci_packets.LegacyAdvertisingProperties.ADV_IND,
+                    400,
+                    450,
+                    7,
+                    hci_packets.OwnAddressType.RANDOM_DEVICE_ADDRESS,
+                    hci_packets.PeerAddressType.
+                    PUBLIC_DEVICE_OR_IDENTITY_ADDRESS,
+                    '00:00:00:00:00:00',
+                    hci_packets.AdvertisingFilterPolicy.ALL_DEVICES,
+                    0xF8,
+                    1,  #SID
+                    hci_packets.Enable.DISABLED  # Scan request notification
+                ),
+                True)
+
+            self.enqueue_hci_command(
+                hci_packets.LeSetExtendedAdvertisingRandomAddressBuilder(
+                    advertising_handle, '0C:05:04:03:02:01'), True)
+
+            gap_name = hci_packets.GapData()
+            gap_name.data_type = hci_packets.GapDataType.COMPLETE_LOCAL_NAME
+            gap_name.data = list(bytes(b'Im_A_Cert'))
+
+            self.enqueue_hci_command(
+                hci_packets.LeSetExtendedAdvertisingDataBuilder(
+                    advertising_handle,
+                    hci_packets.Operation.COMPLETE_ADVERTISEMENT,
+                    hci_packets.FragmentPreference.CONTROLLER_SHOULD_NOT,
+                    [gap_name]), True)
+
+            gap_short_name = hci_packets.GapData()
+            gap_short_name.data_type = hci_packets.GapDataType.SHORTENED_LOCAL_NAME
+            gap_short_name.data = list(bytes(b'Im_A_C'))
+
+            self.enqueue_hci_command(
+                hci_packets.LeSetExtendedAdvertisingScanResponseBuilder(
+                    advertising_handle,
+                    hci_packets.Operation.COMPLETE_ADVERTISEMENT,
+                    hci_packets.FragmentPreference.CONTROLLER_SHOULD_NOT,
+                    [gap_short_name]), True)
+
+            enabled_set = hci_packets.EnabledSet()
+            enabled_set.advertising_handle = advertising_handle
+            enabled_set.duration = 0
+            enabled_set.max_extended_advertising_events = 0
+            self.enqueue_hci_command(
+                hci_packets.LeSetExtendedAdvertisingEnableBuilder(
+                    hci_packets.Enable.ENABLED, [enabled_set]), True)
+
+            with EventCallbackStream(
+                    self.device_under_test.hci_le_acl_manager.CreateConnection(
+                        le_acl_manager_facade.LeConnectionMsg(
+                            address_type=int(
+                                hci_packets.AddressType.RANDOM_DEVICE_ADDRESS),
+                            address=bytes('0C:05:04:03:02:01',
+                                          'utf8')))) as connection_event_stream:
+
+                connection_event_asserts = EventAsserts(connection_event_stream)
+
+                # Cert gets ConnectionComplete with a handle and sends ACL data
+                handle = 0xfff
+
+                def get_handle(packet):
+                    packet_bytes = packet.event
+                    nonlocal handle
+                    if b'\x3e\x13\x01\x00' in packet_bytes:
+                        cc_view = hci_packets.LeConnectionCompleteView(
+                            hci_packets.LeMetaEventView(
+                                hci_packets.EventPacketView(
+                                    bt_packets.PacketViewLittleEndian(
+                                        list(packet_bytes)))))
+                        handle = cc_view.GetConnectionHandle()
+                        return True
+                    if b'\x3e\x13\x0A\x00' in packet_bytes:
+                        cc_view = hci_packets.LeEnhancedConnectionCompleteView(
+                            hci_packets.LeMetaEventView(
+                                hci_packets.EventPacketView(
+                                    bt_packets.PacketViewLittleEndian(
+                                        list(packet_bytes)))))
+                        handle = cc_view.GetConnectionHandle()
+                        return True
+                    return False
+
+                cert_hci_le_event_asserts.assert_event_occurs(get_handle)
+                cert_handle = handle
+
+                # DUT gets a connection complete event
+                connection_event_asserts.assert_event_occurs(get_handle)
+
+                self.enqueue_acl_data(
+                    cert_handle, hci_packets.PacketBoundaryFlag.
+                    FIRST_AUTOMATICALLY_FLUSHABLE,
+                    hci_packets.BroadcastFlag.POINT_TO_POINT,
+                    bytes(b'\x06\x00\x07\x00Hello'))
+                self.enqueue_acl_data(
+                    cert_handle,
+                    hci_packets.PacketBoundaryFlag.CONTINUING_FRAGMENT,
+                    hci_packets.BroadcastFlag.POINT_TO_POINT, bytes(b'!'))
+
+                acl_data_asserts.assert_event_occurs(
+                    lambda packet: b'Hello!' in packet.payload)
diff --git a/gd/hci/cert/le_advertising_manager_test.py b/gd/hci/cert/le_advertising_manager_test.py
new file mode 100644
index 0000000..7d163bf
--- /dev/null
+++ b/gd/hci/cert/le_advertising_manager_test.py
@@ -0,0 +1,134 @@
+#!/usr/bin/env python3
+#
+#   Copyright 2019 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import os
+import sys
+import logging
+
+from cert.gd_base_test_facade_only import GdFacadeOnlyBaseTestClass
+from cert.event_callback_stream import EventCallbackStream
+from cert.event_asserts import EventAsserts
+from google.protobuf import empty_pb2 as empty_proto
+from facade import rootservice_pb2 as facade_rootservice
+from hci.facade import facade_pb2 as hci_facade
+from hci.facade import \
+  le_advertising_manager_facade_pb2 as le_advertising_facade
+from bluetooth_packets_python3 import hci_packets
+from facade import common_pb2 as common
+
+
+class LeAdvertisingManagerTest(GdFacadeOnlyBaseTestClass):
+
+    def setup_test(self):
+        self.device_under_test.rootservice.StartStack(
+            facade_rootservice.StartStackRequest(
+                module_under_test=facade_rootservice.BluetoothModule.Value(
+                    'HCI_INTERFACES'),))
+        self.cert_device.rootservice.StartStack(
+            facade_rootservice.StartStackRequest(
+                module_under_test=facade_rootservice.BluetoothModule.Value(
+                    'HCI'),))
+
+        self.device_under_test.wait_channel_ready()
+        self.cert_device.wait_channel_ready()
+
+    def teardown_test(self):
+        self.device_under_test.rootservice.StopStack(
+            facade_rootservice.StopStackRequest())
+        self.cert_device.rootservice.StopStack(
+            facade_rootservice.StopStackRequest())
+
+    def register_for_event(self, event_code):
+        msg = hci_facade.EventCodeMsg(code=int(event_code))
+        self.cert_device.hci.RegisterEventHandler(msg)
+
+    def register_for_le_event(self, event_code):
+        msg = hci_facade.LeSubeventCodeMsg(code=int(event_code))
+        self.cert_device.hci.RegisterLeEventHandler(msg)
+
+    def enqueue_hci_command(self, command, expect_complete):
+        cmd_bytes = bytes(command.Serialize())
+        cmd = hci_facade.CommandMsg(command=cmd_bytes)
+        if (expect_complete):
+            self.cert_device.hci.EnqueueCommandWithComplete(cmd)
+        else:
+            self.cert_device.hci.EnqueueCommandWithStatus(cmd)
+
+    def test_le_ad_scan_dut_advertises(self):
+        self.register_for_le_event(hci_packets.SubeventCode.ADVERTISING_REPORT)
+        self.register_for_le_event(
+            hci_packets.SubeventCode.EXTENDED_ADVERTISING_REPORT)
+        with EventCallbackStream(
+                self.cert_device.hci.FetchLeSubevents(
+                    empty_proto.Empty())) as hci_le_event_stream:
+
+            hci_event_asserts = EventAsserts(hci_le_event_stream)
+
+            # CERT Scans
+            self.enqueue_hci_command(
+                hci_packets.LeSetRandomAddressBuilder('0C:05:04:03:02:01'),
+                True)
+            scan_parameters = hci_packets.PhyScanParameters()
+            scan_parameters.le_scan_type = hci_packets.LeScanType.ACTIVE
+            scan_parameters.le_scan_interval = 40
+            scan_parameters.le_scan_window = 20
+            self.enqueue_hci_command(
+                hci_packets.LeSetExtendedScanParametersBuilder(
+                    hci_packets.AddressType.RANDOM_DEVICE_ADDRESS,
+                    hci_packets.LeSetScanningFilterPolicy.ACCEPT_ALL, 1,
+                    [scan_parameters]), True)
+            self.enqueue_hci_command(
+                hci_packets.LeSetExtendedScanEnableBuilder(
+                    hci_packets.Enable.ENABLED,
+                    hci_packets.FilterDuplicates.DISABLED, 0, 0), True)
+
+            # DUT Advertises
+            gap_name = hci_packets.GapData()
+            gap_name.data_type = hci_packets.GapDataType.COMPLETE_LOCAL_NAME
+            gap_name.data = list(bytes(b'Im_The_DUT!'))
+            gap_data = le_advertising_facade.GapDataMsg(
+                data=bytes(gap_name.Serialize()))
+            config = le_advertising_facade.AdvertisingConfig(
+                advertisement=[gap_data],
+                random_address=common.BluetoothAddress(
+                    address=bytes(b'0D:05:04:03:02:01')),
+                interval_min=512,
+                interval_max=768,
+                event_type=le_advertising_facade.AdvertisingEventType.ADV_IND,
+                address_type=common.RANDOM_DEVICE_ADDRESS,
+                peer_address_type=common.PUBLIC_DEVICE_OR_IDENTITY_ADDRESS,
+                peer_address=common.BluetoothAddress(
+                    address=bytes(b'A6:A5:A4:A3:A2:A1')),
+                channel_map=7,
+                filter_policy=le_advertising_facade.AdvertisingFilterPolicy.
+                ALL_DEVICES)
+            request = le_advertising_facade.CreateAdvertiserRequest(
+                config=config)
+
+            create_response = self.device_under_test.hci_le_advertising_manager.CreateAdvertiser(
+                request)
+
+            hci_event_asserts.assert_event_occurs(
+                lambda packet: b'Im_The_DUT' in packet.event)
+
+            remove_request = le_advertising_facade.RemoveAdvertiserRequest(
+                advertiser_id=create_response.advertiser_id)
+            self.device_under_test.hci_le_advertising_manager.RemoveAdvertiser(
+                remove_request)
+            self.enqueue_hci_command(
+                hci_packets.LeSetScanEnableBuilder(hci_packets.Enable.DISABLED,
+                                                   hci_packets.Enable.DISABLED),
+                True)
diff --git a/gd/hci/cert/le_scanning_manager_test.py b/gd/hci/cert/le_scanning_manager_test.py
new file mode 100644
index 0000000..5946a22
--- /dev/null
+++ b/gd/hci/cert/le_scanning_manager_test.py
@@ -0,0 +1,110 @@
+#!/usr/bin/env python3
+#
+#   Copyright 2019 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import os
+import sys
+import logging
+
+from cert.gd_base_test_facade_only import GdFacadeOnlyBaseTestClass
+from cert.event_callback_stream import EventCallbackStream
+from cert.event_asserts import EventAsserts
+from google.protobuf import empty_pb2 as empty_proto
+from facade import rootservice_pb2 as facade_rootservice
+from hci.facade import facade_pb2 as hci_facade
+from hci.facade import le_scanning_manager_facade_pb2 as le_scanning_facade
+from hci.facade import le_advertising_manager_facade_pb2 as le_advertising_facade
+from bluetooth_packets_python3 import hci_packets
+from facade import common_pb2 as common
+
+
+class LeScanningManagerTest(GdFacadeOnlyBaseTestClass):
+
+    def setup_test(self):
+        self.device_under_test.rootservice.StartStack(
+            facade_rootservice.StartStackRequest(
+                module_under_test=facade_rootservice.BluetoothModule.Value(
+                    'HCI_INTERFACES'),))
+        self.cert_device.rootservice.StartStack(
+            facade_rootservice.StartStackRequest(
+                module_under_test=facade_rootservice.BluetoothModule.Value(
+                    'HCI_INTERFACES'),))
+
+        self.device_under_test.wait_channel_ready()
+        self.cert_device.wait_channel_ready()
+
+    def teardown_test(self):
+        self.device_under_test.rootservice.StopStack(
+            facade_rootservice.StopStackRequest())
+        self.cert_device.rootservice.StopStack(
+            facade_rootservice.StopStackRequest())
+
+    def register_for_event(self, event_code):
+        msg = hci_facade.EventCodeMsg(code=int(event_code))
+        self.cert_device.hci.RegisterEventHandler(msg)
+
+    def register_for_le_event(self, event_code):
+        msg = hci_facade.LeSubeventCodeMsg(code=int(event_code))
+        self.cert_device.hci.RegisterLeEventHandler(msg)
+
+    def enqueue_hci_command(self, command, expect_complete):
+        cmd_bytes = bytes(command.Serialize())
+        cmd = hci_facade.CommandMsg(command=cmd_bytes)
+        if (expect_complete):
+            self.cert_device.hci.EnqueueCommandWithComplete(cmd)
+        else:
+            self.cert_device.hci.EnqueueCommandWithStatus(cmd)
+
+    def test_le_ad_scan_dut_scans(self):
+        with EventCallbackStream(
+                # DUT Scans
+                self.device_under_test.hci_le_scanning_manager.StartScan(
+                    empty_proto.Empty())) as advertising_event_stream:
+
+            hci_event_asserts = EventAsserts(advertising_event_stream)
+
+            # CERT Advertises
+            gap_name = hci_packets.GapData()
+            gap_name.data_type = hci_packets.GapDataType.COMPLETE_LOCAL_NAME
+            gap_name.data = list(bytes(b'Im_The_CERT!'))
+            gap_data = le_advertising_facade.GapDataMsg(
+                data=bytes(gap_name.Serialize()))
+            config = le_advertising_facade.AdvertisingConfig(
+                advertisement=[gap_data],
+                random_address=common.BluetoothAddress(
+                    address=bytes(b'A6:A5:A4:A3:A2:A1')),
+                interval_min=512,
+                interval_max=768,
+                event_type=le_advertising_facade.AdvertisingEventType.ADV_IND,
+                address_type=common.RANDOM_DEVICE_ADDRESS,
+                peer_address_type=common.PUBLIC_DEVICE_OR_IDENTITY_ADDRESS,
+                peer_address=common.BluetoothAddress(
+                    address=bytes(b'0C:05:04:03:02:01')),
+                channel_map=7,
+                filter_policy=le_advertising_facade.AdvertisingFilterPolicy.
+                ALL_DEVICES)
+            request = le_advertising_facade.CreateAdvertiserRequest(
+                config=config)
+
+            create_response = self.cert_device.hci_le_advertising_manager.CreateAdvertiser(
+                request)
+
+            hci_event_asserts.assert_event_occurs(
+                lambda packet: b'Im_The_CERT' in packet.event)
+
+            remove_request = le_advertising_facade.RemoveAdvertiserRequest(
+                advertiser_id=create_response.advertiser_id)
+            self.cert_device.hci_le_advertising_manager.RemoveAdvertiser(
+                remove_request)
diff --git a/gd/hci/class_of_device.cc b/gd/hci/class_of_device.cc
new file mode 100644
index 0000000..36dd292
--- /dev/null
+++ b/gd/hci/class_of_device.cc
@@ -0,0 +1,97 @@
+/******************************************************************************
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#include "class_of_device.h"
+
+#include <stdint.h>
+#include <algorithm>
+#include <sstream>
+#include <vector>
+
+#include "os/log.h"
+
+namespace bluetooth {
+namespace hci {
+
+static_assert(sizeof(ClassOfDevice) == ClassOfDevice::kLength, "ClassOfDevice must be 3 bytes long!");
+
+ClassOfDevice::ClassOfDevice(const uint8_t (&class_of_device)[kLength]) {
+  std::copy(class_of_device, class_of_device + kLength, cod);
+};
+
+std::string ClassOfDevice::ToString() const {
+  char buffer[] = "000-0-00";
+  std::snprintf(&buffer[0], sizeof(buffer), "%03x-%01x-%02x", (static_cast<uint16_t>(cod[2]) << 4) | cod[1] >> 4,
+                cod[1] & 0x0f, cod[0]);
+  std::string str(buffer);
+  return str;
+}
+
+bool ClassOfDevice::FromString(const std::string& from, ClassOfDevice& to) {
+  ClassOfDevice new_cod;
+  if (from.length() != 8) return false;
+
+  std::istringstream stream(from);
+  std::string token;
+  int index = 0;
+  uint16_t values[3];
+
+  while (getline(stream, token, '-')) {
+    if (index >= 3) {
+      return false;
+    }
+
+    if (index == 0 && token.length() != 3) {
+      return false;
+    } else if (index == 1 && token.length() != 1) {
+      return false;
+    } else if (index == 2 && token.length() != 2) {
+      return false;
+    }
+    char* temp = nullptr;
+    values[index] = strtol(token.c_str(), &temp, 16);
+    if (*temp != '\0') {
+      return false;
+    }
+
+    index++;
+  }
+
+  if (index != 3) {
+    return false;
+  }
+
+  new_cod.cod[0] = values[2];
+  new_cod.cod[1] = values[1] | ((values[0] & 0xf) << 4);
+  new_cod.cod[2] = values[0] >> 4;
+
+  to = new_cod;
+  return true;
+}
+
+size_t ClassOfDevice::FromOctets(const uint8_t* from) {
+  std::copy(from, from + kLength, cod);
+  return kLength;
+};
+
+bool ClassOfDevice::IsValid(const std::string& cod) {
+  ClassOfDevice tmp;
+  return ClassOfDevice::FromString(cod, tmp);
+}
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/class_of_device.h b/gd/hci/class_of_device.h
new file mode 100644
index 0000000..b44a45e
--- /dev/null
+++ b/gd/hci/class_of_device.h
@@ -0,0 +1,63 @@
+/******************************************************************************
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#pragma once
+
+#include <string>
+
+namespace bluetooth {
+namespace hci {
+
+class ClassOfDevice final {
+ public:
+  static constexpr unsigned int kLength = 3;
+
+  uint8_t cod[kLength];
+
+  ClassOfDevice() = default;
+  ClassOfDevice(const uint8_t (&class_of_device)[kLength]);
+
+  bool operator==(const ClassOfDevice& rhs) const {
+    return (std::memcmp(cod, rhs.cod, sizeof(cod)) == 0);
+  }
+
+  bool operator!=(const ClassOfDevice& rhs) const {
+    return std::memcmp(cod, rhs.cod, sizeof(cod)) != 0;
+  }
+
+  std::string ToString() const;
+
+  // Converts |string| to ClassOfDevice and places it in |to|. If |from| does
+  // not represent a Class of Device, |to| is not modified and this function
+  // returns false. Otherwise, it returns true.
+  static bool FromString(const std::string& from, ClassOfDevice& to);
+
+  // Copies |from| raw Class of Device octets to the local object.
+  // Returns the number of copied octets (always ClassOfDevice::kLength)
+  size_t FromOctets(const uint8_t* from);
+
+  static bool IsValid(const std::string& class_of_device);
+};
+
+inline std::ostream& operator<<(std::ostream& os, const ClassOfDevice& c) {
+  os << c.ToString();
+  return os;
+}
+
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/class_of_device_pybind11_type_caster.h b/gd/hci/class_of_device_pybind11_type_caster.h
new file mode 100644
index 0000000..0615e3b
--- /dev/null
+++ b/gd/hci/class_of_device_pybind11_type_caster.h
@@ -0,0 +1,55 @@
+/******************************************************************************
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#pragma once
+
+#include <cstring>
+#include <string>
+
+#include <pybind11/pybind11.h>
+#include <pybind11/stl.h>
+
+#include "hci/class_of_device.h"
+
+namespace py = pybind11;
+
+namespace pybind11 {
+namespace detail {
+template <>
+struct type_caster<::bluetooth::hci::ClassOfDevice> {
+ public:
+  // Set the Python name of ClassOfDevice and declare "value"
+  PYBIND11_TYPE_CASTER(::bluetooth::hci::ClassOfDevice, _("ClassOfDevice"));
+
+  // Convert from Python->C++
+  bool load(handle src, bool) {
+    PyObject* source = src.ptr();
+    if (py::isinstance<py::str>(src)) {
+      bool conversion_successful = bluetooth::hci::ClassOfDevice::FromString(PyUnicode_AsUTF8(source), value);
+      return conversion_successful && !PyErr_Occurred();
+    }
+    return false;
+  }
+
+  // Convert from C++->Python
+  static handle cast(bluetooth::hci::ClassOfDevice src, return_value_policy, handle) {
+    return PyUnicode_FromString(src.ToString().c_str());
+  }
+};
+}  // namespace detail
+}  // namespace pybind11
diff --git a/gd/hci/class_of_device_unittest.cc b/gd/hci/class_of_device_unittest.cc
new file mode 100644
index 0000000..85472dd
--- /dev/null
+++ b/gd/hci/class_of_device_unittest.cc
@@ -0,0 +1,98 @@
+/******************************************************************************
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#include <gtest/gtest.h>
+
+#include "hci/class_of_device.h"
+
+using bluetooth::hci::ClassOfDevice;
+
+static const char* test_class = "efc-d-ab";
+static const uint8_t test_bytes[]{0xab, 0xcd, 0xef};
+
+TEST(ClassOfDeviceUnittest, test_constructor_array) {
+  ClassOfDevice cod(test_bytes);
+
+  ASSERT_EQ(test_bytes[0], cod.cod[0]);
+  ASSERT_EQ(test_bytes[1], cod.cod[1]);
+  ASSERT_EQ(test_bytes[2], cod.cod[2]);
+
+  std::string ret = cod.ToString();
+
+  ASSERT_STREQ(test_class, ret.c_str());
+}
+
+TEST(ClassOfDeviceUnittest, test_to_from_str) {
+  ClassOfDevice cod;
+  ClassOfDevice::FromString(test_class, cod);
+
+  ASSERT_EQ(test_bytes[0], cod.cod[0]);
+  ASSERT_EQ(test_bytes[1], cod.cod[1]);
+  ASSERT_EQ(test_bytes[2], cod.cod[2]);
+
+  std::string ret = cod.ToString();
+
+  ASSERT_STREQ(test_class, ret.c_str());
+}
+
+TEST(ClassOfDeviceUnittest, test_from_octets) {
+  ClassOfDevice cod;
+  size_t expected_result = ClassOfDevice::kLength;
+  ASSERT_EQ(expected_result, cod.FromOctets(test_bytes));
+
+  ASSERT_EQ(test_bytes[0], cod.cod[0]);
+  ASSERT_EQ(test_bytes[1], cod.cod[1]);
+  ASSERT_EQ(test_bytes[2], cod.cod[2]);
+
+  std::string ret = cod.ToString();
+
+  ASSERT_STREQ(test_class, ret.c_str());
+}
+
+TEST(ClassOfDeviceTest, test_copy) {
+  ClassOfDevice cod1;
+  ClassOfDevice cod2;
+  ClassOfDevice::FromString(test_class, cod1);
+  cod2 = cod1;
+
+  ASSERT_EQ(cod1.cod[0], cod2.cod[0]);
+  ASSERT_EQ(cod1.cod[1], cod2.cod[1]);
+  ASSERT_EQ(cod1.cod[2], cod2.cod[2]);
+}
+
+TEST(ClassOfDeviceTest, IsValid) {
+  EXPECT_FALSE(ClassOfDevice::IsValid(""));
+  EXPECT_FALSE(ClassOfDevice::IsValid("000000"));
+  EXPECT_FALSE(ClassOfDevice::IsValid("00-00-00"));
+  EXPECT_FALSE(ClassOfDevice::IsValid("000-0-0"));
+  EXPECT_TRUE(ClassOfDevice::IsValid("000-0-00"));
+  EXPECT_TRUE(ClassOfDevice::IsValid("ABc-d-00"));
+  EXPECT_TRUE(ClassOfDevice::IsValid("aBc-D-eF"));
+}
+
+TEST(ClassOfDeviceTest, classOfDeviceFromString) {
+  ClassOfDevice cod;
+
+  EXPECT_TRUE(ClassOfDevice::FromString("000-0-00", cod));
+  const ClassOfDevice result0 = {{0x00, 0x00, 0x00}};
+  EXPECT_EQ(0, memcmp(&cod, &result0, sizeof(cod)));
+
+  EXPECT_TRUE(ClassOfDevice::FromString("ab2-1-4C", cod));
+  const ClassOfDevice result1 = {{0x4c, 0x21, 0xab}};
+  EXPECT_EQ(0, memcmp(&cod, &result1, sizeof(cod)));
+}
diff --git a/gd/hci/classic_device.h b/gd/hci/classic_device.h
new file mode 100644
index 0000000..53ff0ba
--- /dev/null
+++ b/gd/hci/classic_device.h
@@ -0,0 +1,35 @@
+/******************************************************************************
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+#pragma once
+
+#include "hci/device.h"
+
+namespace bluetooth::hci {
+
+/**
+ * A device representing a CLASSIC device.
+ *
+ * <p>This can be a CLASSIC only or a piece of a DUAL MODE device.
+ */
+class ClassicDevice : public Device {
+ protected:
+  friend class DeviceDatabase;
+  explicit ClassicDevice(Address address) : Device(address, DeviceType::CLASSIC) {}
+};
+
+}  // namespace bluetooth::hci
diff --git a/gd/hci/controller.cc b/gd/hci/controller.cc
new file mode 100644
index 0000000..abb0b5b
--- /dev/null
+++ b/gd/hci/controller.cc
@@ -0,0 +1,870 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "hci/controller.h"
+
+#include <future>
+#include <memory>
+#include <utility>
+
+#include "common/bind.h"
+#include "common/callback.h"
+#include "hci/hci_layer.h"
+
+namespace bluetooth {
+namespace hci {
+
+using common::Bind;
+using common::BindOnce;
+using common::Callback;
+using common::Closure;
+using common::OnceCallback;
+using common::OnceClosure;
+using os::Handler;
+
+struct Controller::impl {
+  impl(Controller& module) : module_(module) {}
+
+  void Start(hci::HciLayer* hci) {
+    hci_ = hci;
+    hci_->RegisterEventHandler(EventCode::NUMBER_OF_COMPLETED_PACKETS,
+                               Bind(&Controller::impl::NumberOfCompletedPackets, common::Unretained(this)),
+                               module_.GetHandler());
+
+    set_event_mask(kDefaultEventMask);
+    hci_->EnqueueCommand(ReadLocalNameBuilder::Create(),
+                         BindOnce(&Controller::impl::read_local_name_complete_handler, common::Unretained(this)),
+                         module_.GetHandler());
+    hci_->EnqueueCommand(
+        ReadLocalVersionInformationBuilder::Create(),
+        BindOnce(&Controller::impl::read_local_version_information_complete_handler, common::Unretained(this)),
+        module_.GetHandler());
+    hci_->EnqueueCommand(
+        ReadLocalSupportedCommandsBuilder::Create(),
+        BindOnce(&Controller::impl::read_local_supported_commands_complete_handler, common::Unretained(this)),
+        module_.GetHandler());
+    hci_->EnqueueCommand(
+        ReadLocalSupportedFeaturesBuilder::Create(),
+        BindOnce(&Controller::impl::read_local_supported_features_complete_handler, common::Unretained(this)),
+        module_.GetHandler());
+
+    // Wait for all extended features read
+    std::promise<void> features_promise;
+    auto features_future = features_promise.get_future();
+    hci_->EnqueueCommand(ReadLocalExtendedFeaturesBuilder::Create(0x00),
+                         BindOnce(&Controller::impl::read_local_extended_features_complete_handler,
+                                  common::Unretained(this), std::move(features_promise)),
+                         module_.GetHandler());
+    features_future.wait();
+
+    hci_->EnqueueCommand(ReadBufferSizeBuilder::Create(),
+                         BindOnce(&Controller::impl::read_buffer_size_complete_handler, common::Unretained(this)),
+                         module_.GetHandler());
+
+    hci_->EnqueueCommand(LeReadBufferSizeBuilder::Create(),
+                         BindOnce(&Controller::impl::le_read_buffer_size_handler, common::Unretained(this)),
+                         module_.GetHandler());
+
+    hci_->EnqueueCommand(
+        LeReadLocalSupportedFeaturesBuilder::Create(),
+        BindOnce(&Controller::impl::le_read_local_supported_features_handler, common::Unretained(this)),
+        module_.GetHandler());
+
+    hci_->EnqueueCommand(LeReadSupportedStatesBuilder::Create(),
+                         BindOnce(&Controller::impl::le_read_supported_states_handler, common::Unretained(this)),
+                         module_.GetHandler());
+
+    if (is_supported(OpCode::LE_READ_MAXIMUM_DATA_LENGTH)) {
+      hci_->EnqueueCommand(LeReadMaximumDataLengthBuilder::Create(),
+                           BindOnce(&Controller::impl::le_read_maximum_data_length_handler, common::Unretained(this)),
+                           module_.GetHandler());
+    }
+    if (is_supported(OpCode::LE_READ_MAXIMUM_ADVERTISING_DATA_LENGTH)) {
+      hci_->EnqueueCommand(
+          LeReadMaximumAdvertisingDataLengthBuilder::Create(),
+          BindOnce(&Controller::impl::le_read_maximum_advertising_data_length_handler, common::Unretained(this)),
+          module_.GetHandler());
+    }
+    if (is_supported(OpCode::LE_READ_NUMBER_OF_SUPPORTED_ADVERTISING_SETS)) {
+      hci_->EnqueueCommand(
+          LeReadNumberOfSupportedAdvertisingSetsBuilder::Create(),
+          BindOnce(&Controller::impl::le_read_number_of_supported_advertising_sets_handler, common::Unretained(this)),
+          module_.GetHandler());
+    }
+
+    hci_->EnqueueCommand(LeGetVendorCapabilitiesBuilder::Create(),
+                         BindOnce(&Controller::impl::le_get_vendor_capabilities_handler, common::Unretained(this)),
+                         module_.GetHandler());
+
+    // We only need to synchronize the last read. Make BD_ADDR to be the last one.
+    std::promise<void> promise;
+    auto future = promise.get_future();
+    hci_->EnqueueCommand(
+        ReadBdAddrBuilder::Create(),
+        BindOnce(&Controller::impl::read_controller_mac_address_handler, common::Unretained(this), std::move(promise)),
+        module_.GetHandler());
+    future.wait();
+  }
+
+  void Stop() {
+    hci_->UnregisterEventHandler(EventCode::NUMBER_OF_COMPLETED_PACKETS);
+    hci_ = nullptr;
+  }
+
+  void NumberOfCompletedPackets(EventPacketView event) {
+    ASSERT(acl_credits_handler_ != nullptr);
+    auto complete_view = NumberOfCompletedPacketsView::Create(event);
+    ASSERT(complete_view.IsValid());
+    for (auto completed_packets : complete_view.GetCompletedPackets()) {
+      uint16_t handle = completed_packets.connection_handle_;
+      uint16_t credits = completed_packets.host_num_of_completed_packets_;
+      acl_credits_handler_->Post(Bind(acl_credits_callback_, handle, credits));
+    }
+  }
+
+  void RegisterCompletedAclPacketsCallback(Callback<void(uint16_t /* handle */, uint16_t /* packets */)> cb,
+                                           Handler* handler) {
+    ASSERT(acl_credits_handler_ == nullptr);
+    acl_credits_callback_ = cb;
+    acl_credits_handler_ = handler;
+  }
+
+  void read_local_name_complete_handler(CommandCompleteView view) {
+    auto complete_view = ReadLocalNameCompleteView::Create(view);
+    ASSERT(complete_view.IsValid());
+    ErrorCode status = complete_view.GetStatus();
+    ASSERT_LOG(status == ErrorCode::SUCCESS, "Status 0x%02hhx, %s", status, ErrorCodeText(status).c_str());
+    std::array<uint8_t, 248> local_name_array = complete_view.GetLocalName();
+
+    local_name_ = std::string(local_name_array.begin(), local_name_array.end());
+    // erase \0
+    local_name_.erase(std::find(local_name_.begin(), local_name_.end(), '\0'), local_name_.end());
+  }
+
+  void read_local_version_information_complete_handler(CommandCompleteView view) {
+    auto complete_view = ReadLocalVersionInformationCompleteView::Create(view);
+    ASSERT(complete_view.IsValid());
+    ErrorCode status = complete_view.GetStatus();
+    ASSERT_LOG(status == ErrorCode::SUCCESS, "Status 0x%02hhx, %s", status, ErrorCodeText(status).c_str());
+
+    local_version_information_ = complete_view.GetLocalVersionInformation();
+  }
+
+  void read_local_supported_commands_complete_handler(CommandCompleteView view) {
+    auto complete_view = ReadLocalSupportedCommandsCompleteView::Create(view);
+    ASSERT(complete_view.IsValid());
+    ErrorCode status = complete_view.GetStatus();
+    ASSERT_LOG(status == ErrorCode::SUCCESS, "Status 0x%02hhx, %s", status, ErrorCodeText(status).c_str());
+    local_supported_commands_ = complete_view.GetSupportedCommands();
+  }
+
+  void read_local_supported_features_complete_handler(CommandCompleteView view) {
+    auto complete_view = ReadLocalSupportedFeaturesCompleteView::Create(view);
+    ASSERT(complete_view.IsValid());
+    ErrorCode status = complete_view.GetStatus();
+    ASSERT_LOG(status == ErrorCode::SUCCESS, "Status 0x%02hhx, %s", status, ErrorCodeText(status).c_str());
+    local_supported_features_ = complete_view.GetLmpFeatures();
+  }
+
+  void read_local_extended_features_complete_handler(std::promise<void> promise, CommandCompleteView view) {
+    auto complete_view = ReadLocalExtendedFeaturesCompleteView::Create(view);
+    ASSERT(complete_view.IsValid());
+    ErrorCode status = complete_view.GetStatus();
+    ASSERT_LOG(status == ErrorCode::SUCCESS, "Status 0x%02hhx, %s", status, ErrorCodeText(status).c_str());
+    uint8_t page_number = complete_view.GetPageNumber();
+    maximum_page_number_ = complete_view.GetMaximumPageNumber();
+    extended_lmp_features_array_.push_back(complete_view.GetExtendedLmpFeatures());
+
+    // Query all extended features
+    if (page_number < maximum_page_number_) {
+      page_number++;
+      hci_->EnqueueCommand(ReadLocalExtendedFeaturesBuilder::Create(page_number),
+                           BindOnce(&Controller::impl::read_local_extended_features_complete_handler,
+                                    common::Unretained(this), std::move(promise)),
+                           module_.GetHandler());
+    } else {
+      promise.set_value();
+    }
+  }
+
+  void read_buffer_size_complete_handler(CommandCompleteView view) {
+    auto complete_view = ReadBufferSizeCompleteView::Create(view);
+    ASSERT(complete_view.IsValid());
+    ErrorCode status = complete_view.GetStatus();
+    ASSERT_LOG(status == ErrorCode::SUCCESS, "Status 0x%02hhx, %s", status, ErrorCodeText(status).c_str());
+    acl_buffer_length_ = complete_view.GetAclDataPacketLength();
+    acl_buffers_ = complete_view.GetTotalNumAclDataPackets();
+
+    sco_buffer_length_ = complete_view.GetSynchronousDataPacketLength();
+    sco_buffers_ = complete_view.GetTotalNumSynchronousDataPackets();
+  }
+
+  void read_controller_mac_address_handler(std::promise<void> promise, CommandCompleteView view) {
+    auto complete_view = ReadBdAddrCompleteView::Create(view);
+    ASSERT(complete_view.IsValid());
+    ErrorCode status = complete_view.GetStatus();
+    ASSERT_LOG(status == ErrorCode::SUCCESS, "Status 0x%02hhx, %s", status, ErrorCodeText(status).c_str());
+    mac_address_ = complete_view.GetBdAddr();
+    promise.set_value();
+  }
+
+  void le_read_buffer_size_handler(CommandCompleteView view) {
+    auto complete_view = LeReadBufferSizeCompleteView::Create(view);
+    ASSERT(complete_view.IsValid());
+    ErrorCode status = complete_view.GetStatus();
+    ASSERT_LOG(status == ErrorCode::SUCCESS, "Status 0x%02hhx, %s", status, ErrorCodeText(status).c_str());
+    le_buffer_size_ = complete_view.GetLeBufferSize();
+  }
+
+  void le_read_local_supported_features_handler(CommandCompleteView view) {
+    auto complete_view = LeReadLocalSupportedFeaturesCompleteView::Create(view);
+    ASSERT(complete_view.IsValid());
+    ErrorCode status = complete_view.GetStatus();
+    ASSERT_LOG(status == ErrorCode::SUCCESS, "Status 0x%02hhx, %s", status, ErrorCodeText(status).c_str());
+    le_local_supported_features_ = complete_view.GetLeFeatures();
+  }
+
+  void le_read_supported_states_handler(CommandCompleteView view) {
+    auto complete_view = LeReadSupportedStatesCompleteView::Create(view);
+    ASSERT(complete_view.IsValid());
+    ErrorCode status = complete_view.GetStatus();
+    ASSERT_LOG(status == ErrorCode::SUCCESS, "Status 0x%02hhx, %s", status, ErrorCodeText(status).c_str());
+    le_supported_states_ = complete_view.GetLeStates();
+  }
+
+  void le_read_maximum_data_length_handler(CommandCompleteView view) {
+    auto complete_view = LeReadMaximumDataLengthCompleteView::Create(view);
+    ASSERT(complete_view.IsValid());
+    ErrorCode status = complete_view.GetStatus();
+    ASSERT_LOG(status == ErrorCode::SUCCESS, "Status 0x%02hhx, %s", status, ErrorCodeText(status).c_str());
+    le_maximum_data_length_ = complete_view.GetLeMaximumDataLength();
+  }
+
+  void le_read_maximum_advertising_data_length_handler(CommandCompleteView view) {
+    auto complete_view = LeReadMaximumAdvertisingDataLengthCompleteView::Create(view);
+    ASSERT(complete_view.IsValid());
+    ErrorCode status = complete_view.GetStatus();
+    ASSERT_LOG(status == ErrorCode::SUCCESS, "Status 0x%02hhx, %s", status, ErrorCodeText(status).c_str());
+    le_maximum_advertising_data_length_ = complete_view.GetMaximumAdvertisingDataLength();
+  }
+
+  void le_read_number_of_supported_advertising_sets_handler(CommandCompleteView view) {
+    auto complete_view = LeReadNumberOfSupportedAdvertisingSetsCompleteView::Create(view);
+    ASSERT(complete_view.IsValid());
+    ErrorCode status = complete_view.GetStatus();
+    ASSERT_LOG(status == ErrorCode::SUCCESS, "Status 0x%02hhx, %s", status, ErrorCodeText(status).c_str());
+    le_number_supported_advertising_sets_ = complete_view.GetNumberSupportedAdvertisingSets();
+  }
+
+  void le_get_vendor_capabilities_handler(CommandCompleteView view) {
+    auto complete_view = LeGetVendorCapabilitiesCompleteView::Create(view);
+
+    vendor_capabilities_.is_supported_ = 0x00;
+    vendor_capabilities_.max_advt_instances_ = 0x00;
+    vendor_capabilities_.offloaded_resolution_of_private_address_ = 0x00;
+    vendor_capabilities_.total_scan_results_storage_ = 0x00;
+    vendor_capabilities_.max_irk_list_sz_ = 0x00;
+    vendor_capabilities_.filtering_support_ = 0x00;
+    vendor_capabilities_.max_filter_ = 0x00;
+    vendor_capabilities_.activity_energy_info_support_ = 0x00;
+    vendor_capabilities_.version_supported_ = 0x00;
+    vendor_capabilities_.version_supported_ = 0x00;
+    vendor_capabilities_.total_num_of_advt_tracked_ = 0x00;
+    vendor_capabilities_.extended_scan_support_ = 0x00;
+    vendor_capabilities_.debug_logging_supported_ = 0x00;
+    vendor_capabilities_.le_address_generation_offloading_support_ = 0x00;
+    vendor_capabilities_.a2dp_source_offload_capability_mask_ = 0x00;
+    vendor_capabilities_.bluetooth_quality_report_support_ = 0x00;
+
+    if (complete_view.IsValid()) {
+      vendor_capabilities_.is_supported_ = 0x01;
+
+      // v0.55
+      BaseVendorCapabilities base_vendor_capabilities = complete_view.GetBaseVendorCapabilities();
+      vendor_capabilities_.max_advt_instances_ = base_vendor_capabilities.max_advt_instances_;
+      vendor_capabilities_.offloaded_resolution_of_private_address_ =
+          base_vendor_capabilities.offloaded_resolution_of_private_address_;
+      vendor_capabilities_.total_scan_results_storage_ = base_vendor_capabilities.total_scan_results_storage_;
+      vendor_capabilities_.max_irk_list_sz_ = base_vendor_capabilities.max_irk_list_sz_;
+      vendor_capabilities_.filtering_support_ = base_vendor_capabilities.filtering_support_;
+      vendor_capabilities_.max_filter_ = base_vendor_capabilities.max_filter_;
+      vendor_capabilities_.activity_energy_info_support_ = base_vendor_capabilities.activity_energy_info_support_;
+      if (complete_view.GetPayload().size() == 0) {
+        vendor_capabilities_.version_supported_ = 55;
+        return;
+      }
+
+      // v0.95
+      auto v95 = LeGetVendorCapabilitiesComplete095View::Create(complete_view);
+      if (!v95.IsValid()) {
+        LOG_ERROR("invalid data for hci requirements v0.95");
+        return;
+      }
+      vendor_capabilities_.version_supported_ = v95.GetVersionSupported();
+      vendor_capabilities_.total_num_of_advt_tracked_ = v95.GetTotalNumOfAdvtTracked();
+      vendor_capabilities_.extended_scan_support_ = v95.GetExtendedScanSupport();
+      vendor_capabilities_.debug_logging_supported_ = v95.GetDebugLoggingSupported();
+      if (vendor_capabilities_.version_supported_ <= 95 || complete_view.GetPayload().size() == 0) {
+        return;
+      }
+
+      // v0.96
+      auto v96 = LeGetVendorCapabilitiesComplete096View::Create(v95);
+      if (!v96.IsValid()) {
+        LOG_ERROR("invalid data for hci requirements v0.96");
+        return;
+      }
+      vendor_capabilities_.le_address_generation_offloading_support_ = v96.GetLeAddressGenerationOffloadingSupport();
+      if (vendor_capabilities_.version_supported_ <= 96 || complete_view.GetPayload().size() == 0) {
+        return;
+      }
+
+      // v0.98
+      auto v98 = LeGetVendorCapabilitiesComplete098View::Create(v96);
+      if (!v98.IsValid()) {
+        LOG_ERROR("invalid data for hci requirements v0.98");
+        return;
+      }
+      vendor_capabilities_.a2dp_source_offload_capability_mask_ = v98.GetA2dpSourceOffloadCapabilityMask();
+      vendor_capabilities_.bluetooth_quality_report_support_ = v98.GetBluetoothQualityReportSupport();
+    }
+  }
+
+  void set_event_mask(uint64_t event_mask) {
+    std::unique_ptr<SetEventMaskBuilder> packet = SetEventMaskBuilder::Create(event_mask);
+    hci_->EnqueueCommand(std::move(packet),
+                         BindOnce(&Controller::impl::check_status<SetEventMaskCompleteView>, common::Unretained(this)),
+                         module_.GetHandler());
+  }
+
+  void reset() {
+    std::unique_ptr<ResetBuilder> packet = ResetBuilder::Create();
+    hci_->EnqueueCommand(std::move(packet),
+                         BindOnce(&Controller::impl::check_status<ResetCompleteView>, common::Unretained(this)),
+                         module_.GetHandler());
+  }
+
+  void set_event_filter(std::unique_ptr<SetEventFilterBuilder> packet) {
+    hci_->EnqueueCommand(
+        std::move(packet),
+        BindOnce(&Controller::impl::check_status<SetEventFilterCompleteView>, common::Unretained(this)),
+        module_.GetHandler());
+  }
+
+  void write_local_name(std::string local_name) {
+    ASSERT(local_name.length() <= 248);
+    // Fill remaining char with 0
+    local_name.append(std::string(248 - local_name.length(), '\0'));
+    std::array<uint8_t, 248> local_name_array;
+    std::copy(std::begin(local_name), std::end(local_name), std::begin(local_name_array));
+
+    std::unique_ptr<WriteLocalNameBuilder> packet = WriteLocalNameBuilder::Create(local_name_array);
+    hci_->EnqueueCommand(
+        std::move(packet),
+        BindOnce(&Controller::impl::check_status<WriteLocalNameCompleteView>, common::Unretained(this)),
+        module_.GetHandler());
+  }
+
+  void host_buffer_size(uint16_t host_acl_data_packet_length, uint8_t host_synchronous_data_packet_length,
+                        uint16_t host_total_num_acl_data_packets, uint16_t host_total_num_synchronous_data_packets) {
+    std::unique_ptr<HostBufferSizeBuilder> packet =
+        HostBufferSizeBuilder::Create(host_acl_data_packet_length, host_synchronous_data_packet_length,
+                                      host_total_num_acl_data_packets, host_total_num_synchronous_data_packets);
+    hci_->EnqueueCommand(
+        std::move(packet),
+        BindOnce(&Controller::impl::check_status<HostBufferSizeCompleteView>, common::Unretained(this)),
+        module_.GetHandler());
+  }
+
+  void le_set_event_mask(uint64_t le_event_mask) {
+    std::unique_ptr<LeSetEventMaskBuilder> packet = LeSetEventMaskBuilder::Create(le_event_mask);
+    hci_->EnqueueCommand(
+        std::move(packet),
+        BindOnce(&Controller::impl::check_status<LeSetEventMaskCompleteView>, common::Unretained(this)),
+        module_.GetHandler());
+  }
+
+  template <class T>
+  void check_status(CommandCompleteView view) {
+    ASSERT(view.IsValid());
+    auto status_view = T::Create(view);
+    ASSERT(status_view.IsValid());
+    ASSERT(status_view.GetStatus() == ErrorCode::SUCCESS);
+  }
+
+#define OP_CODE_MAPPING(name)                                                  \
+  case OpCode::name: {                                                         \
+    uint16_t index = (uint16_t)OpCodeIndex::name;                              \
+    uint16_t byte_index = index / 10;                                          \
+    uint16_t bit_index = index % 10;                                           \
+    bool supported = local_supported_commands_[byte_index] & (1 << bit_index); \
+    if (!supported) {                                                          \
+      LOG_WARN("unsupported command opcode: 0x%04x", (uint16_t)OpCode::name);  \
+    }                                                                          \
+    return supported;                                                          \
+  }
+
+  bool is_supported(OpCode op_code) {
+    switch (op_code) {
+      OP_CODE_MAPPING(INQUIRY)
+      OP_CODE_MAPPING(INQUIRY_CANCEL)
+      OP_CODE_MAPPING(PERIODIC_INQUIRY_MODE)
+      OP_CODE_MAPPING(EXIT_PERIODIC_INQUIRY_MODE)
+      OP_CODE_MAPPING(CREATE_CONNECTION)
+      OP_CODE_MAPPING(DISCONNECT)
+      OP_CODE_MAPPING(CREATE_CONNECTION_CANCEL)
+      OP_CODE_MAPPING(ACCEPT_CONNECTION_REQUEST)
+      OP_CODE_MAPPING(REJECT_CONNECTION_REQUEST)
+      OP_CODE_MAPPING(LINK_KEY_REQUEST_REPLY)
+      OP_CODE_MAPPING(LINK_KEY_REQUEST_NEGATIVE_REPLY)
+      OP_CODE_MAPPING(PIN_CODE_REQUEST_REPLY)
+      OP_CODE_MAPPING(PIN_CODE_REQUEST_NEGATIVE_REPLY)
+      OP_CODE_MAPPING(CHANGE_CONNECTION_PACKET_TYPE)
+      OP_CODE_MAPPING(AUTHENTICATION_REQUESTED)
+      OP_CODE_MAPPING(SET_CONNECTION_ENCRYPTION)
+      OP_CODE_MAPPING(CHANGE_CONNECTION_LINK_KEY)
+      OP_CODE_MAPPING(MASTER_LINK_KEY)
+      OP_CODE_MAPPING(REMOTE_NAME_REQUEST)
+      OP_CODE_MAPPING(REMOTE_NAME_REQUEST_CANCEL)
+      OP_CODE_MAPPING(READ_REMOTE_SUPPORTED_FEATURES)
+      OP_CODE_MAPPING(READ_REMOTE_EXTENDED_FEATURES)
+      OP_CODE_MAPPING(READ_REMOTE_VERSION_INFORMATION)
+      OP_CODE_MAPPING(READ_CLOCK_OFFSET)
+      OP_CODE_MAPPING(READ_LMP_HANDLE)
+      OP_CODE_MAPPING(HOLD_MODE)
+      OP_CODE_MAPPING(SNIFF_MODE)
+      OP_CODE_MAPPING(EXIT_SNIFF_MODE)
+      OP_CODE_MAPPING(QOS_SETUP)
+      OP_CODE_MAPPING(ROLE_DISCOVERY)
+      OP_CODE_MAPPING(SWITCH_ROLE)
+      OP_CODE_MAPPING(READ_LINK_POLICY_SETTINGS)
+      OP_CODE_MAPPING(WRITE_LINK_POLICY_SETTINGS)
+      OP_CODE_MAPPING(READ_DEFAULT_LINK_POLICY_SETTINGS)
+      OP_CODE_MAPPING(WRITE_DEFAULT_LINK_POLICY_SETTINGS)
+      OP_CODE_MAPPING(FLOW_SPECIFICATION)
+      OP_CODE_MAPPING(SET_EVENT_MASK)
+      OP_CODE_MAPPING(RESET)
+      OP_CODE_MAPPING(SET_EVENT_FILTER)
+      OP_CODE_MAPPING(FLUSH)
+      OP_CODE_MAPPING(READ_PIN_TYPE)
+      OP_CODE_MAPPING(WRITE_PIN_TYPE)
+      OP_CODE_MAPPING(READ_STORED_LINK_KEY)
+      OP_CODE_MAPPING(WRITE_STORED_LINK_KEY)
+      OP_CODE_MAPPING(DELETE_STORED_LINK_KEY)
+      OP_CODE_MAPPING(WRITE_LOCAL_NAME)
+      OP_CODE_MAPPING(READ_LOCAL_NAME)
+      OP_CODE_MAPPING(READ_CONNECTION_ACCEPT_TIMEOUT)
+      OP_CODE_MAPPING(WRITE_CONNECTION_ACCEPT_TIMEOUT)
+      OP_CODE_MAPPING(READ_PAGE_TIMEOUT)
+      OP_CODE_MAPPING(WRITE_PAGE_TIMEOUT)
+      OP_CODE_MAPPING(READ_SCAN_ENABLE)
+      OP_CODE_MAPPING(WRITE_SCAN_ENABLE)
+      OP_CODE_MAPPING(READ_PAGE_SCAN_ACTIVITY)
+      OP_CODE_MAPPING(WRITE_PAGE_SCAN_ACTIVITY)
+      OP_CODE_MAPPING(READ_INQUIRY_SCAN_ACTIVITY)
+      OP_CODE_MAPPING(WRITE_INQUIRY_SCAN_ACTIVITY)
+      OP_CODE_MAPPING(READ_AUTHENTICATION_ENABLE)
+      OP_CODE_MAPPING(WRITE_AUTHENTICATION_ENABLE)
+      OP_CODE_MAPPING(READ_CLASS_OF_DEVICE)
+      OP_CODE_MAPPING(WRITE_CLASS_OF_DEVICE)
+      OP_CODE_MAPPING(READ_VOICE_SETTING)
+      OP_CODE_MAPPING(WRITE_VOICE_SETTING)
+      OP_CODE_MAPPING(READ_AUTOMATIC_FLUSH_TIMEOUT)
+      OP_CODE_MAPPING(WRITE_AUTOMATIC_FLUSH_TIMEOUT)
+      OP_CODE_MAPPING(READ_NUM_BROADCAST_RETRANSMITS)
+      OP_CODE_MAPPING(WRITE_NUM_BROADCAST_RETRANSMITS)
+      OP_CODE_MAPPING(READ_HOLD_MODE_ACTIVITY)
+      OP_CODE_MAPPING(WRITE_HOLD_MODE_ACTIVITY)
+      OP_CODE_MAPPING(READ_TRANSMIT_POWER_LEVEL)
+      OP_CODE_MAPPING(READ_SYNCHRONOUS_FLOW_CONTROL_ENABLE)
+      OP_CODE_MAPPING(WRITE_SYNCHRONOUS_FLOW_CONTROL_ENABLE)
+      OP_CODE_MAPPING(SET_CONTROLLER_TO_HOST_FLOW_CONTROL)
+      OP_CODE_MAPPING(HOST_BUFFER_SIZE)
+      OP_CODE_MAPPING(HOST_NUM_COMPLETED_PACKETS)
+      OP_CODE_MAPPING(READ_LINK_SUPERVISION_TIMEOUT)
+      OP_CODE_MAPPING(WRITE_LINK_SUPERVISION_TIMEOUT)
+      OP_CODE_MAPPING(READ_NUMBER_OF_SUPPORTED_IAC)
+      OP_CODE_MAPPING(READ_CURRENT_IAC_LAP)
+      OP_CODE_MAPPING(WRITE_CURRENT_IAC_LAP)
+      OP_CODE_MAPPING(SET_AFH_HOST_CHANNEL_CLASSIFICATION)
+      OP_CODE_MAPPING(READ_INQUIRY_SCAN_TYPE)
+      OP_CODE_MAPPING(WRITE_INQUIRY_SCAN_TYPE)
+      OP_CODE_MAPPING(READ_INQUIRY_MODE)
+      OP_CODE_MAPPING(WRITE_INQUIRY_MODE)
+      OP_CODE_MAPPING(READ_PAGE_SCAN_TYPE)
+      OP_CODE_MAPPING(WRITE_PAGE_SCAN_TYPE)
+      OP_CODE_MAPPING(READ_AFH_CHANNEL_ASSESSMENT_MODE)
+      OP_CODE_MAPPING(WRITE_AFH_CHANNEL_ASSESSMENT_MODE)
+      OP_CODE_MAPPING(READ_LOCAL_VERSION_INFORMATION)
+      OP_CODE_MAPPING(READ_LOCAL_SUPPORTED_FEATURES)
+      OP_CODE_MAPPING(READ_LOCAL_EXTENDED_FEATURES)
+      OP_CODE_MAPPING(READ_BUFFER_SIZE)
+      OP_CODE_MAPPING(READ_BD_ADDR)
+      OP_CODE_MAPPING(READ_FAILED_CONTACT_COUNTER)
+      OP_CODE_MAPPING(RESET_FAILED_CONTACT_COUNTER)
+      OP_CODE_MAPPING(READ_LINK_QUALITY)
+      OP_CODE_MAPPING(READ_RSSI)
+      OP_CODE_MAPPING(READ_AFH_CHANNEL_MAP)
+      OP_CODE_MAPPING(READ_CLOCK)
+      OP_CODE_MAPPING(READ_LOOPBACK_MODE)
+      OP_CODE_MAPPING(WRITE_LOOPBACK_MODE)
+      OP_CODE_MAPPING(ENABLE_DEVICE_UNDER_TEST_MODE)
+      OP_CODE_MAPPING(SETUP_SYNCHRONOUS_CONNECTION)
+      OP_CODE_MAPPING(ACCEPT_SYNCHRONOUS_CONNECTION)
+      OP_CODE_MAPPING(REJECT_SYNCHRONOUS_CONNECTION)
+      OP_CODE_MAPPING(READ_EXTENDED_INQUIRY_RESPONSE)
+      OP_CODE_MAPPING(WRITE_EXTENDED_INQUIRY_RESPONSE)
+      OP_CODE_MAPPING(REFRESH_ENCRYPTION_KEY)
+      OP_CODE_MAPPING(SNIFF_SUBRATING)
+      OP_CODE_MAPPING(READ_SIMPLE_PAIRING_MODE)
+      OP_CODE_MAPPING(WRITE_SIMPLE_PAIRING_MODE)
+      OP_CODE_MAPPING(READ_LOCAL_OOB_DATA)
+      OP_CODE_MAPPING(READ_INQUIRY_RESPONSE_TRANSMIT_POWER_LEVEL)
+      OP_CODE_MAPPING(WRITE_INQUIRY_TRANSMIT_POWER_LEVEL)
+      OP_CODE_MAPPING(IO_CAPABILITY_REQUEST_REPLY)
+      OP_CODE_MAPPING(USER_CONFIRMATION_REQUEST_REPLY)
+      OP_CODE_MAPPING(USER_CONFIRMATION_REQUEST_NEGATIVE_REPLY)
+      OP_CODE_MAPPING(USER_PASSKEY_REQUEST_REPLY)
+      OP_CODE_MAPPING(USER_PASSKEY_REQUEST_NEGATIVE_REPLY)
+      OP_CODE_MAPPING(REMOTE_OOB_DATA_REQUEST_REPLY)
+      OP_CODE_MAPPING(WRITE_SIMPLE_PAIRING_DEBUG_MODE)
+      OP_CODE_MAPPING(REMOTE_OOB_DATA_REQUEST_NEGATIVE_REPLY)
+      OP_CODE_MAPPING(SEND_KEYPRESS_NOTIFICATION)
+      OP_CODE_MAPPING(IO_CAPABILITY_REQUEST_NEGATIVE_REPLY)
+      OP_CODE_MAPPING(READ_ENCRYPTION_KEY_SIZE)
+      OP_CODE_MAPPING(READ_DATA_BLOCK_SIZE)
+      OP_CODE_MAPPING(READ_LE_HOST_SUPPORT)
+      OP_CODE_MAPPING(WRITE_LE_HOST_SUPPORT)
+      OP_CODE_MAPPING(LE_SET_EVENT_MASK)
+      OP_CODE_MAPPING(LE_READ_BUFFER_SIZE)
+      OP_CODE_MAPPING(LE_READ_LOCAL_SUPPORTED_FEATURES)
+      OP_CODE_MAPPING(LE_SET_RANDOM_ADDRESS)
+      OP_CODE_MAPPING(LE_SET_ADVERTISING_PARAMETERS)
+      OP_CODE_MAPPING(LE_READ_ADVERTISING_CHANNEL_TX_POWER)
+      OP_CODE_MAPPING(LE_SET_ADVERTISING_DATA)
+      OP_CODE_MAPPING(LE_SET_SCAN_RESPONSE_DATA)
+      OP_CODE_MAPPING(LE_SET_ADVERTISING_ENABLE)
+      OP_CODE_MAPPING(LE_SET_SCAN_PARAMETERS)
+      OP_CODE_MAPPING(LE_SET_SCAN_ENABLE)
+      OP_CODE_MAPPING(LE_CREATE_CONNECTION)
+      OP_CODE_MAPPING(LE_CREATE_CONNECTION_CANCEL)
+      OP_CODE_MAPPING(LE_READ_WHITE_LIST_SIZE)
+      OP_CODE_MAPPING(LE_CLEAR_WHITE_LIST)
+      OP_CODE_MAPPING(LE_ADD_DEVICE_TO_WHITE_LIST)
+      OP_CODE_MAPPING(LE_REMOVE_DEVICE_FROM_WHITE_LIST)
+      OP_CODE_MAPPING(LE_CONNECTION_UPDATE)
+      OP_CODE_MAPPING(LE_SET_HOST_CHANNEL_CLASSIFICATION)
+      OP_CODE_MAPPING(LE_READ_CHANNEL_MAP)
+      OP_CODE_MAPPING(LE_READ_REMOTE_FEATURES)
+      OP_CODE_MAPPING(LE_ENCRYPT)
+      OP_CODE_MAPPING(LE_RAND)
+      OP_CODE_MAPPING(LE_START_ENCRYPTION)
+      OP_CODE_MAPPING(LE_LONG_TERM_KEY_REQUEST_REPLY)
+      OP_CODE_MAPPING(LE_LONG_TERM_KEY_REQUEST_NEGATIVE_REPLY)
+      OP_CODE_MAPPING(LE_READ_SUPPORTED_STATES)
+      OP_CODE_MAPPING(LE_RECEIVER_TEST)
+      OP_CODE_MAPPING(LE_TRANSMITTER_TEST)
+      OP_CODE_MAPPING(LE_TEST_END)
+      OP_CODE_MAPPING(ENHANCED_SETUP_SYNCHRONOUS_CONNECTION)
+      OP_CODE_MAPPING(ENHANCED_ACCEPT_SYNCHRONOUS_CONNECTION)
+      OP_CODE_MAPPING(READ_LOCAL_SUPPORTED_CODECS)
+      OP_CODE_MAPPING(READ_SECURE_CONNECTIONS_HOST_SUPPORT)
+      OP_CODE_MAPPING(WRITE_SECURE_CONNECTIONS_HOST_SUPPORT)
+      OP_CODE_MAPPING(READ_LOCAL_OOB_EXTENDED_DATA)
+      OP_CODE_MAPPING(WRITE_SECURE_CONNECTIONS_TEST_MODE)
+      OP_CODE_MAPPING(LE_REMOTE_CONNECTION_PARAMETER_REQUEST_REPLY)
+      OP_CODE_MAPPING(LE_REMOTE_CONNECTION_PARAMETER_REQUEST_NEGATIVE_REPLY)
+      OP_CODE_MAPPING(LE_SET_DATA_LENGTH)
+      OP_CODE_MAPPING(LE_READ_SUGGESTED_DEFAULT_DATA_LENGTH)
+      OP_CODE_MAPPING(LE_WRITE_SUGGESTED_DEFAULT_DATA_LENGTH)
+      OP_CODE_MAPPING(LE_READ_LOCAL_P_256_PUBLIC_KEY_COMMAND)
+      OP_CODE_MAPPING(LE_GENERATE_DHKEY_COMMAND_V1)
+      OP_CODE_MAPPING(LE_ADD_DEVICE_TO_RESOLVING_LIST)
+      OP_CODE_MAPPING(LE_REMOVE_DEVICE_FROM_RESOLVING_LIST)
+      OP_CODE_MAPPING(LE_CLEAR_RESOLVING_LIST)
+      OP_CODE_MAPPING(LE_READ_RESOLVING_LIST_SIZE)
+      OP_CODE_MAPPING(LE_READ_PEER_RESOLVABLE_ADDRESS)
+      OP_CODE_MAPPING(LE_READ_LOCAL_RESOLVABLE_ADDRESS)
+      OP_CODE_MAPPING(LE_SET_ADDRESS_RESOLUTION_ENABLE)
+      OP_CODE_MAPPING(LE_SET_RESOLVABLE_PRIVATE_ADDRESS_TIMEOUT)
+      OP_CODE_MAPPING(LE_READ_MAXIMUM_DATA_LENGTH)
+      OP_CODE_MAPPING(LE_READ_PHY)
+      OP_CODE_MAPPING(LE_SET_DEFAULT_PHY)
+      OP_CODE_MAPPING(LE_SET_PHY)
+      OP_CODE_MAPPING(LE_ENHANCED_RECEIVER_TEST)
+      OP_CODE_MAPPING(LE_ENHANCED_TRANSMITTER_TEST)
+      OP_CODE_MAPPING(LE_SET_EXTENDED_ADVERTISING_RANDOM_ADDRESS)
+      OP_CODE_MAPPING(LE_SET_EXTENDED_ADVERTISING_PARAMETERS)
+      OP_CODE_MAPPING(LE_SET_EXTENDED_ADVERTISING_DATA)
+      OP_CODE_MAPPING(LE_SET_EXTENDED_ADVERTISING_SCAN_RESPONSE)
+      OP_CODE_MAPPING(LE_SET_EXTENDED_ADVERTISING_ENABLE)
+      OP_CODE_MAPPING(LE_READ_MAXIMUM_ADVERTISING_DATA_LENGTH)
+      OP_CODE_MAPPING(LE_READ_NUMBER_OF_SUPPORTED_ADVERTISING_SETS)
+      OP_CODE_MAPPING(LE_REMOVE_ADVERTISING_SET)
+      OP_CODE_MAPPING(LE_CLEAR_ADVERTISING_SETS)
+      OP_CODE_MAPPING(LE_SET_PERIODIC_ADVERTISING_PARAM)
+      OP_CODE_MAPPING(LE_SET_PERIODIC_ADVERTISING_DATA)
+      OP_CODE_MAPPING(LE_SET_PERIODIC_ADVERTISING_ENABLE)
+      OP_CODE_MAPPING(LE_SET_EXTENDED_SCAN_PARAMETERS)
+      OP_CODE_MAPPING(LE_SET_EXTENDED_SCAN_ENABLE)
+      OP_CODE_MAPPING(LE_EXTENDED_CREATE_CONNECTION)
+      OP_CODE_MAPPING(LE_PERIODIC_ADVERTISING_CREATE_SYNC)
+      OP_CODE_MAPPING(LE_PERIODIC_ADVERTISING_CREATE_SYNC_CANCEL)
+      OP_CODE_MAPPING(LE_PERIODIC_ADVERTISING_TERMINATE_SYNC)
+      OP_CODE_MAPPING(LE_ADD_DEVICE_TO_PERIODIC_ADVERTISING_LIST)
+      OP_CODE_MAPPING(LE_REMOVE_DEVICE_FROM_PERIODIC_ADVERTISING_LIST)
+      OP_CODE_MAPPING(LE_CLEAR_PERIODIC_ADVERTISING_LIST)
+      OP_CODE_MAPPING(LE_READ_PERIODIC_ADVERTISING_LIST_SIZE)
+      OP_CODE_MAPPING(LE_READ_TRANSMIT_POWER)
+      OP_CODE_MAPPING(LE_READ_RF_PATH_COMPENSATION_POWER)
+      OP_CODE_MAPPING(LE_WRITE_RF_PATH_COMPENSATION_POWER)
+      OP_CODE_MAPPING(LE_SET_PRIVACY_MODE)
+      OP_CODE_MAPPING(LE_GENERATE_DHKEY_COMMAND)
+      // vendor specific
+      case OpCode::LE_GET_VENDOR_CAPABILITIES:
+        return vendor_capabilities_.is_supported_ == 0x01;
+      case OpCode::LE_MULTI_ADVT:
+        return vendor_capabilities_.max_advt_instances_ != 0x00;
+      case OpCode::LE_BATCH_SCAN:
+        return vendor_capabilities_.total_scan_results_storage_ != 0x00;
+      case OpCode::LE_ADV_FILTER:
+        return vendor_capabilities_.filtering_support_ == 0x01;
+      case OpCode::LE_TRACK_ADV:
+        return vendor_capabilities_.total_num_of_advt_tracked_ > 0;
+      case OpCode::LE_ENERGY_INFO:
+        return vendor_capabilities_.activity_energy_info_support_ == 0x01;
+      case OpCode::LE_EXTENDED_SCAN_PARAMS:
+        return vendor_capabilities_.extended_scan_support_ == 0x01;
+      case OpCode::CONTROLLER_DEBUG_INFO:
+        return vendor_capabilities_.debug_logging_supported_ == 0x01;
+      case OpCode::CONTROLLER_A2DP_OPCODE:
+        return vendor_capabilities_.a2dp_source_offload_capability_mask_ != 0x00;
+      case OpCode::CONTROLLER_BQR:
+        return vendor_capabilities_.bluetooth_quality_report_support_ == 0x01;
+      // undefined in local_supported_commands_
+      case OpCode::CREATE_NEW_UNIT_KEY:
+      case OpCode::READ_LOCAL_SUPPORTED_COMMANDS:
+        return true;
+      case OpCode::NONE:
+        return false;
+    }
+    return false;
+  }
+#undef OP_CODE_MAPPING
+
+  Controller& module_;
+
+  HciLayer* hci_;
+
+  Callback<void(uint16_t, uint16_t)> acl_credits_callback_;
+  Handler* acl_credits_handler_ = nullptr;
+  LocalVersionInformation local_version_information_;
+  std::array<uint8_t, 64> local_supported_commands_;
+  uint64_t local_supported_features_;
+  uint8_t maximum_page_number_;
+  std::vector<uint64_t> extended_lmp_features_array_;
+  uint16_t acl_buffer_length_ = 0;
+  uint16_t acl_buffers_ = 0;
+  uint8_t sco_buffer_length_ = 0;
+  uint16_t sco_buffers_ = 0;
+  Address mac_address_;
+  std::string local_name_;
+  LeBufferSize le_buffer_size_;
+  uint64_t le_local_supported_features_;
+  uint64_t le_supported_states_;
+  LeMaximumDataLength le_maximum_data_length_;
+  uint16_t le_maximum_advertising_data_length_;
+  uint8_t le_number_supported_advertising_sets_;
+  VendorCapabilities vendor_capabilities_;
+};  // namespace hci
+
+Controller::Controller() : impl_(std::make_unique<impl>(*this)) {}
+
+Controller::~Controller() = default;
+
+void Controller::RegisterCompletedAclPacketsCallback(Callback<void(uint16_t /* handle */, uint16_t /* packets */)> cb,
+                                                     Handler* handler) {
+  impl_->RegisterCompletedAclPacketsCallback(cb, handler);  // TODO hsz: why here?
+}
+
+std::string Controller::GetControllerLocalName() const {
+  return impl_->local_name_;
+}
+
+LocalVersionInformation Controller::GetControllerLocalVersionInformation() const {
+  return impl_->local_version_information_;
+}
+
+std::array<uint8_t, 64> Controller::GetControllerLocalSupportedCommands() const {
+  return impl_->local_supported_commands_;
+}
+
+uint8_t Controller::GetControllerLocalExtendedFeaturesMaxPageNumber() const {
+  return impl_->maximum_page_number_;
+}
+
+uint64_t Controller::GetControllerLocalSupportedFeatures() const {
+  return impl_->local_supported_features_;
+}
+
+uint64_t Controller::GetControllerLocalExtendedFeatures(uint8_t page_number) const {
+  if (page_number <= impl_->maximum_page_number_) {
+    return impl_->extended_lmp_features_array_[page_number];
+  }
+  return 0x00;
+}
+
+uint16_t Controller::GetControllerAclPacketLength() const {
+  return impl_->acl_buffer_length_;
+}
+
+uint16_t Controller::GetControllerNumAclPacketBuffers() const {
+  return impl_->acl_buffers_;
+}
+
+uint8_t Controller::GetControllerScoPacketLength() const {
+  return impl_->sco_buffer_length_;
+}
+
+uint16_t Controller::GetControllerNumScoPacketBuffers() const {
+  return impl_->sco_buffers_;
+}
+
+Address Controller::GetControllerMacAddress() const {
+  return impl_->mac_address_;
+}
+
+void Controller::SetEventMask(uint64_t event_mask) {
+  GetHandler()->Post(common::BindOnce(&impl::set_event_mask, common::Unretained(impl_.get()), event_mask));
+}
+
+void Controller::Reset() {
+  GetHandler()->Post(common::BindOnce(&impl::reset, common::Unretained(impl_.get())));
+}
+
+void Controller::SetEventFilterClearAll() {
+  std::unique_ptr<SetEventFilterClearAllBuilder> packet = SetEventFilterClearAllBuilder::Create();
+  GetHandler()->Post(common::BindOnce(&impl::set_event_filter, common::Unretained(impl_.get()), std::move(packet)));
+}
+
+void Controller::SetEventFilterInquiryResultAllDevices() {
+  std::unique_ptr<SetEventFilterInquiryResultAllDevicesBuilder> packet =
+      SetEventFilterInquiryResultAllDevicesBuilder::Create();
+  GetHandler()->Post(common::BindOnce(&impl::set_event_filter, common::Unretained(impl_.get()), std::move(packet)));
+}
+
+void Controller::SetEventFilterInquiryResultClassOfDevice(ClassOfDevice class_of_device,
+                                                          ClassOfDevice class_of_device_mask) {
+  std::unique_ptr<SetEventFilterInquiryResultClassOfDeviceBuilder> packet =
+      SetEventFilterInquiryResultClassOfDeviceBuilder::Create(class_of_device, class_of_device_mask);
+  GetHandler()->Post(common::BindOnce(&impl::set_event_filter, common::Unretained(impl_.get()), std::move(packet)));
+}
+
+void Controller::SetEventFilterInquiryResultAddress(Address address) {
+  std::unique_ptr<SetEventFilterInquiryResultAddressBuilder> packet =
+      SetEventFilterInquiryResultAddressBuilder::Create(address);
+  GetHandler()->Post(common::BindOnce(&impl::set_event_filter, common::Unretained(impl_.get()), std::move(packet)));
+}
+
+void Controller::SetEventFilterConnectionSetupAllDevices(AutoAcceptFlag auto_accept_flag) {
+  std::unique_ptr<SetEventFilterConnectionSetupAllDevicesBuilder> packet =
+      SetEventFilterConnectionSetupAllDevicesBuilder::Create(auto_accept_flag);
+  GetHandler()->Post(common::BindOnce(&impl::set_event_filter, common::Unretained(impl_.get()), std::move(packet)));
+}
+
+void Controller::SetEventFilterConnectionSetupClassOfDevice(ClassOfDevice class_of_device,
+                                                            ClassOfDevice class_of_device_mask,
+                                                            AutoAcceptFlag auto_accept_flag) {
+  std::unique_ptr<SetEventFilterConnectionSetupClassOfDeviceBuilder> packet =
+      SetEventFilterConnectionSetupClassOfDeviceBuilder::Create(class_of_device, class_of_device_mask,
+                                                                auto_accept_flag);
+  GetHandler()->Post(common::BindOnce(&impl::set_event_filter, common::Unretained(impl_.get()), std::move(packet)));
+}
+
+void Controller::SetEventFilterConnectionSetupAddress(Address address, AutoAcceptFlag auto_accept_flag) {
+  std::unique_ptr<SetEventFilterConnectionSetupAddressBuilder> packet =
+      SetEventFilterConnectionSetupAddressBuilder::Create(address, auto_accept_flag);
+  GetHandler()->Post(common::BindOnce(&impl::set_event_filter, common::Unretained(impl_.get()), std::move(packet)));
+}
+
+void Controller::WriteLocalName(std::string local_name) {
+  impl_->local_name_ = local_name;
+  GetHandler()->Post(common::BindOnce(&impl::write_local_name, common::Unretained(impl_.get()), local_name));
+}
+
+void Controller::HostBufferSize(uint16_t host_acl_data_packet_length, uint8_t host_synchronous_data_packet_length,
+                                uint16_t host_total_num_acl_data_packets,
+                                uint16_t host_total_num_synchronous_data_packets) {
+  GetHandler()->Post(common::BindOnce(&impl::host_buffer_size, common::Unretained(impl_.get()),
+                                      host_acl_data_packet_length, host_synchronous_data_packet_length,
+                                      host_total_num_acl_data_packets, host_total_num_synchronous_data_packets));
+}
+
+void Controller::LeSetEventMask(uint64_t le_event_mask) {
+  GetHandler()->Post(common::BindOnce(&impl::le_set_event_mask, common::Unretained(impl_.get()), le_event_mask));
+}
+
+LeBufferSize Controller::GetControllerLeBufferSize() const {
+  return impl_->le_buffer_size_;
+}
+
+uint64_t Controller::GetControllerLeLocalSupportedFeatures() const {
+  return impl_->le_local_supported_features_;
+}
+
+uint64_t Controller::GetControllerLeSupportedStates() const {
+  return impl_->le_supported_states_;
+}
+
+LeMaximumDataLength Controller::GetControllerLeMaximumDataLength() const {
+  return impl_->le_maximum_data_length_;
+}
+
+uint16_t Controller::GetControllerLeMaximumAdvertisingDataLength() const {
+  return impl_->le_maximum_advertising_data_length_;
+}
+
+uint8_t Controller::GetControllerLeNumberOfSupportedAdverisingSets() const {
+  return impl_->le_number_supported_advertising_sets_;
+}
+
+VendorCapabilities Controller::GetControllerVendorCapabilities() const {
+  return impl_->vendor_capabilities_;
+}
+
+bool Controller::IsSupported(bluetooth::hci::OpCode op_code) const {
+  return impl_->is_supported(op_code);
+}
+
+const ModuleFactory Controller::Factory = ModuleFactory([]() { return new Controller(); });
+
+void Controller::ListDependencies(ModuleList* list) {
+  list->add<hci::HciLayer>();
+}
+
+void Controller::Start() {
+  impl_->Start(GetDependency<hci::HciLayer>());
+}
+
+void Controller::Stop() {
+  impl_->Stop();
+}
+
+std::string Controller::ToString() const {
+  return "Controller";
+}
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/controller.h b/gd/hci/controller.h
new file mode 100644
index 0000000..ba02dcb
--- /dev/null
+++ b/gd/hci/controller.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "common/callback.h"
+#include "hci/address.h"
+#include "hci/hci_packets.h"
+#include "module.h"
+#include "os/handler.h"
+
+namespace bluetooth {
+namespace hci {
+
+class Controller : public Module {
+ public:
+  Controller();
+  virtual ~Controller();
+  DISALLOW_COPY_AND_ASSIGN(Controller);
+
+  virtual void RegisterCompletedAclPacketsCallback(
+      common::Callback<void(uint16_t /* handle */, uint16_t /* num_packets */)> cb, os::Handler* handler);
+
+  virtual std::string GetControllerLocalName() const;
+
+  virtual LocalVersionInformation GetControllerLocalVersionInformation() const;
+
+  virtual std::array<uint8_t, 64> GetControllerLocalSupportedCommands() const;
+
+  virtual uint64_t GetControllerLocalSupportedFeatures() const;
+
+  virtual uint8_t GetControllerLocalExtendedFeaturesMaxPageNumber() const;
+
+  virtual uint64_t GetControllerLocalExtendedFeatures(uint8_t page_number) const;
+
+  virtual uint16_t GetControllerAclPacketLength() const;
+
+  virtual uint16_t GetControllerNumAclPacketBuffers() const;
+
+  virtual uint8_t GetControllerScoPacketLength() const;
+
+  virtual uint16_t GetControllerNumScoPacketBuffers() const;
+
+  virtual Address GetControllerMacAddress() const;
+
+  virtual void SetEventMask(uint64_t event_mask);
+
+  virtual void Reset();
+
+  virtual void SetEventFilterClearAll();
+
+  virtual void SetEventFilterInquiryResultAllDevices();
+
+  virtual void SetEventFilterInquiryResultClassOfDevice(ClassOfDevice class_of_device,
+                                                        ClassOfDevice class_of_device_mask);
+
+  virtual void SetEventFilterInquiryResultAddress(Address address);
+
+  virtual void SetEventFilterConnectionSetupAllDevices(AutoAcceptFlag auto_accept_flag);
+
+  virtual void SetEventFilterConnectionSetupClassOfDevice(ClassOfDevice class_of_device,
+                                                          ClassOfDevice class_of_device_mask,
+                                                          AutoAcceptFlag auto_accept_flag);
+
+  virtual void SetEventFilterConnectionSetupAddress(Address address, AutoAcceptFlag auto_accept_flag);
+
+  virtual void WriteLocalName(std::string local_name);
+
+  virtual void HostBufferSize(uint16_t host_acl_data_packet_length, uint8_t host_synchronous_data_packet_length,
+                              uint16_t host_total_num_acl_data_packets,
+                              uint16_t host_total_num_synchronous_data_packets);
+
+  // LE controller commands
+  virtual void LeSetEventMask(uint64_t le_event_mask);
+
+  virtual LeBufferSize GetControllerLeBufferSize() const;
+
+  virtual uint64_t GetControllerLeLocalSupportedFeatures() const;
+
+  virtual uint64_t GetControllerLeSupportedStates() const;
+
+  virtual LeMaximumDataLength GetControllerLeMaximumDataLength() const;
+
+  virtual uint16_t GetControllerLeMaximumAdvertisingDataLength() const;
+
+  virtual uint8_t GetControllerLeNumberOfSupportedAdverisingSets() const;
+
+  virtual VendorCapabilities GetControllerVendorCapabilities() const;
+
+  virtual bool IsSupported(OpCode op_code) const;
+
+  static const ModuleFactory Factory;
+
+  static constexpr uint64_t kDefaultEventMask = 0x3dbfffffffffffff;
+
+ protected:
+  void ListDependencies(ModuleList* list) override;
+
+  void Start() override;
+
+  void Stop() override;
+
+  std::string ToString() const override;
+
+ private:
+  struct impl;
+  std::unique_ptr<impl> impl_;
+};
+
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/controller_test.cc b/gd/hci/controller_test.cc
new file mode 100644
index 0000000..c77ed7b
--- /dev/null
+++ b/gd/hci/controller_test.cc
@@ -0,0 +1,465 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "hci/controller.h"
+
+#include <algorithm>
+#include <chrono>
+#include <future>
+#include <map>
+
+#include <gtest/gtest.h>
+
+#include "common/bind.h"
+#include "common/callback.h"
+#include "hci/address.h"
+#include "hci/hci_layer.h"
+#include "os/thread.h"
+#include "packet/raw_builder.h"
+
+namespace bluetooth {
+namespace hci {
+namespace {
+
+using common::BidiQueue;
+using common::BidiQueueEnd;
+using packet::kLittleEndian;
+using packet::PacketView;
+using packet::RawBuilder;
+
+constexpr uint16_t kHandle1 = 0x123;
+constexpr uint16_t kCredits1 = 0x78;
+constexpr uint16_t kHandle2 = 0x456;
+constexpr uint16_t kCredits2 = 0x9a;
+uint16_t feature_spec_version = 55;
+
+PacketView<kLittleEndian> GetPacketView(std::unique_ptr<packet::BasePacketBuilder> packet) {
+  auto bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter i(*bytes);
+  bytes->reserve(packet->size());
+  packet->Serialize(i);
+  return packet::PacketView<packet::kLittleEndian>(bytes);
+}
+
+class TestHciLayer : public HciLayer {
+ public:
+  void EnqueueCommand(std::unique_ptr<CommandPacketBuilder> command,
+                      common::OnceCallback<void(CommandCompleteView)> on_complete, os::Handler* handler) override {
+    GetHandler()->Post(common::BindOnce(&TestHciLayer::HandleCommand, common::Unretained(this), std::move(command),
+                                        std::move(on_complete), common::Unretained(handler)));
+  }
+
+  void EnqueueCommand(std::unique_ptr<CommandPacketBuilder> command,
+                      common::OnceCallback<void(CommandStatusView)> on_status, os::Handler* handler) override {
+    EXPECT_TRUE(false) << "Controller properties should not generate Command Status";
+  }
+
+  void HandleCommand(std::unique_ptr<CommandPacketBuilder> command_builder,
+                     common::OnceCallback<void(CommandCompleteView)> on_complete, os::Handler* handler) {
+    auto packet_view = GetPacketView(std::move(command_builder));
+    CommandPacketView command = CommandPacketView::Create(packet_view);
+    ASSERT(command.IsValid());
+
+    uint8_t num_packets = 1;
+    std::unique_ptr<packet::BasePacketBuilder> event_builder;
+    switch (command.GetOpCode()) {
+      case (OpCode::READ_LOCAL_NAME): {
+        std::array<uint8_t, 248> local_name = {'D', 'U', 'T', '\0'};
+        event_builder = ReadLocalNameCompleteBuilder::Create(num_packets, ErrorCode::SUCCESS, local_name);
+      } break;
+      case (OpCode::READ_LOCAL_VERSION_INFORMATION): {
+        LocalVersionInformation local_version_information;
+        local_version_information.hci_version_ = HciVersion::V_5_0;
+        local_version_information.hci_revision_ = 0x1234;
+        local_version_information.lmp_version_ = LmpVersion::V_4_2;
+        local_version_information.manufacturer_name_ = 0xBAD;
+        local_version_information.lmp_subversion_ = 0x5678;
+        event_builder = ReadLocalVersionInformationCompleteBuilder::Create(num_packets, ErrorCode::SUCCESS,
+                                                                           local_version_information);
+      } break;
+      case (OpCode::READ_LOCAL_SUPPORTED_COMMANDS): {
+        std::array<uint8_t, 64> supported_commands;
+        for (int i = 0; i < 37; i++) {
+          supported_commands[i] = 0xff;
+        }
+        for (int i = 37; i < 64; i++) {
+          supported_commands[i] = 0x00;
+        }
+        event_builder =
+            ReadLocalSupportedCommandsCompleteBuilder::Create(num_packets, ErrorCode::SUCCESS, supported_commands);
+      } break;
+      case (OpCode::READ_LOCAL_SUPPORTED_FEATURES): {
+        uint64_t lmp_features = 0x012345678abcdef;
+        event_builder =
+            ReadLocalSupportedFeaturesCompleteBuilder::Create(num_packets, ErrorCode::SUCCESS, lmp_features);
+      } break;
+      case (OpCode::READ_LOCAL_EXTENDED_FEATURES): {
+        ReadLocalExtendedFeaturesView read_command = ReadLocalExtendedFeaturesView::Create(command);
+        ASSERT(read_command.IsValid());
+        uint8_t page_bumber = read_command.GetPageNumber();
+        uint64_t lmp_features = 0x012345678abcdef;
+        lmp_features += page_bumber;
+        event_builder = ReadLocalExtendedFeaturesCompleteBuilder::Create(num_packets, ErrorCode::SUCCESS, page_bumber,
+                                                                         0x02, lmp_features);
+      } break;
+      case (OpCode::READ_BUFFER_SIZE): {
+        event_builder = ReadBufferSizeCompleteBuilder::Create(
+            num_packets, ErrorCode::SUCCESS, acl_data_packet_length, synchronous_data_packet_length,
+            total_num_acl_data_packets, total_num_synchronous_data_packets);
+      } break;
+      case (OpCode::READ_BD_ADDR): {
+        event_builder = ReadBdAddrCompleteBuilder::Create(num_packets, ErrorCode::SUCCESS, Address::kAny);
+      } break;
+      case (OpCode::LE_READ_BUFFER_SIZE): {
+        LeBufferSize le_buffer_size;
+        le_buffer_size.le_data_packet_length_ = 0x16;
+        le_buffer_size.total_num_le_packets_ = 0x08;
+        event_builder = LeReadBufferSizeCompleteBuilder::Create(num_packets, ErrorCode::SUCCESS, le_buffer_size);
+      } break;
+      case (OpCode::LE_READ_LOCAL_SUPPORTED_FEATURES): {
+        event_builder =
+            LeReadLocalSupportedFeaturesCompleteBuilder::Create(num_packets, ErrorCode::SUCCESS, 0x001f123456789abc);
+      } break;
+      case (OpCode::LE_READ_SUPPORTED_STATES): {
+        event_builder =
+            LeReadSupportedStatesCompleteBuilder::Create(num_packets, ErrorCode::SUCCESS, 0x001f123456789abe);
+      } break;
+      case (OpCode::LE_READ_MAXIMUM_DATA_LENGTH): {
+        LeMaximumDataLength le_maximum_data_length;
+        le_maximum_data_length.supported_max_tx_octets_ = 0x12;
+        le_maximum_data_length.supported_max_tx_time_ = 0x34;
+        le_maximum_data_length.supported_max_rx_octets_ = 0x56;
+        le_maximum_data_length.supported_max_rx_time_ = 0x78;
+        event_builder =
+            LeReadMaximumDataLengthCompleteBuilder::Create(num_packets, ErrorCode::SUCCESS, le_maximum_data_length);
+      } break;
+      case (OpCode::LE_READ_MAXIMUM_ADVERTISING_DATA_LENGTH): {
+        event_builder =
+            LeReadMaximumAdvertisingDataLengthCompleteBuilder::Create(num_packets, ErrorCode::SUCCESS, 0x0672);
+      } break;
+      case (OpCode::LE_READ_NUMBER_OF_SUPPORTED_ADVERTISING_SETS): {
+        event_builder =
+            LeReadNumberOfSupportedAdvertisingSetsCompleteBuilder::Create(num_packets, ErrorCode::SUCCESS, 0xF0);
+      } break;
+      case (OpCode::LE_GET_VENDOR_CAPABILITIES): {
+        BaseVendorCapabilities base_vendor_capabilities;
+        base_vendor_capabilities.max_advt_instances_ = 0x10;
+        base_vendor_capabilities.offloaded_resolution_of_private_address_ = 0x01;
+        base_vendor_capabilities.total_scan_results_storage_ = 0x2800;
+        base_vendor_capabilities.max_irk_list_sz_ = 0x20;
+        base_vendor_capabilities.filtering_support_ = 0x01;
+        base_vendor_capabilities.max_filter_ = 0x10;
+        base_vendor_capabilities.activity_energy_info_support_ = 0x01;
+
+        auto payload = std::make_unique<RawBuilder>();
+        if (feature_spec_version > 55) {
+          std::vector<uint8_t> payload_bytes = {0x20, 0x00, 0x01, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00};
+          payload->AddOctets2(feature_spec_version);
+          payload->AddOctets(payload_bytes);
+        }
+        event_builder = LeGetVendorCapabilitiesCompleteBuilder::Create(num_packets, ErrorCode::SUCCESS,
+                                                                       base_vendor_capabilities, std::move(payload));
+      } break;
+      case (OpCode::SET_EVENT_MASK): {
+        auto view = SetEventMaskView::Create(command);
+        ASSERT(view.IsValid());
+        event_mask = view.GetEventMask();
+        event_builder = SetEventMaskCompleteBuilder::Create(num_packets, ErrorCode::SUCCESS);
+      } break;
+      case (OpCode::RESET):
+      case (OpCode::SET_EVENT_FILTER):
+      case (OpCode::HOST_BUFFER_SIZE):
+      case (OpCode::LE_SET_EVENT_MASK):
+        command_queue_.push(command);
+        not_empty_.notify_all();
+        return;
+      default:
+        LOG_INFO("Dropping unhandled packet");
+        return;
+    }
+    auto packet = GetPacketView(std::move(event_builder));
+    EventPacketView event = EventPacketView::Create(packet);
+    ASSERT(event.IsValid());
+    CommandCompleteView command_complete = CommandCompleteView::Create(event);
+    ASSERT(command_complete.IsValid());
+    handler->Post(common::BindOnce(std::move(on_complete), std::move(command_complete)));
+  }
+
+  void RegisterEventHandler(EventCode event_code, common::Callback<void(EventPacketView)> event_handler,
+                            os::Handler* handler) override {
+    EXPECT_EQ(event_code, EventCode::NUMBER_OF_COMPLETED_PACKETS) << "Only NUMBER_OF_COMPLETED_PACKETS is needed";
+    number_of_completed_packets_callback_ = event_handler;
+    client_handler_ = handler;
+  }
+
+  void UnregisterEventHandler(EventCode event_code) override {
+    EXPECT_EQ(event_code, EventCode::NUMBER_OF_COMPLETED_PACKETS) << "Only NUMBER_OF_COMPLETED_PACKETS is needed";
+    number_of_completed_packets_callback_ = {};
+    client_handler_ = nullptr;
+  }
+
+  void IncomingCredit() {
+    std::vector<CompletedPackets> completed_packets;
+    CompletedPackets cp;
+    cp.host_num_of_completed_packets_ = kCredits1;
+    cp.connection_handle_ = kHandle1;
+    completed_packets.push_back(cp);
+    cp.host_num_of_completed_packets_ = kCredits2;
+    cp.connection_handle_ = kHandle2;
+    completed_packets.push_back(cp);
+    auto event_builder = NumberOfCompletedPacketsBuilder::Create(completed_packets);
+    auto packet = GetPacketView(std::move(event_builder));
+    EventPacketView event = EventPacketView::Create(packet);
+    ASSERT(event.IsValid());
+    client_handler_->Post(common::BindOnce(number_of_completed_packets_callback_, event));
+  }
+
+  CommandPacketView GetCommand(OpCode op_code) {
+    std::unique_lock<std::mutex> lock(mutex_);
+    std::chrono::milliseconds time = std::chrono::milliseconds(3000);
+
+    // wait for command
+    while (command_queue_.size() == 0) {
+      if (not_empty_.wait_for(lock, time) == std::cv_status::timeout) {
+        break;
+      }
+    }
+    ASSERT(command_queue_.size() > 0);
+    CommandPacketView command = command_queue_.front();
+    EXPECT_EQ(command.GetOpCode(), op_code);
+    command_queue_.pop();
+    return command;
+  }
+
+  void ListDependencies(ModuleList* list) override {}
+  void Start() override {}
+  void Stop() override {}
+
+  constexpr static uint16_t acl_data_packet_length = 1024;
+  constexpr static uint8_t synchronous_data_packet_length = 60;
+  constexpr static uint16_t total_num_acl_data_packets = 10;
+  constexpr static uint16_t total_num_synchronous_data_packets = 12;
+  uint64_t event_mask = 0;
+
+ private:
+  common::Callback<void(EventPacketView)> number_of_completed_packets_callback_;
+  os::Handler* client_handler_;
+  std::queue<CommandPacketView> command_queue_;
+  mutable std::mutex mutex_;
+  std::condition_variable not_empty_;
+};
+
+class ControllerTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    test_hci_layer_ = new TestHciLayer;
+    fake_registry_.InjectTestModule(&HciLayer::Factory, test_hci_layer_);
+    client_handler_ = fake_registry_.GetTestModuleHandler(&HciLayer::Factory);
+    fake_registry_.Start<Controller>(&thread_);
+    controller_ = static_cast<Controller*>(fake_registry_.GetModuleUnderTest(&Controller::Factory));
+  }
+
+  void TearDown() override {
+    fake_registry_.StopAll();
+  }
+
+  TestModuleRegistry fake_registry_;
+  TestHciLayer* test_hci_layer_ = nullptr;
+  os::Thread& thread_ = fake_registry_.GetTestThread();
+  Controller* controller_ = nullptr;
+  os::Handler* client_handler_ = nullptr;
+};
+
+TEST_F(ControllerTest, startup_teardown) {}
+
+TEST_F(ControllerTest, read_controller_info) {
+  ASSERT_EQ(controller_->GetControllerAclPacketLength(), test_hci_layer_->acl_data_packet_length);
+  ASSERT_EQ(controller_->GetControllerNumAclPacketBuffers(), test_hci_layer_->total_num_acl_data_packets);
+  ASSERT_EQ(controller_->GetControllerScoPacketLength(), test_hci_layer_->synchronous_data_packet_length);
+  ASSERT_EQ(controller_->GetControllerNumScoPacketBuffers(), test_hci_layer_->total_num_synchronous_data_packets);
+  ASSERT_EQ(controller_->GetControllerMacAddress(), Address::kAny);
+  LocalVersionInformation local_version_information = controller_->GetControllerLocalVersionInformation();
+  ASSERT_EQ(local_version_information.hci_version_, HciVersion::V_5_0);
+  ASSERT_EQ(local_version_information.hci_revision_, 0x1234);
+  ASSERT_EQ(local_version_information.lmp_version_, LmpVersion::V_4_2);
+  ASSERT_EQ(local_version_information.manufacturer_name_, 0xBAD);
+  ASSERT_EQ(local_version_information.lmp_subversion_, 0x5678);
+  std::array<uint8_t, 64> supported_commands;
+  for (int i = 0; i < 37; i++) {
+    supported_commands[i] = 0xff;
+  }
+  for (int i = 37; i < 64; i++) {
+    supported_commands[i] = 0x00;
+  }
+  ASSERT_EQ(controller_->GetControllerLocalSupportedCommands(), supported_commands);
+  ASSERT_EQ(controller_->GetControllerLocalSupportedFeatures(), 0x012345678abcdef);
+  ASSERT_EQ(controller_->GetControllerLocalExtendedFeaturesMaxPageNumber(), 0x02);
+  ASSERT_EQ(controller_->GetControllerLocalExtendedFeatures(0), 0x012345678abcdef);
+  ASSERT_EQ(controller_->GetControllerLocalExtendedFeatures(1), 0x012345678abcdf0);
+  ASSERT_EQ(controller_->GetControllerLocalExtendedFeatures(2), 0x012345678abcdf1);
+  ASSERT_EQ(controller_->GetControllerLocalExtendedFeatures(100), 0x00);
+  ASSERT_EQ(controller_->GetControllerLeBufferSize().le_data_packet_length_, 0x16);
+  ASSERT_EQ(controller_->GetControllerLeBufferSize().total_num_le_packets_, 0x08);
+  ASSERT_EQ(controller_->GetControllerLeLocalSupportedFeatures(), 0x001f123456789abc);
+  ASSERT_EQ(controller_->GetControllerLeSupportedStates(), 0x001f123456789abe);
+  ASSERT_EQ(controller_->GetControllerLeMaximumDataLength().supported_max_tx_octets_, 0x12);
+  ASSERT_EQ(controller_->GetControllerLeMaximumDataLength().supported_max_rx_octets_, 0x56);
+  ASSERT_EQ(controller_->GetControllerLeMaximumAdvertisingDataLength(), 0x0672);
+  ASSERT_EQ(controller_->GetControllerLeNumberOfSupportedAdverisingSets(), 0xF0);
+}
+
+TEST_F(ControllerTest, read_write_local_name) {
+  ASSERT_EQ(controller_->GetControllerLocalName(), "DUT");
+  controller_->WriteLocalName("New name");
+  ASSERT_EQ(controller_->GetControllerLocalName(), "New name");
+}
+
+TEST_F(ControllerTest, send_set_event_mask_command) {
+  uint64_t new_event_mask = test_hci_layer_->event_mask - 1;
+  controller_->SetEventMask(new_event_mask);
+  // Send another command to make sure it was applied
+  controller_->Reset();
+  auto packet = test_hci_layer_->GetCommand(OpCode::RESET);
+  ASSERT_EQ(new_event_mask, test_hci_layer_->event_mask);
+}
+
+TEST_F(ControllerTest, send_reset_command) {
+  controller_->Reset();
+  auto packet = test_hci_layer_->GetCommand(OpCode::RESET);
+  auto command = ResetView::Create(packet);
+  ASSERT(command.IsValid());
+}
+
+TEST_F(ControllerTest, send_set_event_filter_command) {
+  controller_->SetEventFilterInquiryResultAllDevices();
+  auto packet = test_hci_layer_->GetCommand(OpCode::SET_EVENT_FILTER);
+  auto set_event_filter_view1 = SetEventFilterView::Create(packet);
+  auto set_event_filter_inquiry_result_view1 = SetEventFilterInquiryResultView::Create(set_event_filter_view1);
+  auto command1 = SetEventFilterInquiryResultAllDevicesView::Create(set_event_filter_inquiry_result_view1);
+  ASSERT(command1.IsValid());
+
+  ClassOfDevice class_of_device({0xab, 0xcd, 0xef});
+  ClassOfDevice class_of_device_mask({0x12, 0x34, 0x56});
+  controller_->SetEventFilterInquiryResultClassOfDevice(class_of_device, class_of_device_mask);
+  packet = test_hci_layer_->GetCommand(OpCode::SET_EVENT_FILTER);
+  auto set_event_filter_view2 = SetEventFilterView::Create(packet);
+  auto set_event_filter_inquiry_result_view2 = SetEventFilterInquiryResultView::Create(set_event_filter_view2);
+  auto command2 = SetEventFilterInquiryResultClassOfDeviceView::Create(set_event_filter_inquiry_result_view2);
+  ASSERT(command2.IsValid());
+  ASSERT_EQ(command2.GetClassOfDevice(), class_of_device);
+
+  Address bdaddr({0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc});
+  controller_->SetEventFilterConnectionSetupAddress(bdaddr, AutoAcceptFlag::AUTO_ACCEPT_ON_ROLE_SWITCH_ENABLED);
+  packet = test_hci_layer_->GetCommand(OpCode::SET_EVENT_FILTER);
+  auto set_event_filter_view3 = SetEventFilterView::Create(packet);
+  auto set_event_filter_connection_setup_view = SetEventFilterConnectionSetupView::Create(set_event_filter_view3);
+  auto command3 = SetEventFilterConnectionSetupAddressView::Create(set_event_filter_connection_setup_view);
+  ASSERT(command3.IsValid());
+  ASSERT_EQ(command3.GetAddress(), bdaddr);
+}
+
+TEST_F(ControllerTest, send_host_buffer_size_command) {
+  controller_->HostBufferSize(0xFF00, 0xF1, 0xFF02, 0xFF03);
+  auto packet = test_hci_layer_->GetCommand(OpCode::HOST_BUFFER_SIZE);
+  auto command = HostBufferSizeView::Create(packet);
+  ASSERT(command.IsValid());
+  ASSERT_EQ(command.GetHostAclDataPacketLength(), 0xFF00);
+  ASSERT_EQ(command.GetHostSynchronousDataPacketLength(), 0xF1);
+  ASSERT_EQ(command.GetHostTotalNumAclDataPackets(), 0xFF02);
+  ASSERT_EQ(command.GetHostTotalNumSynchronousDataPackets(), 0xFF03);
+}
+
+TEST_F(ControllerTest, send_le_set_event_mask_command) {
+  controller_->LeSetEventMask(0x000000000000001F);
+  auto packet = test_hci_layer_->GetCommand(OpCode::LE_SET_EVENT_MASK);
+  auto command = LeSetEventMaskView::Create(packet);
+  ASSERT(command.IsValid());
+  ASSERT_EQ(command.GetLeEventMask(), 0x000000000000001F);
+}
+
+TEST_F(ControllerTest, is_supported_test) {
+  ASSERT_TRUE(controller_->IsSupported(OpCode::INQUIRY));
+  ASSERT_TRUE(controller_->IsSupported(OpCode::REJECT_CONNECTION_REQUEST));
+  ASSERT_TRUE(controller_->IsSupported(OpCode::ACCEPT_CONNECTION_REQUEST));
+  ASSERT_FALSE(controller_->IsSupported(OpCode::LE_REMOVE_ADVERTISING_SET));
+  ASSERT_FALSE(controller_->IsSupported(OpCode::LE_CLEAR_ADVERTISING_SETS));
+  ASSERT_FALSE(controller_->IsSupported(OpCode::LE_SET_PERIODIC_ADVERTISING_PARAM));
+}
+
+TEST_F(ControllerTest, feature_spec_version_055_test) {
+  EXPECT_EQ(controller_->GetControllerVendorCapabilities().version_supported_, 55);
+  EXPECT_TRUE(controller_->IsSupported(OpCode::LE_MULTI_ADVT));
+  EXPECT_FALSE(controller_->IsSupported(OpCode::LE_TRACK_ADV));
+  EXPECT_FALSE(controller_->IsSupported(OpCode::CONTROLLER_DEBUG_INFO));
+  EXPECT_FALSE(controller_->IsSupported(OpCode::CONTROLLER_A2DP_OPCODE));
+  feature_spec_version = 95;
+}
+
+TEST_F(ControllerTest, feature_spec_version_095_test) {
+  EXPECT_EQ(controller_->GetControllerVendorCapabilities().version_supported_, 95);
+  EXPECT_TRUE(controller_->IsSupported(OpCode::LE_MULTI_ADVT));
+  EXPECT_TRUE(controller_->IsSupported(OpCode::LE_TRACK_ADV));
+  EXPECT_FALSE(controller_->IsSupported(OpCode::CONTROLLER_DEBUG_INFO));
+  EXPECT_FALSE(controller_->IsSupported(OpCode::CONTROLLER_A2DP_OPCODE));
+  feature_spec_version = 96;
+}
+
+TEST_F(ControllerTest, feature_spec_version_096_test) {
+  EXPECT_EQ(controller_->GetControllerVendorCapabilities().version_supported_, 96);
+  EXPECT_TRUE(controller_->IsSupported(OpCode::LE_MULTI_ADVT));
+  EXPECT_TRUE(controller_->IsSupported(OpCode::LE_TRACK_ADV));
+  EXPECT_FALSE(controller_->IsSupported(OpCode::CONTROLLER_DEBUG_INFO));
+  EXPECT_FALSE(controller_->IsSupported(OpCode::CONTROLLER_A2DP_OPCODE));
+  feature_spec_version = 98;
+}
+
+TEST_F(ControllerTest, feature_spec_version_098_test) {
+  EXPECT_EQ(controller_->GetControllerVendorCapabilities().version_supported_, 98);
+  EXPECT_TRUE(controller_->IsSupported(OpCode::LE_MULTI_ADVT));
+  EXPECT_TRUE(controller_->IsSupported(OpCode::LE_TRACK_ADV));
+  EXPECT_FALSE(controller_->IsSupported(OpCode::CONTROLLER_DEBUG_INFO));
+  EXPECT_TRUE(controller_->IsSupported(OpCode::CONTROLLER_A2DP_OPCODE));
+}
+
+std::promise<void> credits1_set;
+std::promise<void> credits2_set;
+
+void CheckReceivedCredits(uint16_t handle, uint16_t credits) {
+  switch (handle) {
+    case (kHandle1):
+      ASSERT_EQ(kCredits1, credits);
+      credits1_set.set_value();
+      break;
+    case (kHandle2):
+      ASSERT_EQ(kCredits2, credits);
+      credits2_set.set_value();
+      break;
+    default:
+      ASSERT_LOG(false, "Unknown handle 0x%0hx with 0x%0hx credits", handle, credits);
+  }
+}
+
+TEST_F(ControllerTest, aclCreditCallbacksTest) {
+  controller_->RegisterCompletedAclPacketsCallback(common::Bind(&CheckReceivedCredits), client_handler_);
+
+  test_hci_layer_->IncomingCredit();
+
+  credits1_set.get_future().wait();
+  credits2_set.get_future().wait();
+}
+}  // namespace
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/device.cc b/gd/hci/device.cc
new file mode 100644
index 0000000..d008dae
--- /dev/null
+++ b/gd/hci/device.cc
@@ -0,0 +1,32 @@
+/******************************************************************************
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+#include "hci/device.h"
+
+using namespace bluetooth::hci;
+
+std::string Device::generate_uid() {
+  // TODO(optedoblivion): Figure out a good way to do this for what we want
+  // to do
+  // TODO(optedoblivion): Need to make a way to override something for LE pub addr case
+  // Not sure if something like this is needed, but here is the idea (I think it came from mylesgw)
+  // CLASSIC: have all 0s in front for classic then have private address
+  // LE: have first public address in front then all 0s
+  // LE: have first public address in front then private address
+  //
+  return address_.ToString();
+}
diff --git a/gd/hci/device.h b/gd/hci/device.h
new file mode 100644
index 0000000..8324d00
--- /dev/null
+++ b/gd/hci/device.h
@@ -0,0 +1,136 @@
+/******************************************************************************
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+#pragma once
+
+#include <string>
+
+#include "hci/address.h"
+#include "hci/class_of_device.h"
+
+namespace bluetooth::hci {
+
+/**
+ * Used to determine device functionality
+ */
+enum DeviceType { DUAL, CLASSIC, LE };
+
+/**
+ * Represents a physical HCI device.
+ *
+ * <p>Contains all of the metadata required to represent a phycial device.
+ *
+ * <p>Devices should only be created and modified by HCI.
+ */
+class Device {
+ public:
+  virtual ~Device() = default;
+
+  Address GetAddress() const {
+    return address_;
+  }
+
+  /**
+   * Returns 1 of 3 enum values for device's type (DUAL, CLASSIC, LE)
+   */
+  DeviceType GetDeviceType() const {
+    return device_type_;
+  }
+
+  /**
+   * Unique identifier for bluetooth devices
+   *
+   * @return string representation of the uuid
+   */
+  std::string /** use UUID when ported */ GetUuid() {
+    return uid_;
+  }
+
+  std::string GetName() {
+    return name_;
+  }
+
+  ClassOfDevice GetClassOfDevice() {
+    return class_of_device_;
+  }
+
+  bool IsBonded() {
+    return is_bonded_;
+  }
+
+  bool operator==(const Device& rhs) const {
+    return this->uid_ == rhs.uid_ && this->address_ == rhs.address_ && this->device_type_ == rhs.device_type_ &&
+           this->is_bonded_ == rhs.is_bonded_;
+  }
+
+ protected:
+  friend class DeviceDatabase;
+  friend class DualDevice;
+
+  /**
+   * @param raw_address the address of the device
+   * @param device_type specify the type of device to create
+   */
+  Device(Address address, DeviceType device_type)
+      : address_(address), device_type_(device_type), uid_(generate_uid()), name_(""), class_of_device_() {}
+
+  /**
+   * Called only by friend class DeviceDatabase
+   *
+   * @param address
+   */
+  virtual void SetAddress(Address address) {
+    address_ = address;
+    uid_ = generate_uid();
+  }
+
+  /**
+   * Set the type of the device.
+   *
+   * <p>Needed by dual mode to arbitrarily set the valure to DUAL for corresponding LE/Classic devices
+   *
+   * @param type of device
+   */
+  void SetDeviceType(DeviceType type) {
+    device_type_ = type;
+  }
+
+  void SetName(std::string& name) {
+    name_ = name;
+  }
+
+  void SetClassOfDevice(ClassOfDevice class_of_device) {
+    class_of_device_ = class_of_device;
+  }
+
+  void SetIsBonded(bool is_bonded) {
+    is_bonded_ = is_bonded;
+  }
+
+ private:
+  Address address_{Address::kEmpty};
+  DeviceType device_type_;
+  std::string uid_;
+  std::string name_;
+  ClassOfDevice class_of_device_;
+  bool is_bonded_ = false;
+
+  /* Uses specific information about the device to calculate a UID */
+  std::string generate_uid();
+};
+
+}  // namespace bluetooth::hci
diff --git a/gd/hci/device_database.cc b/gd/hci/device_database.cc
new file mode 100644
index 0000000..0027074
--- /dev/null
+++ b/gd/hci/device_database.cc
@@ -0,0 +1,276 @@
+/******************************************************************************
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+#include "hci/device_database.h"
+
+#include <memory>
+#include <utility>
+
+#include "hci/classic_device.h"
+#include "hci/dual_device.h"
+#include "hci/le_device.h"
+#include "os/log.h"
+
+using namespace bluetooth::hci;
+
+std::shared_ptr<ClassicDevice> DeviceDatabase::CreateClassicDevice(Address address) {
+  ClassicDevice device(address);
+  const std::string uuid = device.GetUuid();
+  AddDeviceToMap(std::move(device));
+  return GetClassicDevice(uuid);
+}
+
+std::shared_ptr<LeDevice> DeviceDatabase::CreateLeDevice(Address address) {
+  LeDevice device(address);
+  const std::string uuid = device.GetUuid();
+  AddDeviceToMap(std::move(device));
+  return GetLeDevice(uuid);
+}
+
+std::shared_ptr<DualDevice> DeviceDatabase::CreateDualDevice(Address address) {
+  auto classic = CreateClassicDevice(address);
+  auto le = CreateLeDevice(address);
+  if (classic && le) {
+    DualDevice device(address, classic, le);
+    std::string uuid = device.GetUuid();
+    AddDeviceToMap(std::move(device));
+    return GetDualDevice(uuid);
+  }
+  LOG_WARN("Attempting to instert a DUAL device that already exists!");
+  return std::shared_ptr<DualDevice>();
+}
+
+bool DeviceDatabase::RemoveDevice(const std::shared_ptr<Device>& device) {
+  const DeviceType type = device->GetDeviceType();
+  bool success;
+  switch (type) {
+    case CLASSIC:
+      success = false;
+      {
+        std::lock_guard<std::mutex> lock(device_map_mutex_);
+        auto classic_it = classic_device_map_.find(device->GetUuid());
+        // If we have a record with the same key
+        if (classic_it != classic_device_map_.end()) {
+          classic_device_map_.erase(device->GetUuid());
+          success = true;
+        }
+      }
+      if (success) {
+        ASSERT_LOG(WriteToDisk(), "Failed to write data to disk!");
+      } else {
+        LOG_WARN("Device not in database!");
+      }
+      return success;
+    case LE:
+      success = false;
+      {
+        std::lock_guard<std::mutex> lock(device_map_mutex_);
+        auto le_it = le_device_map_.find(device->GetUuid());
+        // If we have a record with the same key
+        if (le_it != le_device_map_.end()) {
+          le_device_map_.erase(device->GetUuid());
+          success = true;
+        }
+      }
+      if (success) {
+        ASSERT_LOG(WriteToDisk(), "Failed to write data to disk!");
+      } else {
+        LOG_WARN("Device not in database!");
+      }
+      return success;
+    case DUAL:
+      std::shared_ptr<DualDevice> dual_device = nullptr;
+      {
+        std::lock_guard<std::mutex> lock(device_map_mutex_);
+        auto dual_it = dual_device_map_.find(device->GetUuid());
+        if (dual_it != dual_device_map_.end()) {
+          dual_device = GetDualDevice(device->GetUuid());
+        }
+      }
+      success = false;
+      if (dual_device != nullptr) {
+        if (RemoveDevice(dual_device->GetClassicDevice()) && RemoveDevice(dual_device->GetLeDevice())) {
+          dual_device_map_.erase(device->GetUuid());
+          success = true;
+        }
+      }
+      if (success) {
+        ASSERT_LOG(WriteToDisk(), "Failed to write data to disk!");
+      } else {
+        LOG_WARN("Device not in database!");
+      }
+      return success;
+  }
+}
+
+std::shared_ptr<ClassicDevice> DeviceDatabase::GetClassicDevice(const std::string& uuid) {
+  std::lock_guard<std::mutex> lock(device_map_mutex_);
+  auto it = classic_device_map_.find(uuid);
+  if (it != classic_device_map_.end()) {
+    return it->second;
+  }
+  LOG_WARN("Device '%s' not found!", uuid.c_str());
+  return std::shared_ptr<ClassicDevice>();
+}
+
+std::shared_ptr<LeDevice> DeviceDatabase::GetLeDevice(const std::string& uuid) {
+  std::lock_guard<std::mutex> lock(device_map_mutex_);
+  auto it = le_device_map_.find(uuid);
+  if (it != le_device_map_.end()) {
+    return it->second;
+  }
+  LOG_WARN("Device '%s' not found!", uuid.c_str());
+  return std::shared_ptr<LeDevice>();
+}
+
+std::shared_ptr<DualDevice> DeviceDatabase::GetDualDevice(const std::string& uuid) {
+  std::lock_guard<std::mutex> lock(device_map_mutex_);
+  auto it = dual_device_map_.find(uuid);
+  if (it != dual_device_map_.end()) {
+    return it->second;
+  }
+  LOG_WARN("Device '%s' not found!", uuid.c_str());
+  return std::shared_ptr<DualDevice>();
+}
+
+bool DeviceDatabase::UpdateDeviceAddress(const std::shared_ptr<Device>& device, Address new_address) {
+  // Hold onto device
+  const DeviceType type = device->GetDeviceType();
+  if (type == CLASSIC) {
+    auto classic_device = GetClassicDevice(device->GetUuid());
+    // This gets rid of the shared_ptr in the map
+    ASSERT_LOG(RemoveDevice(device), "Failed to remove the device!");
+    classic_device->SetAddress(new_address);
+    // Move the value located at the pointer
+    return AddDeviceToMap(std::move(*(classic_device.get())));
+  } else if (type == LE) {
+    auto le_device = GetLeDevice(device->GetUuid());
+    // This gets rid of the shared_ptr in the map
+    ASSERT_LOG(RemoveDevice(device), "Failed to remove the device!");
+    le_device->SetAddress(new_address);
+    // Move the value located at the pointer
+    return AddDeviceToMap(std::move(*(le_device.get())));
+  } else if (type == DUAL) {
+    auto dual_device = GetDualDevice(device->GetUuid());
+    // This gets rid of the shared_ptr in the map
+    ASSERT_LOG(RemoveDevice(device), "Failed to remove the device!");
+    dual_device->SetAddress(new_address);
+    // Move the value located at the pointer
+    return AddDeviceToMap(std::move(*(dual_device.get())));
+  }
+  LOG_ALWAYS_FATAL("Someone added a device type but didn't account for it here.");
+  return false;
+}
+
+bool DeviceDatabase::AddDeviceToMap(ClassicDevice&& device) {
+  const std::string uuid = device.GetUuid();
+  bool success = false;
+  {
+    std::lock_guard<std::mutex> lock(device_map_mutex_);
+    auto it = classic_device_map_.find(device.GetUuid());
+    // If we have a record with the same key
+    if (it != classic_device_map_.end()) {
+      // We don't want to insert and overwrite
+      return false;
+    }
+    std::shared_ptr<ClassicDevice> device_ptr = std::make_shared<ClassicDevice>(std::move(device));
+    // returning the boolean value of insert success
+    if (classic_device_map_
+            .insert(std::pair<std::string, std::shared_ptr<ClassicDevice>>(device_ptr->GetUuid(), device_ptr))
+            .second) {
+      success = true;
+    }
+  }
+  if (success) {
+    ASSERT_LOG(WriteToDisk(), "Failed to write data to disk!");
+  } else {
+    LOG_WARN("Failed to add device '%s' to map.", uuid.c_str());
+  }
+  return success;
+}
+
+bool DeviceDatabase::AddDeviceToMap(LeDevice&& device) {
+  const std::string uuid = device.GetUuid();
+  bool success = false;
+  {
+    std::lock_guard<std::mutex> lock(device_map_mutex_);
+    auto it = le_device_map_.find(device.GetUuid());
+    // If we have a record with the same key
+    if (it != le_device_map_.end()) {
+      // We don't want to insert and overwrite
+      return false;
+    }
+    std::shared_ptr<LeDevice> device_ptr = std::make_shared<LeDevice>(std::move(device));
+    // returning the boolean value of insert success
+    if (le_device_map_.insert(std::pair<std::string, std::shared_ptr<LeDevice>>(device_ptr->GetUuid(), device_ptr))
+            .second) {
+      success = true;
+    }
+  }
+  if (success) {
+    ASSERT_LOG(WriteToDisk(), "Failed to write data to disk!");
+  } else {
+    LOG_WARN("Failed to add device '%s' to map.", uuid.c_str());
+  }
+  return success;
+}
+
+bool DeviceDatabase::AddDeviceToMap(DualDevice&& device) {
+  const std::string uuid = device.GetUuid();
+  bool success = false;
+  {
+    std::lock_guard<std::mutex> lock(device_map_mutex_);
+    auto it = dual_device_map_.find(device.GetUuid());
+    // If we have a record with the same key
+    if (it != dual_device_map_.end()) {
+      // We don't want to insert and overwrite
+      return false;
+    }
+    std::shared_ptr<DualDevice> device_ptr = std::make_shared<DualDevice>(std::move(device));
+    // returning the boolean value of insert success
+    if (dual_device_map_.insert(std::pair<std::string, std::shared_ptr<DualDevice>>(device_ptr->GetUuid(), device_ptr))
+            .second) {
+      success = true;
+    }
+  }
+  if (success) {
+    ASSERT_LOG(WriteToDisk(), "Failed to write data to disk!");
+  } else {
+    LOG_WARN("Failed to add device '%s' to map.", uuid.c_str());
+  }
+  return success;
+}
+
+bool DeviceDatabase::WriteToDisk() {
+  // TODO(optedoblivion): Implement
+  // TODO(optedoblivion): FIX ME!
+  // If synchronous stack dies before async write, we can miss adding device
+  // post(WriteToDisk());
+  // Current Solution: Synchronous disk I/O...
+  std::lock_guard<std::mutex> lock(device_map_mutex_);
+  // Collect information to sync to database
+  // Create SQL query for insert/update
+  // submit SQL
+  return true;
+}
+
+bool DeviceDatabase::ReadFromDisk() {
+  // TODO(optedoblivion): Implement
+  // Current Solution: Synchronous disk I/O...
+  std::lock_guard<std::mutex> lock(device_map_mutex_);
+  return true;
+}
diff --git a/gd/hci/device_database.h b/gd/hci/device_database.h
new file mode 100644
index 0000000..88434a4
--- /dev/null
+++ b/gd/hci/device_database.h
@@ -0,0 +1,149 @@
+/******************************************************************************
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+#pragma once
+
+#include <map>
+#include <mutex>
+
+#include "hci/classic_device.h"
+#include "hci/device.h"
+#include "hci/dual_device.h"
+#include "hci/le_device.h"
+#include "os/log.h"
+
+namespace bluetooth::hci {
+
+/**
+ * Stores all of the paired or connected devices in the database.
+ *
+ * <p>If a device is stored here it is actively being used by the stack.
+ *
+ * <p>This database is not meant for scan results.
+ */
+class DeviceDatabase {
+ public:
+  DeviceDatabase() : classic_device_map_(), le_device_map_(), dual_device_map_() {
+    if (!ReadFromDisk()) {
+      LOG_WARN("First boot or missing data!");
+    }
+  }
+
+  /**
+   * Adds a device to the internal memory map and triggers a WriteToDisk.
+   *
+   * @param address private address for device
+   * @return weak pointer to the device or empty pointer if device already exists
+   */
+  std::shared_ptr<ClassicDevice> CreateClassicDevice(Address address);
+
+  /**
+   * Adds a device to the internal memory map and triggers a WriteToDisk.
+   *
+   * @param address private address for device
+   * @return weak pointer to the device or empty pointer if device already exists
+   */
+  std::shared_ptr<LeDevice> CreateLeDevice(Address address);
+
+  /**
+   * Adds a device to the internal memory map and triggers a WriteToDisk.
+   *
+   * @param address private address for device
+   * @return weak pointer to the device or empty pointer if device already exists
+   */
+  std::shared_ptr<DualDevice> CreateDualDevice(Address address);
+
+  /**
+   * Fetches a Classic Device matching the given uuid.
+   *
+   * @param uuid generated uuid from a Device
+   * @return a weak reference to the matching Device or empty shared_ptr (nullptr)
+   */
+  std::shared_ptr<ClassicDevice> GetClassicDevice(const std::string& uuid);
+
+  /**
+   * Fetches a Le Device matching the given uuid.
+   *
+   * @param uuid generated uuid from a Device
+   * @return a weak reference to the matching Device or empty shared_ptr (nullptr)
+   */
+  std::shared_ptr<LeDevice> GetLeDevice(const std::string& uuid);
+
+  /**
+   * Fetches a Dual Device matching the given uuid.
+   *
+   * @param uuid generated uuid from a Device
+   * @return a weak reference to the matching Device or empty shared_ptr (nullptr)
+   */
+  std::shared_ptr<DualDevice> GetDualDevice(const std::string& uuid);
+
+  /**
+   * Removes a device from the internal database.
+   *
+   * @param device weak pointer to device to remove from the database
+   * @return <code>true</code> if the device is removed
+   */
+  bool RemoveDevice(const std::shared_ptr<Device>& device);
+
+  /**
+   * Changes an address for a device.
+   *
+   * Also fixes the key mapping for the device.
+   *
+   * @param new_address this will replace the existing address
+   * @return <code>true</code> if updated
+   */
+  bool UpdateDeviceAddress(const std::shared_ptr<Device>& device, Address new_address);
+
+  // TODO(optedoblivion): Make interfaces for device modification
+  // We want to keep the device modification encapsulated to the DeviceDatabase.
+  // Pass around shared_ptr to device, device metadata only accessible via Getters.
+  // Choices:
+  //  a) Have Getters/Setters on device object
+  //  b) Have Getters/Setters on device database accepting a device object
+  //  c) Have Getters on device object and Setters on device database accepting a device object
+  // I chose to go with option c for now as I think it is the best option.
+
+  /**
+   * Fetches a list of classic devices.
+   *
+   * @return vector of weak pointers to classic devices
+   */
+  std::vector<std::shared_ptr<Device>> GetClassicDevices();
+
+  /**
+   * Fetches a list of le devices
+   *
+   * @return vector of weak pointers to le devices
+   */
+  std::vector<std::shared_ptr<Device>> GetLeDevices();
+
+ private:
+  std::mutex device_map_mutex_;
+  std::map<std::string, std::shared_ptr<ClassicDevice>> classic_device_map_;
+  std::map<std::string, std::shared_ptr<LeDevice>> le_device_map_;
+  std::map<std::string, std::shared_ptr<DualDevice>> dual_device_map_;
+
+  bool AddDeviceToMap(ClassicDevice&& device);
+  bool AddDeviceToMap(LeDevice&& device);
+  bool AddDeviceToMap(DualDevice&& device);
+
+  bool WriteToDisk();
+  bool ReadFromDisk();
+};
+
+}  // namespace bluetooth::hci
diff --git a/gd/hci/device_database_test.cc b/gd/hci/device_database_test.cc
new file mode 100644
index 0000000..330571d
--- /dev/null
+++ b/gd/hci/device_database_test.cc
@@ -0,0 +1,134 @@
+/******************************************************************************
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+#include "device_database.h"
+#include "classic_device.h"
+
+#include <gtest/gtest.h>
+
+using namespace bluetooth::hci;
+
+namespace bluetooth::hci {
+namespace {
+
+Address address({0x01, 0x02, 0x03, 0x04, 0x05, 0x06});
+std::string address_str = "06:05:04:03:02:01";
+class DeviceDatabaseTest : public ::testing::Test {
+ protected:
+  DeviceDatabaseTest() = default;
+
+  void SetUp() override {}
+
+  void TearDown() override {}
+
+  DeviceDatabase device_database_;
+};
+
+TEST_F(DeviceDatabaseTest, create_classic_device) {
+  auto classic_device = device_database_.CreateClassicDevice(address);
+  ASSERT_TRUE(classic_device);
+  ASSERT_EQ(CLASSIC, classic_device->GetDeviceType());
+  ASSERT_EQ(address_str, classic_device->GetUuid());
+}
+
+TEST_F(DeviceDatabaseTest, create_le_device) {
+  auto le_device = device_database_.CreateLeDevice(address);
+  ASSERT_TRUE(le_device);
+  ASSERT_EQ(LE, le_device->GetDeviceType());
+  ASSERT_EQ(address_str, le_device->GetUuid());
+}
+
+TEST_F(DeviceDatabaseTest, create_dual_device) {
+  auto dual_device = device_database_.CreateDualDevice(address);
+  ASSERT_TRUE(dual_device);
+  ASSERT_EQ(DUAL, dual_device->GetDeviceType());
+  ASSERT_EQ(DUAL, dual_device->GetClassicDevice()->GetDeviceType());
+  ASSERT_EQ(DUAL, dual_device->GetLeDevice()->GetDeviceType());
+  ASSERT_EQ(address_str, dual_device->GetUuid());
+}
+
+// Shouldn't fail when creating twice.  Should just get back a s_ptr the same device
+TEST_F(DeviceDatabaseTest, create_classic_device_twice) {
+  auto classic_device = device_database_.CreateClassicDevice(address);
+  ASSERT_TRUE(classic_device);
+  ASSERT_EQ(CLASSIC, classic_device->GetDeviceType());
+  ASSERT_EQ(address_str, classic_device->GetUuid());
+  ASSERT_TRUE(device_database_.CreateClassicDevice(address));
+}
+
+TEST_F(DeviceDatabaseTest, create_le_device_twice) {
+  auto le_device = device_database_.CreateLeDevice(address);
+  ASSERT_TRUE(le_device);
+  ASSERT_EQ(LE, le_device->GetDeviceType());
+  ASSERT_EQ(address_str, le_device->GetUuid());
+  ASSERT_TRUE(device_database_.CreateLeDevice(address));
+}
+
+TEST_F(DeviceDatabaseTest, create_dual_device_twice) {
+  auto dual_device = device_database_.CreateDualDevice(address);
+  ASSERT_TRUE(dual_device);
+
+  // Dual
+  ASSERT_EQ(DUAL, dual_device->GetDeviceType());
+  ASSERT_EQ(address_str, dual_device->GetUuid());
+
+  // Classic
+  ASSERT_EQ(DUAL, dual_device->GetClassicDevice()->GetDeviceType());
+  ASSERT_EQ(address_str, dual_device->GetClassicDevice()->GetUuid());
+
+  // LE
+  ASSERT_EQ(DUAL, dual_device->GetLeDevice()->GetDeviceType());
+  ASSERT_EQ(address_str, dual_device->GetLeDevice()->GetUuid());
+
+  ASSERT_TRUE(device_database_.CreateDualDevice(address));
+}
+
+TEST_F(DeviceDatabaseTest, remove_device) {
+  std::shared_ptr<Device> created_device = device_database_.CreateClassicDevice(address);
+  ASSERT_TRUE(created_device);
+  ASSERT_TRUE(device_database_.RemoveDevice(created_device));
+  ASSERT_TRUE(device_database_.CreateClassicDevice(address));
+}
+
+TEST_F(DeviceDatabaseTest, remove_device_twice) {
+  std::shared_ptr<Device> created_device = device_database_.CreateClassicDevice(address);
+  ASSERT_TRUE(device_database_.RemoveDevice(created_device));
+  ASSERT_FALSE(device_database_.RemoveDevice(created_device));
+}
+
+TEST_F(DeviceDatabaseTest, get_nonexistent_device) {
+  std::shared_ptr<Device> device_ptr = device_database_.GetClassicDevice(address_str);
+  ASSERT_FALSE(device_ptr);
+}
+
+TEST_F(DeviceDatabaseTest, address_modification_check) {
+  std::shared_ptr<Device> created_device = device_database_.CreateClassicDevice(address);
+  std::shared_ptr<Device> gotten_device = device_database_.GetClassicDevice(address.ToString());
+  ASSERT_TRUE(created_device);
+  ASSERT_TRUE(gotten_device);
+  ASSERT_EQ(address_str, created_device->GetAddress().ToString());
+  ASSERT_EQ(address_str, gotten_device->GetAddress().ToString());
+  device_database_.UpdateDeviceAddress(created_device, Address({0x01, 0x01, 0x01, 0x01, 0x01, 0x01}));
+  ASSERT_EQ("01:01:01:01:01:01", created_device->GetAddress().ToString());
+  ASSERT_EQ("01:01:01:01:01:01", gotten_device->GetAddress().ToString());
+  std::shared_ptr<Device> gotten_modified_device = device_database_.GetClassicDevice("01:01:01:01:01:01");
+  ASSERT_TRUE(gotten_modified_device);
+  ASSERT_TRUE(device_database_.RemoveDevice(gotten_modified_device));
+  ASSERT_FALSE(device_database_.GetClassicDevice("01:01:01:01:01:01"));
+}
+}  // namespace
+}  // namespace bluetooth::hci
diff --git a/gd/hci/device_test.cc b/gd/hci/device_test.cc
new file mode 100644
index 0000000..1b4fafc
--- /dev/null
+++ b/gd/hci/device_test.cc
@@ -0,0 +1,101 @@
+/******************************************************************************
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+#include "device.h"
+#include "classic_device.h"
+
+#include <gtest/gtest.h>
+
+using namespace bluetooth::hci;
+
+static const char* test_addr_str = "bc:9a:78:56:34:12";
+static const uint8_t test_addr[] = {0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc};
+static const Address address(test_addr);
+
+namespace bluetooth::hci {
+namespace {
+class TestableDevice : public Device {
+ public:
+  explicit TestableDevice(Address a) : Device(a, CLASSIC) {}
+
+  void SetTheAddress() {
+    Address a({0x01, 0x02, 0x03, 0x04, 0x05, 0x06});
+    this->SetAddress(a);
+  }
+  void SetTheClassOfDevice() {
+    ClassOfDevice class_of_device({0x01, 0x02, 0x03});
+    this->SetClassOfDevice(class_of_device);
+  }
+  void SetTheName() {
+    std::string name = "Some Name";
+    this->SetName(name);
+  }
+  void SetTheIsBonded() {
+    this->SetIsBonded(true);
+  }
+};
+class DeviceTest : public ::testing::Test {
+ public:
+  DeviceTest() : device_(Address(test_addr)) {}
+
+ protected:
+  void SetUp() override {}
+
+  void TearDown() override {}
+  TestableDevice device_;
+};
+
+TEST_F(DeviceTest, initial_integrity) {
+  ASSERT_STREQ(test_addr_str, device_.GetAddress().ToString().c_str());
+  ASSERT_STREQ(test_addr_str, device_.GetUuid().c_str());
+  ASSERT_EQ(DeviceType::CLASSIC, device_.GetDeviceType());
+  ASSERT_EQ("", device_.GetName());
+}
+
+TEST_F(DeviceTest, set_get_class_of_device) {
+  ClassOfDevice class_of_device({0x01, 0x02, 0x03});
+  ASSERT_NE(class_of_device, device_.GetClassOfDevice());
+  device_.SetTheClassOfDevice();
+  ASSERT_EQ(class_of_device, device_.GetClassOfDevice());
+}
+
+TEST_F(DeviceTest, set_get_name) {
+  std::string name = "Some Name";
+  ASSERT_EQ("", device_.GetName());
+  device_.SetTheName();
+  ASSERT_EQ(name, device_.GetName());
+}
+
+TEST_F(DeviceTest, operator_iseq) {
+  TestableDevice d(address);
+  EXPECT_EQ(device_, d);
+}
+
+TEST_F(DeviceTest, set_address) {
+  ASSERT_EQ(test_addr_str, device_.GetAddress().ToString());
+  device_.SetTheAddress();
+  ASSERT_EQ("06:05:04:03:02:01", device_.GetAddress().ToString());
+}
+
+TEST_F(DeviceTest, set_bonded) {
+  ASSERT_FALSE(device_.IsBonded());
+  device_.SetTheIsBonded();
+  ASSERT_TRUE(device_.IsBonded());
+}
+
+}  // namespace
+}  // namespace bluetooth::hci
diff --git a/gd/hci/dual_device.h b/gd/hci/dual_device.h
new file mode 100644
index 0000000..8d08019
--- /dev/null
+++ b/gd/hci/dual_device.h
@@ -0,0 +1,60 @@
+/******************************************************************************
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+#pragma once
+
+#include "hci/classic_device.h"
+#include "hci/device.h"
+#include "hci/le_device.h"
+
+namespace bluetooth::hci {
+
+/**
+ * A device representing a DUAL device.
+ *
+ * <p>This can be a DUAL only.
+ */
+class DualDevice : public Device {
+ public:
+  std::shared_ptr<Device> GetClassicDevice() {
+    return classic_device_;
+  }
+
+  std::shared_ptr<Device> GetLeDevice() {
+    return le_device_;
+  }
+
+ protected:
+  friend class DeviceDatabase;
+  DualDevice(Address address, std::shared_ptr<ClassicDevice> classic_device, std::shared_ptr<LeDevice> le_device)
+      : Device(address, DUAL), classic_device_(std::move(classic_device)), le_device_(std::move(le_device)) {
+    classic_device_->SetDeviceType(DUAL);
+    le_device_->SetDeviceType(DUAL);
+  }
+
+  void SetAddress(Address address) override {
+    Device::SetAddress(address);
+    GetClassicDevice()->SetAddress(address);
+    GetLeDevice()->SetAddress(address);
+  }
+
+ private:
+  std::shared_ptr<ClassicDevice> classic_device_;
+  std::shared_ptr<LeDevice> le_device_;
+};
+
+}  // namespace bluetooth::hci
diff --git a/gd/hci/dual_device_test.cc b/gd/hci/dual_device_test.cc
new file mode 100644
index 0000000..1c4c2b0
--- /dev/null
+++ b/gd/hci/dual_device_test.cc
@@ -0,0 +1,81 @@
+/******************************************************************************
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+#include "dual_device.h"
+#include "device.h"
+
+#include <gtest/gtest.h>
+
+using namespace bluetooth::hci;
+
+static const char* test_addr_str = "bc:9a:78:56:34:12";
+static const uint8_t test_addr[] = {0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc};
+static const Address address(test_addr);
+
+namespace bluetooth::hci {
+namespace {
+class TestableClassicDevice : public ClassicDevice {
+ public:
+  explicit TestableClassicDevice(Address a) : ClassicDevice(a) {}
+};
+class TestableLeDevice : public LeDevice {
+ public:
+  explicit TestableLeDevice(Address a) : LeDevice(a) {}
+};
+class TestableDevice : public DualDevice {
+ public:
+  TestableDevice(Address a, std::shared_ptr<TestableClassicDevice>& classic_device,
+                 std::shared_ptr<TestableLeDevice>& le_device)
+      : DualDevice(a, classic_device, le_device) {}
+
+  void SetTheAddress() {
+    Address a({0x01, 0x02, 0x03, 0x04, 0x05, 0x06});
+    this->SetAddress(a);
+  }
+};
+std::shared_ptr<TestableClassicDevice> classic_device = std::make_shared<TestableClassicDevice>(address);
+std::shared_ptr<TestableLeDevice> le_device = std::make_shared<TestableLeDevice>(address);
+class DualDeviceTest : public ::testing::Test {
+ public:
+  DualDeviceTest() : device_(Address(test_addr), classic_device, le_device) {}
+
+ protected:
+  void SetUp() override {}
+
+  void TearDown() override {}
+  TestableDevice device_;
+};
+
+TEST_F(DualDeviceTest, initial_integrity) {
+  Address a = device_.GetAddress();
+  ASSERT_EQ(test_addr_str, a.ToString());
+
+  ASSERT_EQ(DUAL, device_.GetClassicDevice()->GetDeviceType());
+  ASSERT_EQ(a.ToString(), device_.GetClassicDevice()->GetAddress().ToString());
+
+  ASSERT_EQ(DUAL, device_.GetLeDevice()->GetDeviceType());
+  ASSERT_EQ(a.ToString(), device_.GetLeDevice()->GetAddress().ToString());
+
+  device_.SetTheAddress();
+
+  ASSERT_EQ("06:05:04:03:02:01", device_.GetAddress().ToString());
+  ASSERT_EQ("06:05:04:03:02:01", device_.GetClassicDevice()->GetAddress().ToString());
+  ASSERT_EQ("06:05:04:03:02:01", device_.GetLeDevice()->GetAddress().ToString());
+}
+
+}  // namespace
+}  // namespace bluetooth::hci
diff --git a/gd/hci/facade/acl_manager_facade.cc b/gd/hci/facade/acl_manager_facade.cc
new file mode 100644
index 0000000..35d8452
--- /dev/null
+++ b/gd/hci/facade/acl_manager_facade.cc
@@ -0,0 +1,330 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "hci/facade/acl_manager_facade.h"
+
+#include <condition_variable>
+#include <memory>
+#include <mutex>
+
+#include "common/bind.h"
+#include "grpc/grpc_event_queue.h"
+#include "hci/acl_manager.h"
+#include "hci/facade/acl_manager_facade.grpc.pb.h"
+#include "hci/facade/acl_manager_facade.pb.h"
+#include "hci/hci_packets.h"
+#include "packet/raw_builder.h"
+
+using ::grpc::ServerAsyncResponseWriter;
+using ::grpc::ServerAsyncWriter;
+using ::grpc::ServerContext;
+
+using ::bluetooth::packet::RawBuilder;
+
+namespace bluetooth {
+namespace hci {
+namespace facade {
+
+class AclManagerFacadeService : public AclManagerFacade::Service,
+                                public ::bluetooth::hci::ConnectionCallbacks,
+                                public ::bluetooth::hci::ConnectionManagementCallbacks,
+                                public ::bluetooth::hci::AclManagerCallbacks {
+ public:
+  AclManagerFacadeService(AclManager* acl_manager, ::bluetooth::os::Handler* facade_handler)
+      : acl_manager_(acl_manager), facade_handler_(facade_handler) {
+    acl_manager_->RegisterCallbacks(this, facade_handler_);
+    acl_manager_->RegisterAclManagerCallbacks(this, facade_handler_);
+  }
+
+  ~AclManagerFacadeService() override {
+    std::unique_lock<std::mutex> lock(acl_connections_mutex_);
+    for (auto connection : acl_connections_) {
+      connection.second->GetAclQueueEnd()->UnregisterDequeue();
+    }
+  }
+
+  ::grpc::Status CreateConnection(::grpc::ServerContext* context, const ConnectionMsg* request,
+                                  ::grpc::ServerWriter<ConnectionEvent>* writer) override {
+    Address peer;
+    ASSERT(Address::FromString(request->address(), peer));
+    acl_manager_->CreateConnection(peer);
+    if (per_connection_events_.size() > current_connection_request_) {
+      return ::grpc::Status(::grpc::StatusCode::RESOURCE_EXHAUSTED, "Only one outstanding request is supported");
+    }
+    per_connection_events_.emplace_back(std::make_unique<::bluetooth::grpc::GrpcEventQueue<ConnectionEvent>>(
+        std::string("connection attempt ") + std::to_string(current_connection_request_)));
+    return per_connection_events_[current_connection_request_]->RunLoop(context, writer);
+  }
+
+  ::grpc::Status Disconnect(::grpc::ServerContext* context, const HandleMsg* request,
+                            ::google::protobuf::Empty* response) override {
+    std::unique_lock<std::mutex> lock(acl_connections_mutex_);
+    auto connection = acl_connections_.find(request->handle());
+    if (connection == acl_connections_.end()) {
+      LOG_ERROR("Invalid handle");
+      return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Invalid handle");
+    } else {
+      connection->second->Disconnect(DisconnectReason::REMOTE_USER_TERMINATED_CONNECTION);
+      return ::grpc::Status::OK;
+    }
+  }
+
+  ::grpc::Status AuthenticationRequested(::grpc::ServerContext* context, const HandleMsg* request,
+                                         ::google::protobuf::Empty* response) override {
+    std::unique_lock<std::mutex> lock(acl_connections_mutex_);
+    auto connection = acl_connections_.find(request->handle());
+    if (connection == acl_connections_.end()) {
+      LOG_ERROR("Invalid handle");
+      return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Invalid handle");
+    } else {
+      connection->second->AuthenticationRequested();
+      return ::grpc::Status::OK;
+    }
+  };
+
+  ::grpc::Status FetchIncomingConnection(::grpc::ServerContext* context, const google::protobuf::Empty* request,
+                                         ::grpc::ServerWriter<ConnectionEvent>* writer) override {
+    if (per_connection_events_.size() > current_connection_request_) {
+      return ::grpc::Status(::grpc::StatusCode::RESOURCE_EXHAUSTED, "Only one outstanding connection is supported");
+    }
+    per_connection_events_.emplace_back(std::make_unique<::bluetooth::grpc::GrpcEventQueue<ConnectionEvent>>(
+        std::string("incoming connection ") + std::to_string(current_connection_request_)));
+    return per_connection_events_[current_connection_request_]->RunLoop(context, writer);
+  }
+
+  ::grpc::Status SendAclData(::grpc::ServerContext* context, const AclData* request,
+                             ::google::protobuf::Empty* response) override {
+    std::promise<void> promise;
+    auto future = promise.get_future();
+    {
+      std::unique_lock<std::mutex> lock(acl_connections_mutex_);
+      auto connection = acl_connections_.find(request->handle());
+      if (connection == acl_connections_.end()) {
+        LOG_ERROR("Invalid handle");
+        return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Invalid handle");
+      } else {
+        connection->second->GetAclQueueEnd()->RegisterEnqueue(
+            facade_handler_, common::Bind(&AclManagerFacadeService::enqueue_packet, common::Unretained(this),
+                                          common::Unretained(request), common::Passed(std::move(promise))));
+      }
+    }
+    future.wait();
+    return ::grpc::Status::OK;
+  }
+
+  std::unique_ptr<BasePacketBuilder> enqueue_packet(const AclData* request, std::promise<void> promise) {
+    acl_connections_[request->handle()]->GetAclQueueEnd()->UnregisterEnqueue();
+    std::unique_ptr<RawBuilder> packet =
+        std::make_unique<RawBuilder>(std::vector<uint8_t>(request->payload().begin(), request->payload().end()));
+    promise.set_value();
+    return packet;
+  }
+
+  ::grpc::Status FetchAclData(::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
+                              ::grpc::ServerWriter<AclData>* writer) override {
+    return pending_acl_data_.RunLoop(context, writer);
+  }
+
+  static inline uint16_t to_handle(uint32_t current_request) {
+    return (current_request + 0x10) % 0xe00;
+  }
+
+  static inline std::string builder_to_string(std::unique_ptr<BasePacketBuilder> builder) {
+    std::vector<uint8_t> bytes;
+    BitInserter bit_inserter(bytes);
+    builder->Serialize(bit_inserter);
+    return std::string(bytes.begin(), bytes.end());
+  }
+
+  void on_incoming_acl(std::shared_ptr<AclConnection> connection, uint16_t handle) {
+    auto packet = connection->GetAclQueueEnd()->TryDequeue();
+    AclData acl_data;
+    acl_data.set_handle(handle);
+    acl_data.set_payload(std::string(packet->begin(), packet->end()));
+    pending_acl_data_.OnIncomingEvent(acl_data);
+  }
+
+  void on_disconnect(std::shared_ptr<AclConnection> connection, uint32_t entry, ErrorCode code) {
+    connection->GetAclQueueEnd()->UnregisterDequeue();
+    connection->Finish();
+    std::unique_ptr<BasePacketBuilder> builder =
+        DisconnectBuilder::Create(to_handle(entry), static_cast<DisconnectReason>(code));
+    ConnectionEvent disconnection;
+    disconnection.set_event(builder_to_string(std::move(builder)));
+    per_connection_events_[entry]->OnIncomingEvent(disconnection);
+  }
+
+  void OnConnectSuccess(std::unique_ptr<::bluetooth::hci::AclConnection> connection) override {
+    std::unique_lock<std::mutex> lock(acl_connections_mutex_);
+    auto addr = connection->GetAddress();
+    std::shared_ptr<::bluetooth::hci::AclConnection> shared_connection = std::move(connection);
+    acl_connections_.emplace(to_handle(current_connection_request_), shared_connection);
+    auto remote_address = shared_connection->GetAddress().ToString();
+    shared_connection->GetAclQueueEnd()->RegisterDequeue(
+        facade_handler_, common::Bind(&AclManagerFacadeService::on_incoming_acl, common::Unretained(this),
+                                      shared_connection, to_handle(current_connection_request_)));
+    shared_connection->RegisterDisconnectCallback(
+        common::BindOnce(&AclManagerFacadeService::on_disconnect, common::Unretained(this), shared_connection,
+                         current_connection_request_),
+        facade_handler_);
+    shared_connection->RegisterCallbacks(this, facade_handler_);
+    std::unique_ptr<BasePacketBuilder> builder = ConnectionCompleteBuilder::Create(
+        ErrorCode::SUCCESS, to_handle(current_connection_request_), addr, LinkType::ACL, Enable::DISABLED);
+    ConnectionEvent success;
+    success.set_event(builder_to_string(std::move(builder)));
+    per_connection_events_[current_connection_request_]->OnIncomingEvent(success);
+    current_connection_request_++;
+  }
+
+  void OnMasterLinkKeyComplete(uint16_t connection_handle, KeyFlag key_flag) override {
+    LOG_DEBUG("OnMasterLinkKeyComplete connection_handle:%d", connection_handle);
+  }
+
+  void OnRoleChange(Address bd_addr, Role new_role) override {
+    LOG_DEBUG("OnRoleChange bd_addr:%s, new_role:%d", bd_addr.ToString().c_str(), (uint8_t)new_role);
+  }
+
+  void OnReadDefaultLinkPolicySettingsComplete(uint16_t default_link_policy_settings) override {
+    LOG_DEBUG("OnReadDefaultLinkPolicySettingsComplete default_link_policy_settings:%d", default_link_policy_settings);
+  }
+
+  void OnConnectFail(Address address, ErrorCode reason) override {
+    std::unique_ptr<BasePacketBuilder> builder =
+        ConnectionCompleteBuilder::Create(reason, 0, address, LinkType::ACL, Enable::DISABLED);
+    ConnectionEvent fail;
+    fail.set_event(builder_to_string(std::move(builder)));
+    per_connection_events_[current_connection_request_]->OnIncomingEvent(fail);
+    current_connection_request_++;
+  }
+
+  void OnConnectionPacketTypeChanged(uint16_t packet_type) override {
+    LOG_DEBUG("OnConnectionPacketTypeChanged packet_type:%d", packet_type);
+  }
+
+  void OnAuthenticationComplete() override {
+    LOG_DEBUG("OnAuthenticationComplete");
+  }
+
+  void OnEncryptionChange(EncryptionEnabled enabled) override {
+    LOG_DEBUG("OnConnectionPacketTypeChanged enabled:%d", (uint8_t)enabled);
+  }
+
+  void OnChangeConnectionLinkKeyComplete() override {
+    LOG_DEBUG("OnChangeConnectionLinkKeyComplete");
+  };
+
+  void OnReadClockOffsetComplete(uint16_t clock_offset) override {
+    LOG_DEBUG("OnReadClockOffsetComplete clock_offset:%d", clock_offset);
+  };
+
+  void OnModeChange(Mode current_mode, uint16_t interval) override {
+    LOG_DEBUG("OnModeChange Mode:%d, interval:%d", (uint8_t)current_mode, interval);
+  };
+
+  void OnQosSetupComplete(ServiceType service_type, uint32_t token_rate, uint32_t peak_bandwidth, uint32_t latency,
+                          uint32_t delay_variation) override {
+    LOG_DEBUG("OnQosSetupComplete service_type:%d, token_rate:%d, peak_bandwidth:%d, latency:%d, delay_variation:%d",
+              (uint8_t)service_type, token_rate, peak_bandwidth, latency, delay_variation);
+  }
+
+  void OnFlowSpecificationComplete(FlowDirection flow_direction, ServiceType service_type, uint32_t token_rate,
+                                   uint32_t token_bucket_size, uint32_t peak_bandwidth,
+                                   uint32_t access_latency) override {
+    LOG_DEBUG(
+        "OnFlowSpecificationComplete flow_direction:%d. service_type:%d, token_rate:%d, token_bucket_size:%d, "
+        "peak_bandwidth:%d, access_latency:%d",
+        (uint8_t)flow_direction, (uint8_t)service_type, token_rate, token_bucket_size, peak_bandwidth, access_latency);
+  }
+
+  void OnFlushOccurred() override {
+    LOG_DEBUG("OnFlushOccurred");
+  }
+
+  void OnRoleDiscoveryComplete(Role current_role) override {
+    LOG_DEBUG("OnRoleDiscoveryComplete current_role:%d", (uint8_t)current_role);
+  }
+
+  void OnReadLinkPolicySettingsComplete(uint16_t link_policy_settings) override {
+    LOG_DEBUG("OnReadLinkPolicySettingsComplete link_policy_settings:%d", link_policy_settings);
+  }
+
+  void OnReadAutomaticFlushTimeoutComplete(uint16_t flush_timeout) override {
+    LOG_DEBUG("OnReadAutomaticFlushTimeoutComplete flush_timeout:%d", flush_timeout);
+  }
+
+  void OnReadTransmitPowerLevelComplete(uint8_t transmit_power_level) override {
+    LOG_DEBUG("OnReadTransmitPowerLevelComplete transmit_power_level:%d", transmit_power_level);
+  }
+
+  void OnReadLinkSupervisionTimeoutComplete(uint16_t link_supervision_timeout) override {
+    LOG_DEBUG("OnReadLinkSupervisionTimeoutComplete link_supervision_timeout:%d", link_supervision_timeout);
+  }
+
+  void OnReadFailedContactCounterComplete(uint16_t failed_contact_counter) override {
+    LOG_DEBUG("OnReadFailedContactCounterComplete failed_contact_counter:%d", failed_contact_counter);
+  }
+
+  void OnReadLinkQualityComplete(uint8_t link_quality) override {
+    LOG_DEBUG("OnReadLinkQualityComplete link_quality:%d", link_quality);
+  }
+
+  void OnReadAfhChannelMapComplete(AfhMode afh_mode, std::array<uint8_t, 10> afh_channel_map) override {
+    LOG_DEBUG("OnReadAfhChannelMapComplete afh_mode:%d", (uint8_t)afh_mode);
+  }
+
+  void OnReadRssiComplete(uint8_t rssi) override {
+    LOG_DEBUG("OnReadRssiComplete rssi:%d", rssi);
+  }
+
+  void OnReadClockComplete(uint32_t clock, uint16_t accuracy) override {
+    LOG_DEBUG("OnReadClockComplete clock:%d, accuracy:%d", clock, accuracy);
+  }
+
+ private:
+  AclManager* acl_manager_;
+  ::bluetooth::os::Handler* facade_handler_;
+  mutable std::mutex acl_connections_mutex_;
+  std::map<uint16_t, std::shared_ptr<AclConnection>> acl_connections_;
+  ::bluetooth::grpc::GrpcEventQueue<AclData> pending_acl_data_{"FetchAclData"};
+  std::vector<std::unique_ptr<::bluetooth::grpc::GrpcEventQueue<ConnectionEvent>>> per_connection_events_;
+  uint32_t current_connection_request_{0};
+};
+
+void AclManagerFacadeModule::ListDependencies(ModuleList* list) {
+  ::bluetooth::grpc::GrpcFacadeModule::ListDependencies(list);
+  list->add<AclManager>();
+}
+
+void AclManagerFacadeModule::Start() {
+  ::bluetooth::grpc::GrpcFacadeModule::Start();
+  service_ = new AclManagerFacadeService(GetDependency<AclManager>(), GetHandler());
+}
+
+void AclManagerFacadeModule::Stop() {
+  delete service_;
+  ::bluetooth::grpc::GrpcFacadeModule::Stop();
+}
+
+::grpc::Service* AclManagerFacadeModule::GetService() const {
+  return service_;
+}
+
+const ModuleFactory AclManagerFacadeModule::Factory =
+    ::bluetooth::ModuleFactory([]() { return new AclManagerFacadeModule(); });
+
+}  // namespace facade
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/facade/acl_manager_facade.h b/gd/hci/facade/acl_manager_facade.h
new file mode 100644
index 0000000..9537349
--- /dev/null
+++ b/gd/hci/facade/acl_manager_facade.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <grpc++/grpc++.h>
+
+#include "grpc/grpc_module.h"
+#include "hci/acl_manager.h"
+
+namespace bluetooth {
+namespace hci {
+namespace facade {
+
+class AclManagerFacadeService;
+
+class AclManagerFacadeModule : public ::bluetooth::grpc::GrpcFacadeModule {
+ public:
+  static const ModuleFactory Factory;
+
+  void ListDependencies(ModuleList* list) override;
+  void Start() override;
+  void Stop() override;
+  ::grpc::Service* GetService() const override;
+
+ private:
+  AclManagerFacadeService* service_;
+};
+
+}  // namespace facade
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/facade/acl_manager_facade.proto b/gd/hci/facade/acl_manager_facade.proto
new file mode 100644
index 0000000..8a40f8f
--- /dev/null
+++ b/gd/hci/facade/acl_manager_facade.proto
@@ -0,0 +1,33 @@
+syntax = "proto3";
+
+package bluetooth.hci;
+
+import "google/protobuf/empty.proto";
+
+service AclManagerFacade {
+  rpc CreateConnection(ConnectionMsg) returns (stream ConnectionEvent) {}
+  rpc CancelConnection(ConnectionMsg) returns (google.protobuf.Empty) {}
+  rpc Disconnect(HandleMsg) returns (google.protobuf.Empty) {}
+  rpc AuthenticationRequested(HandleMsg) returns (google.protobuf.Empty) {}
+  rpc SendAclData(AclData) returns (google.protobuf.Empty) {}
+  rpc FetchAclData(google.protobuf.Empty) returns (stream AclData) {}
+  rpc FetchIncomingConnection(google.protobuf.Empty) returns (stream ConnectionEvent) {}
+}
+
+message HandleMsg {
+  uint32 handle = 1;
+}
+
+message ConnectionMsg {
+  uint32 address_type = 1;
+  bytes address = 2;
+}
+
+message ConnectionEvent {
+  bytes event = 1;
+}
+
+message AclData {
+  uint32 handle = 1;
+  bytes payload = 2;
+}
diff --git a/gd/hci/facade/controller_facade.cc b/gd/hci/facade/controller_facade.cc
new file mode 100644
index 0000000..c65dff0
--- /dev/null
+++ b/gd/hci/facade/controller_facade.cc
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "hci/facade/controller_facade.h"
+
+#include <condition_variable>
+#include <memory>
+#include <mutex>
+
+#include "common/bind.h"
+#include "common/blocking_queue.h"
+#include "grpc/grpc_event_queue.h"
+#include "hci/address.h"
+#include "hci/controller.h"
+#include "hci/facade/controller_facade.grpc.pb.h"
+#include "hci/facade/controller_facade.pb.h"
+
+using ::grpc::ServerAsyncResponseWriter;
+using ::grpc::ServerAsyncWriter;
+using ::grpc::ServerContext;
+
+namespace bluetooth {
+namespace hci {
+namespace facade {
+
+class ControllerFacadeService : public ControllerFacade::Service {
+ public:
+  ControllerFacadeService(Controller* controller, ::bluetooth::os::Handler*) : controller_(controller) {}
+
+  ::grpc::Status GetMacAddress(::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
+                               AddressMsg* response) override {
+    Address local_address = controller_->GetControllerMacAddress();
+    response->set_address(local_address.ToString());
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status GetLocalName(::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
+                              NameMsg* response) override {
+    std::string local_name = controller_->GetControllerLocalName();
+    response->set_name(local_name);
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status WriteLocalName(::grpc::ServerContext* context, const NameMsg* request,
+                                ::google::protobuf::Empty* response) override {
+    controller_->WriteLocalName(request->name());
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status GetLocalExtendedFeatures(::grpc::ServerContext* context, const PageNumberMsg* request,
+                                          FeaturesMsg* response) override {
+    if (request->page_number() > controller_->GetControllerLocalExtendedFeaturesMaxPageNumber()) {
+      return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Local Extended Features page number out of range");
+    }
+    response->set_page(controller_->GetControllerLocalExtendedFeatures(request->page_number()));
+    return ::grpc::Status::OK;
+  }
+
+ private:
+  Controller* controller_;
+};
+
+void ControllerFacadeModule::ListDependencies(ModuleList* list) {
+  ::bluetooth::grpc::GrpcFacadeModule::ListDependencies(list);
+  list->add<Controller>();
+}
+
+void ControllerFacadeModule::Start() {
+  ::bluetooth::grpc::GrpcFacadeModule::Start();
+  service_ = new ControllerFacadeService(GetDependency<Controller>(), GetHandler());
+}
+
+void ControllerFacadeModule::Stop() {
+  delete service_;
+  ::bluetooth::grpc::GrpcFacadeModule::Stop();
+}
+
+::grpc::Service* ControllerFacadeModule::GetService() const {
+  return service_;
+}
+
+const ModuleFactory ControllerFacadeModule::Factory =
+    ::bluetooth::ModuleFactory([]() { return new ControllerFacadeModule(); });
+
+}  // namespace facade
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/facade/controller_facade.h b/gd/hci/facade/controller_facade.h
new file mode 100644
index 0000000..c1f0f18
--- /dev/null
+++ b/gd/hci/facade/controller_facade.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <grpc++/grpc++.h>
+
+#include "grpc/grpc_module.h"
+#include "hci/controller.h"
+
+namespace bluetooth {
+namespace hci {
+namespace facade {
+
+class ControllerFacadeService;
+
+class ControllerFacadeModule : public ::bluetooth::grpc::GrpcFacadeModule {
+ public:
+  static const ModuleFactory Factory;
+
+  void ListDependencies(ModuleList* list) override;
+  void Start() override;
+  void Stop() override;
+  ::grpc::Service* GetService() const override;
+
+ private:
+  ControllerFacadeService* service_;
+};
+
+}  // namespace facade
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/facade/controller_facade.proto b/gd/hci/facade/controller_facade.proto
new file mode 100644
index 0000000..edf9de7
--- /dev/null
+++ b/gd/hci/facade/controller_facade.proto
@@ -0,0 +1,28 @@
+syntax = "proto3";
+
+package bluetooth.hci;
+
+import "google/protobuf/empty.proto";
+
+service ControllerFacade {
+  rpc GetMacAddress(google.protobuf.Empty) returns (AddressMsg) {}
+  rpc WriteLocalName(NameMsg) returns (google.protobuf.Empty) {}
+  rpc GetLocalName(google.protobuf.Empty) returns (NameMsg) {}
+  rpc GetLocalExtendedFeatures(PageNumberMsg) returns (FeaturesMsg) {}
+}
+
+message AddressMsg {
+  bytes address = 1;
+}
+
+message NameMsg {
+  bytes name = 1;
+}
+
+message PageNumberMsg {
+  uint32 page_number = 1;
+}
+
+message FeaturesMsg {
+  uint64 page = 1;
+}
diff --git a/gd/hci/facade/facade.cc b/gd/hci/facade/facade.cc
new file mode 100644
index 0000000..9650bcd
--- /dev/null
+++ b/gd/hci/facade/facade.cc
@@ -0,0 +1,260 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "hci/facade/facade.h"
+
+#include <memory>
+
+#include "common/bind.h"
+#include "grpc/grpc_event_queue.h"
+#include "hci/controller.h"
+#include "hci/facade/facade.grpc.pb.h"
+#include "hci/hci_layer.h"
+#include "hci/hci_packets.h"
+
+using ::grpc::ServerAsyncResponseWriter;
+using ::grpc::ServerAsyncWriter;
+using ::grpc::ServerContext;
+
+namespace bluetooth {
+namespace hci {
+namespace facade {
+
+class HciLayerFacadeService : public HciLayerFacade::Service {
+ public:
+  HciLayerFacadeService(HciLayer* hci_layer, Controller* controller, ::bluetooth::os::Handler* facade_handler)
+      : hci_layer_(hci_layer), controller_(controller), facade_handler_(facade_handler) {}
+
+  virtual ~HciLayerFacadeService() {
+    if (unregister_acl_dequeue_) {
+      hci_layer_->GetAclQueueEnd()->UnregisterDequeue();
+    }
+    if (waiting_acl_packet_ != nullptr) {
+      hci_layer_->GetAclQueueEnd()->UnregisterEnqueue();
+      if (waiting_acl_packet_ != nullptr) {
+        waiting_acl_packet_.reset();
+      }
+    }
+  }
+
+  class TestCommandBuilder : public CommandPacketBuilder {
+   public:
+    explicit TestCommandBuilder(std::vector<uint8_t> bytes)
+        : CommandPacketBuilder(OpCode::NONE), bytes_(std::move(bytes)) {}
+    size_t size() const override {
+      return bytes_.size();
+    }
+    void Serialize(BitInserter& bit_inserter) const override {
+      for (auto&& b : bytes_) {
+        bit_inserter.insert_byte(b);
+      }
+    }
+
+   private:
+    std::vector<uint8_t> bytes_;
+  };
+
+  ::grpc::Status EnqueueCommandWithComplete(::grpc::ServerContext* context, const ::bluetooth::hci::CommandMsg* command,
+                                            ::google::protobuf::Empty* response) override {
+    auto packet = std::make_unique<TestCommandBuilder>(
+        std::vector<uint8_t>(command->command().begin(), command->command().end()));
+    hci_layer_->EnqueueCommand(std::move(packet),
+                               common::BindOnce(&HciLayerFacadeService::on_complete, common::Unretained(this)),
+                               facade_handler_);
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status EnqueueCommandWithStatus(::grpc::ServerContext* context, const ::bluetooth::hci::CommandMsg* command,
+                                          ::google::protobuf::Empty* response) override {
+    auto packet = std::make_unique<TestCommandBuilder>(
+        std::vector<uint8_t>(command->command().begin(), command->command().end()));
+    hci_layer_->EnqueueCommand(std::move(packet),
+                               common::BindOnce(&HciLayerFacadeService::on_status, common::Unretained(this)),
+                               facade_handler_);
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status RegisterEventHandler(::grpc::ServerContext* context, const ::bluetooth::hci::EventCodeMsg* event,
+                                      ::google::protobuf::Empty* response) override {
+    hci_layer_->RegisterEventHandler(static_cast<EventCode>(event->code()),
+                                     common::Bind(&HciLayerFacadeService::on_event, common::Unretained(this)),
+                                     facade_handler_);
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status RegisterLeEventHandler(::grpc::ServerContext* context,
+                                        const ::bluetooth::hci::LeSubeventCodeMsg* event,
+                                        ::google::protobuf::Empty* response) override {
+    hci_layer_->RegisterLeEventHandler(static_cast<SubeventCode>(event->code()),
+                                       common::Bind(&HciLayerFacadeService::on_le_subevent, common::Unretained(this)),
+                                       facade_handler_);
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status FetchEvents(::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
+                             ::grpc::ServerWriter<EventMsg>* writer) override {
+    return pending_events_.RunLoop(context, writer);
+  };
+
+  ::grpc::Status FetchLeSubevents(::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
+                                  ::grpc::ServerWriter<LeSubeventMsg>* writer) override {
+    return pending_le_events_.RunLoop(context, writer);
+  };
+
+  class TestAclBuilder : public AclPacketBuilder {
+   public:
+    explicit TestAclBuilder(uint16_t handle, uint8_t packet_boundary_flag, uint8_t broadcast_flag,
+                            std::vector<uint8_t> payload)
+        : AclPacketBuilder(0xbad, PacketBoundaryFlag::CONTINUING_FRAGMENT, BroadcastFlag::ACTIVE_SLAVE_BROADCAST),
+          handle_(handle), pb_flag_(packet_boundary_flag), b_flag_(broadcast_flag), bytes_(std::move(payload)) {}
+
+    size_t size() const override {
+      return bytes_.size();
+    }
+    void Serialize(BitInserter& bit_inserter) const override {
+      LOG_INFO("handle 0x%hx boundary 0x%hhx broadcast 0x%hhx", handle_, pb_flag_, b_flag_);
+      bit_inserter.insert_byte(handle_);
+      bit_inserter.insert_bits((handle_ >> 8) & 0xf, 4);
+      bit_inserter.insert_bits(pb_flag_, 2);
+      bit_inserter.insert_bits(b_flag_, 2);
+      bit_inserter.insert_byte(bytes_.size() & 0xff);
+      bit_inserter.insert_byte((bytes_.size() & 0xff00) >> 8);
+      for (auto&& b : bytes_) {
+        bit_inserter.insert_byte(b);
+      }
+    }
+
+   private:
+    uint16_t handle_;
+    uint8_t pb_flag_;
+    uint8_t b_flag_;
+    std::vector<uint8_t> bytes_;
+  };
+
+  ::grpc::Status SendAclData(::grpc::ServerContext* context, const ::bluetooth::hci::AclMsg* acl,
+                             ::google::protobuf::Empty* response) override {
+    waiting_acl_packet_ =
+        std::make_unique<TestAclBuilder>(acl->handle(), acl->packet_boundary_flag(), acl->broadcast_flag(),
+                                         std::vector<uint8_t>(acl->data().begin(), acl->data().end()));
+    std::promise<void> enqueued;
+    auto future = enqueued.get_future();
+    if (!completed_packets_callback_registered_) {
+      controller_->RegisterCompletedAclPacketsCallback(common::Bind([](uint16_t, uint16_t) { /* do nothing */ }),
+                                                       facade_handler_);
+      completed_packets_callback_registered_ = true;
+    }
+    hci_layer_->GetAclQueueEnd()->RegisterEnqueue(
+        facade_handler_, common::Bind(&HciLayerFacadeService::handle_enqueue_acl, common::Unretained(this),
+                                      common::Unretained(&enqueued)));
+    auto result = future.wait_for(std::chrono::milliseconds(100));
+    ASSERT(std::future_status::ready == result);
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status FetchAclPackets(::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
+                                 ::grpc::ServerWriter<AclMsg>* writer) override {
+    hci_layer_->GetAclQueueEnd()->RegisterDequeue(
+        facade_handler_, common::Bind(&HciLayerFacadeService::on_acl_ready, common::Unretained(this)));
+    unregister_acl_dequeue_ = true;
+    return pending_acl_events_.RunLoop(context, writer);
+  };
+
+ private:
+  std::unique_ptr<AclPacketBuilder> handle_enqueue_acl(std::promise<void>* promise) {
+    promise->set_value();
+    hci_layer_->GetAclQueueEnd()->UnregisterEnqueue();
+    return std::move(waiting_acl_packet_);
+  }
+
+  void on_acl_ready() {
+    auto acl_ptr = hci_layer_->GetAclQueueEnd()->TryDequeue();
+    ASSERT(acl_ptr != nullptr);
+    ASSERT(acl_ptr->IsValid());
+    LOG_INFO("Got an Acl message for handle 0x%hx", acl_ptr->GetHandle());
+    AclMsg incoming;
+    incoming.set_data(std::string(acl_ptr->begin(), acl_ptr->end()));
+    pending_acl_events_.OnIncomingEvent(std::move(incoming));
+  }
+
+  void on_event(hci::EventPacketView view) {
+    ASSERT(view.IsValid());
+    LOG_INFO("Got an Event %s", EventCodeText(view.GetEventCode()).c_str());
+    EventMsg response;
+    response.set_event(std::string(view.begin(), view.end()));
+    pending_events_.OnIncomingEvent(std::move(response));
+  }
+
+  void on_le_subevent(hci::LeMetaEventView view) {
+    ASSERT(view.IsValid());
+    LOG_INFO("Got an LE Event %s", SubeventCodeText(view.GetSubeventCode()).c_str());
+    LeSubeventMsg response;
+    response.set_event(std::string(view.begin(), view.end()));
+    pending_le_events_.OnIncomingEvent(std::move(response));
+  }
+
+  void on_complete(hci::CommandCompleteView view) {
+    ASSERT(view.IsValid());
+    LOG_INFO("Got a Command complete %s", OpCodeText(view.GetCommandOpCode()).c_str());
+    EventMsg response;
+    response.set_event(std::string(view.begin(), view.end()));
+    pending_events_.OnIncomingEvent(std::move(response));
+  }
+
+  void on_status(hci::CommandStatusView view) {
+    ASSERT(view.IsValid());
+    LOG_INFO("Got a Command status %s", OpCodeText(view.GetCommandOpCode()).c_str());
+    EventMsg response;
+    response.set_event(std::string(view.begin(), view.end()));
+    pending_events_.OnIncomingEvent(std::move(response));
+  }
+
+  HciLayer* hci_layer_;
+  Controller* controller_;
+  ::bluetooth::os::Handler* facade_handler_;
+  ::bluetooth::grpc::GrpcEventQueue<EventMsg> pending_events_{"FetchHciEvent"};
+  ::bluetooth::grpc::GrpcEventQueue<LeSubeventMsg> pending_le_events_{"FetchLeSubevent"};
+  ::bluetooth::grpc::GrpcEventQueue<AclMsg> pending_acl_events_{"FetchAclData"};
+  bool unregister_acl_dequeue_{false};
+  std::unique_ptr<TestAclBuilder> waiting_acl_packet_;
+  bool completed_packets_callback_registered_{false};
+};
+
+void HciLayerFacadeModule::ListDependencies(ModuleList* list) {
+  ::bluetooth::grpc::GrpcFacadeModule::ListDependencies(list);
+  list->add<HciLayer>();
+  list->add<Controller>();
+}
+
+void HciLayerFacadeModule::Start() {
+  ::bluetooth::grpc::GrpcFacadeModule::Start();
+  service_ = new HciLayerFacadeService(GetDependency<HciLayer>(), GetDependency<Controller>(), GetHandler());
+}
+
+void HciLayerFacadeModule::Stop() {
+  delete service_;
+  ::bluetooth::grpc::GrpcFacadeModule::Stop();
+}
+
+::grpc::Service* HciLayerFacadeModule::GetService() const {
+  return service_;
+}
+
+const ModuleFactory HciLayerFacadeModule::Factory =
+    ::bluetooth::ModuleFactory([]() { return new HciLayerFacadeModule(); });
+
+}  // namespace facade
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/facade/facade.h b/gd/hci/facade/facade.h
new file mode 100644
index 0000000..88ba36f
--- /dev/null
+++ b/gd/hci/facade/facade.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <grpc++/grpc++.h>
+
+#include "grpc/grpc_module.h"
+#include "hci/hci_layer.h"
+
+namespace bluetooth {
+namespace hci {
+namespace facade {
+
+class HciLayerFacadeService;
+
+class HciLayerFacadeModule : public ::bluetooth::grpc::GrpcFacadeModule {
+ public:
+  static const ModuleFactory Factory;
+
+  void ListDependencies(ModuleList* list) override;
+  void Start() override;
+  void Stop() override;
+  ::grpc::Service* GetService() const override;
+
+ private:
+  HciLayerFacadeService* service_;
+};
+
+}  // namespace facade
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/facade/facade.proto b/gd/hci/facade/facade.proto
new file mode 100644
index 0000000..bcce542
--- /dev/null
+++ b/gd/hci/facade/facade.proto
@@ -0,0 +1,43 @@
+syntax = "proto3";
+
+package bluetooth.hci;
+
+import "google/protobuf/empty.proto";
+
+service HciLayerFacade {
+  rpc EnqueueCommandWithComplete(CommandMsg) returns (google.protobuf.Empty) {}
+  rpc EnqueueCommandWithStatus(CommandMsg) returns (google.protobuf.Empty) {}
+  rpc RegisterEventHandler(EventCodeMsg) returns (google.protobuf.Empty) {}
+  rpc RegisterLeEventHandler(LeSubeventCodeMsg) returns (google.protobuf.Empty) {}
+  rpc SendAclData(AclMsg) returns (google.protobuf.Empty) {}
+  rpc FetchEvents(google.protobuf.Empty) returns (stream EventMsg) {}
+  rpc FetchLeSubevents(google.protobuf.Empty) returns (stream LeSubeventMsg) {}
+  rpc FetchAclPackets(google.protobuf.Empty) returns (stream AclMsg) {}
+}
+
+message CommandMsg {
+  bytes command = 1;
+}
+
+message EventMsg {
+  bytes event = 1;
+}
+
+message LeSubeventMsg {
+  bytes event = 1;
+}
+
+message AclMsg {
+  uint32 handle = 1;
+  uint32 packet_boundary_flag = 2;
+  uint32 broadcast_flag = 3;
+  bytes data = 4;
+}
+
+message EventCodeMsg {
+  uint32 code = 1;
+}
+
+message LeSubeventCodeMsg {
+  uint32 code = 1;
+}
diff --git a/gd/hci/facade/le_acl_manager_facade.cc b/gd/hci/facade/le_acl_manager_facade.cc
new file mode 100644
index 0000000..38d5ecb
--- /dev/null
+++ b/gd/hci/facade/le_acl_manager_facade.cc
@@ -0,0 +1,337 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "hci/facade/le_acl_manager_facade.h"
+
+#include <condition_variable>
+#include <memory>
+#include <mutex>
+
+#include "common/bind.h"
+#include "grpc/grpc_event_queue.h"
+#include "hci/acl_manager.h"
+#include "hci/facade/le_acl_manager_facade.grpc.pb.h"
+#include "hci/facade/le_acl_manager_facade.pb.h"
+#include "hci/hci_packets.h"
+#include "packet/raw_builder.h"
+
+using ::grpc::ServerAsyncResponseWriter;
+using ::grpc::ServerAsyncWriter;
+using ::grpc::ServerContext;
+
+using ::bluetooth::packet::RawBuilder;
+
+namespace bluetooth {
+namespace hci {
+namespace facade {
+
+class LeAclManagerFacadeService : public LeAclManagerFacade::Service,
+                                  public ::bluetooth::hci::LeConnectionCallbacks,
+                                  public ::bluetooth::hci::ConnectionManagementCallbacks,
+                                  public ::bluetooth::hci::AclManagerCallbacks {
+ public:
+  LeAclManagerFacadeService(AclManager* acl_manager, ::bluetooth::os::Handler* facade_handler)
+      : acl_manager_(acl_manager), facade_handler_(facade_handler) {
+    acl_manager_->RegisterLeCallbacks(this, facade_handler_);
+    acl_manager_->RegisterLeAclManagerCallbacks(this, facade_handler_);
+  }
+
+  ~LeAclManagerFacadeService() override {
+    std::unique_lock<std::mutex> lock(acl_connections_mutex_);
+    for (auto connection : acl_connections_) {
+      connection.second->GetAclQueueEnd()->UnregisterDequeue();
+    }
+  }
+
+  ::grpc::Status CreateConnection(::grpc::ServerContext* context, const LeConnectionMsg* request,
+                                  ::grpc::ServerWriter<LeConnectionEvent>* writer) override {
+    Address peer_address;
+    ASSERT(Address::FromString(request->address(), peer_address));
+    AddressWithType peer(peer_address, static_cast<AddressType>(request->address_type()));
+    acl_manager_->CreateLeConnection(peer);
+    if (per_connection_events_.size() > current_connection_request_) {
+      return ::grpc::Status(::grpc::StatusCode::RESOURCE_EXHAUSTED, "Only one outstanding request is supported");
+    }
+    per_connection_events_.emplace_back(std::make_unique<::bluetooth::grpc::GrpcEventQueue<LeConnectionEvent>>(
+        std::string("connection attempt ") + std::to_string(current_connection_request_)));
+    return per_connection_events_[current_connection_request_]->RunLoop(context, writer);
+  }
+
+  ::grpc::Status Disconnect(::grpc::ServerContext* context, const LeHandleMsg* request,
+                            ::google::protobuf::Empty* response) override {
+    std::unique_lock<std::mutex> lock(acl_connections_mutex_);
+    auto connection = acl_connections_.find(request->handle());
+    if (connection == acl_connections_.end()) {
+      LOG_ERROR("Invalid handle");
+      return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Invalid handle");
+    } else {
+      connection->second->Disconnect(DisconnectReason::REMOTE_USER_TERMINATED_CONNECTION);
+      return ::grpc::Status::OK;
+    }
+  }
+
+  ::grpc::Status AuthenticationRequested(::grpc::ServerContext* context, const LeHandleMsg* request,
+                                         ::google::protobuf::Empty* response) override {
+    std::unique_lock<std::mutex> lock(acl_connections_mutex_);
+    auto connection = acl_connections_.find(request->handle());
+    if (connection == acl_connections_.end()) {
+      LOG_ERROR("Invalid handle");
+      return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Invalid handle");
+    } else {
+      connection->second->AuthenticationRequested();
+      return ::grpc::Status::OK;
+    }
+  };
+
+  ::grpc::Status FetchIncomingConnection(::grpc::ServerContext* context, const google::protobuf::Empty* request,
+                                         ::grpc::ServerWriter<LeConnectionEvent>* writer) override {
+    if (per_connection_events_.size() > current_connection_request_) {
+      return ::grpc::Status(::grpc::StatusCode::RESOURCE_EXHAUSTED, "Only one outstanding connection is supported");
+    }
+    per_connection_events_.emplace_back(std::make_unique<::bluetooth::grpc::GrpcEventQueue<LeConnectionEvent>>(
+        std::string("incoming connection ") + std::to_string(current_connection_request_)));
+    return per_connection_events_[current_connection_request_]->RunLoop(context, writer);
+  }
+
+  ::grpc::Status SendAclData(::grpc::ServerContext* context, const LeAclData* request,
+                             ::google::protobuf::Empty* response) override {
+    std::promise<void> promise;
+    auto future = promise.get_future();
+    {
+      std::unique_lock<std::mutex> lock(acl_connections_mutex_);
+      auto connection = acl_connections_.find(request->handle());
+      if (connection == acl_connections_.end()) {
+        LOG_ERROR("Invalid handle");
+        return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Invalid handle");
+      } else {
+        connection->second->GetAclQueueEnd()->RegisterEnqueue(
+            facade_handler_, common::Bind(&LeAclManagerFacadeService::enqueue_packet, common::Unretained(this),
+                                          common::Unretained(request), common::Passed(std::move(promise))));
+      }
+    }
+    future.wait();
+    return ::grpc::Status::OK;
+  }
+
+  std::unique_ptr<BasePacketBuilder> enqueue_packet(const LeAclData* request, std::promise<void> promise) {
+    acl_connections_[request->handle()]->GetAclQueueEnd()->UnregisterEnqueue();
+    std::unique_ptr<RawBuilder> packet =
+        std::make_unique<RawBuilder>(std::vector<uint8_t>(request->payload().begin(), request->payload().end()));
+    promise.set_value();
+    return packet;
+  }
+
+  ::grpc::Status FetchAclData(::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
+                              ::grpc::ServerWriter<LeAclData>* writer) override {
+    return pending_acl_data_.RunLoop(context, writer);
+  }
+
+  static inline uint16_t to_handle(uint32_t current_request) {
+    return (current_request + 0x10) % 0xe00;
+  }
+
+  static inline std::string builder_to_string(std::unique_ptr<BasePacketBuilder> builder) {
+    std::vector<uint8_t> bytes;
+    BitInserter bit_inserter(bytes);
+    builder->Serialize(bit_inserter);
+    return std::string(bytes.begin(), bytes.end());
+  }
+
+  void on_incoming_acl(std::shared_ptr<AclConnection> connection, uint16_t handle) {
+    auto packet = connection->GetAclQueueEnd()->TryDequeue();
+    LeAclData acl_data;
+    acl_data.set_handle(handle);
+    acl_data.set_payload(std::string(packet->begin(), packet->end()));
+    pending_acl_data_.OnIncomingEvent(acl_data);
+  }
+
+  void on_disconnect(std::shared_ptr<AclConnection> connection, uint32_t entry, ErrorCode code) {
+    connection->GetAclQueueEnd()->UnregisterDequeue();
+    connection->Finish();
+    std::unique_ptr<BasePacketBuilder> builder =
+        DisconnectBuilder::Create(to_handle(entry), static_cast<DisconnectReason>(code));
+    LeConnectionEvent disconnection;
+    disconnection.set_event(builder_to_string(std::move(builder)));
+    per_connection_events_[entry]->OnIncomingEvent(disconnection);
+  }
+
+  void OnLeConnectSuccess(AddressWithType address_with_type,
+                          std::unique_ptr<::bluetooth::hci::AclConnection> connection) override {
+    LOG_DEBUG("%s", address_with_type.ToString().c_str());
+
+    std::unique_lock<std::mutex> lock(acl_connections_mutex_);
+    auto addr = address_with_type.GetAddress();
+    std::shared_ptr<::bluetooth::hci::AclConnection> shared_connection = std::move(connection);
+    acl_connections_.emplace(to_handle(current_connection_request_), shared_connection);
+    auto remote_address = shared_connection->GetAddress().ToString();
+    shared_connection->GetAclQueueEnd()->RegisterDequeue(
+        facade_handler_, common::Bind(&LeAclManagerFacadeService::on_incoming_acl, common::Unretained(this),
+                                      shared_connection, to_handle(current_connection_request_)));
+    shared_connection->RegisterDisconnectCallback(
+        common::BindOnce(&LeAclManagerFacadeService::on_disconnect, common::Unretained(this), shared_connection,
+                         current_connection_request_),
+        facade_handler_);
+    shared_connection->RegisterCallbacks(this, facade_handler_);
+    {
+      std::unique_ptr<BasePacketBuilder> builder = LeConnectionCompleteBuilder::Create(
+          ErrorCode::SUCCESS, to_handle(current_connection_request_), Role::MASTER, address_with_type.GetAddressType(),
+          addr, 1, 2, 3, MasterClockAccuracy::PPM_20);
+      LeConnectionEvent success;
+      success.set_event(builder_to_string(std::move(builder)));
+      per_connection_events_[current_connection_request_]->OnIncomingEvent(success);
+    }
+    current_connection_request_++;
+  }
+
+  void OnMasterLinkKeyComplete(uint16_t connection_handle, KeyFlag key_flag) override {
+    LOG_DEBUG("OnMasterLinkKeyComplete connection_handle:%d", connection_handle);
+  }
+
+  void OnRoleChange(Address bd_addr, Role new_role) override {
+    LOG_DEBUG("OnRoleChange bd_addr:%s, new_role:%d", bd_addr.ToString().c_str(), (uint8_t)new_role);
+  }
+
+  void OnReadDefaultLinkPolicySettingsComplete(uint16_t default_link_policy_settings) override {
+    LOG_DEBUG("OnReadDefaultLinkPolicySettingsComplete default_link_policy_settings:%d", default_link_policy_settings);
+  }
+
+  void OnLeConnectFail(AddressWithType address, ErrorCode reason) override {
+    std::unique_ptr<BasePacketBuilder> builder = LeConnectionCompleteBuilder::Create(
+        reason, 0, Role::MASTER, address.GetAddressType(), address.GetAddress(), 0, 0, 0, MasterClockAccuracy::PPM_20);
+    LeConnectionEvent fail;
+    fail.set_event(builder_to_string(std::move(builder)));
+    per_connection_events_[current_connection_request_]->OnIncomingEvent(fail);
+    current_connection_request_++;
+  }
+
+  void OnConnectionPacketTypeChanged(uint16_t packet_type) override {
+    LOG_DEBUG("OnConnectionPacketTypeChanged packet_type:%d", packet_type);
+  }
+
+  void OnAuthenticationComplete() override {
+    LOG_DEBUG("OnAuthenticationComplete");
+  }
+
+  void OnEncryptionChange(EncryptionEnabled enabled) override {
+    LOG_DEBUG("OnConnectionPacketTypeChanged enabled:%d", (uint8_t)enabled);
+  }
+
+  void OnChangeConnectionLinkKeyComplete() override {
+    LOG_DEBUG("OnChangeConnectionLinkKeyComplete");
+  };
+
+  void OnReadClockOffsetComplete(uint16_t clock_offset) override {
+    LOG_DEBUG("OnReadClockOffsetComplete clock_offset:%d", clock_offset);
+  };
+
+  void OnModeChange(Mode current_mode, uint16_t interval) override {
+    LOG_DEBUG("OnModeChange Mode:%d, interval:%d", (uint8_t)current_mode, interval);
+  };
+
+  void OnQosSetupComplete(ServiceType service_type, uint32_t token_rate, uint32_t peak_bandwidth, uint32_t latency,
+                          uint32_t delay_variation) override {
+    LOG_DEBUG("OnQosSetupComplete service_type:%d, token_rate:%d, peak_bandwidth:%d, latency:%d, delay_variation:%d",
+              (uint8_t)service_type, token_rate, peak_bandwidth, latency, delay_variation);
+  }
+
+  void OnFlowSpecificationComplete(FlowDirection flow_direction, ServiceType service_type, uint32_t token_rate,
+                                   uint32_t token_bucket_size, uint32_t peak_bandwidth,
+                                   uint32_t access_latency) override {
+    LOG_DEBUG(
+        "OnFlowSpecificationComplete flow_direction:%d. service_type:%d, token_rate:%d, token_bucket_size:%d, "
+        "peak_bandwidth:%d, access_latency:%d",
+        (uint8_t)flow_direction, (uint8_t)service_type, token_rate, token_bucket_size, peak_bandwidth, access_latency);
+  }
+
+  void OnFlushOccurred() override {
+    LOG_DEBUG("OnFlushOccurred");
+  }
+
+  void OnRoleDiscoveryComplete(Role current_role) override {
+    LOG_DEBUG("OnRoleDiscoveryComplete current_role:%d", (uint8_t)current_role);
+  }
+
+  void OnReadLinkPolicySettingsComplete(uint16_t link_policy_settings) override {
+    LOG_DEBUG("OnReadLinkPolicySettingsComplete link_policy_settings:%d", link_policy_settings);
+  }
+
+  void OnReadAutomaticFlushTimeoutComplete(uint16_t flush_timeout) override {
+    LOG_DEBUG("OnReadAutomaticFlushTimeoutComplete flush_timeout:%d", flush_timeout);
+  }
+
+  void OnReadTransmitPowerLevelComplete(uint8_t transmit_power_level) override {
+    LOG_DEBUG("OnReadTransmitPowerLevelComplete transmit_power_level:%d", transmit_power_level);
+  }
+
+  void OnReadLinkSupervisionTimeoutComplete(uint16_t link_supervision_timeout) override {
+    LOG_DEBUG("OnReadLinkSupervisionTimeoutComplete link_supervision_timeout:%d", link_supervision_timeout);
+  }
+
+  void OnReadFailedContactCounterComplete(uint16_t failed_contact_counter) override {
+    LOG_DEBUG("OnReadFailedContactCounterComplete failed_contact_counter:%d", failed_contact_counter);
+  }
+
+  void OnReadLinkQualityComplete(uint8_t link_quality) override {
+    LOG_DEBUG("OnReadLinkQualityComplete link_quality:%d", link_quality);
+  }
+
+  void OnReadAfhChannelMapComplete(AfhMode afh_mode, std::array<uint8_t, 10> afh_channel_map) override {
+    LOG_DEBUG("OnReadAfhChannelMapComplete afh_mode:%d", (uint8_t)afh_mode);
+  }
+
+  void OnReadRssiComplete(uint8_t rssi) override {
+    LOG_DEBUG("OnReadRssiComplete rssi:%d", rssi);
+  }
+
+  void OnReadClockComplete(uint32_t clock, uint16_t accuracy) override {
+    LOG_DEBUG("OnReadClockComplete clock:%d, accuracy:%d", clock, accuracy);
+  }
+
+ private:
+  AclManager* acl_manager_;
+  ::bluetooth::os::Handler* facade_handler_;
+  mutable std::mutex acl_connections_mutex_;
+  std::map<uint16_t, std::shared_ptr<AclConnection>> acl_connections_;
+  ::bluetooth::grpc::GrpcEventQueue<LeAclData> pending_acl_data_{"FetchAclData"};
+  std::vector<std::unique_ptr<::bluetooth::grpc::GrpcEventQueue<LeConnectionEvent>>> per_connection_events_;
+  uint32_t current_connection_request_{0};
+};
+
+void LeAclManagerFacadeModule::ListDependencies(ModuleList* list) {
+  ::bluetooth::grpc::GrpcFacadeModule::ListDependencies(list);
+  list->add<AclManager>();
+}
+
+void LeAclManagerFacadeModule::Start() {
+  ::bluetooth::grpc::GrpcFacadeModule::Start();
+  service_ = new LeAclManagerFacadeService(GetDependency<AclManager>(), GetHandler());
+}
+
+void LeAclManagerFacadeModule::Stop() {
+  delete service_;
+  ::bluetooth::grpc::GrpcFacadeModule::Stop();
+}
+
+::grpc::Service* LeAclManagerFacadeModule::GetService() const {
+  return service_;
+}
+
+const ModuleFactory LeAclManagerFacadeModule::Factory =
+    ::bluetooth::ModuleFactory([]() { return new LeAclManagerFacadeModule(); });
+
+}  // namespace facade
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/facade/le_acl_manager_facade.h b/gd/hci/facade/le_acl_manager_facade.h
new file mode 100644
index 0000000..899a6cc
--- /dev/null
+++ b/gd/hci/facade/le_acl_manager_facade.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <grpc++/grpc++.h>
+
+#include "grpc/grpc_module.h"
+#include "hci/acl_manager.h"
+
+namespace bluetooth {
+namespace hci {
+namespace facade {
+
+class LeAclManagerFacadeService;
+
+class LeAclManagerFacadeModule : public ::bluetooth::grpc::GrpcFacadeModule {
+ public:
+  static const ModuleFactory Factory;
+
+  void ListDependencies(ModuleList* list) override;
+  void Start() override;
+  void Stop() override;
+  ::grpc::Service* GetService() const override;
+
+ private:
+  LeAclManagerFacadeService* service_;
+};
+
+}  // namespace facade
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/facade/le_acl_manager_facade.proto b/gd/hci/facade/le_acl_manager_facade.proto
new file mode 100644
index 0000000..8450b92
--- /dev/null
+++ b/gd/hci/facade/le_acl_manager_facade.proto
@@ -0,0 +1,33 @@
+syntax = "proto3";
+
+package bluetooth.hci;
+
+import "google/protobuf/empty.proto";
+
+service LeAclManagerFacade {
+  rpc CreateConnection(LeConnectionMsg) returns (stream LeConnectionEvent) {}
+  rpc CancelConnection(LeConnectionMsg) returns (google.protobuf.Empty) {}
+  rpc Disconnect(LeHandleMsg) returns (google.protobuf.Empty) {}
+  rpc AuthenticationRequested(LeHandleMsg) returns (google.protobuf.Empty) {}
+  rpc SendAclData(LeAclData) returns (google.protobuf.Empty) {}
+  rpc FetchAclData(google.protobuf.Empty) returns (stream LeAclData) {}
+  rpc FetchIncomingConnection(google.protobuf.Empty) returns (stream LeConnectionEvent) {}
+}
+
+message LeHandleMsg {
+  uint32 handle = 1;
+}
+
+message LeConnectionMsg {
+  uint32 address_type = 1;
+  bytes address = 2;
+}
+
+message LeConnectionEvent {
+  bytes event = 1;
+}
+
+message LeAclData {
+  uint32 handle = 1;
+  bytes payload = 2;
+}
diff --git a/gd/hci/facade/le_advertising_manager_facade.cc b/gd/hci/facade/le_advertising_manager_facade.cc
new file mode 100644
index 0000000..bfba5da
--- /dev/null
+++ b/gd/hci/facade/le_advertising_manager_facade.cc
@@ -0,0 +1,213 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cstdint>
+#include <unordered_map>
+#include <utility>
+
+#include "common/bidi_queue.h"
+#include "common/bind.h"
+#include "hci/address.h"
+#include "hci/address_with_type.h"
+#include "hci/facade/le_advertising_manager_facade.grpc.pb.h"
+#include "hci/facade/le_advertising_manager_facade.h"
+#include "hci/facade/le_advertising_manager_facade.pb.h"
+#include "hci/le_advertising_manager.h"
+#include "os/log.h"
+
+namespace bluetooth {
+namespace hci {
+namespace facade {
+
+using ::grpc::ServerAsyncResponseWriter;
+using ::grpc::ServerAsyncWriter;
+using ::grpc::ServerContext;
+using ::grpc::ServerWriter;
+using ::grpc::Status;
+
+using ::bluetooth::facade::BluetoothAddress;
+using ::bluetooth::facade::BluetoothAddressTypeEnum;
+
+hci::GapData GapDataFromProto(const GapDataMsg& gap_data_proto) {
+  hci::GapData gap_data;
+  auto data_copy = std::make_shared<std::vector<uint8_t>>(gap_data_proto.data().begin(), gap_data_proto.data().end());
+  packet::PacketView<packet::kLittleEndian> packet(data_copy);
+  auto after = hci::GapData::Parse(&gap_data, packet.begin());
+  ASSERT(after != packet.begin());
+  return gap_data;
+}
+
+bool AdvertisingConfigFromProto(const AdvertisingConfig& config_proto, hci::AdvertisingConfig* config) {
+  for (const auto& elem : config_proto.advertisement()) {
+    config->advertisement.push_back(GapDataFromProto(elem));
+  }
+
+  for (const auto& elem : config_proto.scan_response()) {
+    config->scan_response.push_back(GapDataFromProto(elem));
+  }
+
+  hci::Address::FromString(config_proto.random_address().address(), config->random_address);
+
+  if (config_proto.interval_min() > UINT16_MAX || config_proto.interval_min() < 0) {
+    LOG_WARN("Bad interval_min: %d", config_proto.interval_min());
+    return false;
+  }
+  config->interval_min = static_cast<uint16_t>(config_proto.interval_min());
+
+  if (config_proto.interval_max() > UINT16_MAX || config_proto.interval_max() < 0) {
+    LOG_WARN("Bad interval_max: %d", config_proto.interval_max());
+    return false;
+  }
+  config->interval_max = static_cast<uint16_t>(config_proto.interval_max());
+
+  config->event_type = static_cast<hci::AdvertisingEventType>(config_proto.event_type());
+
+  config->address_type = static_cast<::bluetooth::hci::AddressType>(config_proto.address_type());
+
+  config->peer_address_type = static_cast<::bluetooth::hci::PeerAddressType>(config_proto.peer_address_type());
+
+  hci::Address::FromString(config_proto.peer_address().address(), config->peer_address);
+
+  if (config_proto.channel_map() > UINT8_MAX || config_proto.channel_map() < 0) {
+    LOG_WARN("Bad channel_map: %d", config_proto.channel_map());
+    return false;
+  }
+  config->channel_map = static_cast<uint8_t>(config_proto.channel_map());
+
+  if (config_proto.tx_power() > UINT8_MAX || config_proto.tx_power() < 0) {
+    LOG_WARN("Bad tx_power: %d", config_proto.tx_power());
+    return false;
+  }
+
+  config->filter_policy = static_cast<hci::AdvertisingFilterPolicy>(config_proto.filter_policy());
+
+  config->tx_power = static_cast<uint8_t>(config_proto.tx_power());
+  return true;
+}
+
+class LeAdvertiser {
+ public:
+  LeAdvertiser(hci::AdvertisingConfig config) : config_(std::move(config)) {}
+
+  void ScanCallback(Address address, AddressType address_type) {}
+
+  void TerminatedCallback(ErrorCode error_code, uint8_t, uint8_t) {}
+
+  hci::AdvertiserId GetAdvertiserId() {
+    return id_;
+  }
+
+  void SetAdvertiserId(hci::AdvertiserId id) {
+    id_ = id;
+  }
+
+ private:
+  hci::AdvertiserId id_ = LeAdvertisingManager::kInvalidId;
+  hci::AdvertisingConfig config_;
+};
+
+class LeAdvertisingManagerFacadeService : public LeAdvertisingManagerFacade::Service {
+ public:
+  LeAdvertisingManagerFacadeService(LeAdvertisingManager* le_advertising_manager, os::Handler* facade_handler)
+      : le_advertising_manager_(le_advertising_manager), facade_handler_(facade_handler) {
+    ASSERT(le_advertising_manager_ != nullptr);
+    ASSERT(facade_handler_ != nullptr);
+  }
+
+  ::grpc::Status CreateAdvertiser(::grpc::ServerContext* context, const CreateAdvertiserRequest* request,
+                                  CreateAdvertiserResponse* response) override {
+    hci::AdvertisingConfig config = {};
+    if (!AdvertisingConfigFromProto(request->config(), &config)) {
+      LOG_WARN("Error parsing advertising config %s", request->SerializeAsString().c_str());
+      response->set_advertiser_id(LeAdvertisingManager::kInvalidId);
+      return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Error while parsing advertising config");
+    }
+    LeAdvertiser le_advertiser(config);
+    auto advertiser_id = le_advertising_manager_->CreateAdvertiser(
+        config, common::Bind(&LeAdvertiser::ScanCallback, common::Unretained(&le_advertiser)),
+        common::Bind(&LeAdvertiser::TerminatedCallback, common::Unretained(&le_advertiser)), facade_handler_);
+    if (advertiser_id != LeAdvertisingManager::kInvalidId) {
+      le_advertiser.SetAdvertiserId(advertiser_id);
+      le_advertisers_.push_back(le_advertiser);
+    } else {
+      LOG_WARN("Failed to create advertiser");
+    }
+    response->set_advertiser_id(advertiser_id);
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status ExtendedCreateAdvertiser(::grpc::ServerContext* context,
+                                          const ExtendedCreateAdvertiserRequest* request,
+                                          ExtendedCreateAdvertiserResponse* response) override {
+    LOG_WARN("ExtendedCreateAdvertiser is not implemented");
+    response->set_advertiser_id(LeAdvertisingManager::kInvalidId);
+    return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "ExtendedCreateAdvertiser is not implemented");
+  }
+
+  ::grpc::Status GetNumberOfAdvertisingInstances(::grpc::ServerContext* context,
+                                                 const ::google::protobuf::Empty* request,
+                                                 GetNumberOfAdvertisingInstancesResponse* response) override {
+    response->set_num_advertising_instances(le_advertising_manager_->GetNumberOfAdvertisingInstances());
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status RemoveAdvertiser(::grpc::ServerContext* context, const RemoveAdvertiserRequest* request,
+                                  ::google::protobuf::Empty* response) override {
+    if (request->advertiser_id() == LeAdvertisingManager::kInvalidId) {
+      LOG_WARN("Invalid advertiser ID %d", request->advertiser_id());
+      return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Invlid advertiser ID received");
+    }
+    le_advertising_manager_->RemoveAdvertiser(request->advertiser_id());
+    for (auto iter = le_advertisers_.begin(); iter != le_advertisers_.end();) {
+      if (iter->GetAdvertiserId() == request->advertiser_id()) {
+        iter = le_advertisers_.erase(iter);
+      } else {
+        ++iter;
+      }
+    }
+    return ::grpc::Status::OK;
+  }
+
+  std::vector<LeAdvertiser> le_advertisers_;
+  LeAdvertisingManager* le_advertising_manager_;
+  os::Handler* facade_handler_;
+};
+
+void LeAdvertisingManagerFacadeModule::ListDependencies(ModuleList* list) {
+  ::bluetooth::grpc::GrpcFacadeModule::ListDependencies(list);
+  list->add<hci::LeAdvertisingManager>();
+}
+
+void LeAdvertisingManagerFacadeModule::Start() {
+  ::bluetooth::grpc::GrpcFacadeModule::Start();
+  service_ = new LeAdvertisingManagerFacadeService(GetDependency<hci::LeAdvertisingManager>(), GetHandler());
+}
+
+void LeAdvertisingManagerFacadeModule::Stop() {
+  delete service_;
+  ::bluetooth::grpc::GrpcFacadeModule::Stop();
+}
+
+::grpc::Service* LeAdvertisingManagerFacadeModule::GetService() const {
+  return service_;
+}
+
+const ModuleFactory LeAdvertisingManagerFacadeModule::Factory =
+    ::bluetooth::ModuleFactory([]() { return new LeAdvertisingManagerFacadeModule(); });
+
+}  // namespace facade
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/facade/le_advertising_manager_facade.h b/gd/hci/facade/le_advertising_manager_facade.h
new file mode 100644
index 0000000..721132e
--- /dev/null
+++ b/gd/hci/facade/le_advertising_manager_facade.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <grpc++/grpc++.h>
+
+#include "grpc/grpc_module.h"
+
+namespace bluetooth {
+namespace hci {
+namespace facade {
+
+class LeAdvertisingManagerFacadeService;
+
+class LeAdvertisingManagerFacadeModule : public ::bluetooth::grpc::GrpcFacadeModule {
+ public:
+  static const ModuleFactory Factory;
+
+  void ListDependencies(ModuleList* list) override;
+  void Start() override;
+  void Stop() override;
+
+  ::grpc::Service* GetService() const override;
+
+ private:
+  LeAdvertisingManagerFacadeService* service_;
+};
+
+}  // namespace facade
+}  // namespace hci
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/hci/facade/le_advertising_manager_facade.proto b/gd/hci/facade/le_advertising_manager_facade.proto
new file mode 100644
index 0000000..9c75bd9
--- /dev/null
+++ b/gd/hci/facade/le_advertising_manager_facade.proto
@@ -0,0 +1,90 @@
+syntax = "proto3";
+
+package bluetooth.hci.facade;
+
+import "google/protobuf/empty.proto";
+import "facade/common.proto";
+
+service LeAdvertisingManagerFacade {
+  rpc CreateAdvertiser(CreateAdvertiserRequest) returns (CreateAdvertiserResponse) {}
+  rpc ExtendedCreateAdvertiser(ExtendedCreateAdvertiserRequest) returns (ExtendedCreateAdvertiserResponse) {}
+  rpc GetNumberOfAdvertisingInstances(google.protobuf.Empty) returns (GetNumberOfAdvertisingInstancesResponse) {}
+  rpc RemoveAdvertiser(RemoveAdvertiserRequest) returns (google.protobuf.Empty) {}
+}
+
+message GapDataMsg {
+  bytes data = 1;
+}
+
+enum AdvertisingEventType {
+  ADV_IND = 0x0;
+  ADV_DIRECT_IND = 0x1;
+  ADV_SCAN_IND = 0x2;
+  ADV_NONCONN_IND = 0x3;
+  ADV_DIRECT_IND_LOW = 0x4;
+}
+
+enum AdvertisingFilterPolicy {
+  ALL_DEVICES = 0x0;
+  WHITELISTED_SCAN = 0x1;
+  WHITELISTED_CONNECT = 0x2;
+  WHITELISTED_SCAN_AND_CONNECT = 0x3;
+};
+
+message AdvertisingConfig {
+  repeated GapDataMsg advertisement = 1;
+  repeated GapDataMsg scan_response = 2;
+  bluetooth.facade.BluetoothAddress random_address = 3;
+  // Unit: number of Bluetooth slots in 0.125 ms increment
+  int32 interval_min = 4;
+  // Unit: number of Bluetooth slots in 0.125 ms increment
+  int32 interval_max = 5;
+  AdvertisingEventType event_type = 6;
+  bluetooth.facade.BluetoothAddressTypeEnum address_type = 7;
+  bluetooth.facade.BluetoothPeerAddressTypeEnum peer_address_type = 8;
+  bluetooth.facade.BluetoothAddress peer_address = 9;
+  int32 channel_map = 10;
+  AdvertisingFilterPolicy filter_policy = 11;
+  int32 tx_power = 12;
+}
+
+message ExtendedAdvertisingConfig {
+  AdvertisingConfig advertising_config = 1;
+  bool connectable = 2;
+  bool scannable = 3;
+  bool directed = 4;
+  bool high_duty_directed_connectable = 5;
+  bool legacy_pdus = 6;
+  bool anonymous = 7;
+  bool include_tx_power = 8;
+  bool use_le_coded_phy = 9;
+  int32 secondary_map_skip = 10;
+  int32 secondary_advertising_phy = 11;
+  int32 sid = 12;
+  bool enable_scan_request_notification = 13;
+}
+
+message CreateAdvertiserRequest {
+  AdvertisingConfig config = 1;
+}
+
+message CreateAdvertiserResponse {
+  // -1 on error
+  int32 advertiser_id = 1;
+}
+
+message ExtendedCreateAdvertiserRequest {
+  ExtendedAdvertisingConfig config = 1;
+}
+
+message ExtendedCreateAdvertiserResponse {
+  int32 advertiser_id = 1;
+}
+
+message GetNumberOfAdvertisingInstancesResponse {
+  int32 num_advertising_instances = 1;
+}
+
+message RemoveAdvertiserRequest {
+  int32 advertiser_id = 1;
+}
\ No newline at end of file
diff --git a/gd/hci/facade/le_scanning_manager_facade.cc b/gd/hci/facade/le_scanning_manager_facade.cc
new file mode 100644
index 0000000..bf790b7
--- /dev/null
+++ b/gd/hci/facade/le_scanning_manager_facade.cc
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "hci/le_scanning_manager.h"
+
+#include <cstdint>
+#include <unordered_map>
+#include <utility>
+
+#include "common/bidi_queue.h"
+#include "common/bind.h"
+#include "grpc/grpc_event_queue.h"
+#include "hci/facade/le_scanning_manager_facade.grpc.pb.h"
+#include "hci/facade/le_scanning_manager_facade.h"
+#include "hci/facade/le_scanning_manager_facade.pb.h"
+#include "hci/le_report.h"
+#include "os/log.h"
+#include "packet/raw_builder.h"
+
+namespace bluetooth {
+namespace hci {
+namespace facade {
+
+using ::grpc::ServerAsyncResponseWriter;
+using ::grpc::ServerAsyncWriter;
+using ::grpc::ServerContext;
+using ::grpc::ServerWriter;
+using ::grpc::Status;
+
+class LeScanningManagerFacadeService : public LeScanningManagerFacade::Service, public LeScanningManagerCallbacks {
+ public:
+  LeScanningManagerFacadeService(LeScanningManager* le_scanning_manager, os::Handler* facade_handler)
+      : le_scanning_manager_(le_scanning_manager), facade_handler_(facade_handler) {
+    ASSERT(le_scanning_manager_ != nullptr);
+    ASSERT(facade_handler_ != nullptr);
+  }
+
+  ::grpc::Status StartScan(::grpc::ServerContext* context, const ::google::protobuf::Empty*,
+                           ::grpc::ServerWriter<LeReportMsg>* writer) override {
+    le_scanning_manager_->StartScan(this);
+    return pending_events_.RunLoop(context, writer);
+  }
+
+  ::grpc::Status StopScan(::grpc::ServerContext* context, const ::google::protobuf::Empty*,
+                          ScanStoppedMsg* response) override {
+    std::shared_ptr<std::promise<void>> on_stopped = std::make_shared<std::promise<void>>();
+    auto future = on_stopped->get_future();
+    le_scanning_manager_->StopScan(
+        common::Bind([](std::shared_ptr<std::promise<void>> p) { p->set_value(); }, on_stopped));
+    auto result = future.wait_for(std::chrono::milliseconds(1000));
+    ASSERT(result == std::future_status::ready);
+    return ::grpc::Status::OK;
+  }
+
+  void on_advertisements(std::vector<std::shared_ptr<LeReport>> reports) override {
+    for (const auto report : reports) {
+      switch (report->report_type_) {
+        case hci::LeReport::ReportType::ADVERTISING_EVENT: {
+          LeReportMsg le_report_msg;
+          std::vector<LeAdvertisingReport> advertisements;
+          LeAdvertisingReport le_advertising_report;
+          le_advertising_report.address_type_ = report->address_type_;
+          le_advertising_report.address_ = report->address_;
+          le_advertising_report.advertising_data_ = report->gap_data_;
+          le_advertising_report.event_type_ = report->advertising_event_type_;
+          le_advertising_report.rssi_ = report->rssi_;
+          advertisements.push_back(le_advertising_report);
+
+          auto builder = LeAdvertisingReportBuilder::Create(advertisements);
+          std::vector<uint8_t> bytes;
+          BitInserter bit_inserter(bytes);
+          builder->Serialize(bit_inserter);
+          le_report_msg.set_event(std::string(bytes.begin(), bytes.end()));
+          pending_events_.OnIncomingEvent(std::move(le_report_msg));
+        } break;
+        case hci::LeReport::ReportType::EXTENDED_ADVERTISING_EVENT: {
+          LeReportMsg le_report_msg;
+          std::vector<LeExtendedAdvertisingReport> advertisements;
+          LeExtendedAdvertisingReport le_extended_advertising_report;
+          le_extended_advertising_report.address_ = report->address_;
+          le_extended_advertising_report.advertising_data_ = report->gap_data_;
+          le_extended_advertising_report.rssi_ = report->rssi_;
+          advertisements.push_back(le_extended_advertising_report);
+
+          auto builder = LeExtendedAdvertisingReportBuilder::Create(advertisements);
+          std::vector<uint8_t> bytes;
+          BitInserter bit_inserter(bytes);
+          builder->Serialize(bit_inserter);
+          le_report_msg.set_event(std::string(bytes.begin(), bytes.end()));
+          pending_events_.OnIncomingEvent(std::move(le_report_msg));
+        } break;
+        case hci::LeReport::ReportType::DIRECTED_ADVERTISING_EVENT: {
+          LeReportMsg le_report_msg;
+          std::vector<LeDirectedAdvertisingReport> advertisements;
+          LeDirectedAdvertisingReport le_directed_advertising_report;
+          le_directed_advertising_report.address_ = report->address_;
+          le_directed_advertising_report.direct_address_ = ((DirectedLeReport*)report.get())->direct_address_;
+          le_directed_advertising_report.direct_address_type_ = DirectAddressType::RANDOM_DEVICE_ADDRESS;
+          le_directed_advertising_report.event_type_ = DirectAdvertisingEventType::ADV_DIRECT_IND;
+          le_directed_advertising_report.rssi_ = report->rssi_;
+          advertisements.push_back(le_directed_advertising_report);
+
+          auto builder = LeDirectedAdvertisingReportBuilder::Create(advertisements);
+          std::vector<uint8_t> bytes;
+          BitInserter bit_inserter(bytes);
+          builder->Serialize(bit_inserter);
+          le_report_msg.set_event(std::string(bytes.begin(), bytes.end()));
+          pending_events_.OnIncomingEvent(std::move(le_report_msg));
+        } break;
+        default:
+          LOG_INFO("Skipping unknown report type %d", static_cast<int>(report->report_type_));
+      }
+    }
+  }
+
+  void on_timeout() override {
+    LeReportMsg le_report_msg;
+    auto builder = LeScanTimeoutBuilder::Create();
+    std::vector<uint8_t> bytes;
+    BitInserter bit_inserter(bytes);
+    builder->Serialize(bit_inserter);
+    le_report_msg.set_event(std::string(bytes.begin(), bytes.end()));
+    pending_events_.OnIncomingEvent(std::move(le_report_msg));
+  }
+
+  os::Handler* Handler() override {
+    return facade_handler_;
+  }
+
+  LeScanningManager* le_scanning_manager_;
+  os::Handler* facade_handler_;
+  ::bluetooth::grpc::GrpcEventQueue<LeReportMsg> pending_events_{"LeReports"};
+};
+
+void LeScanningManagerFacadeModule::ListDependencies(ModuleList* list) {
+  ::bluetooth::grpc::GrpcFacadeModule::ListDependencies(list);
+  list->add<hci::LeScanningManager>();
+}
+
+void LeScanningManagerFacadeModule::Start() {
+  ::bluetooth::grpc::GrpcFacadeModule::Start();
+  service_ = new LeScanningManagerFacadeService(GetDependency<hci::LeScanningManager>(), GetHandler());
+}
+
+void LeScanningManagerFacadeModule::Stop() {
+  delete service_;
+  ::bluetooth::grpc::GrpcFacadeModule::Stop();
+}
+
+::grpc::Service* LeScanningManagerFacadeModule::GetService() const {
+  return service_;
+}
+
+const ModuleFactory LeScanningManagerFacadeModule::Factory =
+    ::bluetooth::ModuleFactory([]() { return new LeScanningManagerFacadeModule(); });
+
+}  // namespace facade
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/facade/le_scanning_manager_facade.h b/gd/hci/facade/le_scanning_manager_facade.h
new file mode 100644
index 0000000..8731bd0
--- /dev/null
+++ b/gd/hci/facade/le_scanning_manager_facade.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <grpc++/grpc++.h>
+
+#include "grpc/grpc_module.h"
+
+namespace bluetooth {
+namespace hci {
+namespace facade {
+
+class LeScanningManagerFacadeService;
+
+class LeScanningManagerFacadeModule : public ::bluetooth::grpc::GrpcFacadeModule {
+ public:
+  static const ModuleFactory Factory;
+
+  void ListDependencies(ModuleList* list) override;
+  void Start() override;
+  void Stop() override;
+
+  ::grpc::Service* GetService() const override;
+
+ private:
+  LeScanningManagerFacadeService* service_;
+};
+
+}  // namespace facade
+}  // namespace hci
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/hci/facade/le_scanning_manager_facade.proto b/gd/hci/facade/le_scanning_manager_facade.proto
new file mode 100644
index 0000000..9a450e5
--- /dev/null
+++ b/gd/hci/facade/le_scanning_manager_facade.proto
@@ -0,0 +1,16 @@
+syntax = "proto3";
+
+package bluetooth.hci.facade;
+
+import "google/protobuf/empty.proto";
+
+service LeScanningManagerFacade {
+  rpc StartScan(google.protobuf.Empty) returns (stream LeReportMsg) {}
+  rpc StopScan(google.protobuf.Empty) returns (ScanStoppedMsg) {}
+}
+
+message LeReportMsg {
+  bytes event = 1;
+}
+
+message ScanStoppedMsg {}
diff --git a/gd/hci/hci_layer.cc b/gd/hci/hci_layer.cc
new file mode 100644
index 0000000..9f707da
--- /dev/null
+++ b/gd/hci/hci_layer.cc
@@ -0,0 +1,536 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "hci/hci_layer.h"
+
+#include "common/bind.h"
+#include "common/callback.h"
+#include "os/alarm.h"
+#include "os/queue.h"
+#include "packet/packet_builder.h"
+
+namespace {
+using bluetooth::common::Bind;
+using bluetooth::common::BindOnce;
+using bluetooth::common::Callback;
+using bluetooth::common::Closure;
+using bluetooth::common::OnceCallback;
+using bluetooth::common::OnceClosure;
+using bluetooth::hci::CommandCompleteView;
+using bluetooth::hci::CommandPacketBuilder;
+using bluetooth::hci::CommandStatusView;
+using bluetooth::hci::EventPacketView;
+using bluetooth::hci::LeMetaEventView;
+using bluetooth::os::Handler;
+
+class EventHandler {
+ public:
+  EventHandler() : event_handler(), handler(nullptr) {}
+  EventHandler(Callback<void(EventPacketView)> on_event, Handler* on_event_handler)
+      : event_handler(std::move(on_event)), handler(on_event_handler) {}
+  Callback<void(EventPacketView)> event_handler;
+  Handler* handler;
+};
+
+class SubeventHandler {
+ public:
+  SubeventHandler() : subevent_handler(), handler(nullptr) {}
+  SubeventHandler(Callback<void(LeMetaEventView)> on_event, Handler* on_event_handler)
+      : subevent_handler(std::move(on_event)), handler(on_event_handler) {}
+  Callback<void(LeMetaEventView)> subevent_handler;
+  Handler* handler;
+};
+
+class CommandQueueEntry {
+ public:
+  CommandQueueEntry(std::unique_ptr<CommandPacketBuilder> command_packet,
+                    OnceCallback<void(CommandCompleteView)> on_complete_function, Handler* handler)
+      : command(std::move(command_packet)), waiting_for_status_(false), on_complete(std::move(on_complete_function)),
+        caller_handler(handler) {}
+
+  CommandQueueEntry(std::unique_ptr<CommandPacketBuilder> command_packet,
+                    OnceCallback<void(CommandStatusView)> on_status_function, Handler* handler)
+      : command(std::move(command_packet)), waiting_for_status_(true), on_status(std::move(on_status_function)),
+        caller_handler(handler) {}
+
+  std::unique_ptr<CommandPacketBuilder> command;
+  bool waiting_for_status_;
+  OnceCallback<void(CommandStatusView)> on_status;
+  OnceCallback<void(CommandCompleteView)> on_complete;
+  Handler* caller_handler;
+};
+}  // namespace
+
+namespace bluetooth {
+namespace hci {
+
+using common::BidiQueue;
+using common::BidiQueueEnd;
+using os::Alarm;
+using os::Handler;
+
+namespace {
+using hci::OpCode;
+using hci::ResetCompleteView;
+
+void fail_if_reset_complete_not_success(CommandCompleteView complete) {
+  auto reset_complete = ResetCompleteView::Create(complete);
+  ASSERT(reset_complete.IsValid());
+  ASSERT(reset_complete.GetStatus() == ErrorCode::SUCCESS);
+}
+
+void on_hci_timeout(OpCode op_code) {
+  ASSERT_LOG(false, "Timed out waiting for 0x%02hx (%s)", op_code, OpCodeText(op_code).c_str());
+}
+}  // namespace
+
+class SecurityInterfaceImpl : public SecurityInterface {
+ public:
+  SecurityInterfaceImpl(HciLayer& hci) : hci_(hci) {}
+  virtual ~SecurityInterfaceImpl() = default;
+
+  virtual void EnqueueCommand(std::unique_ptr<SecurityCommandBuilder> command,
+                              common::OnceCallback<void(CommandCompleteView)> on_complete,
+                              os::Handler* handler) override {
+    hci_.EnqueueCommand(std::move(command), std::move(on_complete), handler);
+  }
+
+  virtual void EnqueueCommand(std::unique_ptr<SecurityCommandBuilder> command,
+                              common::OnceCallback<void(CommandStatusView)> on_status, os::Handler* handler) override {
+    hci_.EnqueueCommand(std::move(command), std::move(on_status), handler);
+  }
+  HciLayer& hci_;
+};
+
+class LeSecurityInterfaceImpl : public LeSecurityInterface {
+ public:
+  LeSecurityInterfaceImpl(HciLayer& hci) : hci_(hci) {}
+  virtual ~LeSecurityInterfaceImpl() = default;
+
+  virtual void EnqueueCommand(std::unique_ptr<LeSecurityCommandBuilder> command,
+                              common::OnceCallback<void(CommandCompleteView)> on_complete,
+                              os::Handler* handler) override {
+    hci_.EnqueueCommand(std::move(command), std::move(on_complete), handler);
+  }
+
+  virtual void EnqueueCommand(std::unique_ptr<LeSecurityCommandBuilder> command,
+                              common::OnceCallback<void(CommandStatusView)> on_status, os::Handler* handler) override {
+    hci_.EnqueueCommand(std::move(command), std::move(on_status), handler);
+  }
+  HciLayer& hci_;
+};
+
+class LeAdvertisingInterfaceImpl : public LeAdvertisingInterface {
+ public:
+  LeAdvertisingInterfaceImpl(HciLayer& hci) : hci_(hci) {}
+  virtual ~LeAdvertisingInterfaceImpl() = default;
+
+  virtual void EnqueueCommand(std::unique_ptr<LeAdvertisingCommandBuilder> command,
+                              common::OnceCallback<void(CommandCompleteView)> on_complete,
+                              os::Handler* handler) override {
+    hci_.EnqueueCommand(std::move(command), std::move(on_complete), handler);
+  }
+
+  virtual void EnqueueCommand(std::unique_ptr<LeAdvertisingCommandBuilder> command,
+                              common::OnceCallback<void(CommandStatusView)> on_status, os::Handler* handler) override {
+    hci_.EnqueueCommand(std::move(command), std::move(on_status), handler);
+  }
+  HciLayer& hci_;
+};
+
+class LeScanningInterfaceImpl : public LeScanningInterface {
+ public:
+  LeScanningInterfaceImpl(HciLayer& hci) : hci_(hci) {}
+  virtual ~LeScanningInterfaceImpl() = default;
+
+  virtual void EnqueueCommand(std::unique_ptr<LeScanningCommandBuilder> command,
+                              common::OnceCallback<void(CommandCompleteView)> on_complete,
+                              os::Handler* handler) override {
+    hci_.EnqueueCommand(std::move(command), std::move(on_complete), handler);
+  }
+
+  virtual void EnqueueCommand(std::unique_ptr<LeScanningCommandBuilder> command,
+                              common::OnceCallback<void(CommandStatusView)> on_status, os::Handler* handler) override {
+    hci_.EnqueueCommand(std::move(command), std::move(on_status), handler);
+  }
+  HciLayer& hci_;
+};
+
+struct HciLayer::impl : public hal::HciHalCallbacks {
+  impl(HciLayer& module) : hal_(nullptr), module_(module) {}
+
+  ~impl() {}
+
+  void Start(hal::HciHal* hal) {
+    hal_ = hal;
+    hci_timeout_alarm_ = new Alarm(module_.GetHandler());
+
+    auto queue_end = acl_queue_.GetDownEnd();
+    Handler* handler = module_.GetHandler();
+    queue_end->RegisterDequeue(handler, Bind(&impl::dequeue_and_send_acl, common::Unretained(this)));
+    RegisterEventHandler(EventCode::COMMAND_COMPLETE, Bind(&impl::command_complete_callback, common::Unretained(this)),
+                         handler);
+    RegisterEventHandler(EventCode::COMMAND_STATUS, Bind(&impl::command_status_callback, common::Unretained(this)),
+                         handler);
+    RegisterEventHandler(EventCode::LE_META_EVENT, Bind(&impl::le_meta_event_callback, common::Unretained(this)),
+                         handler);
+    // TODO find the right place
+    RegisterEventHandler(EventCode::PAGE_SCAN_REPETITION_MODE_CHANGE, Bind(&impl::drop, common::Unretained(this)),
+                         handler);
+    RegisterEventHandler(EventCode::MAX_SLOTS_CHANGE, Bind(&impl::drop, common::Unretained(this)), handler);
+    RegisterEventHandler(EventCode::VENDOR_SPECIFIC, Bind(&impl::drop, common::Unretained(this)), handler);
+
+    EnqueueCommand(ResetBuilder::Create(), BindOnce(&fail_if_reset_complete_not_success), handler);
+    hal_->registerIncomingPacketCallback(this);
+  }
+
+  void drop(EventPacketView) {}
+
+  void dequeue_and_send_acl() {
+    auto packet = acl_queue_.GetDownEnd()->TryDequeue();
+    send_acl(std::move(packet));
+  }
+
+  void Stop() {
+    hal_->unregisterIncomingPacketCallback();
+    UnregisterEventHandler(EventCode::COMMAND_COMPLETE);
+    UnregisterEventHandler(EventCode::COMMAND_STATUS);
+    UnregisterEventHandler(EventCode::LE_META_EVENT);
+    UnregisterEventHandler(EventCode::PAGE_SCAN_REPETITION_MODE_CHANGE);
+    UnregisterEventHandler(EventCode::MAX_SLOTS_CHANGE);
+    UnregisterEventHandler(EventCode::VENDOR_SPECIFIC);
+
+    acl_queue_.GetDownEnd()->UnregisterDequeue();
+    incoming_acl_packet_buffer_.Clear();
+    delete hci_timeout_alarm_;
+    command_queue_.clear();
+    hal_ = nullptr;
+  }
+
+  void send_acl(std::unique_ptr<hci::BasePacketBuilder> packet) {
+    std::vector<uint8_t> bytes;
+    BitInserter bi(bytes);
+    packet->Serialize(bi);
+    hal_->sendAclData(bytes);
+  }
+
+  void send_sco(std::unique_ptr<hci::BasePacketBuilder> packet) {
+    std::vector<uint8_t> bytes;
+    BitInserter bi(bytes);
+    packet->Serialize(bi);
+    hal_->sendScoData(bytes);
+  }
+
+  void command_status_callback(EventPacketView event) {
+    CommandStatusView status_view = CommandStatusView::Create(event);
+    ASSERT(status_view.IsValid());
+    command_credits_ = status_view.GetNumHciCommandPackets();
+    OpCode op_code = status_view.GetCommandOpCode();
+    if (op_code == OpCode::NONE) {
+      send_next_command();
+      return;
+    }
+    ASSERT_LOG(!command_queue_.empty(), "Unexpected status event with OpCode 0x%02hx (%s)", op_code,
+               OpCodeText(op_code).c_str());
+    ASSERT_LOG(waiting_command_ == op_code, "Waiting for 0x%02hx (%s), got 0x%02hx (%s)", waiting_command_,
+               OpCodeText(waiting_command_).c_str(), op_code, OpCodeText(op_code).c_str());
+    ASSERT_LOG(command_queue_.front().waiting_for_status_,
+               "Waiting for command complete 0x%02hx (%s), got command status for 0x%02hx (%s)", waiting_command_,
+               OpCodeText(waiting_command_).c_str(), op_code, OpCodeText(op_code).c_str());
+    auto caller_handler = command_queue_.front().caller_handler;
+    caller_handler->Post(BindOnce(std::move(command_queue_.front().on_status), std::move(status_view)));
+    command_queue_.pop_front();
+    waiting_command_ = OpCode::NONE;
+    hci_timeout_alarm_->Cancel();
+    send_next_command();
+  }
+
+  void command_complete_callback(EventPacketView event) {
+    CommandCompleteView complete_view = CommandCompleteView::Create(event);
+    ASSERT(complete_view.IsValid());
+    command_credits_ = complete_view.GetNumHciCommandPackets();
+    OpCode op_code = complete_view.GetCommandOpCode();
+    if (op_code == OpCode::NONE) {
+      send_next_command();
+      return;
+    }
+    ASSERT_LOG(command_queue_.size() > 0, "Unexpected command complete with OpCode 0x%02hx (%s)", op_code,
+               OpCodeText(op_code).c_str());
+    ASSERT_LOG(waiting_command_ == op_code, "Waiting for 0x%02hx (%s), got 0x%02hx (%s)", waiting_command_,
+               OpCodeText(waiting_command_).c_str(), op_code, OpCodeText(op_code).c_str());
+    ASSERT_LOG(!command_queue_.front().waiting_for_status_,
+               "Waiting for command status 0x%02hx (%s), got command complete for 0x%02hx (%s)", waiting_command_,
+               OpCodeText(waiting_command_).c_str(), op_code, OpCodeText(op_code).c_str());
+    auto caller_handler = command_queue_.front().caller_handler;
+    caller_handler->Post(BindOnce(std::move(command_queue_.front().on_complete), complete_view));
+    command_queue_.pop_front();
+    waiting_command_ = OpCode::NONE;
+    hci_timeout_alarm_->Cancel();
+    send_next_command();
+  }
+
+  void le_meta_event_callback(EventPacketView event) {
+    LeMetaEventView meta_event_view = LeMetaEventView::Create(event);
+    ASSERT(meta_event_view.IsValid());
+    SubeventCode subevent_code = meta_event_view.GetSubeventCode();
+    ASSERT_LOG(subevent_handlers_.find(subevent_code) != subevent_handlers_.end(),
+               "Unhandled le event of type 0x%02hhx (%s)", subevent_code, SubeventCodeText(subevent_code).c_str());
+    auto& registered_handler = subevent_handlers_[subevent_code].subevent_handler;
+    subevent_handlers_[subevent_code].handler->Post(BindOnce(registered_handler, meta_event_view));
+  }
+
+  void hciEventReceived(hal::HciPacket event_bytes) override {
+    auto packet = packet::PacketView<packet::kLittleEndian>(std::make_shared<std::vector<uint8_t>>(event_bytes));
+    EventPacketView event = EventPacketView::Create(packet);
+    ASSERT(event.IsValid());
+    module_.GetHandler()->Post(
+        BindOnce(&HciLayer::impl::hci_event_received_handler, common::Unretained(this), std::move(event)));
+  }
+
+  void hci_event_received_handler(EventPacketView event) {
+    EventCode event_code = event.GetEventCode();
+    ASSERT_LOG(event_handlers_.find(event_code) != event_handlers_.end(), "Unhandled event of type 0x%02hhx (%s)",
+               event_code, EventCodeText(event_code).c_str());
+    auto& registered_handler = event_handlers_[event_code].event_handler;
+    event_handlers_[event_code].handler->Post(BindOnce(registered_handler, std::move(event)));
+  }
+
+  void aclDataReceived(hal::HciPacket data_bytes) override {
+    auto packet =
+        packet::PacketView<packet::kLittleEndian>(std::make_shared<std::vector<uint8_t>>(std::move(data_bytes)));
+    AclPacketView acl = AclPacketView::Create(packet);
+    incoming_acl_packet_buffer_.Enqueue(std::make_unique<AclPacketView>(acl), module_.GetHandler());
+  }
+
+  void scoDataReceived(hal::HciPacket data_bytes) override {
+    auto packet = packet::PacketView<packet::kLittleEndian>(std::make_shared<std::vector<uint8_t>>(data_bytes));
+    ScoPacketView sco = ScoPacketView::Create(packet);
+  }
+
+  void EnqueueCommand(std::unique_ptr<CommandPacketBuilder> command,
+                      OnceCallback<void(CommandCompleteView)> on_complete, os::Handler* handler) {
+    module_.GetHandler()->Post(common::BindOnce(&impl::handle_enqueue_command_with_complete, common::Unretained(this),
+                                                std::move(command), std::move(on_complete),
+                                                common::Unretained(handler)));
+  }
+
+  void EnqueueCommand(std::unique_ptr<CommandPacketBuilder> command, OnceCallback<void(CommandStatusView)> on_status,
+                      os::Handler* handler) {
+    module_.GetHandler()->Post(common::BindOnce(&impl::handle_enqueue_command_with_status, common::Unretained(this),
+                                                std::move(command), std::move(on_status), common::Unretained(handler)));
+  }
+
+  void handle_enqueue_command_with_complete(std::unique_ptr<CommandPacketBuilder> command,
+                                            OnceCallback<void(CommandCompleteView)> on_complete, os::Handler* handler) {
+    command_queue_.emplace_back(std::move(command), std::move(on_complete), handler);
+
+    send_next_command();
+  }
+
+  void handle_enqueue_command_with_status(std::unique_ptr<CommandPacketBuilder> command,
+                                          OnceCallback<void(CommandStatusView)> on_status, os::Handler* handler) {
+    command_queue_.emplace_back(std::move(command), std::move(on_status), handler);
+
+    send_next_command();
+  }
+
+  void send_next_command() {
+    if (command_credits_ == 0) {
+      return;
+    }
+    if (waiting_command_ != OpCode::NONE) {
+      return;
+    }
+    if (command_queue_.size() == 0) {
+      return;
+    }
+    std::shared_ptr<std::vector<uint8_t>> bytes = std::make_shared<std::vector<uint8_t>>();
+    BitInserter bi(*bytes);
+    command_queue_.front().command->Serialize(bi);
+    hal_->sendHciCommand(*bytes);
+    auto cmd_view = CommandPacketView::Create(bytes);
+    ASSERT(cmd_view.IsValid());
+    OpCode op_code = cmd_view.GetOpCode();
+    waiting_command_ = op_code;
+    command_credits_ = 0;  // Only allow one outstanding command
+    hci_timeout_alarm_->Schedule(BindOnce(&on_hci_timeout, op_code), kHciTimeoutMs);
+  }
+
+  BidiQueueEnd<AclPacketBuilder, AclPacketView>* GetAclQueueEnd() {
+    return acl_queue_.GetUpEnd();
+  }
+
+  void RegisterEventHandler(EventCode event_code, Callback<void(EventPacketView)> event_handler, os::Handler* handler) {
+    module_.GetHandler()->Post(common::BindOnce(&impl::handle_register_event_handler, common::Unretained(this),
+                                                event_code, event_handler, common::Unretained(handler)));
+  }
+
+  void handle_register_event_handler(EventCode event_code, Callback<void(EventPacketView)> event_handler,
+                                     os::Handler* handler) {
+    ASSERT_LOG(event_handlers_.count(event_code) == 0, "Can not register a second handler for event_code %02hhx (%s)",
+               event_code, EventCodeText(event_code).c_str());
+    EventHandler to_save(event_handler, handler);
+    event_handlers_[event_code] = to_save;
+  }
+
+  void UnregisterEventHandler(EventCode event_code) {
+    module_.GetHandler()->Post(
+        common::BindOnce(&impl::handle_unregister_event_handler, common::Unretained(this), event_code));
+  }
+
+  void handle_unregister_event_handler(EventCode event_code) {
+    event_handlers_.erase(event_code);
+  }
+
+  void RegisterLeEventHandler(SubeventCode subevent_code, Callback<void(LeMetaEventView)> event_handler,
+                              os::Handler* handler) {
+    module_.GetHandler()->Post(common::BindOnce(&impl::handle_register_le_event_handler, common::Unretained(this),
+                                                subevent_code, event_handler, common::Unretained(handler)));
+  }
+
+  void handle_register_le_event_handler(SubeventCode subevent_code, Callback<void(LeMetaEventView)> subevent_handler,
+                                        os::Handler* handler) {
+    ASSERT_LOG(subevent_handlers_.count(subevent_code) == 0,
+               "Can not register a second handler for subevent_code %02hhx (%s)", subevent_code,
+               SubeventCodeText(subevent_code).c_str());
+    SubeventHandler to_save(subevent_handler, handler);
+    subevent_handlers_[subevent_code] = to_save;
+  }
+
+  void UnregisterLeEventHandler(SubeventCode subevent_code) {
+    module_.GetHandler()->Post(
+        common::BindOnce(&impl::handle_unregister_le_event_handler, common::Unretained(this), subevent_code));
+  }
+
+  void handle_unregister_le_event_handler(SubeventCode subevent_code) {
+    subevent_handlers_.erase(subevent_code);
+  }
+
+  // The HAL
+  hal::HciHal* hal_;
+
+  // A reference to the HciLayer module
+  HciLayer& module_;
+
+  // Interfaces
+  SecurityInterfaceImpl security_interface{module_};
+  LeSecurityInterfaceImpl le_security_interface{module_};
+  LeAdvertisingInterfaceImpl le_advertising_interface{module_};
+  LeScanningInterfaceImpl le_scanning_interface{module_};
+
+  // Command Handling
+  std::list<CommandQueueEntry> command_queue_;
+
+  std::map<EventCode, EventHandler> event_handlers_;
+  std::map<SubeventCode, SubeventHandler> subevent_handlers_;
+  OpCode waiting_command_{OpCode::NONE};
+  uint8_t command_credits_{1};  // Send reset first
+  Alarm* hci_timeout_alarm_{nullptr};
+
+  // Acl packets
+  BidiQueue<AclPacketView, AclPacketBuilder> acl_queue_{3 /* TODO: Set queue depth */};
+  os::EnqueueBuffer<AclPacketView> incoming_acl_packet_buffer_{acl_queue_.GetDownEnd()};
+};
+
+HciLayer::HciLayer() : impl_(std::make_unique<impl>(*this)) {}
+
+HciLayer::~HciLayer() {
+  impl_.reset();
+}
+
+void HciLayer::EnqueueCommand(std::unique_ptr<CommandPacketBuilder> command,
+                              common::OnceCallback<void(CommandCompleteView)> on_complete, os::Handler* handler) {
+  impl_->EnqueueCommand(std::move(command), std::move(on_complete), handler);
+}
+
+void HciLayer::EnqueueCommand(std::unique_ptr<CommandPacketBuilder> command,
+                              common::OnceCallback<void(CommandStatusView)> on_status, os::Handler* handler) {
+  impl_->EnqueueCommand(std::move(command), std::move(on_status), handler);
+}
+
+common::BidiQueueEnd<AclPacketBuilder, AclPacketView>* HciLayer::GetAclQueueEnd() {
+  return impl_->GetAclQueueEnd();
+}
+
+void HciLayer::RegisterEventHandler(EventCode event_code, common::Callback<void(EventPacketView)> event_handler,
+                                    os::Handler* handler) {
+  impl_->RegisterEventHandler(event_code, std::move(event_handler), handler);
+}
+
+void HciLayer::UnregisterEventHandler(EventCode event_code) {
+  impl_->UnregisterEventHandler(event_code);
+}
+
+void HciLayer::RegisterLeEventHandler(SubeventCode subevent_code, common::Callback<void(LeMetaEventView)> event_handler,
+                                      os::Handler* handler) {
+  impl_->RegisterLeEventHandler(subevent_code, std::move(event_handler), handler);
+}
+
+void HciLayer::UnregisterLeEventHandler(SubeventCode subevent_code) {
+  impl_->UnregisterLeEventHandler(subevent_code);
+}
+
+SecurityInterface* HciLayer::GetSecurityInterface(common::Callback<void(EventPacketView)> event_handler,
+                                                  os::Handler* handler) {
+  for (const auto event : SecurityInterface::SecurityEvents) {
+    RegisterEventHandler(event, event_handler, handler);
+  }
+  return &impl_->security_interface;
+}
+
+LeSecurityInterface* HciLayer::GetLeSecurityInterface(common::Callback<void(LeMetaEventView)> event_handler,
+                                                      os::Handler* handler) {
+  for (const auto subevent : LeSecurityInterface::LeSecurityEvents) {
+    RegisterLeEventHandler(subevent, event_handler, handler);
+  }
+  return &impl_->le_security_interface;
+}
+
+LeAdvertisingInterface* HciLayer::GetLeAdvertisingInterface(common::Callback<void(LeMetaEventView)> event_handler,
+                                                            os::Handler* handler) {
+  for (const auto subevent : LeAdvertisingInterface::LeAdvertisingEvents) {
+    RegisterLeEventHandler(subevent, event_handler, handler);
+  }
+  return &impl_->le_advertising_interface;
+}
+
+LeScanningInterface* HciLayer::GetLeScanningInterface(common::Callback<void(LeMetaEventView)> event_handler,
+                                                      os::Handler* handler) {
+  for (const auto subevent : LeScanningInterface::LeScanningEvents) {
+    RegisterLeEventHandler(subevent, event_handler, handler);
+  }
+  return &impl_->le_scanning_interface;
+}
+
+const ModuleFactory HciLayer::Factory = ModuleFactory([]() { return new HciLayer(); });
+
+void HciLayer::ListDependencies(ModuleList* list) {
+  list->add<hal::HciHal>();
+}
+
+void HciLayer::Start() {
+  impl_->Start(GetDependency<hal::HciHal>());
+}
+
+void HciLayer::Stop() {
+  impl_->Stop();
+}
+
+std::string HciLayer::ToString() const {
+  return "Hci Layer";
+}
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/hci_layer.h b/gd/hci/hci_layer.h
new file mode 100644
index 0000000..3e299d4
--- /dev/null
+++ b/gd/hci/hci_layer.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <chrono>
+#include <map>
+
+#include "address.h"
+#include "class_of_device.h"
+#include "common/bidi_queue.h"
+#include "common/callback.h"
+#include "hal/hci_hal.h"
+#include "hci/hci_packets.h"
+#include "hci/le_advertising_interface.h"
+#include "hci/le_scanning_interface.h"
+#include "hci/le_security_interface.h"
+#include "hci/security_interface.h"
+#include "module.h"
+#include "os/utils.h"
+
+namespace bluetooth {
+namespace hci {
+
+class HciLayer : public Module {
+ public:
+  HciLayer();
+  virtual ~HciLayer();
+  DISALLOW_COPY_AND_ASSIGN(HciLayer);
+
+  virtual void EnqueueCommand(std::unique_ptr<CommandPacketBuilder> command,
+                              common::OnceCallback<void(CommandCompleteView)> on_complete, os::Handler* handler);
+
+  virtual void EnqueueCommand(std::unique_ptr<CommandPacketBuilder> command,
+                              common::OnceCallback<void(CommandStatusView)> on_status, os::Handler* handler);
+
+  virtual common::BidiQueueEnd<AclPacketBuilder, AclPacketView>* GetAclQueueEnd();
+
+  virtual void RegisterEventHandler(EventCode event_code, common::Callback<void(EventPacketView)> event_handler,
+                                    os::Handler* handler);
+
+  virtual void UnregisterEventHandler(EventCode event_code);
+
+  virtual void RegisterLeEventHandler(SubeventCode subevent_code, common::Callback<void(LeMetaEventView)> event_handler,
+                                      os::Handler* handler);
+
+  virtual void UnregisterLeEventHandler(SubeventCode subevent_code);
+
+  SecurityInterface* GetSecurityInterface(common::Callback<void(EventPacketView)> event_handler, os::Handler* handler);
+
+  LeSecurityInterface* GetLeSecurityInterface(common::Callback<void(LeMetaEventView)> event_handler,
+                                              os::Handler* handler);
+
+  LeAdvertisingInterface* GetLeAdvertisingInterface(common::Callback<void(LeMetaEventView)> event_handler,
+                                                    os::Handler* handler);
+
+  LeScanningInterface* GetLeScanningInterface(common::Callback<void(LeMetaEventView)> event_handler,
+                                              os::Handler* handler);
+
+  static const ModuleFactory Factory;
+
+  void ListDependencies(ModuleList* list) override;
+
+  void Start() override;
+
+  void Stop() override;
+
+  std::string ToString() const override;
+  static constexpr std::chrono::milliseconds kHciTimeoutMs = std::chrono::milliseconds(2000);
+
+ private:
+  struct impl;
+  std::unique_ptr<impl> impl_;
+};
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/hci_layer_test.cc b/gd/hci/hci_layer_test.cc
new file mode 100644
index 0000000..d796488
--- /dev/null
+++ b/gd/hci/hci_layer_test.cc
@@ -0,0 +1,729 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "hci/hci_layer.h"
+
+#include <gtest/gtest.h>
+#include <list>
+#include <memory>
+
+#include "hal/hci_hal.h"
+#include "hci/hci_packets.h"
+#include "module.h"
+#include "os/log.h"
+#include "os/thread.h"
+#include "packet/bit_inserter.h"
+#include "packet/raw_builder.h"
+
+using bluetooth::os::Thread;
+using bluetooth::packet::BitInserter;
+using bluetooth::packet::RawBuilder;
+using std::vector;
+
+namespace {
+vector<uint8_t> information_request = {
+    0xfe, 0x2e, 0x0a, 0x00, 0x06, 0x00, 0x01, 0x00, 0x0a, 0x02, 0x02, 0x00, 0x02, 0x00,
+};
+// 0x00, 0x01, 0x02, 0x03, ...
+vector<uint8_t> counting_bytes;
+// 0xFF, 0xFE, 0xFD, 0xFC, ...
+vector<uint8_t> counting_down_bytes;
+const size_t count_size = 0x8;
+
+}  // namespace
+
+namespace bluetooth {
+namespace hci {
+
+constexpr std::chrono::milliseconds kTimeout = HciLayer::kHciTimeoutMs / 2;
+constexpr std::chrono::milliseconds kAclTimeout = std::chrono::milliseconds(1000);
+
+class TestHciHal : public hal::HciHal {
+ public:
+  TestHciHal() : hal::HciHal() {}
+
+  ~TestHciHal() {
+    ASSERT_LOG(callbacks == nullptr, "unregisterIncomingPacketCallback() must be called");
+  }
+
+  void registerIncomingPacketCallback(hal::HciHalCallbacks* callback) override {
+    callbacks = callback;
+  }
+
+  void unregisterIncomingPacketCallback() override {
+    callbacks = nullptr;
+  }
+
+  void sendHciCommand(hal::HciPacket command) override {
+    outgoing_commands_.push_back(std::move(command));
+    if (sent_command_promise_ != nullptr) {
+      auto promise = std::move(sent_command_promise_);
+      sent_command_promise_.reset();
+      promise->set_value();
+    }
+  }
+
+  void sendAclData(hal::HciPacket data) override {
+    outgoing_acl_.push_front(std::move(data));
+    if (sent_acl_promise_ != nullptr) {
+      auto promise = std::move(sent_acl_promise_);
+      sent_acl_promise_.reset();
+      promise->set_value();
+    }
+  }
+
+  void sendScoData(hal::HciPacket data) override {
+    outgoing_sco_.push_front(std::move(data));
+  }
+
+  hal::HciHalCallbacks* callbacks = nullptr;
+
+  PacketView<kLittleEndian> GetPacketView(hal::HciPacket data) {
+    auto shared = std::make_shared<std::vector<uint8_t>>(data);
+    return PacketView<kLittleEndian>(shared);
+  }
+
+  size_t GetNumSentCommands() {
+    return outgoing_commands_.size();
+  }
+
+  std::future<void> GetSentCommandFuture() {
+    ASSERT_LOG(sent_command_promise_ == nullptr, "Promises promises ... Only one at a time");
+    sent_command_promise_ = std::make_unique<std::promise<void>>();
+    return sent_command_promise_->get_future();
+  }
+
+  CommandPacketView GetSentCommand() {
+    auto packetview = GetPacketView(std::move(outgoing_commands_.front()));
+    outgoing_commands_.pop_front();
+    return CommandPacketView::Create(packetview);
+  }
+
+  std::future<void> GetSentAclFuture() {
+    ASSERT_LOG(sent_acl_promise_ == nullptr, "Promises promises ... Only one at a time");
+    sent_acl_promise_ = std::make_unique<std::promise<void>>();
+    return sent_acl_promise_->get_future();
+  }
+
+  PacketView<kLittleEndian> GetSentAcl() {
+    auto packetview = GetPacketView(std::move(outgoing_acl_.front()));
+    outgoing_acl_.pop_front();
+    return packetview;
+  }
+
+  void Start() {}
+
+  void Stop() {}
+
+  void ListDependencies(ModuleList*) {}
+
+  static const ModuleFactory Factory;
+
+ private:
+  std::list<hal::HciPacket> outgoing_commands_;
+  std::list<hal::HciPacket> outgoing_acl_;
+  std::list<hal::HciPacket> outgoing_sco_;
+  std::unique_ptr<std::promise<void>> sent_command_promise_;
+  std::unique_ptr<std::promise<void>> sent_acl_promise_;
+};
+
+const ModuleFactory TestHciHal::Factory = ModuleFactory([]() { return new TestHciHal(); });
+
+class DependsOnHci : public Module {
+ public:
+  DependsOnHci() : Module() {}
+
+  void SendHciCommandExpectingStatus(std::unique_ptr<CommandPacketBuilder> command) {
+    hci_->EnqueueCommand(std::move(command),
+                         common::Bind(&DependsOnHci::handle_event<CommandStatusView>, common::Unretained(this)),
+                         GetHandler());
+  }
+
+  void SendHciCommandExpectingComplete(std::unique_ptr<CommandPacketBuilder> command) {
+    hci_->EnqueueCommand(std::move(command),
+                         common::Bind(&DependsOnHci::handle_event<CommandCompleteView>, common::Unretained(this)),
+                         GetHandler());
+  }
+
+  void SendSecurityCommandExpectingComplete(std::unique_ptr<SecurityCommandBuilder> command) {
+    if (security_interface_ == nullptr) {
+      security_interface_ = hci_->GetSecurityInterface(
+          common::Bind(&DependsOnHci::handle_event<EventPacketView>, common::Unretained(this)), GetHandler());
+    }
+    hci_->EnqueueCommand(std::move(command),
+                         common::Bind(&DependsOnHci::handle_event<CommandCompleteView>, common::Unretained(this)),
+                         GetHandler());
+  }
+
+  void SendLeSecurityCommandExpectingComplete(std::unique_ptr<LeSecurityCommandBuilder> command) {
+    if (le_security_interface_ == nullptr) {
+      le_security_interface_ = hci_->GetLeSecurityInterface(
+          common::Bind(&DependsOnHci::handle_event<LeMetaEventView>, common::Unretained(this)), GetHandler());
+    }
+    hci_->EnqueueCommand(std::move(command),
+                         common::Bind(&DependsOnHci::handle_event<CommandCompleteView>, common::Unretained(this)),
+                         GetHandler());
+  }
+
+  void SendAclData(std::unique_ptr<AclPacketBuilder> acl) {
+    outgoing_acl_.push(std::move(acl));
+    auto queue_end = hci_->GetAclQueueEnd();
+    queue_end->RegisterEnqueue(GetHandler(), common::Bind(&DependsOnHci::handle_enqueue, common::Unretained(this)));
+  }
+
+  std::future<void> GetReceivedEventFuture() {
+    ASSERT_LOG(event_promise_ == nullptr, "Promises promises ... Only one at a time");
+    event_promise_ = std::make_unique<std::promise<void>>();
+    return event_promise_->get_future();
+  }
+
+  EventPacketView GetReceivedEvent() {
+    EventPacketView packetview = incoming_events_.front();
+    incoming_events_.pop_front();
+    return packetview;
+  }
+
+  std::future<void> GetReceivedAclFuture() {
+    ASSERT_LOG(acl_promise_ == nullptr, "Promises promises ... Only one at a time");
+    acl_promise_ = std::make_unique<std::promise<void>>();
+    return acl_promise_->get_future();
+  }
+
+  size_t GetNumReceivedAclPackets() {
+    return incoming_acl_packets_.size();
+  }
+
+  AclPacketView GetReceivedAcl() {
+    AclPacketView packetview = incoming_acl_packets_.front();
+    incoming_acl_packets_.pop_front();
+    return packetview;
+  }
+
+  void Start() {
+    hci_ = GetDependency<HciLayer>();
+    hci_->RegisterEventHandler(EventCode::CONNECTION_COMPLETE,
+                               common::Bind(&DependsOnHci::handle_event<EventPacketView>, common::Unretained(this)),
+                               GetHandler());
+    hci_->RegisterLeEventHandler(SubeventCode::CONNECTION_COMPLETE,
+                                 common::Bind(&DependsOnHci::handle_event<LeMetaEventView>, common::Unretained(this)),
+                                 GetHandler());
+    hci_->GetAclQueueEnd()->RegisterDequeue(GetHandler(),
+                                            common::Bind(&DependsOnHci::handle_acl, common::Unretained(this)));
+  }
+
+  void Stop() {
+    hci_->GetAclQueueEnd()->UnregisterDequeue();
+  }
+
+  void ListDependencies(ModuleList* list) {
+    list->add<HciLayer>();
+  }
+
+  static const ModuleFactory Factory;
+
+ private:
+  HciLayer* hci_ = nullptr;
+  const SecurityInterface* security_interface_;
+  const LeSecurityInterface* le_security_interface_;
+  std::list<EventPacketView> incoming_events_;
+  std::list<AclPacketView> incoming_acl_packets_;
+  std::unique_ptr<std::promise<void>> event_promise_;
+  std::unique_ptr<std::promise<void>> acl_promise_;
+
+  void handle_acl() {
+    auto acl_ptr = hci_->GetAclQueueEnd()->TryDequeue();
+    incoming_acl_packets_.push_back(*acl_ptr);
+    if (acl_promise_ != nullptr) {
+      auto promise = std::move(acl_promise_);
+      acl_promise_.reset();
+      promise->set_value();
+    }
+  }
+
+  template <typename T>
+  void handle_event(T event) {
+    incoming_events_.push_back(event);
+    if (event_promise_ != nullptr) {
+      auto promise = std::move(event_promise_);
+      event_promise_.reset();
+      promise->set_value();
+    }
+  }
+
+  std::queue<std::unique_ptr<AclPacketBuilder>> outgoing_acl_;
+
+  std::unique_ptr<AclPacketBuilder> handle_enqueue() {
+    hci_->GetAclQueueEnd()->UnregisterEnqueue();
+    auto acl = std::move(outgoing_acl_.front());
+    outgoing_acl_.pop();
+    return acl;
+  }
+};
+
+const ModuleFactory DependsOnHci::Factory = ModuleFactory([]() { return new DependsOnHci(); });
+
+class HciTest : public ::testing::Test {
+ public:
+  void SetUp() override {
+    counting_bytes.reserve(count_size);
+    counting_down_bytes.reserve(count_size);
+    for (size_t i = 0; i < count_size; i++) {
+      counting_bytes.push_back(i);
+      counting_down_bytes.push_back(~i);
+    }
+    hal = new TestHciHal();
+
+    auto command_future = hal->GetSentCommandFuture();
+
+    fake_registry_.InjectTestModule(&hal::HciHal::Factory, hal);
+    fake_registry_.Start<DependsOnHci>(&fake_registry_.GetTestThread());
+    hci = static_cast<HciLayer*>(fake_registry_.GetModuleUnderTest(&HciLayer::Factory));
+    upper = static_cast<DependsOnHci*>(fake_registry_.GetModuleUnderTest(&DependsOnHci::Factory));
+    ASSERT(fake_registry_.IsStarted<HciLayer>());
+
+    auto reset_sent_status = command_future.wait_for(kTimeout);
+    ASSERT_EQ(reset_sent_status, std::future_status::ready);
+
+    // Verify that reset was received
+    ASSERT_EQ(1, hal->GetNumSentCommands());
+
+    auto sent_command = hal->GetSentCommand();
+    auto reset_view = ResetView::Create(CommandPacketView::Create(sent_command));
+    ASSERT_TRUE(reset_view.IsValid());
+
+    // Verify that only one was sent
+    ASSERT_EQ(0, hal->GetNumSentCommands());
+
+    // Send the response event
+    uint8_t num_packets = 1;
+    ErrorCode error_code = ErrorCode::SUCCESS;
+    hal->callbacks->hciEventReceived(GetPacketBytes(ResetCompleteBuilder::Create(num_packets, error_code)));
+  }
+
+  void TearDown() override {
+    fake_registry_.StopAll();
+  }
+
+  std::vector<uint8_t> GetPacketBytes(std::unique_ptr<packet::BasePacketBuilder> packet) {
+    std::vector<uint8_t> bytes;
+    BitInserter i(bytes);
+    bytes.reserve(packet->size());
+    packet->Serialize(i);
+    return bytes;
+  }
+
+  DependsOnHci* upper = nullptr;
+  TestHciHal* hal = nullptr;
+  HciLayer* hci = nullptr;
+  TestModuleRegistry fake_registry_;
+};
+
+TEST_F(HciTest, initAndClose) {}
+
+TEST_F(HciTest, leMetaEvent) {
+  auto event_future = upper->GetReceivedEventFuture();
+
+  // Send an LE event
+  ErrorCode status = ErrorCode::SUCCESS;
+  uint16_t handle = 0x123;
+  Role role = Role::MASTER;
+  AddressType peer_address_type = AddressType::PUBLIC_DEVICE_ADDRESS;
+  Address peer_address = Address::kAny;
+  uint16_t conn_interval = 0x0ABC;
+  uint16_t conn_latency = 0x0123;
+  uint16_t supervision_timeout = 0x0B05;
+  MasterClockAccuracy master_clock_accuracy = MasterClockAccuracy::PPM_50;
+  hal->callbacks->hciEventReceived(GetPacketBytes(
+      LeConnectionCompleteBuilder::Create(status, handle, role, peer_address_type, peer_address, conn_interval,
+                                          conn_latency, supervision_timeout, master_clock_accuracy)));
+
+  // Wait for the event
+  auto event_status = event_future.wait_for(kTimeout);
+  ASSERT_EQ(event_status, std::future_status::ready);
+
+  auto event = upper->GetReceivedEvent();
+  ASSERT(LeConnectionCompleteView::Create(LeMetaEventView::Create(EventPacketView::Create(event))).IsValid());
+}
+
+TEST_F(HciTest, noOpCredits) {
+  ASSERT_EQ(0, hal->GetNumSentCommands());
+
+  // Send 0 credits
+  uint8_t num_packets = 0;
+  hal->callbacks->hciEventReceived(GetPacketBytes(NoCommandCompleteBuilder::Create(num_packets)));
+
+  auto command_future = hal->GetSentCommandFuture();
+  upper->SendHciCommandExpectingComplete(ReadLocalVersionInformationBuilder::Create());
+
+  // Verify that nothing was sent
+  ASSERT_EQ(0, hal->GetNumSentCommands());
+
+  num_packets = 1;
+  hal->callbacks->hciEventReceived(GetPacketBytes(NoCommandCompleteBuilder::Create(num_packets)));
+
+  auto command_sent_status = command_future.wait_for(kTimeout);
+  ASSERT_EQ(command_sent_status, std::future_status::ready);
+
+  // Verify that one was sent
+  ASSERT_EQ(1, hal->GetNumSentCommands());
+
+  auto event_future = upper->GetReceivedEventFuture();
+
+  // Send the response event
+  ErrorCode error_code = ErrorCode::SUCCESS;
+  LocalVersionInformation local_version_information;
+  local_version_information.hci_version_ = HciVersion::V_5_0;
+  local_version_information.hci_revision_ = 0x1234;
+  local_version_information.lmp_version_ = LmpVersion::V_4_2;
+  local_version_information.manufacturer_name_ = 0xBAD;
+  local_version_information.lmp_subversion_ = 0x5678;
+  hal->callbacks->hciEventReceived(GetPacketBytes(
+      ReadLocalVersionInformationCompleteBuilder::Create(num_packets, error_code, local_version_information)));
+
+  // Wait for the event
+  auto event_status = event_future.wait_for(kTimeout);
+  ASSERT_EQ(event_status, std::future_status::ready);
+
+  auto event = upper->GetReceivedEvent();
+  ASSERT(ReadLocalVersionInformationCompleteView::Create(CommandCompleteView::Create(EventPacketView::Create(event)))
+             .IsValid());
+}
+
+TEST_F(HciTest, creditsTest) {
+  ASSERT_EQ(0, hal->GetNumSentCommands());
+
+  auto command_future = hal->GetSentCommandFuture();
+
+  // Send all three commands
+  upper->SendHciCommandExpectingComplete(ReadLocalVersionInformationBuilder::Create());
+  upper->SendHciCommandExpectingComplete(ReadLocalSupportedCommandsBuilder::Create());
+  upper->SendHciCommandExpectingComplete(ReadLocalSupportedFeaturesBuilder::Create());
+
+  auto command_sent_status = command_future.wait_for(kTimeout);
+  ASSERT_EQ(command_sent_status, std::future_status::ready);
+
+  // Verify that the first one is sent
+  ASSERT_EQ(1, hal->GetNumSentCommands());
+
+  auto sent_command = hal->GetSentCommand();
+  auto version_view = ReadLocalVersionInformationView::Create(CommandPacketView::Create(sent_command));
+  ASSERT_TRUE(version_view.IsValid());
+
+  // Verify that only one was sent
+  ASSERT_EQ(0, hal->GetNumSentCommands());
+
+  // Get a new future
+  auto event_future = upper->GetReceivedEventFuture();
+
+  // Send the response event
+  uint8_t num_packets = 1;
+  ErrorCode error_code = ErrorCode::SUCCESS;
+  LocalVersionInformation local_version_information;
+  local_version_information.hci_version_ = HciVersion::V_5_0;
+  local_version_information.hci_revision_ = 0x1234;
+  local_version_information.lmp_version_ = LmpVersion::V_4_2;
+  local_version_information.manufacturer_name_ = 0xBAD;
+  local_version_information.lmp_subversion_ = 0x5678;
+  hal->callbacks->hciEventReceived(GetPacketBytes(
+      ReadLocalVersionInformationCompleteBuilder::Create(num_packets, error_code, local_version_information)));
+
+  // Wait for the event
+  auto event_status = event_future.wait_for(kTimeout);
+  ASSERT_EQ(event_status, std::future_status::ready);
+
+  auto event = upper->GetReceivedEvent();
+  ASSERT(ReadLocalVersionInformationCompleteView::Create(CommandCompleteView::Create(EventPacketView::Create(event)))
+             .IsValid());
+
+  // Verify that the second one is sent
+  command_sent_status = command_future.wait_for(kTimeout);
+  ASSERT_EQ(command_sent_status, std::future_status::ready);
+  ASSERT_EQ(1, hal->GetNumSentCommands());
+
+  sent_command = hal->GetSentCommand();
+  auto supported_commands_view = ReadLocalSupportedCommandsView::Create(CommandPacketView::Create(sent_command));
+  ASSERT_TRUE(supported_commands_view.IsValid());
+
+  // Verify that only one was sent
+  ASSERT_EQ(0, hal->GetNumSentCommands());
+  event_future = upper->GetReceivedEventFuture();
+  command_future = hal->GetSentCommandFuture();
+
+  // Send the response event
+  std::array<uint8_t, 64> supported_commands;
+  for (uint8_t i = 0; i < 64; i++) {
+    supported_commands[i] = i;
+  }
+  hal->callbacks->hciEventReceived(
+      GetPacketBytes(ReadLocalSupportedCommandsCompleteBuilder::Create(num_packets, error_code, supported_commands)));
+  // Wait for the event
+  event_status = event_future.wait_for(kTimeout);
+  ASSERT_EQ(event_status, std::future_status::ready);
+
+  event = upper->GetReceivedEvent();
+  ASSERT(ReadLocalSupportedCommandsCompleteView::Create(CommandCompleteView::Create(EventPacketView::Create(event)))
+             .IsValid());
+  // Verify that the third one is sent
+  command_sent_status = command_future.wait_for(kTimeout);
+  ASSERT_EQ(command_sent_status, std::future_status::ready);
+  ASSERT_EQ(1, hal->GetNumSentCommands());
+
+  sent_command = hal->GetSentCommand();
+  auto supported_features_view = ReadLocalSupportedFeaturesView::Create(CommandPacketView::Create(sent_command));
+  ASSERT_TRUE(supported_features_view.IsValid());
+
+  // Verify that only one was sent
+  ASSERT_EQ(0, hal->GetNumSentCommands());
+  event_future = upper->GetReceivedEventFuture();
+
+  // Send the response event
+  uint64_t lmp_features = 0x012345678abcdef;
+  hal->callbacks->hciEventReceived(
+      GetPacketBytes(ReadLocalSupportedFeaturesCompleteBuilder::Create(num_packets, error_code, lmp_features)));
+
+  // Wait for the event
+  event_status = event_future.wait_for(kTimeout);
+  ASSERT_EQ(event_status, std::future_status::ready);
+  event = upper->GetReceivedEvent();
+  ASSERT(ReadLocalSupportedFeaturesCompleteView::Create(CommandCompleteView::Create(EventPacketView::Create(event)))
+             .IsValid());
+}
+
+TEST_F(HciTest, leSecurityInterfaceTest) {
+  // Send LeRand to the controller
+  auto command_future = hal->GetSentCommandFuture();
+  upper->SendLeSecurityCommandExpectingComplete(LeRandBuilder::Create());
+
+  auto command_sent_status = command_future.wait_for(kTimeout);
+  ASSERT_EQ(command_sent_status, std::future_status::ready);
+
+  // Check the command
+  auto sent_command = hal->GetSentCommand();
+  ASSERT_LT(0, sent_command.size());
+  LeRandView view = LeRandView::Create(LeSecurityCommandView::Create(CommandPacketView::Create(sent_command)));
+  ASSERT_TRUE(view.IsValid());
+
+  // Send a Command Complete to the host
+  auto event_future = upper->GetReceivedEventFuture();
+  uint8_t num_packets = 1;
+  ErrorCode status = ErrorCode::SUCCESS;
+  uint64_t rand = 0x0123456789abcdef;
+  hal->callbacks->hciEventReceived(GetPacketBytes(LeRandCompleteBuilder::Create(num_packets, status, rand)));
+
+  // Verify the event
+  auto event_status = event_future.wait_for(kTimeout);
+  ASSERT_EQ(event_status, std::future_status::ready);
+  auto event = upper->GetReceivedEvent();
+  ASSERT_TRUE(event.IsValid());
+  ASSERT_EQ(EventCode::COMMAND_COMPLETE, event.GetEventCode());
+  ASSERT_TRUE(LeRandCompleteView::Create(CommandCompleteView::Create(event)).IsValid());
+}
+
+TEST_F(HciTest, securityInterfacesTest) {
+  // Send WriteSimplePairingMode to the controller
+  auto command_future = hal->GetSentCommandFuture();
+  Enable enable = Enable::ENABLED;
+  upper->SendSecurityCommandExpectingComplete(WriteSimplePairingModeBuilder::Create(enable));
+
+  auto command_sent_status = command_future.wait_for(kTimeout);
+  ASSERT_EQ(command_sent_status, std::future_status::ready);
+
+  // Check the command
+  auto sent_command = hal->GetSentCommand();
+  ASSERT_LT(0, sent_command.size());
+  auto view = WriteSimplePairingModeView::Create(SecurityCommandView::Create(CommandPacketView::Create(sent_command)));
+  ASSERT_TRUE(view.IsValid());
+
+  // Send a Command Complete to the host
+  auto event_future = upper->GetReceivedEventFuture();
+  uint8_t num_packets = 1;
+  ErrorCode status = ErrorCode::SUCCESS;
+  hal->callbacks->hciEventReceived(GetPacketBytes(WriteSimplePairingModeCompleteBuilder::Create(num_packets, status)));
+
+  // Verify the event
+  auto event_status = event_future.wait_for(kTimeout);
+  ASSERT_EQ(event_status, std::future_status::ready);
+  auto event = upper->GetReceivedEvent();
+  ASSERT_TRUE(event.IsValid());
+  ASSERT_EQ(EventCode::COMMAND_COMPLETE, event.GetEventCode());
+  ASSERT_TRUE(WriteSimplePairingModeCompleteView::Create(CommandCompleteView::Create(event)).IsValid());
+}
+
+TEST_F(HciTest, createConnectionTest) {
+  // Send CreateConnection to the controller
+  auto command_future = hal->GetSentCommandFuture();
+  Address bd_addr;
+  ASSERT_TRUE(Address::FromString("A1:A2:A3:A4:A5:A6", bd_addr));
+  uint16_t packet_type = 0x1234;
+  PageScanRepetitionMode page_scan_repetition_mode = PageScanRepetitionMode::R0;
+  uint16_t clock_offset = 0x3456;
+  ClockOffsetValid clock_offset_valid = ClockOffsetValid::VALID;
+  CreateConnectionRoleSwitch allow_role_switch = CreateConnectionRoleSwitch::ALLOW_ROLE_SWITCH;
+  upper->SendHciCommandExpectingStatus(CreateConnectionBuilder::Create(
+      bd_addr, packet_type, page_scan_repetition_mode, clock_offset, clock_offset_valid, allow_role_switch));
+
+  auto command_sent_status = command_future.wait_for(kTimeout);
+  ASSERT_EQ(command_sent_status, std::future_status::ready);
+
+  // Check the command
+  auto sent_command = hal->GetSentCommand();
+  ASSERT_LT(0, sent_command.size());
+  CreateConnectionView view =
+      CreateConnectionView::Create(ConnectionManagementCommandView::Create(CommandPacketView::Create(sent_command)));
+  ASSERT_TRUE(view.IsValid());
+  ASSERT_EQ(bd_addr, view.GetBdAddr());
+  ASSERT_EQ(packet_type, view.GetPacketType());
+  ASSERT_EQ(page_scan_repetition_mode, view.GetPageScanRepetitionMode());
+  ASSERT_EQ(clock_offset, view.GetClockOffset());
+  ASSERT_EQ(clock_offset_valid, view.GetClockOffsetValid());
+  ASSERT_EQ(allow_role_switch, view.GetAllowRoleSwitch());
+
+  // Send a Command Status to the host
+  auto event_future = upper->GetReceivedEventFuture();
+  ErrorCode status = ErrorCode::SUCCESS;
+  uint16_t handle = 0x123;
+  LinkType link_type = LinkType::ACL;
+  Enable encryption_enabled = Enable::DISABLED;
+  hal->callbacks->hciEventReceived(GetPacketBytes(CreateConnectionStatusBuilder::Create(ErrorCode::SUCCESS, 1)));
+
+  // Verify the event
+  auto event_status = event_future.wait_for(kTimeout);
+  ASSERT_EQ(event_status, std::future_status::ready);
+  auto event = upper->GetReceivedEvent();
+  ASSERT_TRUE(event.IsValid());
+  ASSERT_EQ(EventCode::COMMAND_STATUS, event.GetEventCode());
+
+  // Send a ConnectionComplete to the host
+  event_future = upper->GetReceivedEventFuture();
+  hal->callbacks->hciEventReceived(
+      GetPacketBytes(ConnectionCompleteBuilder::Create(status, handle, bd_addr, link_type, encryption_enabled)));
+
+  // Verify the event
+  event_status = event_future.wait_for(kTimeout);
+  ASSERT_EQ(event_status, std::future_status::ready);
+  event = upper->GetReceivedEvent();
+  ASSERT_TRUE(event.IsValid());
+  ASSERT_EQ(EventCode::CONNECTION_COMPLETE, event.GetEventCode());
+  ConnectionCompleteView connection_complete_view = ConnectionCompleteView::Create(event);
+  ASSERT_TRUE(connection_complete_view.IsValid());
+  ASSERT_EQ(status, connection_complete_view.GetStatus());
+  ASSERT_EQ(handle, connection_complete_view.GetConnectionHandle());
+  ASSERT_EQ(link_type, connection_complete_view.GetLinkType());
+  ASSERT_EQ(encryption_enabled, connection_complete_view.GetEncryptionEnabled());
+
+  // Send an ACL packet from the remote
+  PacketBoundaryFlag packet_boundary_flag = PacketBoundaryFlag::FIRST_AUTOMATICALLY_FLUSHABLE;
+  BroadcastFlag broadcast_flag = BroadcastFlag::POINT_TO_POINT;
+  auto acl_payload = std::make_unique<RawBuilder>();
+  acl_payload->AddAddress(bd_addr);
+  acl_payload->AddOctets2(handle);
+  auto incoming_acl_future = upper->GetReceivedAclFuture();
+  hal->callbacks->aclDataReceived(
+      GetPacketBytes(AclPacketBuilder::Create(handle, packet_boundary_flag, broadcast_flag, std::move(acl_payload))));
+
+  // Verify the ACL packet
+  auto incoming_acl_status = incoming_acl_future.wait_for(kAclTimeout);
+  ASSERT_EQ(incoming_acl_status, std::future_status::ready);
+  auto acl_view = upper->GetReceivedAcl();
+  ASSERT_TRUE(acl_view.IsValid());
+  ASSERT_EQ(sizeof(bd_addr) + sizeof(handle), acl_view.GetPayload().size());
+  auto itr = acl_view.GetPayload().begin();
+  ASSERT_EQ(bd_addr, itr.extract<Address>());
+  ASSERT_EQ(handle, itr.extract<uint16_t>());
+
+  // Send an ACL packet from DependsOnHci
+  PacketBoundaryFlag packet_boundary_flag2 = PacketBoundaryFlag::FIRST_AUTOMATICALLY_FLUSHABLE;
+  BroadcastFlag broadcast_flag2 = BroadcastFlag::POINT_TO_POINT;
+  auto acl_payload2 = std::make_unique<RawBuilder>();
+  acl_payload2->AddOctets2(handle);
+  acl_payload2->AddAddress(bd_addr);
+  auto sent_acl_future = hal->GetSentAclFuture();
+  upper->SendAclData(AclPacketBuilder::Create(handle, packet_boundary_flag2, broadcast_flag2, std::move(acl_payload2)));
+
+  // Verify the ACL packet
+  auto sent_acl_status = sent_acl_future.wait_for(kAclTimeout);
+  ASSERT_EQ(sent_acl_status, std::future_status::ready);
+  auto sent_acl = hal->GetSentAcl();
+  ASSERT_LT(0, sent_acl.size());
+  AclPacketView sent_acl_view = AclPacketView::Create(sent_acl);
+  ASSERT_TRUE(sent_acl_view.IsValid());
+  ASSERT_EQ(sizeof(bd_addr) + sizeof(handle), sent_acl_view.GetPayload().size());
+  auto sent_itr = sent_acl_view.GetPayload().begin();
+  ASSERT_EQ(handle, sent_itr.extract<uint16_t>());
+  ASSERT_EQ(bd_addr, sent_itr.extract<Address>());
+}
+
+TEST_F(HciTest, receiveMultipleAclPackets) {
+  Address bd_addr;
+  ASSERT_TRUE(Address::FromString("A1:A2:A3:A4:A5:A6", bd_addr));
+  uint16_t handle = 0x0001;
+  uint16_t num_packets = 100;
+  PacketBoundaryFlag packet_boundary_flag = PacketBoundaryFlag::FIRST_AUTOMATICALLY_FLUSHABLE;
+  BroadcastFlag broadcast_flag = BroadcastFlag::POINT_TO_POINT;
+  for (uint16_t i = 0; i < num_packets; i++) {
+    auto acl_payload = std::make_unique<RawBuilder>();
+    acl_payload->AddAddress(bd_addr);
+    acl_payload->AddOctets2(handle);
+    acl_payload->AddOctets2(i);
+    hal->callbacks->aclDataReceived(
+        GetPacketBytes(AclPacketBuilder::Create(handle, packet_boundary_flag, broadcast_flag, std::move(acl_payload))));
+  }
+  auto incoming_acl_future = upper->GetReceivedAclFuture();
+  uint16_t received_packets = 0;
+  while (received_packets < num_packets - 1) {
+    auto incoming_acl_status = incoming_acl_future.wait_for(kAclTimeout);
+    // Get the next future.
+    incoming_acl_future = upper->GetReceivedAclFuture();
+    ASSERT_EQ(incoming_acl_status, std::future_status::ready);
+    size_t num_packets = upper->GetNumReceivedAclPackets();
+    for (size_t i = 0; i < num_packets; i++) {
+      auto acl_view = upper->GetReceivedAcl();
+      ASSERT_TRUE(acl_view.IsValid());
+      ASSERT_EQ(sizeof(bd_addr) + sizeof(handle) + sizeof(received_packets), acl_view.GetPayload().size());
+      auto itr = acl_view.GetPayload().begin();
+      ASSERT_EQ(bd_addr, itr.extract<Address>());
+      ASSERT_EQ(handle, itr.extract<uint16_t>());
+      ASSERT_EQ(received_packets, itr.extract<uint16_t>());
+      received_packets += 1;
+    }
+  }
+
+  // Check to see if this future was already fulfilled.
+  auto acl_race_status = incoming_acl_future.wait_for(std::chrono::milliseconds(1));
+  if (acl_race_status == std::future_status::ready) {
+    // Get the next future.
+    incoming_acl_future = upper->GetReceivedAclFuture();
+  }
+
+  // One last packet to make sure they were all sent.  Already got the future.
+  auto acl_payload = std::make_unique<RawBuilder>();
+  acl_payload->AddAddress(bd_addr);
+  acl_payload->AddOctets2(handle);
+  acl_payload->AddOctets2(num_packets);
+  hal->callbacks->aclDataReceived(
+      GetPacketBytes(AclPacketBuilder::Create(handle, packet_boundary_flag, broadcast_flag, std::move(acl_payload))));
+  auto incoming_acl_status = incoming_acl_future.wait_for(kAclTimeout);
+  ASSERT_EQ(incoming_acl_status, std::future_status::ready);
+  auto acl_view = upper->GetReceivedAcl();
+  ASSERT_TRUE(acl_view.IsValid());
+  ASSERT_EQ(sizeof(bd_addr) + sizeof(handle) + sizeof(received_packets), acl_view.GetPayload().size());
+  auto itr = acl_view.GetPayload().begin();
+  ASSERT_EQ(bd_addr, itr.extract<Address>());
+  ASSERT_EQ(handle, itr.extract<uint16_t>());
+  ASSERT_EQ(received_packets, itr.extract<uint16_t>());
+}
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/hci_packets.pdl b/gd/hci/hci_packets.pdl
new file mode 100644
index 0000000..1a3db54
--- /dev/null
+++ b/gd/hci/hci_packets.pdl
@@ -0,0 +1,3986 @@
+little_endian_packets
+
+custom_field Address : 48 "hci/"
+custom_field ClassOfDevice : 24 "hci/"
+
+enum Enable : 8 {
+  DISABLED = 0x00,
+  ENABLED = 0x01,
+}
+
+// https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile
+enum GapDataType : 8 {
+  INVALID = 0x00,
+  FLAGS = 0x01,
+  INCOMPLETE_LIST_16_BIT_UUIDS = 0x02,
+  COMPLETE_LIST_16_BIT_UUIDS = 0x03,
+  INCOMPLETE_LIST_32_BIT_UUIDS = 0x04,
+  COMPLETE_LIST_32_BIT_UUIDS = 0x05,
+  INCOMPLETE_LIST_128_BIT_UUIDS = 0x06,
+  COMPLETE_LIST_128_BIT_UUIDS = 0x07,
+  SHORTENED_LOCAL_NAME = 0x08,
+  COMPLETE_LOCAL_NAME = 0x09,
+  TX_POWER_LEVEL = 0x0A,
+  CLASS_OF_DEVICE = 0x0D,
+  SERVICE_DATA_16_BIT_UUIDS = 0x16,
+  APPEARANCE = 0x19,
+  SERVICE_DATA_32_BIT_UUIDS = 0x20,
+  SERVICE_DATA_128_BIT_UUIDS = 0x21,
+  MANUFACTURER_SPECIFIC_DATA = 0xFF,
+}
+
+struct GapData {
+  _size_(data) : 8, // Including one byte for data_type
+  data_type : GapDataType,
+  data : 8[+1*8],
+}
+
+// HCI ACL Packets
+
+enum PacketBoundaryFlag : 2 {
+  FIRST_NON_AUTOMATICALLY_FLUSHABLE = 0,
+  CONTINUING_FRAGMENT = 1,
+  FIRST_AUTOMATICALLY_FLUSHABLE = 2,
+}
+
+enum BroadcastFlag : 2 {
+  POINT_TO_POINT = 0,
+  ACTIVE_SLAVE_BROADCAST = 1,
+}
+
+packet AclPacket {
+  handle : 12,
+  packet_boundary_flag : PacketBoundaryFlag,
+  broadcast_flag : BroadcastFlag,
+  _size_(_payload_) : 16,
+  _payload_,
+}
+
+// HCI SCO Packets
+
+enum PacketStatusFlag : 2 {
+  CORRECTLY_RECEIVED = 0,
+  POSSIBLY_INCOMPLETE = 1,
+  NO_DATA = 2,
+  PARTIALLY_LOST = 3,
+}
+
+packet ScoPacket {
+  handle : 12,
+  packet_status_flag : PacketStatusFlag,
+  _reserved_ : 2, // BroadcastFlag
+  _size_(data) : 8,
+  data : 8[],
+}
+
+// HCI Command Packets
+
+enum OpCode : 16 {
+  NONE = 0x0000,
+
+  // LINK_CONTROL
+  INQUIRY = 0x0401,
+  INQUIRY_CANCEL = 0x0402,
+  PERIODIC_INQUIRY_MODE = 0x0403,
+  EXIT_PERIODIC_INQUIRY_MODE = 0x0404,
+  CREATE_CONNECTION = 0x0405,
+  DISCONNECT = 0x0406,
+  CREATE_CONNECTION_CANCEL = 0x0408,
+  ACCEPT_CONNECTION_REQUEST = 0x0409,
+  REJECT_CONNECTION_REQUEST = 0x040A,
+  LINK_KEY_REQUEST_REPLY = 0x040B,
+  LINK_KEY_REQUEST_NEGATIVE_REPLY = 0x040C,
+  PIN_CODE_REQUEST_REPLY = 0x040D,
+  PIN_CODE_REQUEST_NEGATIVE_REPLY = 0x040E,
+  CHANGE_CONNECTION_PACKET_TYPE = 0x040F,
+  AUTHENTICATION_REQUESTED = 0x0411,
+  SET_CONNECTION_ENCRYPTION = 0x0413,
+  CHANGE_CONNECTION_LINK_KEY = 0x0415,
+  MASTER_LINK_KEY = 0x0417,
+  REMOTE_NAME_REQUEST = 0x0419,
+  REMOTE_NAME_REQUEST_CANCEL = 0x041A,
+  READ_REMOTE_SUPPORTED_FEATURES = 0x041B,
+  READ_REMOTE_EXTENDED_FEATURES = 0x041C,
+  READ_REMOTE_VERSION_INFORMATION = 0x041D,
+  READ_CLOCK_OFFSET = 0x041F,
+  READ_LMP_HANDLE = 0x0420,
+  SETUP_SYNCHRONOUS_CONNECTION = 0x0428,
+  ACCEPT_SYNCHRONOUS_CONNECTION = 0x0429,
+  REJECT_SYNCHRONOUS_CONNECTION = 0x042A,
+  IO_CAPABILITY_REQUEST_REPLY = 0x042B,
+  USER_CONFIRMATION_REQUEST_REPLY = 0x042C,
+  USER_CONFIRMATION_REQUEST_NEGATIVE_REPLY = 0x042D,
+  USER_PASSKEY_REQUEST_REPLY = 0x042E,
+  USER_PASSKEY_REQUEST_NEGATIVE_REPLY = 0x042F,
+  REMOTE_OOB_DATA_REQUEST_REPLY = 0x0430,
+  REMOTE_OOB_DATA_REQUEST_NEGATIVE_REPLY = 0x0433,
+  IO_CAPABILITY_REQUEST_NEGATIVE_REPLY = 0x0434,
+  ENHANCED_SETUP_SYNCHRONOUS_CONNECTION = 0x043D,
+  ENHANCED_ACCEPT_SYNCHRONOUS_CONNECTION = 0x043E,
+
+  // LINK_POLICY
+  HOLD_MODE = 0x0801,
+  SNIFF_MODE = 0x0803,
+  EXIT_SNIFF_MODE = 0x0804,
+  QOS_SETUP = 0x0807,
+  ROLE_DISCOVERY = 0x0809,
+  SWITCH_ROLE = 0x080B,
+  READ_LINK_POLICY_SETTINGS = 0x080C,
+  WRITE_LINK_POLICY_SETTINGS = 0x080D,
+  READ_DEFAULT_LINK_POLICY_SETTINGS = 0x080E,
+  WRITE_DEFAULT_LINK_POLICY_SETTINGS = 0x080F,
+  FLOW_SPECIFICATION = 0x0810,
+  SNIFF_SUBRATING = 0x0811,
+
+  // CONTROLLER_AND_BASEBAND
+  SET_EVENT_MASK = 0x0C01,
+  RESET = 0x0C03,
+  SET_EVENT_FILTER = 0x0C05,
+  FLUSH = 0x0C08,
+  READ_PIN_TYPE = 0x0C09,
+  WRITE_PIN_TYPE = 0x0C0A,
+  CREATE_NEW_UNIT_KEY = 0x0C0B,
+  READ_STORED_LINK_KEY = 0x0C0D,
+  WRITE_STORED_LINK_KEY = 0x0C11,
+  DELETE_STORED_LINK_KEY = 0x0C12,
+  WRITE_LOCAL_NAME = 0x0C13,
+  READ_LOCAL_NAME = 0x0C14,
+  READ_CONNECTION_ACCEPT_TIMEOUT = 0x0C15,
+  WRITE_CONNECTION_ACCEPT_TIMEOUT = 0x0C16,
+  READ_PAGE_TIMEOUT = 0x0C17,
+  WRITE_PAGE_TIMEOUT = 0x0C18,
+  READ_SCAN_ENABLE = 0x0C19,
+  WRITE_SCAN_ENABLE = 0x0C1A,
+  READ_PAGE_SCAN_ACTIVITY = 0x0C1B,
+  WRITE_PAGE_SCAN_ACTIVITY = 0x0C1C,
+  READ_INQUIRY_SCAN_ACTIVITY = 0x0C1D,
+  WRITE_INQUIRY_SCAN_ACTIVITY = 0x0C1E,
+  READ_AUTHENTICATION_ENABLE = 0x0C1F,
+  WRITE_AUTHENTICATION_ENABLE = 0x0C20,
+  READ_CLASS_OF_DEVICE = 0x0C23,
+  WRITE_CLASS_OF_DEVICE = 0x0C24,
+  READ_VOICE_SETTING = 0x0C25,
+  WRITE_VOICE_SETTING = 0x0C26,
+  READ_AUTOMATIC_FLUSH_TIMEOUT = 0x0C27,
+  WRITE_AUTOMATIC_FLUSH_TIMEOUT = 0x0C28,
+  READ_NUM_BROADCAST_RETRANSMITS = 0x0C29,
+  WRITE_NUM_BROADCAST_RETRANSMITS = 0x0C2A,
+  READ_HOLD_MODE_ACTIVITY = 0x0C2B,
+  WRITE_HOLD_MODE_ACTIVITY = 0x0C2C,
+  READ_TRANSMIT_POWER_LEVEL = 0x0C2D,
+  READ_SYNCHRONOUS_FLOW_CONTROL_ENABLE = 0x0C2E,
+  WRITE_SYNCHRONOUS_FLOW_CONTROL_ENABLE = 0x0C2F,
+  SET_CONTROLLER_TO_HOST_FLOW_CONTROL = 0x0C31,
+  HOST_BUFFER_SIZE = 0x0C33,
+  HOST_NUM_COMPLETED_PACKETS = 0x0C35,
+  READ_LINK_SUPERVISION_TIMEOUT = 0x0C36,
+  WRITE_LINK_SUPERVISION_TIMEOUT = 0x0C37,
+  READ_NUMBER_OF_SUPPORTED_IAC = 0x0C38,
+  READ_CURRENT_IAC_LAP = 0x0C39,
+  WRITE_CURRENT_IAC_LAP = 0x0C3A,
+  SET_AFH_HOST_CHANNEL_CLASSIFICATION = 0x0C3F,
+  READ_INQUIRY_SCAN_TYPE = 0x0C42,
+  WRITE_INQUIRY_SCAN_TYPE = 0x0C43,
+  READ_INQUIRY_MODE = 0x0C44,
+  WRITE_INQUIRY_MODE = 0x0C45,
+  READ_PAGE_SCAN_TYPE = 0x0C46,
+  WRITE_PAGE_SCAN_TYPE = 0x0C47,
+  READ_AFH_CHANNEL_ASSESSMENT_MODE = 0x0C48,
+  WRITE_AFH_CHANNEL_ASSESSMENT_MODE = 0x0C49,
+  READ_EXTENDED_INQUIRY_RESPONSE = 0x0C51,
+  WRITE_EXTENDED_INQUIRY_RESPONSE = 0x0C52,
+  REFRESH_ENCRYPTION_KEY = 0x0C53,
+  READ_SIMPLE_PAIRING_MODE = 0x0C55,
+  WRITE_SIMPLE_PAIRING_MODE = 0x0C56,
+  READ_LOCAL_OOB_DATA = 0x0C57,
+  READ_INQUIRY_RESPONSE_TRANSMIT_POWER_LEVEL = 0x0C58,
+  WRITE_INQUIRY_TRANSMIT_POWER_LEVEL = 0x0C59,
+  SEND_KEYPRESS_NOTIFICATION = 0x0C60,
+
+  READ_LE_HOST_SUPPORT = 0x0C6C,
+  WRITE_LE_HOST_SUPPORT = 0x0C6D,
+
+  READ_SECURE_CONNECTIONS_HOST_SUPPORT = 0x0C79,
+  WRITE_SECURE_CONNECTIONS_HOST_SUPPORT = 0x0C7A,
+  READ_LOCAL_OOB_EXTENDED_DATA = 0x0C7D,
+
+  // INFORMATIONAL_PARAMETERS
+  READ_LOCAL_VERSION_INFORMATION = 0x1001,
+  READ_LOCAL_SUPPORTED_COMMANDS = 0x1002,
+  READ_LOCAL_SUPPORTED_FEATURES = 0x1003,
+  READ_LOCAL_EXTENDED_FEATURES = 0x1004,
+  READ_BUFFER_SIZE = 0x1005,
+  READ_BD_ADDR = 0x1009,
+  READ_DATA_BLOCK_SIZE = 0x100A,
+  READ_LOCAL_SUPPORTED_CODECS = 0x100B,
+
+  // STATUS_PARAMETERS
+  READ_FAILED_CONTACT_COUNTER = 0x1401,
+  RESET_FAILED_CONTACT_COUNTER = 0x1402,
+  READ_LINK_QUALITY = 0x1403,
+  READ_RSSI = 0x1405,
+  READ_AFH_CHANNEL_MAP = 0x1406,
+  READ_CLOCK = 0x1407,
+  READ_ENCRYPTION_KEY_SIZE = 0x1408,
+
+  // TESTING
+  READ_LOOPBACK_MODE = 0x1801,
+  WRITE_LOOPBACK_MODE = 0x1802,
+  ENABLE_DEVICE_UNDER_TEST_MODE = 0x1803,
+  WRITE_SIMPLE_PAIRING_DEBUG_MODE = 0x1804,
+  WRITE_SECURE_CONNECTIONS_TEST_MODE = 0x180A,
+
+  // LE_CONTROLLER
+  LE_SET_EVENT_MASK = 0x2001,
+  LE_READ_BUFFER_SIZE = 0x2002,
+  LE_READ_LOCAL_SUPPORTED_FEATURES = 0x2003,
+  LE_SET_RANDOM_ADDRESS = 0x2005,
+  LE_SET_ADVERTISING_PARAMETERS = 0x2006,
+  LE_READ_ADVERTISING_CHANNEL_TX_POWER = 0x2007,
+  LE_SET_ADVERTISING_DATA = 0x2008,
+  LE_SET_SCAN_RESPONSE_DATA = 0x2009,
+  LE_SET_ADVERTISING_ENABLE = 0x200A,
+  LE_SET_SCAN_PARAMETERS = 0x200B,
+  LE_SET_SCAN_ENABLE = 0x200C,
+  LE_CREATE_CONNECTION = 0x200D,
+  LE_CREATE_CONNECTION_CANCEL = 0x200E,
+  LE_READ_WHITE_LIST_SIZE = 0x200F,
+  LE_CLEAR_WHITE_LIST = 0x2010,
+  LE_ADD_DEVICE_TO_WHITE_LIST = 0x2011,
+  LE_REMOVE_DEVICE_FROM_WHITE_LIST = 0x2012,
+  LE_CONNECTION_UPDATE = 0x2013,
+  LE_SET_HOST_CHANNEL_CLASSIFICATION = 0x2014,
+  LE_READ_CHANNEL_MAP = 0x2015,
+  LE_READ_REMOTE_FEATURES = 0x2016,
+  LE_ENCRYPT = 0x2017,
+  LE_RAND = 0x2018,
+  LE_START_ENCRYPTION = 0x2019,
+  LE_LONG_TERM_KEY_REQUEST_REPLY = 0x201A,
+  LE_LONG_TERM_KEY_REQUEST_NEGATIVE_REPLY = 0x201B,
+  LE_READ_SUPPORTED_STATES = 0x201C,
+  LE_RECEIVER_TEST = 0x201D,
+  LE_TRANSMITTER_TEST = 0x201E,
+  LE_TEST_END = 0x201F,
+  LE_REMOTE_CONNECTION_PARAMETER_REQUEST_REPLY = 0x2020,
+  LE_REMOTE_CONNECTION_PARAMETER_REQUEST_NEGATIVE_REPLY = 0x2021,
+
+  LE_SET_DATA_LENGTH = 0x2022,
+  LE_READ_SUGGESTED_DEFAULT_DATA_LENGTH = 0x2023,
+  LE_WRITE_SUGGESTED_DEFAULT_DATA_LENGTH = 0x2024,
+  LE_READ_LOCAL_P_256_PUBLIC_KEY_COMMAND = 0x2025,
+  LE_GENERATE_DHKEY_COMMAND_V1 = 0x2026,
+  LE_ADD_DEVICE_TO_RESOLVING_LIST = 0x2027,
+  LE_REMOVE_DEVICE_FROM_RESOLVING_LIST = 0x2028,
+  LE_CLEAR_RESOLVING_LIST = 0x2029,
+  LE_READ_RESOLVING_LIST_SIZE = 0x202A,
+  LE_READ_PEER_RESOLVABLE_ADDRESS = 0x202B,
+  LE_READ_LOCAL_RESOLVABLE_ADDRESS = 0x202C,
+  LE_SET_ADDRESS_RESOLUTION_ENABLE = 0x202D,
+  LE_SET_RESOLVABLE_PRIVATE_ADDRESS_TIMEOUT = 0x202E,
+  LE_READ_MAXIMUM_DATA_LENGTH = 0x202F,
+  LE_READ_PHY = 0x2030,
+  LE_SET_DEFAULT_PHY = 0x2031,
+  LE_SET_PHY = 0x2032,
+  LE_ENHANCED_RECEIVER_TEST = 0x2033,
+  LE_ENHANCED_TRANSMITTER_TEST = 0x2034,
+  LE_SET_EXTENDED_ADVERTISING_RANDOM_ADDRESS = 0x2035,
+  LE_SET_EXTENDED_ADVERTISING_PARAMETERS = 0x2036,
+  LE_SET_EXTENDED_ADVERTISING_DATA = 0x2037,
+  LE_SET_EXTENDED_ADVERTISING_SCAN_RESPONSE = 0x2038,
+  LE_SET_EXTENDED_ADVERTISING_ENABLE = 0x2039,
+  LE_READ_MAXIMUM_ADVERTISING_DATA_LENGTH = 0x203A,
+  LE_READ_NUMBER_OF_SUPPORTED_ADVERTISING_SETS = 0x203B,
+  LE_REMOVE_ADVERTISING_SET = 0x203C,
+  LE_CLEAR_ADVERTISING_SETS = 0x203D,
+  LE_SET_PERIODIC_ADVERTISING_PARAM = 0x203E,
+  LE_SET_PERIODIC_ADVERTISING_DATA = 0x203F,
+  LE_SET_PERIODIC_ADVERTISING_ENABLE = 0x2040,
+  LE_SET_EXTENDED_SCAN_PARAMETERS = 0x2041,
+  LE_SET_EXTENDED_SCAN_ENABLE = 0x2042,
+  LE_EXTENDED_CREATE_CONNECTION = 0x2043,
+  LE_PERIODIC_ADVERTISING_CREATE_SYNC = 0x2044,
+  LE_PERIODIC_ADVERTISING_CREATE_SYNC_CANCEL = 0x2045,
+  LE_PERIODIC_ADVERTISING_TERMINATE_SYNC = 0x2046,
+  LE_ADD_DEVICE_TO_PERIODIC_ADVERTISING_LIST = 0x2047,
+  LE_REMOVE_DEVICE_FROM_PERIODIC_ADVERTISING_LIST = 0x2048,
+  LE_CLEAR_PERIODIC_ADVERTISING_LIST = 0x2049,
+  LE_READ_PERIODIC_ADVERTISING_LIST_SIZE = 0x204A,
+  LE_READ_TRANSMIT_POWER = 0x204B,
+  LE_READ_RF_PATH_COMPENSATION_POWER = 0x204C,
+  LE_WRITE_RF_PATH_COMPENSATION_POWER = 0x204D,
+  LE_SET_PRIVACY_MODE = 0x204E,
+  LE_GENERATE_DHKEY_COMMAND = 0x205E,
+
+  // VENDOR_SPECIFIC
+  LE_GET_VENDOR_CAPABILITIES = 0xFD53,
+  LE_MULTI_ADVT = 0xFD54,
+  LE_BATCH_SCAN = 0xFD56,
+  LE_ADV_FILTER = 0xFD57,
+  LE_TRACK_ADV = 0xFD58,
+  LE_ENERGY_INFO = 0xFD59,
+  LE_EXTENDED_SCAN_PARAMS = 0xFD5A,
+  CONTROLLER_DEBUG_INFO = 0xFD5B,
+  CONTROLLER_A2DP_OPCODE = 0xFD5D,
+  CONTROLLER_BQR = 0xFD5E,
+}
+
+// For mapping Local Supported Commands command
+// Value = Octet * 10 + bit
+enum OpCodeIndex : 16 {
+  INQUIRY = 0,
+  INQUIRY_CANCEL = 1,
+  PERIODIC_INQUIRY_MODE = 2,
+  EXIT_PERIODIC_INQUIRY_MODE = 3,
+  CREATE_CONNECTION = 4,
+  DISCONNECT = 5,
+  CREATE_CONNECTION_CANCEL = 7,
+  ACCEPT_CONNECTION_REQUEST = 10,
+  REJECT_CONNECTION_REQUEST = 11,
+  LINK_KEY_REQUEST_REPLY = 12,
+  LINK_KEY_REQUEST_NEGATIVE_REPLY = 13,
+  PIN_CODE_REQUEST_REPLY = 14,
+  PIN_CODE_REQUEST_NEGATIVE_REPLY = 15,
+  CHANGE_CONNECTION_PACKET_TYPE = 16,
+  AUTHENTICATION_REQUESTED = 17,
+  SET_CONNECTION_ENCRYPTION = 20,
+  CHANGE_CONNECTION_LINK_KEY = 21,
+  MASTER_LINK_KEY = 22,
+  REMOTE_NAME_REQUEST = 23,
+  REMOTE_NAME_REQUEST_CANCEL = 24,
+  READ_REMOTE_SUPPORTED_FEATURES = 25,
+  READ_REMOTE_EXTENDED_FEATURES = 26,
+  READ_REMOTE_VERSION_INFORMATION = 27,
+  READ_CLOCK_OFFSET = 30,
+  READ_LMP_HANDLE = 31,
+  HOLD_MODE = 41,
+  SNIFF_MODE = 42,
+  EXIT_SNIFF_MODE = 43,
+  QOS_SETUP = 46,
+  ROLE_DISCOVERY = 47,
+  SWITCH_ROLE = 50,
+  READ_LINK_POLICY_SETTINGS = 51,
+  WRITE_LINK_POLICY_SETTINGS = 52,
+  READ_DEFAULT_LINK_POLICY_SETTINGS = 53,
+  WRITE_DEFAULT_LINK_POLICY_SETTINGS = 54,
+  FLOW_SPECIFICATION = 55,
+  SET_EVENT_MASK = 56,
+  RESET = 57,
+  SET_EVENT_FILTER = 60,
+  FLUSH = 61,
+  READ_PIN_TYPE = 62,
+  WRITE_PIN_TYPE = 63,
+  READ_STORED_LINK_KEY = 65,
+  WRITE_STORED_LINK_KEY = 66,
+  DELETE_STORED_LINK_KEY = 67,
+  WRITE_LOCAL_NAME = 70,
+  READ_LOCAL_NAME = 71,
+  READ_CONNECTION_ACCEPT_TIMEOUT = 72,
+  WRITE_CONNECTION_ACCEPT_TIMEOUT = 73,
+  READ_PAGE_TIMEOUT = 74,
+  WRITE_PAGE_TIMEOUT = 75,
+  READ_SCAN_ENABLE = 76,
+  WRITE_SCAN_ENABLE = 77,
+  READ_PAGE_SCAN_ACTIVITY = 80,
+  WRITE_PAGE_SCAN_ACTIVITY = 81,
+  READ_INQUIRY_SCAN_ACTIVITY = 82,
+  WRITE_INQUIRY_SCAN_ACTIVITY = 83,
+  READ_AUTHENTICATION_ENABLE = 84,
+  WRITE_AUTHENTICATION_ENABLE = 85,
+  READ_CLASS_OF_DEVICE = 90,
+  WRITE_CLASS_OF_DEVICE = 91,
+  READ_VOICE_SETTING = 92,
+  WRITE_VOICE_SETTING = 93,
+  READ_AUTOMATIC_FLUSH_TIMEOUT = 94,
+  WRITE_AUTOMATIC_FLUSH_TIMEOUT = 95,
+  READ_NUM_BROADCAST_RETRANSMITS = 96,
+  WRITE_NUM_BROADCAST_RETRANSMITS = 97,
+  READ_HOLD_MODE_ACTIVITY = 100,
+  WRITE_HOLD_MODE_ACTIVITY = 101,
+  READ_TRANSMIT_POWER_LEVEL = 102,
+  READ_SYNCHRONOUS_FLOW_CONTROL_ENABLE = 103,
+  WRITE_SYNCHRONOUS_FLOW_CONTROL_ENABLE = 104,
+  SET_CONTROLLER_TO_HOST_FLOW_CONTROL = 105,
+  HOST_BUFFER_SIZE = 106,
+  HOST_NUM_COMPLETED_PACKETS = 107,
+  READ_LINK_SUPERVISION_TIMEOUT = 110,
+  WRITE_LINK_SUPERVISION_TIMEOUT = 111,
+  READ_NUMBER_OF_SUPPORTED_IAC = 112,
+  READ_CURRENT_IAC_LAP = 113,
+  WRITE_CURRENT_IAC_LAP = 114,
+  SET_AFH_HOST_CHANNEL_CLASSIFICATION = 121,
+  READ_INQUIRY_SCAN_TYPE = 124,
+  WRITE_INQUIRY_SCAN_TYPE = 125,
+  READ_INQUIRY_MODE = 126,
+  WRITE_INQUIRY_MODE = 127,
+  READ_PAGE_SCAN_TYPE = 130,
+  WRITE_PAGE_SCAN_TYPE = 131,
+  READ_AFH_CHANNEL_ASSESSMENT_MODE = 132,
+  WRITE_AFH_CHANNEL_ASSESSMENT_MODE = 133,
+  READ_LOCAL_VERSION_INFORMATION = 143,
+  READ_LOCAL_SUPPORTED_FEATURES = 145,
+  READ_LOCAL_EXTENDED_FEATURES = 146,
+  READ_BUFFER_SIZE = 147,
+  READ_BD_ADDR = 151,
+  READ_FAILED_CONTACT_COUNTER = 152,
+  RESET_FAILED_CONTACT_COUNTER = 153,
+  READ_LINK_QUALITY = 154,
+  READ_RSSI = 155,
+  READ_AFH_CHANNEL_MAP = 156,
+  READ_CLOCK = 157,
+  READ_LOOPBACK_MODE = 160,
+  WRITE_LOOPBACK_MODE = 161,
+  ENABLE_DEVICE_UNDER_TEST_MODE = 162,
+  SETUP_SYNCHRONOUS_CONNECTION = 163,
+  ACCEPT_SYNCHRONOUS_CONNECTION = 164,
+  REJECT_SYNCHRONOUS_CONNECTION = 165,
+  READ_EXTENDED_INQUIRY_RESPONSE = 170,
+  WRITE_EXTENDED_INQUIRY_RESPONSE = 171,
+  REFRESH_ENCRYPTION_KEY = 172,
+  SNIFF_SUBRATING = 174,
+  READ_SIMPLE_PAIRING_MODE = 175,
+  WRITE_SIMPLE_PAIRING_MODE = 176,
+  READ_LOCAL_OOB_DATA = 177,
+  READ_INQUIRY_RESPONSE_TRANSMIT_POWER_LEVEL = 180,
+  WRITE_INQUIRY_TRANSMIT_POWER_LEVEL = 181,
+  IO_CAPABILITY_REQUEST_REPLY = 187,
+  USER_CONFIRMATION_REQUEST_REPLY = 190,
+  USER_CONFIRMATION_REQUEST_NEGATIVE_REPLY = 191,
+  USER_PASSKEY_REQUEST_REPLY = 192,
+  USER_PASSKEY_REQUEST_NEGATIVE_REPLY = 193,
+  REMOTE_OOB_DATA_REQUEST_REPLY = 194,
+  WRITE_SIMPLE_PAIRING_DEBUG_MODE = 195,
+  REMOTE_OOB_DATA_REQUEST_NEGATIVE_REPLY = 197,
+  SEND_KEYPRESS_NOTIFICATION = 202,
+  IO_CAPABILITY_REQUEST_NEGATIVE_REPLY = 203,
+  READ_ENCRYPTION_KEY_SIZE = 204,
+  READ_DATA_BLOCK_SIZE = 232,
+  READ_LE_HOST_SUPPORT = 245,
+  WRITE_LE_HOST_SUPPORT = 246,
+  LE_SET_EVENT_MASK = 250,
+  LE_READ_BUFFER_SIZE = 251,
+  LE_READ_LOCAL_SUPPORTED_FEATURES = 252,
+  LE_SET_RANDOM_ADDRESS = 254,
+  LE_SET_ADVERTISING_PARAMETERS = 255,
+  LE_READ_ADVERTISING_CHANNEL_TX_POWER = 256,
+  LE_SET_ADVERTISING_DATA = 257,
+  LE_SET_SCAN_RESPONSE_DATA = 260,
+  LE_SET_ADVERTISING_ENABLE = 261,
+  LE_SET_SCAN_PARAMETERS = 262,
+  LE_SET_SCAN_ENABLE = 263,
+  LE_CREATE_CONNECTION = 264,
+  LE_CREATE_CONNECTION_CANCEL = 265,
+  LE_READ_WHITE_LIST_SIZE = 266,
+  LE_CLEAR_WHITE_LIST = 267,
+  LE_ADD_DEVICE_TO_WHITE_LIST = 270,
+  LE_REMOVE_DEVICE_FROM_WHITE_LIST = 271,
+  LE_CONNECTION_UPDATE = 272,
+  LE_SET_HOST_CHANNEL_CLASSIFICATION = 273,
+  LE_READ_CHANNEL_MAP = 274,
+  LE_READ_REMOTE_FEATURES = 275,
+  LE_ENCRYPT = 276,
+  LE_RAND = 277,
+  LE_START_ENCRYPTION = 280,
+  LE_LONG_TERM_KEY_REQUEST_REPLY = 281,
+  LE_LONG_TERM_KEY_REQUEST_NEGATIVE_REPLY = 282,
+  LE_READ_SUPPORTED_STATES = 283,
+  LE_RECEIVER_TEST = 284,
+  LE_TRANSMITTER_TEST = 285,
+  LE_TEST_END = 286,
+  ENHANCED_SETUP_SYNCHRONOUS_CONNECTION = 293,
+  ENHANCED_ACCEPT_SYNCHRONOUS_CONNECTION = 294,
+  READ_LOCAL_SUPPORTED_CODECS = 295,
+  READ_SECURE_CONNECTIONS_HOST_SUPPORT = 322,
+  WRITE_SECURE_CONNECTIONS_HOST_SUPPORT = 323,
+  READ_LOCAL_OOB_EXTENDED_DATA = 326,
+  WRITE_SECURE_CONNECTIONS_TEST_MODE = 327,
+  LE_REMOTE_CONNECTION_PARAMETER_REQUEST_REPLY = 334,
+  LE_REMOTE_CONNECTION_PARAMETER_REQUEST_NEGATIVE_REPLY = 335,
+  LE_SET_DATA_LENGTH = 336,
+  LE_READ_SUGGESTED_DEFAULT_DATA_LENGTH = 337,
+  LE_WRITE_SUGGESTED_DEFAULT_DATA_LENGTH = 340,
+  LE_READ_LOCAL_P_256_PUBLIC_KEY_COMMAND = 341,
+  LE_GENERATE_DHKEY_COMMAND_V1 = 342,
+  LE_ADD_DEVICE_TO_RESOLVING_LIST = 343,
+  LE_REMOVE_DEVICE_FROM_RESOLVING_LIST = 344,
+  LE_CLEAR_RESOLVING_LIST = 345,
+  LE_READ_RESOLVING_LIST_SIZE = 346,
+  LE_READ_PEER_RESOLVABLE_ADDRESS = 347,
+  LE_READ_LOCAL_RESOLVABLE_ADDRESS = 350,
+  LE_SET_ADDRESS_RESOLUTION_ENABLE = 351,
+  LE_SET_RESOLVABLE_PRIVATE_ADDRESS_TIMEOUT = 352,
+  LE_READ_MAXIMUM_DATA_LENGTH = 353,
+  LE_READ_PHY = 354,
+  LE_SET_DEFAULT_PHY = 355,
+  LE_SET_PHY = 356,
+  LE_ENHANCED_RECEIVER_TEST = 357,
+  LE_ENHANCED_TRANSMITTER_TEST = 360,
+  LE_SET_EXTENDED_ADVERTISING_RANDOM_ADDRESS = 361,
+  LE_SET_EXTENDED_ADVERTISING_PARAMETERS = 362,
+  LE_SET_EXTENDED_ADVERTISING_DATA = 363,
+  LE_SET_EXTENDED_ADVERTISING_SCAN_RESPONSE = 364,
+  LE_SET_EXTENDED_ADVERTISING_ENABLE = 365,
+  LE_READ_MAXIMUM_ADVERTISING_DATA_LENGTH = 366,
+  LE_READ_NUMBER_OF_SUPPORTED_ADVERTISING_SETS = 367,
+  LE_REMOVE_ADVERTISING_SET = 370,
+  LE_CLEAR_ADVERTISING_SETS = 371,
+  LE_SET_PERIODIC_ADVERTISING_PARAM = 372,
+  LE_SET_PERIODIC_ADVERTISING_DATA = 373,
+  LE_SET_PERIODIC_ADVERTISING_ENABLE = 374,
+  LE_SET_EXTENDED_SCAN_PARAMETERS = 375,
+  LE_SET_EXTENDED_SCAN_ENABLE = 376,
+  LE_EXTENDED_CREATE_CONNECTION = 377,
+  LE_PERIODIC_ADVERTISING_CREATE_SYNC = 380,
+  LE_PERIODIC_ADVERTISING_CREATE_SYNC_CANCEL = 381,
+  LE_PERIODIC_ADVERTISING_TERMINATE_SYNC = 382,
+  LE_ADD_DEVICE_TO_PERIODIC_ADVERTISING_LIST = 383,
+  LE_REMOVE_DEVICE_FROM_PERIODIC_ADVERTISING_LIST = 384,
+  LE_CLEAR_PERIODIC_ADVERTISING_LIST = 385,
+  LE_READ_PERIODIC_ADVERTISING_LIST_SIZE = 386,
+  LE_READ_TRANSMIT_POWER = 387,
+  LE_READ_RF_PATH_COMPENSATION_POWER = 390,
+  LE_WRITE_RF_PATH_COMPENSATION_POWER = 391,
+  LE_SET_PRIVACY_MODE = 392,
+  LE_GENERATE_DHKEY_COMMAND = 412,
+}
+
+packet CommandPacket {
+  op_code : OpCode,
+  _size_(_payload_) : 8,
+  _payload_,
+}
+
+// Packets for interfaces
+
+packet DiscoveryCommand : CommandPacket { _payload_, }
+packet ConnectionManagementCommand : CommandPacket { _payload_, }
+packet SecurityCommand : CommandPacket { _payload_, }
+packet ScoConnectionCommand : CommandPacket { _payload_, }
+packet LeAdvertisingCommand : CommandPacket { _payload_, }
+packet LeScanningCommand : CommandPacket { _payload_, }
+packet LeConnectionManagementCommand : CommandPacket { _payload_, }
+packet LeSecurityCommand : CommandPacket { _payload_, }
+packet VendorCommand : CommandPacket { _payload_, }
+
+// HCI Event Packets
+
+enum EventCode : 8 {
+  INQUIRY_COMPLETE = 0x01,
+  INQUIRY_RESULT = 0x02,
+  CONNECTION_COMPLETE = 0x03,
+  CONNECTION_REQUEST = 0x04,
+  DISCONNECTION_COMPLETE = 0x05,
+  AUTHENTICATION_COMPLETE = 0x06,
+  REMOTE_NAME_REQUEST_COMPLETE = 0x07,
+  ENCRYPTION_CHANGE = 0x08,
+  CHANGE_CONNECTION_LINK_KEY_COMPLETE = 0x09,
+  MASTER_LINK_KEY_COMPLETE = 0x0A,
+  READ_REMOTE_SUPPORTED_FEATURES_COMPLETE = 0x0B,
+  READ_REMOTE_VERSION_INFORMATION_COMPLETE = 0x0C,
+  QOS_SETUP_COMPLETE = 0x0D,
+  COMMAND_COMPLETE = 0x0E,
+  COMMAND_STATUS = 0x0F,
+  HARDWARE_ERROR = 0x10,
+  FLUSH_OCCURRED = 0x11,
+  ROLE_CHANGE = 0x12,
+  NUMBER_OF_COMPLETED_PACKETS = 0x13,
+  MODE_CHANGE = 0x14,
+  RETURN_LINK_KEYS = 0x15,
+  PIN_CODE_REQUEST = 0x16,
+  LINK_KEY_REQUEST = 0x17,
+  LINK_KEY_NOTIFICATION = 0x18,
+  LOOPBACK_COMMAND = 0x19,
+  DATA_BUFFER_OVERFLOW = 0x1A,
+  MAX_SLOTS_CHANGE = 0x1B,
+  READ_CLOCK_OFFSET_COMPLETE = 0x1C,
+  CONNECTION_PACKET_TYPE_CHANGED = 0x1D,
+  QOS_VIOLATION = 0x1E,
+  PAGE_SCAN_REPETITION_MODE_CHANGE = 0x20,
+  FLOW_SPECIFICATION_COMPLETE = 0x21,
+  INQUIRY_RESULT_WITH_RSSI = 0x22,
+  READ_REMOTE_EXTENDED_FEATURES_COMPLETE = 0x23,
+  SYNCHRONOUS_CONNECTION_COMPLETE = 0x2C,
+  SYNCHRONOUS_CONNECTION_CHANGED = 0x2D,
+  SNIFF_SUBRATING = 0x2E,
+  EXTENDED_INQUIRY_RESULT = 0x2F,
+  ENCRYPTION_KEY_REFRESH_COMPLETE = 0x30,
+  IO_CAPABILITY_REQUEST = 0x31,
+  IO_CAPABILITY_RESPONSE = 0x32,
+  USER_CONFIRMATION_REQUEST = 0x33,
+  USER_PASSKEY_REQUEST = 0x34,
+  REMOTE_OOB_DATA_REQUEST = 0x35,
+  SIMPLE_PAIRING_COMPLETE = 0x36,
+  LINK_SUPERVISION_TIMEOUT_CHANGED = 0x38,
+  ENHANCED_FLUSH_COMPLETE = 0x39,
+  USER_PASSKEY_NOTIFICATION = 0x3B,
+  KEYPRESS_NOTIFICATION = 0x3C,
+  REMOTE_HOST_SUPPORTED_FEATURES_NOTIFICATION = 0x3D,
+  LE_META_EVENT = 0x3e,
+  NUMBER_OF_COMPLETED_DATA_BLOCKS = 0x48,
+  VENDOR_SPECIFIC = 0xFF,
+}
+
+packet EventPacket {
+  event_code : EventCode,
+  _size_(_payload_) : 8,
+  _payload_,
+}
+
+// LE Events
+
+enum SubeventCode : 8 {
+  CONNECTION_COMPLETE = 0x01,
+  ADVERTISING_REPORT = 0x02,
+  CONNECTION_UPDATE_COMPLETE = 0x03,
+  READ_REMOTE_FEATURES_COMPLETE = 0x04,
+  LONG_TERM_KEY_REQUEST = 0x05,
+  REMOTE_CONNECTION_PARAMETER_REQUEST = 0x06,
+  DATA_LENGTH_CHANGE = 0x07,
+  READ_LOCAL_P256_PUBLIC_KEY_COMPLETE = 0x08,
+  GENERATE_DHKEY_COMPLETE = 0x09,
+  ENHANCED_CONNECTION_COMPLETE = 0x0a,
+  DIRECTED_ADVERTISING_REPORT = 0x0b,
+  PHY_UPDATE_COMPLETE = 0x0c,
+  EXTENDED_ADVERTISING_REPORT = 0x0D,
+  PERIODIC_ADVERTISING_SYNC_ESTABLISHED = 0x0E,
+  PERIODIC_ADVERTISING_REPORT = 0x0F,
+  PERIODIC_ADVERTISING_SYNC_LOST = 0x10,
+  SCAN_TIMEOUT = 0x11,
+  ADVERTISING_SET_TERMINATED = 0x12,
+  SCAN_REQUEST_RECEIVED = 0x13,
+}
+
+// Vendor specific events
+enum VseSubeventCode : 8 {
+  BLE_THRESHOLD = 0x54,
+  BLE_TRACKING = 0x56,
+  DEBUG_INFO = 0x57,
+  BQR_EVENT = 0x58,
+}
+
+// Common definitions for commands and events
+
+enum FeatureFlag : 1 {
+  UNSUPPORTED = 0,
+  SUPPORTED = 1,
+}
+
+enum ErrorCode: 8 {
+  SUCCESS = 0x00,
+  UNKNOWN_HCI_COMMAND = 0x01,
+  UNKNOWN_CONNECTION = 0x02,
+  HARDWARE_FAILURE = 0x03,
+  PAGE_TIMEOUT = 0x04,
+  AUTHENTICATION_FAILURE = 0x05,
+  PIN_OR_KEY_MISSING = 0x06,
+  MEMORY_CAPACITY_EXCEEDED = 0x07,
+  CONNECTION_TIMEOUT = 0x08,
+  CONNECTION_LIMIT_EXCEEDED = 0x09,
+  SYNCHRONOUS_CONNECTION_LIMIT_EXCEEDED = 0x0A,
+  CONNECTION_ALREADY_EXISTS = 0x0B,
+  COMMAND_DISALLOWED = 0x0C,
+  CONNECTION_REJECTED_LIMITED_RESOURCES = 0x0D,
+  CONNECTION_REJECTED_SECURITY_REASONS = 0x0E,
+  CONNECTION_REJECTED_UNACCEPTABLE_BD_ADDR = 0x0F,
+  CONNECTION_ACCEPT_TIMEOUT = 0x10,
+  UNSUPORTED_FEATURE_OR_PARAMETER_VALUE = 0x11,
+  INVALID_HCI_COMMAND_PARAMETERS = 0x12,
+  REMOTE_USER_TERMINATED_CONNECTION = 0x13,
+  REMOTE_DEVICE_TERMINATED_CONNECTION_LOW_RESOURCES = 0x14,
+  REMOTE_DEVICE_TERMINATED_CONNECTION_POWER_OFF = 0x15,
+  CONNECTION_TERMINATED_BY_LOCAL_HOST = 0x16,
+  REPEATED_ATTEMPTS = 0x17,
+  PAIRING_NOT_ALLOWED = 0x18,
+  UNKNOWN_LMP_PDU = 0x19,
+  UNSUPPORTED_REMOTE_OR_LMP_FEATURE = 0x1A,
+  SCO_OFFSET_REJECTED = 0x1B,
+  SCO_INTERVAL_REJECTED = 0x1C,
+  SCO_AIR_MODE_REJECTED = 0x1D,
+  INVALID_LMP_OR_LL_PARAMETERS = 0x1E,
+  UNSPECIFIED_ERROR = 0x1F,
+  UNSUPPORTED_LMP_OR_LL_PARAMETER = 0x20,
+  ROLE_CHANGE_NOT_ALLOWED = 0x21,
+  LINK_LAYER_COLLISION = 0x23,
+  ENCRYPTION_MODE_NOT_ACCEPTABLE = 0x25,
+  CONTROLLER_BUSY = 0x3A,
+}
+
+// Events that are defined with their respective commands
+
+packet CommandComplete : EventPacket (event_code = COMMAND_COMPLETE){
+  num_hci_command_packets : 8,
+  command_op_code : OpCode,
+  _payload_,
+}
+
+packet CommandStatus : EventPacket (event_code = COMMAND_STATUS){
+  status : ErrorCode, // SUCCESS means PENDING
+  num_hci_command_packets : 8,
+  command_op_code : OpCode,
+  _payload_,
+}
+
+  // Credits
+packet NoCommandComplete : CommandComplete (command_op_code = NONE){
+}
+
+struct Lap { // Lower Address Part
+  lap : 6,
+  _reserved_ : 2,
+  _fixed_ = 0x9e8b : 16,
+}
+
+  // LINK_CONTROL
+packet Inquiry : DiscoveryCommand (op_code = INQUIRY) {
+  lap : Lap,
+  inquiry_length : 8, // 0x1 - 0x30 (times 1.28s)
+  num_responses : 8, // 0x00 unlimited
+}
+
+packet InquiryStatus : CommandStatus (command_op_code = INQUIRY) {
+}
+
+packet InquiryCancel : DiscoveryCommand (op_code = INQUIRY_CANCEL) {
+}
+
+packet InquiryCancelComplete : CommandComplete (command_op_code = INQUIRY_CANCEL) {
+  status : ErrorCode,
+}
+
+packet PeriodicInquiryMode : DiscoveryCommand (op_code = PERIODIC_INQUIRY_MODE) {
+  max_period_length : 16, // Range 0x0003 to 0xffff (times 1.28s)
+  min_period_length : 16, // Range 0x0002 to 0xfffe (times 1.28s)
+  lap : Lap,
+  inquiry_length : 8, // 0x1 - 0x30 (times 1.28s)
+  num_responses : 8, // 0x00 unlimited
+}
+
+packet PeriodicInquiryModeComplete : CommandComplete (command_op_code = PERIODIC_INQUIRY_MODE) {
+  status : ErrorCode,
+}
+
+packet ExitPeriodicInquiryMode : DiscoveryCommand (op_code = EXIT_PERIODIC_INQUIRY_MODE) {
+}
+
+packet ExitPeriodicInquiryModeComplete : CommandComplete (command_op_code = EXIT_PERIODIC_INQUIRY_MODE) {
+  status : ErrorCode,
+}
+
+enum PageScanRepetitionMode : 8 {
+  R0 = 0x00,
+  R1 = 0x01,
+  R2 = 0x02,
+}
+
+enum ClockOffsetValid : 1 {
+  INVALID = 0,
+  VALID = 1,
+}
+
+enum CreateConnectionRoleSwitch : 8 {
+  REMAIN_MASTER = 0x00,
+  ALLOW_ROLE_SWITCH = 0x01,
+}
+
+packet CreateConnection : ConnectionManagementCommand (op_code = CREATE_CONNECTION) {
+  bd_addr : Address,
+  packet_type : 16,
+  page_scan_repetition_mode : PageScanRepetitionMode,
+  _reserved_ : 8,
+  clock_offset : 15,
+  clock_offset_valid : ClockOffsetValid,
+  allow_role_switch : CreateConnectionRoleSwitch,
+}
+
+packet CreateConnectionStatus : CommandStatus (command_op_code = CREATE_CONNECTION) {
+}
+
+enum DisconnectReason : 8 {
+  AUTHENTICATION_FAILURE = 0x05,
+  REMOTE_USER_TERMINATED_CONNECTION = 0x13,
+  REMOTE_DEVICE_TERMINATED_CONNECTION_LOW_RESOURCES = 0x14,
+  REMOTE_DEVICE_TERMINATED_CONNECTION_POWER_OFF = 0x15,
+  UNSUPPORTED_REMOTE_FEATURE = 0x1A,
+  PAIRING_WITH_UNIT_KEY_NOT_SUPPORTED = 0x29,
+  UNACCEPTABLE_CONNECTION_PARAMETERS = 0x3B,
+}
+
+packet Disconnect : ConnectionManagementCommand (op_code = DISCONNECT) {
+  connection_handle : 12,
+  _reserved_ : 4,
+  reason : DisconnectReason,
+}
+
+packet DisconnectStatus : CommandStatus (command_op_code = DISCONNECT) {
+}
+
+packet CreateConnectionCancel : ConnectionManagementCommand (op_code = CREATE_CONNECTION_CANCEL) {
+  bd_addr : Address,
+}
+
+packet CreateConnectionCancelComplete : CommandComplete (command_op_code = CREATE_CONNECTION_CANCEL) {
+  status : ErrorCode,
+  bd_addr : Address,
+}
+
+enum AcceptConnectionRequestRole : 8 {
+  BECOME_MASTER = 0x00,
+  REMAIN_SLAVE = 0x01,
+}
+
+packet AcceptConnectionRequest : ConnectionManagementCommand (op_code = ACCEPT_CONNECTION_REQUEST) {
+  bd_addr : Address,
+  role : AcceptConnectionRequestRole,
+}
+
+packet AcceptConnectionRequestStatus : CommandStatus (command_op_code = ACCEPT_CONNECTION_REQUEST) {
+}
+
+enum RejectConnectionReason : 8 {
+  LIMITED_RESOURCES = 0x0D,
+  SECURITY_REASONS = 0x0E,
+  UNACCEPTABLE_BD_ADDR = 0x0F,
+}
+
+packet RejectConnectionRequest : ConnectionManagementCommand (op_code = REJECT_CONNECTION_REQUEST) {
+  bd_addr : Address,
+  reason : RejectConnectionReason,
+}
+
+packet RejectConnectionRequestStatus : CommandStatus (command_op_code = REJECT_CONNECTION_REQUEST) {
+}
+
+packet LinkKeyRequestReply : SecurityCommand (op_code = LINK_KEY_REQUEST_REPLY) {
+  bd_addr : Address,
+  link_key : 8[16],
+}
+
+packet LinkKeyRequestReplyComplete : CommandComplete (command_op_code = LINK_KEY_REQUEST_REPLY) {
+  status : ErrorCode,
+}
+
+packet LinkKeyRequestNegativeReply : SecurityCommand (op_code = LINK_KEY_REQUEST_NEGATIVE_REPLY) {
+  bd_addr : Address,
+}
+
+packet LinkKeyRequestNegativeReplyComplete : CommandComplete (command_op_code = LINK_KEY_REQUEST_NEGATIVE_REPLY) {
+  status : ErrorCode,
+  bd_addr : Address,
+}
+
+packet PinCodeRequestReply : SecurityCommand (op_code = PIN_CODE_REQUEST_REPLY) {
+  bd_addr : Address,
+  pin_code_length : 5, // 0x01 - 0x10
+  _reserved_ : 3,
+  pin_code : 8[16], // string parameter, first octet first
+}
+
+packet PinCodeRequestReplyComplete : CommandComplete (command_op_code = PIN_CODE_REQUEST_REPLY) {
+  status : ErrorCode,
+  bd_addr : Address,
+}
+
+packet PinCodeRequestNegativeReply : SecurityCommand (op_code = PIN_CODE_REQUEST_NEGATIVE_REPLY) {
+  bd_addr : Address,
+}
+
+packet PinCodeRequestNegativeReplyComplete : CommandComplete (command_op_code = PIN_CODE_REQUEST_NEGATIVE_REPLY) {
+  status : ErrorCode,
+  bd_addr : Address,
+}
+
+packet ChangeConnectionPacketType : ConnectionManagementCommand (op_code = CHANGE_CONNECTION_PACKET_TYPE) {
+  connection_handle : 12,
+  _reserved_ : 4,
+  packet_type : 16,
+}
+
+packet ChangeConnectionPacketTypeStatus : CommandStatus (command_op_code = CHANGE_CONNECTION_PACKET_TYPE) {
+}
+
+packet AuthenticationRequested : ConnectionManagementCommand (op_code = AUTHENTICATION_REQUESTED) {
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+packet AuthenticationRequestedStatus : CommandStatus (command_op_code = AUTHENTICATION_REQUESTED) {
+}
+
+packet SetConnectionEncryption : ConnectionManagementCommand (op_code = SET_CONNECTION_ENCRYPTION) {
+  connection_handle : 12,
+  _reserved_ : 4,
+  encryption_enable : Enable,
+}
+
+packet SetConnectionEncryptionStatus : CommandStatus (command_op_code = SET_CONNECTION_ENCRYPTION) {
+}
+
+packet ChangeConnectionLinkKey : ConnectionManagementCommand (op_code = CHANGE_CONNECTION_LINK_KEY) {
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+packet ChangeConnectionLinkKeyStatus : CommandStatus (command_op_code = CHANGE_CONNECTION_LINK_KEY) {
+}
+
+enum KeyFlag : 8 {
+  SEMI_PERMANENT = 0x00,
+  TEMPORARY = 0x01,
+}
+
+packet MasterLinkKey : ConnectionManagementCommand (op_code = MASTER_LINK_KEY) {
+  key_flag : KeyFlag,
+}
+
+packet MasterLinkKeyStatus : CommandStatus (command_op_code = MASTER_LINK_KEY) {
+}
+
+packet RemoteNameRequest : DiscoveryCommand (op_code = REMOTE_NAME_REQUEST) {
+  bd_addr : Address,
+  page_scan_repetition_mode : PageScanRepetitionMode,
+  _reserved_ : 8,
+  clock_offset : 15,
+  clock_offset_valid : ClockOffsetValid,
+}
+
+packet RemoteNameRequestStatus : CommandStatus (command_op_code = REMOTE_NAME_REQUEST) {
+}
+
+packet RemoteNameRequestCancel : DiscoveryCommand (op_code = REMOTE_NAME_REQUEST_CANCEL) {
+  bd_addr : Address,
+}
+
+packet RemoteNameRequestCancelComplete : CommandComplete (command_op_code = REMOTE_NAME_REQUEST_CANCEL) {
+  status : ErrorCode,
+  bd_addr : Address,
+}
+
+packet ReadRemoteSupportedFeatures : DiscoveryCommand (op_code = READ_REMOTE_SUPPORTED_FEATURES) {
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+packet ReadRemoteSupportedFeaturesStatus : CommandStatus (command_op_code = READ_REMOTE_SUPPORTED_FEATURES) {
+}
+
+packet ReadRemoteExtendedFeatures : DiscoveryCommand (op_code = READ_REMOTE_EXTENDED_FEATURES) {
+  connection_handle : 12,
+  _reserved_ : 4,
+  page_number : 8,
+}
+
+packet ReadRemoteExtendedFeaturesStatus : CommandStatus (command_op_code = READ_REMOTE_EXTENDED_FEATURES) {
+}
+
+packet ReadRemoteVersionInformation : DiscoveryCommand (op_code = READ_REMOTE_VERSION_INFORMATION) {
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+packet ReadRemoteVersionInformationStatus : CommandStatus (command_op_code = READ_REMOTE_VERSION_INFORMATION) {
+}
+
+packet ReadClockOffset : ConnectionManagementCommand (op_code = READ_CLOCK_OFFSET) {
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+packet ReadClockOffsetStatus : CommandStatus (command_op_code = READ_CLOCK_OFFSET) {
+}
+
+packet ReadLmpHandle : ConnectionManagementCommand (op_code = READ_LMP_HANDLE) {
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+packet ReadLmpHandleComplete : CommandComplete (command_op_code = READ_LMP_HANDLE) {
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+  lmp_handle : 8,
+  _reserved_ : 32,
+}
+
+packet SetupSynchronousConnection : ScoConnectionCommand (op_code = SETUP_SYNCHRONOUS_CONNECTION) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet AcceptSynchronousConnection : ScoConnectionCommand (op_code = ACCEPT_SYNCHRONOUS_CONNECTION) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet RejectSynchronousConnection : ScoConnectionCommand (op_code = REJECT_SYNCHRONOUS_CONNECTION) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+enum IoCapability : 8 {
+  DISPLAY_ONLY = 0x00,
+  DISPLAY_YES_NO = 0x01,
+  KEYBOARD_ONLY = 0x02,
+  NO_INPUT_NO_OUTPUT = 0x03,
+}
+
+enum OobDataPresent : 8 {
+  NOT_PRESENT = 0x00,
+  P_192_PRESENT = 0x01,
+  P_256_PRESENT = 0x02,
+  P_192_AND_256_PRESENT = 0x03,
+}
+
+enum AuthenticationRequirements : 8 {
+  NO_BONDING = 0x00,
+  NO_BONDING_MITM_PROTECTION = 0x01,
+  DEDICATED_BONDING = 0x02,
+  DEDICATED_BONDING_MITM_PROTECTION = 0x03,
+  GENERAL_BONDING = 0x04,
+  GENERAL_BONDING_MITM_PROTECTION = 0x05,
+}
+
+packet IoCapabilityRequestReply : SecurityCommand (op_code = IO_CAPABILITY_REQUEST_REPLY) {
+  bd_addr : Address,
+  io_capability : IoCapability,
+  oob_present : OobDataPresent,
+  authentication_requirements : AuthenticationRequirements,
+}
+
+packet IoCapabilityRequestReplyComplete : CommandComplete (command_op_code = IO_CAPABILITY_REQUEST_REPLY) {
+  status : ErrorCode,
+  bd_addr : Address,
+}
+
+packet UserConfirmationRequestReply : SecurityCommand (op_code = USER_CONFIRMATION_REQUEST_REPLY) {
+  bd_addr : Address,
+}
+
+packet UserConfirmationRequestReplyComplete : CommandComplete (command_op_code = USER_CONFIRMATION_REQUEST_REPLY) {
+  status : ErrorCode,
+  bd_addr : Address,
+}
+
+packet UserConfirmationRequestNegativeReply : SecurityCommand (op_code = USER_CONFIRMATION_REQUEST_NEGATIVE_REPLY) {
+  bd_addr : Address,
+}
+
+packet UserConfirmationRequestNegativeReplyComplete : CommandComplete (command_op_code = USER_CONFIRMATION_REQUEST_NEGATIVE_REPLY) {
+  status : ErrorCode,
+  bd_addr : Address,
+}
+
+packet UserPasskeyRequestReply : SecurityCommand (op_code = USER_PASSKEY_REQUEST_REPLY) {
+  bd_addr : Address,
+  numeric_value : 32, // 000000-999999 decimal or 0x0-0xF423F
+}
+
+packet UserPasskeyRequestReplyComplete : CommandComplete (command_op_code = USER_PASSKEY_REQUEST_REPLY) {
+  status : ErrorCode,
+  bd_addr : Address,
+}
+
+packet UserPasskeyRequestNegativeReply : SecurityCommand (op_code = USER_PASSKEY_REQUEST_NEGATIVE_REPLY) {
+  bd_addr : Address,
+}
+
+packet UserPasskeyRequestNegativeReplyComplete : CommandComplete (command_op_code = USER_PASSKEY_REQUEST_NEGATIVE_REPLY) {
+  status : ErrorCode,
+  bd_addr : Address,
+}
+
+packet RemoteOobDataRequestReply : SecurityCommand (op_code = REMOTE_OOB_DATA_REQUEST_REPLY) {
+  bd_addr : Address,
+  c : 8[16],
+  r : 8[16],
+}
+
+packet RemoteOobDataRequestReplyComplete : CommandComplete (command_op_code = REMOTE_OOB_DATA_REQUEST_REPLY) {
+  status : ErrorCode,
+  bd_addr : Address,
+}
+
+packet RemoteOobDataRequestNegativeReply : SecurityCommand (op_code = REMOTE_OOB_DATA_REQUEST_NEGATIVE_REPLY) {
+  bd_addr : Address,
+}
+
+packet RemoteOobDataRequestNegativeReplyComplete : CommandComplete (command_op_code = REMOTE_OOB_DATA_REQUEST_NEGATIVE_REPLY) {
+  status : ErrorCode,
+  bd_addr : Address,
+}
+
+packet IoCapabilityRequestNegativeReply : SecurityCommand (op_code = IO_CAPABILITY_REQUEST_NEGATIVE_REPLY) {
+  bd_addr : Address,
+  reason : ErrorCode,
+}
+
+packet IoCapabilityRequestNegativeReplyComplete : CommandComplete (command_op_code = IO_CAPABILITY_REQUEST_NEGATIVE_REPLY) {
+  status : ErrorCode,
+  bd_addr : Address,
+}
+
+packet EnhancedSetupSynchronousConnection : ScoConnectionCommand (op_code = ENHANCED_SETUP_SYNCHRONOUS_CONNECTION) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet EnhancedAcceptSynchronousConnection : ScoConnectionCommand (op_code = ENHANCED_ACCEPT_SYNCHRONOUS_CONNECTION) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+
+  // LINK_POLICY
+packet HoldMode : ConnectionManagementCommand (op_code = HOLD_MODE) {
+  connection_handle : 12,
+  _reserved_ : 4,
+  hold_mode_max_interval: 16, // 0x0002-0xFFFE (1.25ms-40.9s)
+  hold_mode_min_interval: 16, // 0x0002-0xFFFE (1.25ms-40.9s)
+}
+
+packet HoldModeStatus : CommandStatus (command_op_code = HOLD_MODE) {
+}
+
+
+packet SniffMode : ConnectionManagementCommand (op_code = SNIFF_MODE) {
+  connection_handle : 12,
+  _reserved_ : 4,
+  sniff_max_interval: 16, // 0x0002-0xFFFE (1.25ms-40.9s)
+  sniff_min_interval: 16, // 0x0002-0xFFFE (1.25ms-40.9s)
+  sniff_attempt: 16, // 0x0001-0x7FFF (1.25ms-40.9s)
+  sniff_timeout: 16, // 0x0000-0x7FFF (0ms-40.9s)
+}
+
+packet SniffModeStatus : CommandStatus (command_op_code = SNIFF_MODE) {
+}
+
+
+packet ExitSniffMode : ConnectionManagementCommand (op_code = EXIT_SNIFF_MODE) {
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+packet ExitSniffModeStatus : CommandStatus (command_op_code = EXIT_SNIFF_MODE) {
+}
+
+enum ServiceType : 8 {
+  NO_TRAFFIC = 0x00,
+  BEST_EFFORT = 0x01,
+  GUARANTEED = 0x02,
+}
+
+packet QosSetup : ConnectionManagementCommand (op_code = QOS_SETUP) {
+  connection_handle : 12,
+  _reserved_ : 4,
+  _reserved_ : 8,
+  service_type : ServiceType,
+  token_rate : 32, // Octets/s
+  peak_bandwidth : 32, // Octets/s
+  latency : 32, // Octets/s
+  delay_variation : 32, // microseconds
+}
+
+packet QosSetupStatus : CommandStatus (command_op_code = QOS_SETUP) {
+}
+
+packet RoleDiscovery : ConnectionManagementCommand (op_code = ROLE_DISCOVERY) {
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+enum Role : 8 {
+  MASTER = 0x00,
+  SLAVE = 0x01,
+}
+
+packet RoleDiscoveryComplete : CommandComplete (command_op_code = ROLE_DISCOVERY) {
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+  current_role : Role,
+}
+
+packet SwitchRole : ConnectionManagementCommand (op_code = SWITCH_ROLE) {
+  bd_addr : Address,
+  role : Role,
+}
+
+packet SwitchRoleStatus : CommandStatus (command_op_code = SWITCH_ROLE) {
+}
+
+
+packet ReadLinkPolicySettings : ConnectionManagementCommand (op_code = READ_LINK_POLICY_SETTINGS) {
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+enum LinkPolicy : 16 {
+  ENABLE_ROLE_SWITCH = 0x01,
+  ENABLE_HOLD_MODE = 0x02,
+  ENABLE_SNIFF_MODE = 0x04,
+  ENABLE_PARK_MODE = 0x08, // deprecated after 5.0
+}
+
+packet ReadLinkPolicySettingsComplete : CommandComplete (command_op_code = READ_LINK_POLICY_SETTINGS) {
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+  link_policy_settings : 16,
+}
+
+packet WriteLinkPolicySettings : ConnectionManagementCommand (op_code = WRITE_LINK_POLICY_SETTINGS) {
+  connection_handle : 12,
+  _reserved_ : 4,
+  link_policy_settings : 16,
+}
+
+packet WriteLinkPolicySettingsComplete : CommandComplete (command_op_code = WRITE_LINK_POLICY_SETTINGS) {
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+packet ReadDefaultLinkPolicySettings : ConnectionManagementCommand (op_code = READ_DEFAULT_LINK_POLICY_SETTINGS) {
+}
+
+packet ReadDefaultLinkPolicySettingsComplete : CommandComplete (command_op_code = READ_DEFAULT_LINK_POLICY_SETTINGS) {
+  status : ErrorCode,
+  default_link_policy_settings : 16,
+}
+
+packet WriteDefaultLinkPolicySettings : ConnectionManagementCommand (op_code = WRITE_DEFAULT_LINK_POLICY_SETTINGS) {
+  default_link_policy_settings : 16,
+}
+
+packet WriteDefaultLinkPolicySettingsComplete : CommandComplete (command_op_code = WRITE_DEFAULT_LINK_POLICY_SETTINGS) {
+  status : ErrorCode,
+}
+
+enum FlowDirection : 8 {
+  OUTGOING_FLOW = 0x00,
+  INCOMING_FLOW = 0x01,
+}
+
+packet FlowSpecification : ConnectionManagementCommand (op_code = FLOW_SPECIFICATION) {
+  connection_handle : 12,
+  _reserved_ : 4,
+  _reserved_ : 8,
+  flow_direction : FlowDirection,
+  service_type : ServiceType,
+  token_rate : 32, // Octets/s
+  token_bucket_size : 32,
+  peak_bandwidth : 32, // Octets/s
+  access_latency : 32, // Octets/s
+}
+
+packet FlowSpecificationStatus : CommandStatus (command_op_code = FLOW_SPECIFICATION) {
+}
+
+packet SniffSubrating : ConnectionManagementCommand (op_code = SNIFF_SUBRATING) {
+  connection_handle : 12,
+  _reserved_ : 4,
+  maximum_latency : 16,  // 0x0002-0xFFFE (1.25ms-40.9s)
+  minimum_remote_timeout : 16, // 0x0000-0xFFFE (0-40.9s)
+  minimum_local_timeout: 16, // 0x0000-0xFFFE (0-40.9s)
+}
+
+packet SniffSubratingComplete : CommandComplete (command_op_code = SNIFF_SUBRATING) {
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+  // CONTROLLER_AND_BASEBAND
+packet SetEventMask : CommandPacket (op_code = SET_EVENT_MASK) {
+  event_mask : 64,
+}
+
+packet SetEventMaskComplete : CommandComplete (command_op_code = SET_EVENT_MASK) {
+  status : ErrorCode,
+}
+
+packet Reset : CommandPacket (op_code = RESET) {
+}
+
+packet ResetComplete : CommandComplete (command_op_code = RESET) {
+  status : ErrorCode,
+}
+
+enum FilterType : 8 {
+  CLEAR_ALL_FILTERS = 0x00,
+  INQUIRY_RESULT = 0x01,
+  CONNECTION_SETUP = 0x02,
+}
+
+packet SetEventFilter : CommandPacket (op_code = SET_EVENT_FILTER) {
+  filter_type : FilterType,
+  _body_,
+}
+
+packet SetEventFilterComplete : CommandComplete (command_op_code = SET_EVENT_FILTER) {
+  status : ErrorCode,
+}
+
+packet SetEventFilterClearAll : SetEventFilter (filter_type = CLEAR_ALL_FILTERS) {
+}
+
+enum FilterConditionType : 8 {
+  ALL_DEVICES = 0x00,
+  CLASS_OF_DEVICE = 0x01,
+  ADDRESS = 0x02,
+}
+
+packet SetEventFilterInquiryResult : SetEventFilter (filter_type = INQUIRY_RESULT) {
+  filter_condition_type : FilterConditionType,
+  _body_,
+}
+
+packet SetEventFilterInquiryResultAllDevices : SetEventFilterInquiryResult (filter_condition_type = ALL_DEVICES) {
+}
+
+packet SetEventFilterInquiryResultClassOfDevice : SetEventFilterInquiryResult (filter_condition_type = CLASS_OF_DEVICE) {
+  class_of_device : ClassOfDevice,
+  class_of_device_mask : ClassOfDevice,
+}
+
+packet SetEventFilterInquiryResultAddress : SetEventFilterInquiryResult (filter_condition_type = ADDRESS) {
+  address : Address,
+}
+
+packet SetEventFilterConnectionSetup : SetEventFilter (filter_type = CONNECTION_SETUP) {
+  filter_condition_type : FilterConditionType,
+  _body_,
+}
+
+enum AutoAcceptFlag : 8 {
+  AUTO_ACCEPT_OFF = 0x01,
+  AUTO_ACCEPT_ON_ROLE_SWITCH_DISABLED = 0x02,
+  AUTO_ACCEPT_ON_ROLE_SWITCH_ENABLED = 0x03,
+}
+
+packet SetEventFilterConnectionSetupAllDevices : SetEventFilterConnectionSetup (filter_condition_type = ALL_DEVICES) {
+  auto_accept_flag : AutoAcceptFlag,
+}
+
+packet SetEventFilterConnectionSetupClassOfDevice : SetEventFilterConnectionSetup (filter_condition_type = CLASS_OF_DEVICE) {
+  class_of_device : ClassOfDevice,
+  class_of_device_mask : ClassOfDevice,
+  auto_accept_flag : AutoAcceptFlag,
+}
+
+packet SetEventFilterConnectionSetupAddress : SetEventFilterConnectionSetup (filter_condition_type = ADDRESS) {
+  address : Address,
+  auto_accept_flag : AutoAcceptFlag,
+}
+
+packet Flush : ConnectionManagementCommand (op_code = FLUSH) {
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+packet FlushComplete : CommandComplete (command_op_code = FLUSH) {
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+enum PinType : 8 {
+  VARIABLE = 0,
+  FIXED = 1,
+}
+
+packet ReadPinType : CommandPacket (op_code = READ_PIN_TYPE) {
+}
+
+packet ReadPinTypeComplete : CommandComplete (command_op_code = READ_PIN_TYPE) {
+  status : ErrorCode,
+  pin_type : PinType,
+}
+
+packet WritePinType : CommandPacket (op_code = WRITE_PIN_TYPE) {
+  pin_type : PinType,
+}
+
+packet WritePinTypeComplete : CommandComplete (command_op_code = WRITE_PIN_TYPE) {
+  status : ErrorCode,
+}
+
+packet CreateNewUnitKey : CommandPacket (op_code = CREATE_NEW_UNIT_KEY) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+enum ReadStoredLinkKeyReadAllFlag : 8 {
+  SPECIFIED_BD_ADDR = 0x00,
+  ALL = 0x01,
+}
+
+packet ReadStoredLinkKey : SecurityCommand (op_code = READ_STORED_LINK_KEY) {
+  bd_addr : Address,
+  read_all_flag : ReadStoredLinkKeyReadAllFlag,
+}
+
+packet ReadStoredLinkKeyComplete : CommandComplete (command_op_code = READ_STORED_LINK_KEY) {
+  status : ErrorCode,
+  max_num_keys : 16,
+  num_keys_read : 16,
+}
+
+struct KeyAndAddress {
+  address : Address,
+  link_key : 8[16],
+}
+
+packet WriteStoredLinkKey : SecurityCommand (op_code = WRITE_STORED_LINK_KEY) {
+  _count_(keys_to_write) : 8, // 0x01-0x0B
+  keys_to_write : KeyAndAddress[],
+}
+
+packet WriteStoredLinkKeyComplete : CommandComplete (command_op_code = WRITE_STORED_LINK_KEY) {
+  status : ErrorCode,
+  num_keys_written : 8,
+}
+
+enum DeleteStoredLinkKeyDeleteAllFlag : 8 {
+  SPECIFIED_BD_ADDR = 0x00,
+  ALL = 0x01,
+}
+
+packet DeleteStoredLinkKey : SecurityCommand (op_code = DELETE_STORED_LINK_KEY) {
+  bd_addr : Address,
+  delete_all_flag : DeleteStoredLinkKeyDeleteAllFlag,
+}
+
+packet DeleteStoredLinkKeyComplete : CommandComplete (command_op_code = DELETE_STORED_LINK_KEY) {
+  status : ErrorCode,
+  num_keys_deleted : 16,
+}
+
+packet WriteLocalName : CommandPacket (op_code = WRITE_LOCAL_NAME) {
+  local_name : 8[248], // Null-terminated UTF-8 encoded name
+}
+
+packet WriteLocalNameComplete : CommandComplete (command_op_code = WRITE_LOCAL_NAME) {
+  status : ErrorCode,
+}
+
+packet ReadLocalName : CommandPacket (op_code = READ_LOCAL_NAME) {
+}
+
+packet ReadLocalNameComplete : CommandComplete (command_op_code = READ_LOCAL_NAME) {
+  status : ErrorCode,
+  local_name : 8[248], // Null-terminated UTF-8 encoded name
+}
+
+packet ReadConnectionAcceptTimeout : ConnectionManagementCommand (op_code = READ_CONNECTION_ACCEPT_TIMEOUT) {
+}
+
+packet ReadConnectionAcceptTimeoutComplete : CommandComplete (command_op_code = READ_CONNECTION_ACCEPT_TIMEOUT) {
+  status : ErrorCode,
+  conn_accept_timeout : 16, // 0x0001 to 0xB540 (N * 0.625 ms) 0.625 ms to 29 s
+}
+
+packet WriteConnectionAcceptTimeout : ConnectionManagementCommand (op_code = WRITE_CONNECTION_ACCEPT_TIMEOUT) {
+  conn_accept_timeout : 16, // 0x0001 to 0xB540 (N * 0.625 ms) 0.625 ms to 29 s, Default 0x1FA0, 5.06s
+}
+
+packet WriteConnectionAcceptTimeoutComplete : CommandComplete (command_op_code = WRITE_CONNECTION_ACCEPT_TIMEOUT) {
+  status : ErrorCode,
+}
+
+packet ReadPageTimeout : DiscoveryCommand (op_code = READ_PAGE_TIMEOUT) {
+}
+
+packet ReadPageTimeoutComplete : CommandComplete (command_op_code = READ_PAGE_TIMEOUT) {
+  status : ErrorCode,
+  page_timeout : 16,
+}
+
+packet WritePageTimeout : DiscoveryCommand (op_code = WRITE_PAGE_TIMEOUT) {
+  page_timeout : 16,
+}
+
+packet WritePageTimeoutComplete : CommandComplete (command_op_code = WRITE_PAGE_TIMEOUT) {
+  status : ErrorCode,
+}
+
+enum ScanEnable : 8 {
+  NO_SCANS = 0x00,
+  INQUIRY_SCAN_ONLY = 0x01,
+  PAGE_SCAN_ONLY = 0x02,
+  INQUIRY_AND_PAGE_SCAN = 0x03,
+}
+
+packet ReadScanEnable : DiscoveryCommand (op_code = READ_SCAN_ENABLE) {
+}
+
+packet ReadScanEnableComplete : CommandComplete (command_op_code = READ_SCAN_ENABLE) {
+  status : ErrorCode,
+  scan_enable : ScanEnable,
+}
+
+packet WriteScanEnable : DiscoveryCommand (op_code = WRITE_SCAN_ENABLE) {
+  scan_enable : ScanEnable,
+}
+
+packet WriteScanEnableComplete : CommandComplete (command_op_code = WRITE_SCAN_ENABLE) {
+  status : ErrorCode,
+}
+
+packet ReadPageScanActivity : DiscoveryCommand (op_code = READ_PAGE_SCAN_ACTIVITY) {
+}
+
+packet ReadPageScanActivityComplete : CommandComplete (command_op_code = READ_PAGE_SCAN_ACTIVITY) {
+  status : ErrorCode,
+  page_scan_interval : 16, // Range: 0x0012 to 0x1000; only even values are valid * 0x625 ms
+  page_scan_window : 16, // 0x0011 to PageScanInterval
+}
+
+packet WritePageScanActivity : DiscoveryCommand (op_code = WRITE_PAGE_SCAN_ACTIVITY) {
+  page_scan_interval : 16, // Range: 0x0012 to 0x1000; only even values are valid * 0x625 ms
+  page_scan_window : 16, // 0x0011 to PageScanInterval
+}
+
+packet WritePageScanActivityComplete : CommandComplete (command_op_code = WRITE_PAGE_SCAN_ACTIVITY) {
+  status : ErrorCode,
+}
+
+packet ReadInquiryScanActivity : DiscoveryCommand (op_code = READ_INQUIRY_SCAN_ACTIVITY) {
+}
+
+packet ReadInquiryScanActivityComplete : CommandComplete (command_op_code = READ_INQUIRY_SCAN_ACTIVITY) {
+  status : ErrorCode,
+  inquiry_scan_interval : 16, // Range: 0x0012 to 0x1000; only even values are valid * 0x625 ms
+  inquiry_scan_window : 16, // Range: 0x0011 to 0x1000
+}
+
+packet WriteInquiryScanActivity : DiscoveryCommand (op_code = WRITE_INQUIRY_SCAN_ACTIVITY) {
+  inquiry_scan_interval : 16, // Range: 0x0012 to 0x1000; only even values are valid * 0x625 ms
+  inquiry_scan_window : 16, // Range: 0x0011 to 0x1000
+}
+
+packet WriteInquiryScanActivityComplete : CommandComplete (command_op_code = WRITE_INQUIRY_SCAN_ACTIVITY) {
+  status : ErrorCode,
+}
+
+enum AuthenticationEnable : 8 {
+  NOT_REQUIRED = 0x00,
+  REQUIRED = 0x01,
+}
+
+packet ReadAuthenticationEnable : CommandPacket (op_code = READ_AUTHENTICATION_ENABLE) {
+}
+
+packet ReadAuthenticationEnableComplete : CommandComplete (command_op_code = READ_AUTHENTICATION_ENABLE) {
+  status : ErrorCode,
+  authentication_enable : AuthenticationEnable,
+}
+
+packet WriteAuthenticationEnable : SecurityCommand (op_code = WRITE_AUTHENTICATION_ENABLE) {
+  authentication_enable : AuthenticationEnable,
+}
+
+packet WriteAuthenticationEnableComplete : CommandComplete (command_op_code = WRITE_AUTHENTICATION_ENABLE) {
+  status : ErrorCode,
+}
+
+packet ReadClassOfDevice : DiscoveryCommand (op_code = READ_CLASS_OF_DEVICE) {
+}
+
+packet ReadClassOfDeviceComplete : CommandComplete (command_op_code = READ_CLASS_OF_DEVICE) {
+  status : ErrorCode,
+  class_of_device : ClassOfDevice,
+}
+
+packet WriteClassOfDevice : DiscoveryCommand (op_code = WRITE_CLASS_OF_DEVICE) {
+  class_of_device : ClassOfDevice,
+}
+
+packet WriteClassOfDeviceComplete : CommandComplete (command_op_code = WRITE_CLASS_OF_DEVICE) {
+  status : ErrorCode,
+}
+
+packet ReadVoiceSetting : CommandPacket (op_code = READ_VOICE_SETTING) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet WriteVoiceSetting : CommandPacket (op_code = WRITE_VOICE_SETTING) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet WriteVoiceSettingComplete : CommandComplete (command_op_code = WRITE_VOICE_SETTING) {
+  status : ErrorCode,
+}
+
+packet ReadAutomaticFlushTimeout : ConnectionManagementCommand (op_code = READ_AUTOMATIC_FLUSH_TIMEOUT) {
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+packet ReadAutomaticFlushTimeoutComplete : CommandComplete (command_op_code = READ_AUTOMATIC_FLUSH_TIMEOUT) {
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+  flush_timeout : 16,
+}
+
+packet WriteAutomaticFlushTimeout : ConnectionManagementCommand (op_code = WRITE_AUTOMATIC_FLUSH_TIMEOUT) {
+  connection_handle : 12,
+  _reserved_ : 4,
+  flush_timeout : 16, // 0x0000-0x07FF Default 0x0000 (No Automatic Flush)
+}
+
+packet WriteAutomaticFlushTimeoutComplete : CommandComplete (command_op_code = WRITE_AUTOMATIC_FLUSH_TIMEOUT) {
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+packet ReadNumBroadcastRetransmits : CommandPacket (op_code = READ_NUM_BROADCAST_RETRANSMITS) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet WriteNumBroadcastRetransmits : CommandPacket (op_code = WRITE_NUM_BROADCAST_RETRANSMITS) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet ReadHoldModeActivity : CommandPacket (op_code = READ_HOLD_MODE_ACTIVITY) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet WriteHoldModeActivity : CommandPacket (op_code = WRITE_HOLD_MODE_ACTIVITY) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+
+enum TransmitPowerLevelType : 8 {
+  CURRENT = 0x00,
+  MAXIMUM = 0x01,
+}
+
+packet ReadTransmitPowerLevel : ConnectionManagementCommand (op_code = READ_TRANSMIT_POWER_LEVEL) {
+  connection_handle : 12,
+  _reserved_ : 4,
+  type : TransmitPowerLevelType,
+
+}
+
+packet ReadTransmitPowerLevelComplete : CommandComplete (command_op_code = READ_TRANSMIT_POWER_LEVEL) {
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+  transmit_power_level : 8,
+}
+
+packet ReadSynchronousFlowControlEnable : CommandPacket (op_code = READ_SYNCHRONOUS_FLOW_CONTROL_ENABLE) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet WriteSynchronousFlowControlEnable : CommandPacket (op_code = WRITE_SYNCHRONOUS_FLOW_CONTROL_ENABLE) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet SetControllerToHostFlowControl : CommandPacket (op_code = SET_CONTROLLER_TO_HOST_FLOW_CONTROL) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet HostBufferSize : CommandPacket (op_code = HOST_BUFFER_SIZE) {
+  host_acl_data_packet_length : 16,
+  host_synchronous_data_packet_length : 8,
+  host_total_num_acl_data_packets : 16,
+  host_total_num_synchronous_data_packets : 16,
+}
+
+packet HostBufferSizeComplete : CommandComplete (command_op_code = HOST_BUFFER_SIZE) {
+  status : ErrorCode,
+}
+
+struct CompletedPackets {
+  connection_handle : 12,
+  _reserved_ : 4,
+  host_num_of_completed_packets : 16,
+}
+
+packet HostNumCompletedPackets : CommandPacket (op_code = HOST_NUM_COMPLETED_PACKETS) {
+  _count_(completed_packets) : 8,
+  completed_packets : CompletedPackets[],
+}
+
+packet HostNumCompletedPacketsError : CommandComplete (command_op_code = HOST_NUM_COMPLETED_PACKETS) {
+  error_code : ErrorCode,
+}
+
+packet ReadLinkSupervisionTimeout : ConnectionManagementCommand (op_code = READ_LINK_SUPERVISION_TIMEOUT) {
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+packet ReadLinkSupervisionTimeoutComplete : CommandComplete (command_op_code = READ_LINK_SUPERVISION_TIMEOUT) {
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+  link_supervision_timeout : 16, // 0x001-0xFFFF (0.625ms-40.9s)
+}
+
+packet WriteLinkSupervisionTimeout : ConnectionManagementCommand (op_code = WRITE_LINK_SUPERVISION_TIMEOUT) {
+  handle : 12,
+  _reserved_ : 4,
+  link_supervision_timeout : 16, // 0x001-0xFFFF (0.625ms-40.9s)
+}
+
+packet WriteLinkSupervisionTimeoutComplete : CommandComplete (command_op_code = WRITE_LINK_SUPERVISION_TIMEOUT) {
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+packet ReadNumberOfSupportedIac : DiscoveryCommand (op_code = READ_NUMBER_OF_SUPPORTED_IAC) {
+}
+
+packet ReadNumberOfSupportedIacComplete : CommandComplete (command_op_code = READ_NUMBER_OF_SUPPORTED_IAC) {
+  status : ErrorCode,
+  num_support_iac : 8,
+}
+
+packet ReadCurrentIacLap : DiscoveryCommand (op_code = READ_CURRENT_IAC_LAP) {
+}
+
+packet ReadCurrentIacLapComplete : CommandComplete (command_op_code = READ_CURRENT_IAC_LAP) {
+  status : ErrorCode,
+  _count_(laps_to_read) : 8,
+  laps_to_read : Lap[],
+}
+
+packet WriteCurrentIacLap : DiscoveryCommand (op_code = WRITE_CURRENT_IAC_LAP) {
+  _count_(laps_to_write) : 8,
+  laps_to_write : Lap[],
+}
+
+packet WriteCurrentIacLapComplete : CommandComplete (command_op_code = WRITE_CURRENT_IAC_LAP) {
+  status : ErrorCode,
+}
+
+packet SetAfhHostChannelClassification : CommandPacket (op_code = SET_AFH_HOST_CHANNEL_CLASSIFICATION) {
+  afh_host_channel_classification : 8[10],
+}
+
+packet SetAfhHostChannelClassificationComplete : CommandComplete (command_op_code = SET_AFH_HOST_CHANNEL_CLASSIFICATION) {
+  status : ErrorCode,
+}
+
+enum InquiryScanType : 8 {
+  STANDARD = 0x00,
+  INTERLACED = 0x01,
+}
+
+packet ReadInquiryScanType : DiscoveryCommand (op_code = READ_INQUIRY_SCAN_TYPE) {
+}
+
+packet ReadInquiryScanTypeComplete : CommandComplete (command_op_code = READ_INQUIRY_SCAN_TYPE) {
+  status : ErrorCode,
+  inquiry_scan_type : InquiryScanType,
+}
+
+packet WriteInquiryScanType : DiscoveryCommand (op_code = WRITE_INQUIRY_SCAN_TYPE) {
+  inquiry_scan_type : InquiryScanType,
+}
+
+packet WriteInquiryScanTypeComplete : CommandComplete (command_op_code = WRITE_INQUIRY_SCAN_TYPE) {
+  status : ErrorCode,
+}
+
+enum InquiryMode : 8 {
+  STANDARD = 0x00,
+  RSSI = 0x01,
+  RSSI_OR_EXTENDED = 0x02,
+}
+
+packet ReadInquiryMode : DiscoveryCommand (op_code = READ_INQUIRY_MODE) {
+}
+
+packet ReadInquiryModeComplete : CommandComplete (command_op_code = READ_INQUIRY_MODE) {
+  status : ErrorCode,
+  inquiry_mode : InquiryMode,
+}
+
+packet WriteInquiryMode : DiscoveryCommand (op_code = WRITE_INQUIRY_MODE) {
+  inquiry_mode : InquiryMode,
+}
+
+packet WriteInquiryModeComplete : CommandComplete (command_op_code = WRITE_INQUIRY_MODE) {
+  status : ErrorCode,
+}
+
+enum PageScanType : 8 {
+  STANDARD = 0x00,
+  INTERLACED = 0x01,
+}
+
+packet ReadPageScanType : DiscoveryCommand (op_code = READ_PAGE_SCAN_TYPE) {
+}
+
+packet ReadPageScanTypeComplete : CommandComplete (command_op_code = READ_PAGE_SCAN_TYPE) {
+  status : ErrorCode,
+  page_scan_type : PageScanType,
+}
+
+packet WritePageScanType : DiscoveryCommand (op_code = WRITE_PAGE_SCAN_TYPE) {
+  page_scan_type : PageScanType,
+}
+
+packet WritePageScanTypeComplete : CommandComplete (command_op_code = WRITE_PAGE_SCAN_TYPE) {
+  status : ErrorCode,
+}
+
+packet ReadAfhChannelAssessmentMode : CommandPacket (op_code = READ_AFH_CHANNEL_ASSESSMENT_MODE) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet WriteAfhChannelAssessmentMode : CommandPacket (op_code = WRITE_AFH_CHANNEL_ASSESSMENT_MODE) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+enum FecRequired : 8 {
+  NOT_REQUIRED = 0x00,
+  REQUIRED = 0x01,
+}
+
+packet ReadExtendedInquiryResponse : CommandPacket (op_code = READ_EXTENDED_INQUIRY_RESPONSE) {
+}
+
+packet ReadExtendedInquiryResponseComplete : CommandComplete (command_op_code = READ_EXTENDED_INQUIRY_RESPONSE) {
+  status : ErrorCode,
+  fec_required : FecRequired,
+  extended_inquiry_response : GapData[],
+}
+
+packet WriteExtendedInquiryResponse : CommandPacket (op_code = WRITE_EXTENDED_INQUIRY_RESPONSE) {
+  fec_required : FecRequired,
+  extended_inquiry_response : GapData[],
+  _padding_[244], // Zero padding to be 240 octets (GapData[]) + 2 (opcode) + 1 (size) + 1 (FecRequired)
+}
+
+packet WriteExtendedInquiryResponseComplete : CommandComplete (command_op_code = WRITE_EXTENDED_INQUIRY_RESPONSE) {
+  status : ErrorCode,
+}
+
+packet RefreshEncryptionKey : SecurityCommand (op_code = REFRESH_ENCRYPTION_KEY) {
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+packet RefreshEncryptionKeyStatus : CommandStatus (command_op_code = REFRESH_ENCRYPTION_KEY) {
+}
+
+packet ReadSimplePairingMode : SecurityCommand (op_code = READ_SIMPLE_PAIRING_MODE) {
+}
+
+packet ReadSimplePairingModeComplete : CommandComplete (command_op_code = READ_SIMPLE_PAIRING_MODE) {
+  status : ErrorCode,
+  simple_pairing_mode : Enable,
+}
+
+packet WriteSimplePairingMode : SecurityCommand (op_code = WRITE_SIMPLE_PAIRING_MODE) {
+  simple_pairing_mode : Enable,
+}
+
+packet WriteSimplePairingModeComplete : CommandComplete (command_op_code = WRITE_SIMPLE_PAIRING_MODE) {
+  status : ErrorCode,
+}
+
+packet ReadLocalOobData : SecurityCommand (op_code = READ_LOCAL_OOB_DATA) {
+}
+
+packet ReadLocalOobDataComplete : CommandComplete (command_op_code = READ_LOCAL_OOB_DATA) {
+  status : ErrorCode,
+  c : 8[16],
+  r : 8[16],
+}
+
+packet ReadInquiryResponseTransmitPowerLevel : DiscoveryCommand (op_code = READ_INQUIRY_RESPONSE_TRANSMIT_POWER_LEVEL) {
+}
+
+packet ReadInquiryResponseTransmitPowerLevelComplete : CommandComplete (command_op_code = READ_INQUIRY_RESPONSE_TRANSMIT_POWER_LEVEL) {
+  status : ErrorCode,
+  tx_power : 8, // (-70dBm to 20dBm)
+}
+
+packet WriteInquiryTransmitPowerLevel : DiscoveryCommand (op_code = WRITE_INQUIRY_TRANSMIT_POWER_LEVEL) {
+  tx_power : 8,
+}
+
+packet WriteInquiryResponseTransmitPowerLevelComplete : CommandComplete (command_op_code = WRITE_INQUIRY_TRANSMIT_POWER_LEVEL) {
+  status : ErrorCode,
+}
+
+enum KeypressNotificationType : 8 {
+  ENTRY_STARTED = 0,
+  DIGIT_ENTERED = 1,
+  DIGIT_ERASED = 2,
+  CLEARED = 3,
+  ENTRY_COMPLETED = 4,
+}
+
+packet SendKeypressNotification : SecurityCommand (op_code = SEND_KEYPRESS_NOTIFICATION) {
+  bd_addr : Address,
+  notification_type : KeypressNotificationType,
+}
+
+packet SendKeypressNotificationComplete : CommandComplete (command_op_code = SEND_KEYPRESS_NOTIFICATION) {
+  status : ErrorCode,
+  bd_addr : Address,
+}
+
+enum SimultaneousLeHost : 8 {
+  DISABLED = 0x00,
+}
+
+packet ReadLeHostSupport : CommandPacket (op_code = READ_LE_HOST_SUPPORT) {
+}
+
+packet ReadLeHostSupportComplete : CommandComplete (command_op_code = READ_LE_HOST_SUPPORT) {
+  status : ErrorCode,
+  le_supported_host : Enable,
+  simultaneous_le_host : SimultaneousLeHost,
+}
+
+packet WriteLeHostSupport : CommandPacket (op_code = WRITE_LE_HOST_SUPPORT) {
+  le_supported_host : Enable,
+  simultaneous_le_host : SimultaneousLeHost,
+}
+
+packet WriteLeHostSupportComplete : CommandComplete (command_op_code = WRITE_LE_HOST_SUPPORT) {
+  status : ErrorCode,
+}
+
+packet ReadSecureConnectionsHostSupport : CommandPacket (op_code = READ_SECURE_CONNECTIONS_HOST_SUPPORT) {
+}
+
+packet ReadSecureConnectionsHostSupportComplete : CommandComplete (command_op_code = READ_SECURE_CONNECTIONS_HOST_SUPPORT) {
+  status : ErrorCode,
+  secure_connections_host_support : Enable,
+}
+
+packet WriteSecureConnectionsHostSupport : SecurityCommand (op_code = WRITE_SECURE_CONNECTIONS_HOST_SUPPORT) {
+  secure_connections_host_support : Enable,
+}
+
+packet WriteSecureConnectionsHostSupportComplete : CommandComplete (command_op_code = WRITE_SECURE_CONNECTIONS_HOST_SUPPORT) {
+  status : ErrorCode,
+}
+
+packet ReadLocalOobExtendedData : SecurityCommand (op_code = READ_LOCAL_OOB_EXTENDED_DATA) {
+}
+
+packet ReadLocalOobExtendedDataComplete : CommandComplete (command_op_code = READ_LOCAL_OOB_EXTENDED_DATA) {
+  status : ErrorCode,
+  c_192 : 8[16],
+  r_192 : 8[16],
+  c_256 : 8[16],
+  r_256 : 8[16],
+}
+
+
+  // INFORMATIONAL_PARAMETERS
+packet ReadLocalVersionInformation : CommandPacket (op_code = READ_LOCAL_VERSION_INFORMATION) {
+}
+
+enum HciVersion : 8 {
+  V_1_0B = 0x00,
+  V_1_1 = 0x01,
+  V_1_2 = 0x02,
+  V_2_0 = 0x03, //  + EDR
+  V_2_1 = 0x04, //  + EDR
+  V_3_0 = 0x05, //  + HS
+  V_4_0 = 0x06,
+  V_4_1 = 0x07,
+  V_4_2 = 0x08,
+  V_5_0 = 0x09,
+  V_5_1 = 0x0a,
+}
+
+enum LmpVersion : 8 {
+  V_1_0B = 0x00, // withdrawn
+  V_1_1 = 0x01, // withdrawn
+  V_1_2 = 0x02, // withdrawn
+  V_2_0 = 0x03, //  + EDR
+  V_2_1 = 0x04, //  + EDR
+  V_3_0 = 0x05, //  + HS
+  V_4_0 = 0x06,
+  V_4_1 = 0x07,
+  V_4_2 = 0x08,
+  V_5_0 = 0x09,
+  V_5_1 = 0x0a,
+}
+
+struct LocalVersionInformation {
+  hci_version : HciVersion,
+  hci_revision : 16,
+  lmp_version : LmpVersion,
+  manufacturer_name : 16,
+  lmp_subversion : 16,
+}
+
+packet ReadLocalVersionInformationComplete : CommandComplete (command_op_code = READ_LOCAL_VERSION_INFORMATION) {
+  status : ErrorCode,
+  local_version_information : LocalVersionInformation,
+}
+
+packet ReadLocalSupportedCommands : CommandPacket (op_code = READ_LOCAL_SUPPORTED_COMMANDS) {
+}
+
+packet ReadLocalSupportedCommandsComplete : CommandComplete (command_op_code = READ_LOCAL_SUPPORTED_COMMANDS) {
+  status : ErrorCode,
+  supported_commands : 8[64],
+}
+
+packet ReadLocalSupportedFeatures : CommandPacket (op_code = READ_LOCAL_SUPPORTED_FEATURES) {
+}
+
+packet ReadLocalSupportedFeaturesComplete : CommandComplete (command_op_code = READ_LOCAL_SUPPORTED_FEATURES) {
+  status : ErrorCode,
+  lmp_features : 64,
+}
+
+packet ReadLocalExtendedFeatures : CommandPacket (op_code = READ_LOCAL_EXTENDED_FEATURES) {
+  page_number : 8,
+}
+
+packet ReadLocalExtendedFeaturesComplete : CommandComplete (command_op_code = READ_LOCAL_EXTENDED_FEATURES) {
+  status : ErrorCode,
+  page_number : 8,
+  maximum_page_number : 8,
+  extended_lmp_features : 64,
+}
+
+packet ReadBufferSize : CommandPacket (op_code = READ_BUFFER_SIZE) {
+}
+
+packet ReadBufferSizeComplete : CommandComplete (command_op_code = READ_BUFFER_SIZE) {
+  status : ErrorCode,
+  acl_data_packet_length : 16,
+  synchronous_data_packet_length : 8,
+  total_num_acl_data_packets : 16,
+  total_num_synchronous_data_packets : 16,
+}
+
+packet ReadBdAddr : CommandPacket (op_code = READ_BD_ADDR) {
+}
+
+packet ReadBdAddrComplete : CommandComplete (command_op_code = READ_BD_ADDR) {
+  status : ErrorCode,
+  bd_addr : Address,
+}
+
+packet ReadDataBlockSize : CommandPacket (op_code = READ_DATA_BLOCK_SIZE) {
+}
+
+packet ReadLocalSupportedCodecs : CommandPacket (op_code = READ_LOCAL_SUPPORTED_CODECS) {
+}
+
+packet ReadLocalSupportedCodecsComplete : CommandComplete (command_op_code = READ_LOCAL_SUPPORTED_CODECS) {
+  status : ErrorCode,
+  _size_(supported_codecs) : 8,
+  supported_codecs : 8[],
+  _size_(vendor_specific_codecs) : 8,
+  vendor_specific_codecs : 32[],
+}
+
+  // STATUS_PARAMETERS
+packet ReadFailedContactCounter : ConnectionManagementCommand (op_code = READ_FAILED_CONTACT_COUNTER) {
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+packet ReadFailedContactCounterComplete : CommandComplete (command_op_code = READ_FAILED_CONTACT_COUNTER) {
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+  failed_contact_counter : 16,
+}
+
+packet ResetFailedContactCounter : ConnectionManagementCommand (op_code = RESET_FAILED_CONTACT_COUNTER) {
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+packet ResetFailedContactCounterComplete : CommandComplete (command_op_code = RESET_FAILED_CONTACT_COUNTER) {
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+packet ReadLinkQuality : ConnectionManagementCommand (op_code = READ_LINK_QUALITY) {
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+packet ReadLinkQualityComplete : CommandComplete (command_op_code = READ_LINK_QUALITY) {
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+  link_quality : 8,
+}
+
+packet ReadRssi : ConnectionManagementCommand (op_code = READ_RSSI) {
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+packet ReadRssiComplete : CommandComplete (command_op_code = READ_RSSI) {
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+  rssi : 8,
+}
+
+packet ReadAfhChannelMap : ConnectionManagementCommand (op_code = READ_AFH_CHANNEL_MAP) {
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+enum AfhMode : 8 {
+  AFH_DISABLED = 0x00,
+  AFH_ENABLED = 0x01,
+}
+
+packet ReadAfhChannelMapComplete : CommandComplete (command_op_code = READ_AFH_CHANNEL_MAP) {
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+  afh_mode : AfhMode,
+  afh_channel_map : 8[10],
+}
+
+
+enum WhichClock : 8 {
+  LOCAL = 0x00,
+  PICONET = 0x01,
+}
+
+packet ReadClock : ConnectionManagementCommand (op_code = READ_CLOCK) {
+  connection_handle : 12,
+  _reserved_ : 4,
+  which_clock : WhichClock,
+}
+
+packet ReadClockComplete : CommandComplete (command_op_code = READ_CLOCK) {
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+  clock : 28,
+  _reserved_ : 4,
+  accuracy : 16,
+}
+
+packet ReadEncryptionKeySize : SecurityCommand (op_code = READ_ENCRYPTION_KEY_SIZE) {
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+packet ReadEncryptionKeySizeComplete : CommandComplete (command_op_code = READ_ENCRYPTION_KEY_SIZE) {
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+  key_size : 8,
+}
+
+  // TESTING
+enum LoopbackMode : 8 {
+  NO_LOOPBACK = 0x00,
+  ENABLE_LOCAL = 0x01,
+  ENABLE_REMOTE = 0x02,
+}
+
+packet ReadLoopbackMode : CommandPacket (op_code = READ_LOOPBACK_MODE) {
+}
+
+packet ReadLoopbackModeComplete : CommandComplete (command_op_code = READ_LOOPBACK_MODE) {
+  status : ErrorCode,
+  loopback_mode : LoopbackMode,
+}
+
+packet WriteLoopbackMode : CommandPacket (op_code = WRITE_LOOPBACK_MODE) {
+  loopback_mode : LoopbackMode,
+}
+
+packet WriteLoopbackModeComplete : CommandComplete (command_op_code = WRITE_LOOPBACK_MODE) {
+  status : ErrorCode,
+}
+
+packet EnableDeviceUnderTestMode : CommandPacket (op_code = ENABLE_DEVICE_UNDER_TEST_MODE) {
+}
+
+packet EnableDeviceUnderTestModeComplete : CommandComplete (command_op_code = ENABLE_DEVICE_UNDER_TEST_MODE) {
+  status : ErrorCode,
+}
+
+packet WriteSimplePairingDebugMode : SecurityCommand (op_code = WRITE_SIMPLE_PAIRING_DEBUG_MODE) {
+  simple_pairing_debug_mode : Enable,
+}
+
+packet WriteSimplePairingDebugModeComplete : CommandComplete (command_op_code = WRITE_SIMPLE_PAIRING_DEBUG_MODE) {
+  status : ErrorCode,
+}
+
+packet WriteSecureConnectionsTestMode : CommandPacket (op_code = WRITE_SECURE_CONNECTIONS_TEST_MODE) {
+  connection_handle : 12,
+  _reserved_ : 4,
+  dm1_aclu_mode : Enable,
+  esco_loopback_mode : Enable,
+}
+
+packet WriteSecureConnectionsTestModeComplete : CommandComplete (command_op_code = WRITE_SECURE_CONNECTIONS_TEST_MODE) {
+  status : ErrorCode,
+}
+
+  // LE_CONTROLLER
+packet LeSetEventMask : CommandPacket (op_code = LE_SET_EVENT_MASK) {
+  le_event_mask : 64,
+}
+
+packet LeSetEventMaskComplete : CommandComplete (command_op_code = LE_SET_EVENT_MASK) {
+  status : ErrorCode,
+}
+
+packet LeReadBufferSize : CommandPacket (op_code = LE_READ_BUFFER_SIZE) {
+}
+
+struct LeBufferSize {
+  le_data_packet_length : 16,
+  total_num_le_packets : 8,
+}
+
+packet LeReadBufferSizeComplete : CommandComplete (command_op_code = LE_READ_BUFFER_SIZE) {
+  status : ErrorCode,
+  le_buffer_size : LeBufferSize,
+}
+
+packet LeReadLocalSupportedFeatures : CommandPacket (op_code = LE_READ_LOCAL_SUPPORTED_FEATURES) {
+}
+
+packet LeReadLocalSupportedFeaturesComplete : CommandComplete (command_op_code = LE_READ_LOCAL_SUPPORTED_FEATURES) {
+  status : ErrorCode,
+  le_features : 64,
+}
+
+packet LeSetRandomAddress : LeAdvertisingCommand (op_code = LE_SET_RANDOM_ADDRESS) {
+  random_address : Address,
+}
+
+packet LeSetRandomAddressComplete : CommandComplete (command_op_code = LE_SET_RANDOM_ADDRESS) {
+  status : ErrorCode,
+}
+
+enum AdvertisingFilterPolicy : 2 {
+  ALL_DEVICES = 0, // Default
+  WHITELISTED_SCAN = 1,
+  WHITELISTED_CONNECT = 2,
+  WHITELISTED_SCAN_AND_CONNECT = 3,
+}
+
+enum PeerAddressType : 8 {
+  PUBLIC_DEVICE_OR_IDENTITY_ADDRESS = 0x00,
+  RANDOM_DEVICE_OR_IDENTITY_ADDRESS = 0x01,
+}
+
+enum AdvertisingEventType : 8 {
+  ADV_IND = 0x00,
+  ADV_DIRECT_IND = 0x01,
+  ADV_SCAN_IND = 0x02,
+  ADV_NONCONN_IND = 0x03,
+  ADV_DIRECT_IND_LOW = 0x04,
+}
+
+enum AddressType : 8 {
+  PUBLIC_DEVICE_ADDRESS = 0x00,
+  RANDOM_DEVICE_ADDRESS = 0x01,
+  PUBLIC_IDENTITY_ADDRESS = 0x02,
+  RANDOM_IDENTITY_ADDRESS = 0x03,
+}
+
+packet LeSetAdvertisingParameters : LeAdvertisingCommand (op_code = LE_SET_ADVERTISING_PARAMETERS) {
+  interval_min : 16,
+  interval_max : 16,
+  type : AdvertisingEventType,
+  own_address_type : AddressType,
+  peer_address_type : PeerAddressType,
+  peer_address : Address,
+  channel_map : 8,
+  filter_policy : AdvertisingFilterPolicy,
+  _reserved_ : 6,
+}
+
+packet LeSetAdvertisingParametersComplete : CommandComplete (command_op_code = LE_SET_ADVERTISING_PARAMETERS) {
+  status : ErrorCode,
+}
+
+packet LeReadAdvertisingChannelTxPower : LeAdvertisingCommand (op_code = LE_READ_ADVERTISING_CHANNEL_TX_POWER) {
+}
+
+packet LeReadAdvertisingChannelTxPowerComplete : CommandComplete (command_op_code = LE_READ_ADVERTISING_CHANNEL_TX_POWER) {
+  status : ErrorCode,
+  transmit_power_level : 8, // (-20dBm to 10dBm) Accuracy: +/-4dB
+}
+
+packet LeSetAdvertisingData : LeAdvertisingCommand (op_code = LE_SET_ADVERTISING_DATA) {
+  _size_(advertising_data) : 8,
+  advertising_data : GapData[],
+  _padding_[35], // Zero padding to 31 bytes of advertising_data + 1 size + 2 opcode + 1 total_size
+}
+
+packet LeSetAdvertisingDataComplete : CommandComplete (command_op_code = LE_SET_ADVERTISING_DATA) {
+  status : ErrorCode,
+}
+
+packet LeSetScanResponseData : LeAdvertisingCommand (op_code = LE_SET_SCAN_RESPONSE_DATA) {
+  _size_(advertising_data) : 8,
+  advertising_data : GapData[],
+  _padding_[35], // Zero padding to 31 bytes of advertising_data + 1 size + 2 opcode + 1 total_size
+}
+
+packet LeSetScanResponseDataComplete : CommandComplete (command_op_code = LE_SET_SCAN_RESPONSE_DATA) {
+  status : ErrorCode,
+}
+
+packet LeSetAdvertisingEnable : LeAdvertisingCommand (op_code = LE_SET_ADVERTISING_ENABLE) {
+  advertising_enable : Enable, // Default DISABLED
+}
+
+packet LeSetAdvertisingEnableComplete : CommandComplete (command_op_code = LE_SET_ADVERTISING_ENABLE) {
+  status : ErrorCode,
+}
+
+enum LeScanType : 8 {
+  PASSIVE = 0x00, // Default
+  ACTIVE = 0x01,
+}
+
+enum LeSetScanningFilterPolicy : 8 {
+  ACCEPT_ALL = 0x00, // Default
+  WHITE_LIST_ONLY = 0x01,
+  CHECK_INITIATORS_IDENTITY = 0x02,
+  WHITE_LIST_AND_INITIATORS_IDENTITY = 0x03,
+}
+
+packet LeSetScanParameters : LeScanningCommand (op_code = LE_SET_SCAN_PARAMETERS) {
+  le_scan_type : LeScanType,
+  le_scan_interval : 16, // 0x0004-0x4000 Default 0x10 (10ms)
+  le_scan_window : 16, // Default 0x10 (10ms)
+  own_address_type : AddressType,
+  scanning_filter_policy : LeSetScanningFilterPolicy,
+}
+
+packet LeSetScanParametersComplete : CommandComplete (command_op_code = LE_SET_SCAN_PARAMETERS) {
+  status : ErrorCode,
+}
+
+packet LeSetScanEnable : LeScanningCommand (op_code = LE_SET_SCAN_ENABLE) {
+  le_scan_enable : Enable,
+  filter_duplicates : Enable,
+}
+
+packet LeSetScanEnableComplete : CommandComplete (command_op_code = LE_SET_SCAN_ENABLE) {
+  status : ErrorCode,
+}
+
+enum InitiatorFilterPolicy : 8 {
+  USE_PEER_ADDRESS = 0x00,
+  USE_WHITE_LIST = 0x01,
+}
+
+enum OwnAddressType : 8 {
+  PUBLIC_DEVICE_ADDRESS = 0x00,
+  RANDOM_DEVICE_ADDRESS = 0x01,
+  RESOLVABLE_OR_PUBLIC_ADDRESS = 0x02,
+  RESOLVABLE_OR_RANDOM_ADDRESS = 0x03,
+}
+
+packet LeCreateConnection : LeConnectionManagementCommand (op_code = LE_CREATE_CONNECTION) {
+  le_scan_interval : 16, // 0x0004-0x4000
+  le_scan_window : 16, // < = LeScanInterval
+  initiator_filter_policy : InitiatorFilterPolicy,
+  peer_address_type : AddressType,
+  peer_address : Address,
+  own_address_type : OwnAddressType,
+  conn_interval_min : 16, // 0x0006-0x0C80 (7.5ms to 4s)
+  conn_interval_max : 16, // 0x0006-0x0C80 (7.5ms to 4s)
+  conn_latency : 16, // 0x0006-0x01F3
+  supervision_timeout : 16, // 0x00A to 0x0C80 (100ms to 32s)
+  minimum_ce_length : 16, // 0.625ms
+  maximum_ce_length : 16, // 0.625ms
+}
+
+packet LeCreateConnectionStatus : CommandStatus (command_op_code = LE_CREATE_CONNECTION) {
+}
+
+packet LeCreateConnectionCancel : LeConnectionManagementCommand (op_code = LE_CREATE_CONNECTION_CANCEL) {
+}
+
+packet LeCreateConnectionCancelStatus : CommandStatus (command_op_code = LE_CREATE_CONNECTION_CANCEL) {
+}
+
+packet LeCreateConnectionCancelComplete : CommandComplete (command_op_code = LE_CREATE_CONNECTION_CANCEL) {
+  status : ErrorCode,
+}
+
+packet LeReadWhiteListSize : LeConnectionManagementCommand (op_code = LE_READ_WHITE_LIST_SIZE) {
+}
+
+packet LeReadWhiteListSizeComplete : CommandComplete (command_op_code = LE_READ_WHITE_LIST_SIZE) {
+  status : ErrorCode,
+  white_list_size : 8,
+}
+
+packet LeClearWhiteList : LeConnectionManagementCommand (op_code = LE_CLEAR_WHITE_LIST) {
+}
+
+packet LeClearWhiteListComplete : CommandComplete (command_op_code = LE_CLEAR_WHITE_LIST) {
+  status : ErrorCode,
+}
+
+enum WhiteListAddressType : 8 {
+  PUBLIC = 0x00,
+  RANDOM = 0x01,
+  ANONYMOUS_ADVERTISERS = 0xFF,
+}
+
+packet LeAddDeviceToWhiteList : LeConnectionManagementCommand (op_code = LE_ADD_DEVICE_TO_WHITE_LIST) {
+  address_type : WhiteListAddressType,
+  address : Address,
+}
+
+packet LeAddDeviceToWhiteListComplete : CommandComplete (command_op_code = LE_ADD_DEVICE_TO_WHITE_LIST) {
+  status : ErrorCode,
+}
+
+packet LeRemoveDeviceFromWhiteList : LeConnectionManagementCommand (op_code = LE_REMOVE_DEVICE_FROM_WHITE_LIST) {
+  address_type : WhiteListAddressType,
+  address : Address,
+}
+
+packet LeRemoveDeviceFromWhiteListComplete : CommandComplete (command_op_code = LE_REMOVE_DEVICE_FROM_WHITE_LIST) {
+  status : ErrorCode,
+}
+
+packet LeConnectionUpdate : LeConnectionManagementCommand (op_code = LE_CONNECTION_UPDATE) {
+  connection_handle : 12,
+  _reserved_ : 4,
+  conn_interval_min : 16, // 0x0006-0x0C80 (7.5ms to 4s)
+  conn_interval_max : 16, // 0x0006-0x0C80 (7.5ms to 4s)
+  conn_latency : 16, // 0x0006-0x01F3
+  supervision_timeout : 16, // 0x00A to 0x0C80 (100ms to 32s)
+  minimum_ce_length : 16, // 0.625ms
+  maximum_ce_length : 16, // 0.625ms
+}
+
+packet LeConnectionUpdateStatus : CommandStatus (command_op_code = LE_CONNECTION_UPDATE) {
+}
+
+packet LeSetHostChannelClassification : LeConnectionManagementCommand (op_code = LE_SET_HOST_CHANNEL_CLASSIFICATION) {
+  channel_map : 8[5],
+}
+
+packet LeSetHostChannelClassificationComplete : CommandComplete (command_op_code = LE_SET_HOST_CHANNEL_CLASSIFICATION) {
+  status : ErrorCode,
+}
+
+packet LeReadChannelMap : LeConnectionManagementCommand (op_code = LE_READ_CHANNEL_MAP) {
+}
+
+packet LeReadChannelMapComplete : CommandComplete (command_op_code = LE_SET_HOST_CHANNEL_CLASSIFICATION) {
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+  channel_map : 8[5],
+}
+
+packet LeReadRemoteFeatures : LeConnectionManagementCommand (op_code = LE_READ_REMOTE_FEATURES) {
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+packet LeEncrypt : LeSecurityCommand (op_code = LE_ENCRYPT) {
+  key : 8[16],
+  plaintext_data : 8[16],
+}
+
+packet LeEncryptComplete : CommandComplete (command_op_code = LE_ENCRYPT) {
+  status : ErrorCode,
+  encrypted_data : 8[16],
+}
+
+packet LeRand : LeSecurityCommand (op_code = LE_RAND) {
+}
+
+packet LeRandComplete : CommandComplete (command_op_code = LE_RAND) {
+  status : ErrorCode,
+  random_number : 64,
+}
+
+packet LeStartEncryption : LeSecurityCommand (op_code = LE_START_ENCRYPTION) {
+  connection_handle: 16,
+  rand: 8[8],
+  ediv: 16,
+  ltk: 8[16],
+}
+
+packet LeStartEncryptionStatus : CommandStatus (command_op_code = LE_START_ENCRYPTION) {
+}
+
+packet LeLongTermKeyRequestReply : LeSecurityCommand (op_code = LE_LONG_TERM_KEY_REQUEST_REPLY) {
+  connection_handle: 16,
+  long_term_key: 8[16],
+}
+
+packet LeLongTermKeyRequestReplyComplete : CommandComplete (command_op_code = LE_LONG_TERM_KEY_REQUEST_REPLY) {
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+packet LeLongTermKeyRequestNegativeReply : LeSecurityCommand (op_code = LE_LONG_TERM_KEY_REQUEST_NEGATIVE_REPLY) {
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+packet LeLongTermKeyRequestNegativeReplyComplete : CommandComplete (command_op_code = LE_LONG_TERM_KEY_REQUEST_NEGATIVE_REPLY) {
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+packet LeReadSupportedStates : CommandPacket (op_code = LE_READ_SUPPORTED_STATES) {
+}
+
+packet LeReadSupportedStatesComplete : CommandComplete (command_op_code = LE_READ_SUPPORTED_STATES) {
+  status : ErrorCode,
+  le_states : 64,
+}
+
+packet LeReceiverTest : CommandPacket (op_code = LE_RECEIVER_TEST) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeTransmitterTest : CommandPacket (op_code = LE_TRANSMITTER_TEST) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeTestEnd : CommandPacket (op_code = LE_TEST_END) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeRemoteConnectionParameterRequestReply : LeConnectionManagementCommand (op_code = LE_REMOTE_CONNECTION_PARAMETER_REQUEST_REPLY) {
+  connection_handle : 12,
+  _reserved_ : 4,
+  interval_min : 16, // 0x0006-0x0C80 (7.5ms to 4s)
+  interval_max : 16, // 0x0006-0x0C80 (7.5ms to 4s)
+  latency : 16, // 0x0006-0x01F3
+  timeout : 16, // 0x00A to 0x0C80 (100ms to 32s)
+  minimum_ce_length : 16, // 0.625ms
+  maximum_ce_length : 16, // 0.625ms
+}
+
+packet LeRemoteConnectionParameterRequestReplyComplete : CommandComplete (command_op_code = LE_REMOTE_CONNECTION_PARAMETER_REQUEST_REPLY) {
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+packet LeRemoteConnectionParameterRequestNegativeReply : LeConnectionManagementCommand (op_code = LE_REMOTE_CONNECTION_PARAMETER_REQUEST_NEGATIVE_REPLY) {
+  connection_handle : 12,
+  _reserved_ : 4,
+  reason : ErrorCode,
+}
+
+packet LeRemoteConnectionParameterRequestNegativeReplyComplete : CommandComplete (command_op_code = LE_REMOTE_CONNECTION_PARAMETER_REQUEST_NEGATIVE_REPLY) {
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+packet LeSetDataLength : LeConnectionManagementCommand (op_code = LE_SET_DATA_LENGTH) {
+  connection_handle : 12,
+  _reserved_ : 4,
+  tx_octets : 16, // payload octets per single PDU 0x1B to 0x00FB
+  tx_time : 16, // microseconds used to transmit a single PDU 0x0148 to 0x4290
+}
+
+packet LeSetDataLengthComplete : CommandComplete (command_op_code = LE_SET_DATA_LENGTH) {
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+packet LeReadSuggestedDefaultDataLength : LeConnectionManagementCommand (op_code = LE_READ_SUGGESTED_DEFAULT_DATA_LENGTH) {
+}
+
+packet LeReadSuggestedDefaultDataLengthComplete : CommandComplete (command_op_code = LE_READ_SUGGESTED_DEFAULT_DATA_LENGTH) {
+  status : ErrorCode,
+  tx_octets : 16, // payload octets per single PDU 0x1B to 0x00FB
+  tx_time : 16, // microseconds used to transmit a single PDU 0x0148 to 0x4290
+}
+
+packet LeWriteSuggestedDefaultDataLength : LeConnectionManagementCommand (op_code = LE_WRITE_SUGGESTED_DEFAULT_DATA_LENGTH) {
+  tx_octets : 16, // payload octets per single PDU 0x1B to 0x00FB
+  tx_time : 16, // microseconds used to transmit a single PDU 0x0148 to 0x4290
+}
+
+packet LeWriteSuggestedDefaultDataLengthComplete : CommandComplete (command_op_code = LE_WRITE_SUGGESTED_DEFAULT_DATA_LENGTH) {
+  status : ErrorCode,
+}
+
+packet LeReadLocalP256PublicKeyCommand : LeSecurityCommand (op_code = LE_READ_LOCAL_P_256_PUBLIC_KEY_COMMAND) {
+}
+
+packet LeGenerateDhkeyV1Command : LeSecurityCommand (op_code = LE_GENERATE_DHKEY_COMMAND_V1) {
+  remote_p_256_public_key : 8[64],
+}
+
+packet LeGenerateDhkeyV1CommandStatus : CommandStatus (command_op_code = LE_GENERATE_DHKEY_COMMAND_V1) {
+}
+
+packet LeAddDeviceToResolvingList : LeSecurityCommand (op_code = LE_ADD_DEVICE_TO_RESOLVING_LIST) {
+  peer_identity_address_type : PeerAddressType,
+  peer_identity_address : Address,
+  peer_irk : 8[16],
+  local_irk : 8[16],
+}
+
+packet LeAddDeviceToResolvingListComplete : CommandComplete (command_op_code = LE_ADD_DEVICE_TO_RESOLVING_LIST) {
+  status : ErrorCode,
+}
+
+packet LeRemoveDeviceFromResolvingList : LeSecurityCommand (op_code = LE_REMOVE_DEVICE_FROM_RESOLVING_LIST) {
+  peer_identity_address_type : PeerAddressType,
+  peer_identity_address : Address,
+}
+
+packet LeRemoveDeviceFromResolvingListComplete : CommandComplete (command_op_code = LE_REMOVE_DEVICE_FROM_RESOLVING_LIST) {
+  status : ErrorCode,
+}
+
+packet LeClearResolvingList : LeSecurityCommand (op_code = LE_CLEAR_RESOLVING_LIST) {
+}
+
+packet LeClearResolvingListComplete : CommandComplete (command_op_code = LE_CLEAR_RESOLVING_LIST) {
+  status : ErrorCode,
+}
+
+packet LeReadResolvingListSize : LeSecurityCommand (op_code = LE_READ_RESOLVING_LIST_SIZE) {
+}
+
+packet LeReadResolvingListSizeComplete : CommandComplete (command_op_code = LE_READ_RESOLVING_LIST_SIZE) {
+  status : ErrorCode,
+}
+
+packet LeReadPeerResolvableAddress : LeSecurityCommand (op_code = LE_READ_PEER_RESOLVABLE_ADDRESS) {
+  peer_identity_address_type : PeerAddressType,
+  peer_identity_address : Address,
+}
+
+packet LeReadPeerResolvableAddressComplete : CommandComplete (command_op_code = LE_READ_PEER_RESOLVABLE_ADDRESS) {
+  status : ErrorCode,
+  peer_resolvable_address : Address,
+}
+
+packet LeReadLocalResolvableAddress : LeSecurityCommand (op_code = LE_READ_LOCAL_RESOLVABLE_ADDRESS) {
+  peer_identity_address_type : PeerAddressType,
+  peer_identity_address : Address,
+}
+
+packet LeReadLocalResolvableAddressComplete : CommandComplete (command_op_code = LE_READ_LOCAL_RESOLVABLE_ADDRESS) {
+  status : ErrorCode,
+  local_resolvable_address : Address,
+}
+
+packet LeSetAddressResolutionEnable : LeSecurityCommand (op_code = LE_SET_ADDRESS_RESOLUTION_ENABLE) {
+  address_resolution_enable : Enable,
+}
+
+packet LeSetAddressResolutionEnableComplete : CommandComplete (command_op_code = LE_SET_ADDRESS_RESOLUTION_ENABLE) {
+  status : ErrorCode,
+}
+
+packet LeSetResolvablePrivateAddressTimeout : LeSecurityCommand (op_code = LE_SET_RESOLVABLE_PRIVATE_ADDRESS_TIMEOUT) {
+  rpa_timeout : 16, // RPA_Timeout measured in seconds 0x0001 to 0xA1B8 1s to 11.5 hours
+}
+
+packet LeSetResolvablePrivateAddressTimeoutComplete : CommandComplete (command_op_code = LE_SET_RESOLVABLE_PRIVATE_ADDRESS_TIMEOUT) {
+  status : ErrorCode,
+}
+
+packet LeReadMaximumDataLength : CommandPacket (op_code = LE_READ_MAXIMUM_DATA_LENGTH) {
+}
+
+struct LeMaximumDataLength {
+  supported_max_tx_octets : 16,
+  supported_max_tx_time: 16,
+  supported_max_rx_octets : 16,
+  supported_max_rx_time: 16,
+}
+
+packet LeReadMaximumDataLengthComplete : CommandComplete (command_op_code = LE_READ_MAXIMUM_DATA_LENGTH) {
+  status : ErrorCode,
+  le_maximum_data_length : LeMaximumDataLength,
+}
+
+packet LeReadPhy : LeConnectionManagementCommand (op_code = LE_READ_PHY) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeSetDefaultPhy : LeConnectionManagementCommand (op_code = LE_SET_DEFAULT_PHY) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeSetPhy : LeConnectionManagementCommand (op_code = LE_SET_PHY) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeEnhancedReceiverTest : CommandPacket (op_code = LE_ENHANCED_RECEIVER_TEST) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeEnhancedTransmitterTest : CommandPacket (op_code = LE_ENHANCED_TRANSMITTER_TEST) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeSetExtendedAdvertisingRandomAddress : LeAdvertisingCommand (op_code = LE_SET_EXTENDED_ADVERTISING_RANDOM_ADDRESS) {
+  advertising_handle : 8,
+  advertising_random_address : Address,
+}
+
+packet LeSetExtendedAdvertisingRandomAddressComplete : CommandComplete (command_op_code = LE_SET_EXTENDED_ADVERTISING_RANDOM_ADDRESS) {
+  status : ErrorCode,
+}
+
+// The lower 4 bits of the advertising event properties
+enum LegacyAdvertisingProperties : 4 {
+  ADV_IND = 0x3,
+  ADV_DIRECT_IND_LOW = 0x5,
+  ADV_DIRECT_IND_HIGH = 0xD,
+  ADV_SCAN_IND = 0x2,
+  ADV_NONCONN_IND = 0,
+}
+
+enum PrimaryPhyType : 8 {
+  LE_1M = 0x01,
+  LE_CODED = 0x03,
+}
+
+enum SecondaryPhyType : 8 {
+  NO_PACKETS = 0x00,
+  LE_1M = 0x01,
+  LE_2M = 0x02,
+  LE_CODED = 0x03,
+}
+
+packet LeSetExtendedAdvertisingLegacyParameters : LeAdvertisingCommand (op_code = LE_SET_EXTENDED_ADVERTISING_PARAMETERS) {
+  advertising_handle : 8,
+  advertising_event_legacy_properties : LegacyAdvertisingProperties,
+  _fixed_ = 0x1 : 1, // legacy bit set
+  _reserved_ : 11, // advertising_event_properties
+  primary_advertising_interval_min : 24, // 0x20 - 0xFFFFFF N * 0.625 ms
+  primary_advertising_interval_max : 24, // 0x20 - 0xFFFFFF N * 0.625 ms
+  primary_advertising_channel_map : 3,  // bit 0 - Channel 37, bit 1 - 38, bit 2 - 39
+  _reserved_ : 5,
+  own_address_type : OwnAddressType,
+  peer_address_type : PeerAddressType,
+  peer_address : Address,
+  advertising_filter_policy : AdvertisingFilterPolicy,
+  _reserved_ : 6,
+  advertising_tx_power : 8, // -127 to +20, 0x7F - no preference
+  _fixed_ = 0x1 : 8, // PrimaryPhyType LE_1M
+  _reserved_ : 8, // secondary_advertising_max_skip
+  _fixed_ = 0x1 : 8, // secondary_advertising_phy LE_1M
+  advertising_sid : 8, // SID subfield from the ADI field of the PDU
+  scan_request_notification_enable : Enable,
+}
+
+packet LeSetExtendedAdvertisingParameters : LeAdvertisingCommand (op_code = LE_SET_EXTENDED_ADVERTISING_PARAMETERS) {
+  advertising_handle : 8,
+  advertising_event_legacy_properties : 4,
+  _fixed_ = 0 : 1, // legacy bit cleared
+  advertising_event_properties : 3,
+  _reserved_ : 8,
+  primary_advertising_interval_min : 24, // 0x20 - 0xFFFFFF N * 0.625 ms
+  primary_advertising_interval_max : 24, // 0x20 - 0xFFFFFF N * 0.625 ms
+  primary_advertising_channel_map : 3,  // bit 0 - Channel 37, bit 1 - 38, bit 2 - 39
+  _reserved_ : 5,
+  own_address_type : OwnAddressType,
+  peer_address_type : PeerAddressType,
+  peer_address : Address,
+  advertising_filter_policy : AdvertisingFilterPolicy,
+  _reserved_ : 6,
+  advertising_tx_power : 8, // -127 to +20, 0x7F - no preference
+  primary_advertising_phy : PrimaryPhyType,
+  secondary_advertising_max_skip : 8, // 1 to 255, 0x00 - AUX_ADV_IND sent before next advertising event
+  secondary_advertising_phy : SecondaryPhyType,
+  advertising_sid : 8, // SID subfield from the ADI field of the PDU
+  scan_request_notification_enable : Enable,
+}
+
+packet LeSetExtendedAdvertisingParametersComplete : CommandComplete (command_op_code = LE_SET_EXTENDED_ADVERTISING_PARAMETERS) {
+  status : ErrorCode,
+  selected_tx_power : 8, // -127 to +20
+}
+
+enum Operation : 3 {
+  INTERMEDIATE_FRAGMENT = 0,
+  FIRST_FRAGMENT = 1,
+  LAST_FRAGMENT = 2,
+  COMPLETE_ADVERTISEMENT = 3,
+  UNCHANGED_DATA = 4,
+}
+
+enum FragmentPreference : 1 {
+  CONTROLLER_MAY_FRAGMENT = 0,
+  CONTROLLER_SHOULD_NOT = 1,
+}
+
+packet LeSetExtendedAdvertisingData : LeAdvertisingCommand (op_code = LE_SET_EXTENDED_ADVERTISING_DATA) {
+  advertising_handle : 8,
+  operation : Operation,
+  _reserved_ : 5,
+  fragment_preference : FragmentPreference,
+  _reserved_ : 7,
+  _size_(advertising_data) : 8,
+  advertising_data : GapData[],
+}
+
+packet LeSetExtendedAdvertisingDataRaw : LeAdvertisingCommand (op_code = LE_SET_EXTENDED_ADVERTISING_DATA) {
+  advertising_handle : 8,
+  operation : Operation,
+  _reserved_ : 5,
+  fragment_preference : FragmentPreference,
+  _reserved_ : 7,
+  _size_(advertising_data) : 8,
+  advertising_data : 8[],
+}
+
+packet LeSetExtendedAdvertisingDataComplete : CommandComplete (command_op_code = LE_SET_EXTENDED_ADVERTISING_DATA) {
+  status : ErrorCode,
+}
+
+packet LeSetExtendedAdvertisingScanResponse : LeAdvertisingCommand (op_code = LE_SET_EXTENDED_ADVERTISING_SCAN_RESPONSE) {
+  advertising_handle : 8,
+  operation : Operation,
+  _reserved_ : 5,
+  fragment_preference : FragmentPreference,
+  _reserved_ : 7,
+  _size_(scan_response_data) : 8,
+  scan_response_data : GapData[],
+}
+
+packet LeSetExtendedAdvertisingScanResponseRaw : LeAdvertisingCommand (op_code = LE_SET_EXTENDED_ADVERTISING_SCAN_RESPONSE) {
+  advertising_handle : 8,
+  operation : Operation,
+  _reserved_ : 5,
+  fragment_preference : FragmentPreference,
+  _reserved_ : 7,
+  _size_(scan_response_data) : 8,
+  scan_response_data : 8[],
+}
+
+packet LeSetExtendedAdvertisingScanResponseComplete : CommandComplete (command_op_code = LE_SET_EXTENDED_ADVERTISING_SCAN_RESPONSE) {
+  status : ErrorCode,
+}
+
+packet LeSetExtendedAdvertisingEnableDisableAll : LeAdvertisingCommand (op_code = LE_SET_EXTENDED_ADVERTISING_ENABLE) {
+  _fixed_ = 0x00 : 8, // Enable::DISABLED
+  _fixed_ = 0x00 : 8, // Disable all sets
+}
+
+struct EnabledSet {
+  advertising_handle : 8,
+  duration : 16,
+  max_extended_advertising_events : 8,
+}
+
+struct DisabledSet {
+  advertising_handle : 8,
+  _fixed_ = 0x00 : 16, // duration
+  _fixed_ = 0x00 : 8, // max_extended_advertising_events
+}
+
+packet LeSetExtendedAdvertisingDisable : LeAdvertisingCommand (op_code = LE_SET_EXTENDED_ADVERTISING_ENABLE) {
+  _fixed_ = 0x00 : 8, // Enable::DISABLED
+  _count_(disabled_sets) : 8,
+  disabled_sets : DisabledSet[],
+}
+
+packet LeSetExtendedAdvertisingEnable : LeAdvertisingCommand (op_code = LE_SET_EXTENDED_ADVERTISING_ENABLE) {
+  enable : Enable,
+  _count_(enabled_sets) : 8,
+  enabled_sets : EnabledSet[],
+}
+
+packet LeSetExtendedAdvertisingEnableComplete : CommandComplete (command_op_code = LE_SET_EXTENDED_ADVERTISING_ENABLE) {
+  status : ErrorCode,
+}
+
+packet LeReadMaximumAdvertisingDataLength : CommandPacket (op_code = LE_READ_MAXIMUM_ADVERTISING_DATA_LENGTH) {
+}
+
+packet LeReadMaximumAdvertisingDataLengthComplete : CommandComplete (command_op_code = LE_READ_MAXIMUM_ADVERTISING_DATA_LENGTH) {
+  status : ErrorCode,
+  maximum_advertising_data_length : 16,
+}
+
+packet LeReadNumberOfSupportedAdvertisingSets : CommandPacket (op_code = LE_READ_NUMBER_OF_SUPPORTED_ADVERTISING_SETS) {
+}
+
+packet LeReadNumberOfSupportedAdvertisingSetsComplete : CommandComplete (command_op_code = LE_READ_NUMBER_OF_SUPPORTED_ADVERTISING_SETS) {
+  status : ErrorCode,
+  number_supported_advertising_sets : 8,
+}
+
+packet LeRemoveAdvertisingSet : LeAdvertisingCommand (op_code = LE_REMOVE_ADVERTISING_SET) {
+  advertising_handle : 8,
+}
+
+packet LeRemoveAdvertisingSetComplete : CommandComplete (command_op_code = LE_REMOVE_ADVERTISING_SET) {
+  status : ErrorCode,
+}
+
+packet LeClearAdvertisingSets : LeAdvertisingCommand (op_code = LE_CLEAR_ADVERTISING_SETS) {
+}
+
+packet LeClearAdvertisingSetsComplete : CommandComplete (command_op_code = LE_CLEAR_ADVERTISING_SETS) {
+  status : ErrorCode,
+}
+
+packet LeSetPeriodicAdvertisingParam : LeAdvertisingCommand (op_code = LE_SET_PERIODIC_ADVERTISING_PARAM) {
+  advertising_handle : 8,
+  periodic_advertising_interval_min : 16, // 0x006 to 0xFFFF (7.5 ms to 82s)
+  periodic_advertising_interval_max : 16, // 0x006 to 0xFFFF (7.5 ms to 82s)
+  _reserved_ : 6,
+  include_tx_power : 1,
+  _reserved_ : 9,
+}
+
+packet LeSetPeriodicAdvertisingParamComplete : CommandComplete (command_op_code = LE_SET_PERIODIC_ADVERTISING_PARAM) {
+  status : ErrorCode,
+}
+
+packet LeSetPeriodicAdvertisingData : LeAdvertisingCommand (op_code = LE_SET_PERIODIC_ADVERTISING_DATA) {
+  advertising_handle : 8,
+  operation : Operation,
+  _reserved_ : 5,
+  _size_(scan_response_data) : 8,
+  scan_response_data : 8[],
+}
+
+packet LeSetPeriodicAdvertisingDataComplete : CommandComplete (command_op_code = LE_SET_PERIODIC_ADVERTISING_DATA) {
+  status : ErrorCode,
+}
+
+packet LeSetPeriodicAdvertisingEnable : LeAdvertisingCommand (op_code = LE_SET_PERIODIC_ADVERTISING_ENABLE) {
+  advertising_handle : 8,
+  enable : Enable,
+}
+
+packet LeSetPeriodicAdvertisingEnableComplete : CommandComplete (command_op_code = LE_SET_PERIODIC_ADVERTISING_ENABLE) {
+  status : ErrorCode,
+}
+
+struct PhyScanParameters {
+  le_scan_type : LeScanType,
+  le_scan_interval : 16, // 0x0004-0xFFFF Default 0x10 (10ms)
+  le_scan_window : 16, // 0x004-0xFFFF Default 0x10 (10ms)
+}
+
+packet LeSetExtendedScanParameters : LeScanningCommand (op_code = LE_SET_EXTENDED_SCAN_PARAMETERS) {
+  own_address_type : AddressType,
+  scanning_filter_policy : LeSetScanningFilterPolicy,
+  scanning_phys : 8,
+  parameters : PhyScanParameters[],
+}
+
+packet LeSetExtendedScanParametersComplete : CommandComplete (command_op_code = LE_SET_EXTENDED_SCAN_PARAMETERS) {
+  status : ErrorCode,
+}
+
+enum FilterDuplicates : 8 {
+  DISABLED = 0,
+  ENABLED = 1,
+  RESET_EACH_PERIOD = 2,
+}
+
+packet LeSetExtendedScanEnable : LeScanningCommand (op_code = LE_SET_EXTENDED_SCAN_ENABLE) {
+  enable : Enable,
+  filter_duplicates : FilterDuplicates,
+  duration : 16, // 0 - Scan continuously,  N * 10 ms
+  period : 16, // 0 - Scan continuously,  N * 1.28 sec
+}
+
+packet LeSetExtendedScanEnableComplete : CommandComplete (command_op_code = LE_SET_EXTENDED_SCAN_ENABLE) {
+  status : ErrorCode,
+}
+
+struct LeCreateConnPhyScanParameters {
+  scan_interval : 16, // 0x0004-0xFFFF
+  scan_window : 16, // < = LeScanInterval
+  conn_interval_min : 16, // 0x0006-0x0C80 (7.5ms to 4s)
+  conn_interval_max : 16, // 0x0006-0x0C80 (7.5ms to 4s)
+  conn_latency : 16, // 0x0006-0x01F3
+  supervision_timeout : 16, // 0x00A to 0x0C80 (100ms to 32s)
+  min_ce_length : 16, // 0.625ms
+  max_ce_length : 16, // 0.625ms
+}
+
+packet LeExtendedCreateConnection : LeConnectionManagementCommand (op_code = LE_EXTENDED_CREATE_CONNECTION) {
+  initiator_filter_policy : InitiatorFilterPolicy,
+  own_address_type : OwnAddressType,
+  peer_address_type : AddressType,
+  peer_address : Address,
+  initiating_phys : 8,
+  phy_scan_parameters : LeCreateConnPhyScanParameters[],
+}
+
+packet LeExtendedCreateConnectionStatus : CommandStatus (command_op_code = LE_EXTENDED_CREATE_CONNECTION) {
+}
+
+packet LePeriodicAdvertisingCreateSync : LeAdvertisingCommand (op_code = LE_PERIODIC_ADVERTISING_CREATE_SYNC) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LePeriodicAdvertisingCreateSyncCancel : LeAdvertisingCommand (op_code = LE_PERIODIC_ADVERTISING_CREATE_SYNC_CANCEL) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LePeriodicAdvertisingTerminateSync : LeAdvertisingCommand (op_code = LE_PERIODIC_ADVERTISING_TERMINATE_SYNC) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeAddDeviceToPeriodicAdvertisingList : LeAdvertisingCommand (op_code = LE_ADD_DEVICE_TO_PERIODIC_ADVERTISING_LIST) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeRemoveDeviceFromPeriodicAdvertisingList : LeAdvertisingCommand (op_code = LE_REMOVE_DEVICE_FROM_PERIODIC_ADVERTISING_LIST) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeClearPeriodicAdvertisingList : LeAdvertisingCommand (op_code = LE_CLEAR_PERIODIC_ADVERTISING_LIST) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeReadPeriodicAdvertisingListSize : LeAdvertisingCommand (op_code = LE_READ_PERIODIC_ADVERTISING_LIST_SIZE) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeReadTransmitPower : LeAdvertisingCommand (op_code = LE_READ_TRANSMIT_POWER) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeReadRfPathCompensationPower : LeAdvertisingCommand (op_code = LE_READ_RF_PATH_COMPENSATION_POWER) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeWriteRfPathCompensationPower : LeAdvertisingCommand (op_code = LE_WRITE_RF_PATH_COMPENSATION_POWER) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+enum PrivacyMode : 8 {
+  NETWORK = 0,
+  DEVICE = 1,
+}
+
+packet LeSetPrivacyMode : LeSecurityCommand (op_code = LE_SET_PRIVACY_MODE) {
+  peer_identity_address_type : PeerAddressType,
+  peer_identity_address : Address,
+  privacy_mode : PrivacyMode,
+}
+
+packet LeSetPrivacyModeComplete : CommandComplete (command_op_code = LE_SET_PRIVACY_MODE) {
+  status : ErrorCode,
+}
+
+enum UseDebugKey : 8 {
+  USE_GENERATED_KEY = 0,
+  USE_DEBUG_KEY = 1,
+}
+
+packet LeGenerateDhkeyCommand : LeSecurityCommand (op_code = LE_GENERATE_DHKEY_COMMAND) {
+  remote_p_256_public_key : 8[64],
+  key_type : UseDebugKey,
+}
+
+packet LeGenerateDhkeyCommandStatus : CommandStatus (command_op_code = LE_GENERATE_DHKEY_COMMAND) {
+}
+
+  // VENDOR_SPECIFIC
+packet LeGetVendorCapabilities : VendorCommand (op_code = LE_GET_VENDOR_CAPABILITIES) {
+}
+
+struct VendorCapabilities {
+  is_supported : 8,
+  max_advt_instances: 8,
+  offloaded_resolution_of_private_address : 8,
+  total_scan_results_storage: 16,
+  max_irk_list_sz: 8,
+  filtering_support: 8,
+  max_filter: 8,
+  activity_energy_info_support: 8,
+  version_supported: 16,
+  total_num_of_advt_tracked: 16,
+  extended_scan_support: 8,
+  debug_logging_supported: 8,
+  le_address_generation_offloading_support: 8,
+  a2dp_source_offload_capability_mask: 32,
+  bluetooth_quality_report_support: 8
+}
+
+struct BaseVendorCapabilities {
+  max_advt_instances: 8,
+  offloaded_resolution_of_private_address : 8,
+  total_scan_results_storage: 16,
+  max_irk_list_sz: 8,
+  filtering_support: 8,
+  max_filter: 8,
+  activity_energy_info_support: 8,
+}
+
+packet LeGetVendorCapabilitiesComplete : CommandComplete (command_op_code = LE_GET_VENDOR_CAPABILITIES) {
+  status : ErrorCode,
+  base_vendor_capabilities : BaseVendorCapabilities,
+  _payload_,
+}
+packet LeGetVendorCapabilitiesComplete095 : LeGetVendorCapabilitiesComplete {
+  version_supported: 16,
+  total_num_of_advt_tracked: 16,
+  extended_scan_support: 8,
+  debug_logging_supported: 8,
+  _payload_,
+}
+packet LeGetVendorCapabilitiesComplete096 : LeGetVendorCapabilitiesComplete095 {
+  le_address_generation_offloading_support: 8,
+  _payload_,
+}
+
+packet LeGetVendorCapabilitiesComplete098 : LeGetVendorCapabilitiesComplete096 {
+  a2dp_source_offload_capability_mask: 32,
+  bluetooth_quality_report_support: 8
+}
+
+enum SubOcf : 8 {
+  SET_PARAM = 0x01,
+  SET_DATA = 0x02,
+  SET_SCAN_RESP = 0x03,
+  SET_RANDOM_ADDR = 0x04,
+  SET_ENABLE = 0x05,
+}
+
+packet LeMultiAdvt : LeAdvertisingCommand (op_code = LE_MULTI_ADVT) {
+  sub_cmd : SubOcf,
+  _body_,
+}
+
+packet LeMultiAdvtComplete : CommandComplete (command_op_code = LE_MULTI_ADVT) {
+  status : ErrorCode,
+  sub_cmd : SubOcf,
+}
+
+packet LeMultiAdvtParam : LeMultiAdvt (sub_cmd = SET_PARAM) {
+  interval_min : 16,
+  interval_max : 16,
+  type : AdvertisingEventType,
+  own_address_type : AddressType,
+  peer_address_type : PeerAddressType,
+  peer_address : Address,
+  channel_map : 8,
+  filter_policy : AdvertisingFilterPolicy,
+  _reserved_ : 6,
+  instance : 8,
+  tx_power : 8,
+}
+
+packet LeMultiAdvtParamComplete : LeMultiAdvtComplete (sub_cmd = SET_PARAM) {
+}
+
+packet LeMultiAdvtSetData : LeMultiAdvt (sub_cmd = SET_DATA) {
+  _size_(advertising_data) : 8,
+  advertising_data : GapData[],
+  _padding_[36], // Zero padding to 31 bytes of advertising_data + 1 size + 2 opcode + 1 total_size + 1 set
+  advertising_instance : 8,
+}
+
+packet LeMultiAdvtSetDataComplete : LeMultiAdvtComplete (sub_cmd = SET_DATA) {
+}
+
+packet LeMultiAdvtSetScanResp : LeMultiAdvt (sub_cmd = SET_SCAN_RESP) {
+  _size_(advertising_data) : 8,
+  advertising_data : GapData[],
+  _padding_[36], // Zero padding to 31 bytes of advertising_data + 1 size + 2 opcode + 1 total_size + 1 set
+  advertising_instance : 8,
+}
+
+packet LeMultiAdvtSetScanRespComplete : LeMultiAdvtComplete (sub_cmd = SET_SCAN_RESP) {
+}
+
+packet LeMultiAdvtSetRandomAddr : LeMultiAdvt (sub_cmd = SET_RANDOM_ADDR) {
+  random_address : Address,
+  advertising_instance : 8,
+}
+
+packet LeMultiAdvtSetRandomAddrComplete : LeMultiAdvtComplete (sub_cmd = SET_RANDOM_ADDR) {
+}
+
+packet LeMultiAdvtSetEnable : LeMultiAdvt (sub_cmd = SET_ENABLE) {
+  advertising_enable : Enable, // Default DISABLED
+  advertising_instance : 8,
+}
+
+packet LeMultiAdvtSetEnableComplete : LeMultiAdvtComplete (sub_cmd = SET_ENABLE) {
+}
+
+packet LeBatchScan : VendorCommand (op_code = LE_BATCH_SCAN) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+enum ApcfOpcode : 8 {
+  ENABLE = 0x00,
+  SET_FILTERING_PARAMETERS = 0x01,
+  BROADCASTER_ADDRESS = 0x02,
+  SERVICE_UUID = 0x03,
+  SERVICE_SOLICITATION_UUID = 0x04,
+  LOCAL_NAME = 0x05,
+  MANUFACTURER_DATA = 0x06,
+  SERVICE_DATA = 0x07,
+}
+
+packet LeAdvFilter : LeScanningCommand (op_code = LE_ADV_FILTER) {
+  apcf_opcode : ApcfOpcode,
+  _body_,
+}
+
+packet LeAdvFilterComplete : CommandComplete (command_op_code = LE_ADV_FILTER) {
+  status : ErrorCode,
+  apcf_opcode : ApcfOpcode,
+}
+
+packet LeTrackAdv : VendorCommand (op_code = LE_TRACK_ADV) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeEnergyInfo : VendorCommand (op_code = LE_ENERGY_INFO) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeExtendedScanParams : LeScanningCommand (op_code = LE_EXTENDED_SCAN_PARAMS) {
+  le_scan_type : LeScanType,
+  le_scan_interval : 32, // 0x0004-0x4000 Default 0x10 (10ms)
+  le_scan_window : 32, // Default 0x10 (10ms)
+  own_address_type : AddressType,
+  scanning_filter_policy : LeSetScanningFilterPolicy,
+}
+
+packet LeExtendedScanParamsComplete : CommandComplete (command_op_code = LE_EXTENDED_SCAN_PARAMS) {
+  status : ErrorCode,
+}
+
+packet ControllerDebugInfo : VendorCommand (op_code = CONTROLLER_DEBUG_INFO) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+packet ControllerA2DPOpcode : VendorCommand (op_code = CONTROLLER_A2DP_OPCODE) {
+  _payload_,  // placeholder (unimplemented)
+}
+
+enum BqrReportAction : 8 {
+  ADD = 0x00,
+  DELETE = 0x01,
+  CLEAR = 0x02,
+}
+
+packet ControllerBqr : VendorCommand(op_code = CONTROLLER_BQR) {
+  bqr_report_action : BqrReportAction,
+  bqr_quality_event_mask : 32,
+  bqr_minimum_report_interval : 16,
+}
+
+packet ControllerBqrComplete : CommandComplete (command_op_code = CONTROLLER_BQR) {
+  status : ErrorCode,
+  current_quality_event_mask : 32
+}
+
+// HCI Event Packets
+
+packet InquiryComplete : EventPacket (event_code = INQUIRY_COMPLETE){
+  status : ErrorCode,
+}
+
+struct InquiryResult {
+  bd_addr : Address,
+  page_scan_repetition_mode : PageScanRepetitionMode,
+  _reserved_ : 8,
+  _reserved_ : 8,
+  class_of_device : ClassOfDevice,
+  clock_offset : 15,
+  _reserved_ : 1,
+}
+
+packet InquiryResult : EventPacket (event_code = INQUIRY_RESULT){
+  _count_(inquiry_results) : 8,
+  inquiry_results : InquiryResult[],
+}
+
+enum LinkType : 8 {
+  SCO = 0x00,
+  ACL = 0x01,
+}
+
+packet ConnectionComplete : EventPacket (event_code = CONNECTION_COMPLETE){
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+  bd_addr : Address,
+  link_type : LinkType,
+  encryption_enabled : Enable,
+}
+
+enum ConnectionRequestLinkType : 8 {
+  SCO = 0x00,
+  ACL = 0x01,
+  ESCO = 0x02,
+}
+
+packet ConnectionRequest : EventPacket (event_code = CONNECTION_REQUEST){
+  bd_addr : Address,
+  class_of_device : ClassOfDevice,
+  link_type : ConnectionRequestLinkType,
+}
+
+packet DisconnectionComplete : EventPacket (event_code = DISCONNECTION_COMPLETE){
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+  reason : ErrorCode,
+}
+
+packet AuthenticationComplete : EventPacket (event_code = AUTHENTICATION_COMPLETE){
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+packet RemoteNameRequestComplete : EventPacket (event_code = REMOTE_NAME_REQUEST_COMPLETE){
+  status : ErrorCode,
+  bd_addr : Address,
+  remote_name : 8[248], // UTF-8 encoded user-friendly descriptive name
+}
+
+enum EncryptionEnabled : 8 {
+  OFF = 0x00,
+  ON = 0x01, // E0 for BR/EDR and AES-CCM for LE
+  BR_EDR_AES_CCM = 0x02,
+}
+
+packet EncryptionChange : EventPacket (event_code = ENCRYPTION_CHANGE){
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+  encryption_enabled : EncryptionEnabled,
+}
+
+packet ChangeConnectionLinkKeyComplete : EventPacket (event_code = CHANGE_CONNECTION_LINK_KEY_COMPLETE){
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+packet MasterLinkKeyComplete : EventPacket (event_code = MASTER_LINK_KEY_COMPLETE){
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+  key_flag : KeyFlag,
+}
+
+packet ReadRemoteSupportedFeaturesComplete : EventPacket (event_code = READ_REMOTE_SUPPORTED_FEATURES_COMPLETE){
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+  lmp_features : 64,
+}
+
+packet ReadRemoteVersionInformationComplete : EventPacket (event_code = READ_REMOTE_VERSION_INFORMATION_COMPLETE){
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+  version : 8,
+  manufacturer_name : 16,
+  sub_version : 16,
+}
+
+packet QosSetupComplete : EventPacket (event_code = QOS_SETUP_COMPLETE){
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+  _reserved_ : 8,
+  service_type : ServiceType,
+  token_rate : 32, // Octets/s
+  peak_bandwidth : 32, // Octets/s
+  latency : 32, // Octets/s
+  delay_variation : 32, // microseconds
+}
+
+// Command Complete and Command Status Events are implemented above Commands.
+
+packet HardwareError : EventPacket (event_code = HARDWARE_ERROR){
+  hardware_code : 8,
+}
+
+packet FlushOccurred : EventPacket (event_code = FLUSH_OCCURRED){
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+packet RoleChange : EventPacket (event_code = ROLE_CHANGE){
+  status : ErrorCode,
+  bd_addr : Address,
+  new_role : Role,
+}
+
+packet NumberOfCompletedPackets : EventPacket (event_code = NUMBER_OF_COMPLETED_PACKETS){
+  _count_(completed_packets) : 8,
+  completed_packets : CompletedPackets[],
+}
+
+enum Mode : 8 {
+  ACTIVE = 0x00,
+  HOLD = 0x01,
+  SNIFF = 0x02,
+}
+
+packet ModeChange : EventPacket (event_code = MODE_CHANGE){
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+  current_mode : Mode,
+  interval : 16, // 0x002 - 0xFFFE (1.25ms - 40.9s)
+}
+
+struct ZeroKeyAndAddress {
+  address : Address,
+  _fixed_ = 0 : 64,
+  _fixed_ = 0 : 64,
+}
+
+packet ReturnLinkKeys : EventPacket (event_code = RETURN_LINK_KEYS){
+  _count_(keys) : 8,
+  keys : ZeroKeyAndAddress[],
+}
+
+packet PinCodeRequest : EventPacket (event_code = PIN_CODE_REQUEST){
+  bd_addr : Address,
+}
+
+packet LinkKeyRequest : EventPacket (event_code = LINK_KEY_REQUEST){
+  bd_addr : Address,
+}
+
+enum KeyType : 8 {
+  COMBINATION = 0x00,
+  DEBUG_COMBINATION = 0x03,
+  UNAUTHENTICATED_P192 = 0x04,
+  AUTHENTICATED_P192 = 0x05,
+  CHANGED = 0x06,
+  UNAUTHENTICATED_P256 = 0x07,
+  AUTHENTICATED_P256 = 0x08,
+}
+
+packet LinkKeyNotification : EventPacket (event_code = LINK_KEY_NOTIFICATION){
+  bd_addr : Address,
+  link_key : 8[16],
+  key_type : KeyType,
+}
+
+packet LoopbackCommand : EventPacket (event_code = LOOPBACK_COMMAND){
+  _payload_, // Command packet, truncated if it was longer than 252 bytes
+}
+
+packet DataBufferOverflow : EventPacket (event_code = DATA_BUFFER_OVERFLOW){
+  link_type : LinkType,
+}
+
+packet MaxSlotsChange : EventPacket (event_code = MAX_SLOTS_CHANGE){
+  connection_handle : 12,
+  _reserved_ : 4,
+  lmp_max_slots : 8,
+}
+
+packet ReadClockOffsetComplete : EventPacket (event_code = READ_CLOCK_OFFSET_COMPLETE){
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+  clock_offset : 15,
+  _reserved_ : 1,
+}
+
+packet ConnectionPacketTypeChanged : EventPacket (event_code = CONNECTION_PACKET_TYPE_CHANGED){
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+  packet_type : 16,
+}
+
+packet QosViolation : EventPacket (event_code = QOS_VIOLATION){
+  _payload_, // placeholder (unimplemented)
+}
+
+packet PageScanRepetitionModeChange : EventPacket (event_code = PAGE_SCAN_REPETITION_MODE_CHANGE){
+  _payload_, // placeholder (unimplemented)
+}
+
+packet FlowSpecificationComplete : EventPacket (event_code = FLOW_SPECIFICATION_COMPLETE){
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+  _reserved_ : 8,
+  flow_direction : FlowDirection,
+  service_type : ServiceType,
+  token_rate : 32, // Octets/s
+  token_bucket_size : 32,
+  peak_bandwidth : 32, // Octets/s
+  access_latency : 32, // Octets/s
+}
+
+struct InquiryResultWithRssi {
+  address : Address,
+  page_scan_repetition_mode : PageScanRepetitionMode,
+  _reserved_ : 8,
+  class_of_device : ClassOfDevice,
+  clock_offset : 15,
+  _reserved_ : 1,
+  rssi : 8,
+}
+
+packet InquiryResultWithRssi : EventPacket (event_code = INQUIRY_RESULT_WITH_RSSI){
+  _count_(inquiry_results) : 8,
+  inquiry_results : InquiryResultWithRssi[],
+}
+
+packet ReadRemoteExtendedFeaturesComplete : EventPacket (event_code = READ_REMOTE_EXTENDED_FEATURES_COMPLETE){
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+  page_number : 8,
+  maximum_page_number : 8,
+  extended_lmp_features : 64,
+}
+
+packet SynchronousConnectionComplete : EventPacket (event_code = SYNCHRONOUS_CONNECTION_COMPLETE){
+  _payload_, // placeholder (unimplemented)
+}
+
+packet SynchronousConnectionChanged : EventPacket (event_code = SYNCHRONOUS_CONNECTION_CHANGED){
+  _payload_, // placeholder (unimplemented)
+}
+
+packet SniffSubratingEvent : EventPacket (event_code = SNIFF_SUBRATING){
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+  maximum_transmit_latency : 16, // 0x000 - 0xFFFE (0s - 40.9s)
+  maximum_receive_latency : 16, // 0x000 - 0xFFFE (0s - 40.9s)
+  minimum_remote_timeout : 16, // 0x000 - 0xFFFE (0s - 40.9s)
+  minimum_local_timeout : 16, // 0x000 - 0xFFFE (0s - 40.9s)
+}
+
+packet ExtendedInquiryResult : EventPacket (event_code = EXTENDED_INQUIRY_RESULT) {
+  _fixed_ = 0x01 : 8,
+  address : Address,
+  page_scan_repetition_mode : PageScanRepetitionMode,
+  _reserved_ : 8,
+  class_of_device : ClassOfDevice,
+  clock_offset : 15,
+  _reserved_ : 1,
+  rssi : 8,
+  extended_inquiry_response : GapData[],
+}
+
+packet EncryptionKeyRefreshComplete : EventPacket (event_code = ENCRYPTION_KEY_REFRESH_COMPLETE){
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+packet IoCapabilityRequest : EventPacket (event_code = IO_CAPABILITY_REQUEST){
+  bd_addr : Address,
+}
+
+packet IoCapabilityResponse : EventPacket (event_code = IO_CAPABILITY_RESPONSE){
+  bd_addr : Address,
+  io_capability : IoCapability,
+  oob_data_present : OobDataPresent,
+  authentication_requirements : AuthenticationRequirements,
+}
+
+packet UserConfirmationRequest : EventPacket (event_code = USER_CONFIRMATION_REQUEST){
+  bd_addr : Address,
+  numeric_value : 20, // 0x00000-0xF423F (000000 - 999999)
+  _reserved_ : 12,
+}
+
+packet UserPasskeyRequest : EventPacket (event_code = USER_PASSKEY_REQUEST){
+  bd_addr : Address,
+}
+
+packet RemoteOobDataRequest : EventPacket (event_code = REMOTE_OOB_DATA_REQUEST){
+  bd_addr : Address,
+}
+
+packet SimplePairingComplete : EventPacket (event_code = SIMPLE_PAIRING_COMPLETE){
+  status : ErrorCode,
+  bd_addr : Address,
+}
+
+packet LinkSupervisionTimeoutChanged : EventPacket (event_code = LINK_SUPERVISION_TIMEOUT_CHANGED){
+  connection_handle : 12,
+  _reserved_ : 4,
+  link_supervision_timeout : 16, // 0x001-0xFFFF (0.625ms-40.9s)
+}
+
+packet EnhancedFlushComplete : EventPacket (event_code = ENHANCED_FLUSH_COMPLETE){
+  connection_handle : 12,
+  _reserved_ : 4,
+}
+
+packet UserPasskeyNotification : EventPacket (event_code = USER_PASSKEY_NOTIFICATION){
+  bd_addr : Address,
+  passkey : 20, // 0x00000-0xF423F (000000 - 999999)
+  _reserved_ : 12,
+}
+
+packet KeypressNotification : EventPacket (event_code = KEYPRESS_NOTIFICATION){
+  bd_addr : Address,
+  notification_type : KeypressNotificationType,
+}
+
+packet RemoteHostSupportedFeaturesNotification : EventPacket (event_code = REMOTE_HOST_SUPPORTED_FEATURES_NOTIFICATION){
+  bd_addr : Address,
+  host_supported_features : 64,
+}
+
+packet LeMetaEvent : EventPacket (event_code = LE_META_EVENT) {
+  subevent_code : SubeventCode,
+  _payload_,
+}
+
+packet NumberOfCompletedDataBlocks : EventPacket (event_code = NUMBER_OF_COMPLETED_DATA_BLOCKS){
+  _payload_, // placeholder (unimplemented)
+}
+
+// LE Events
+
+enum MasterClockAccuracy : 8 {
+  PPM_500 = 0x00,
+  PPM_250 = 0x01,
+  PPM_150 = 0x02,
+  PPM_100 = 0x03,
+  PPM_75 = 0x04,
+  PPM_50 = 0x05,
+  PPM_30 = 0x06,
+  PPM_20 = 0x07,
+}
+
+packet LeConnectionComplete : LeMetaEvent (subevent_code = CONNECTION_COMPLETE) {
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+  role : Role,
+  peer_address_type : AddressType,
+  peer_address : Address,
+  conn_interval : 16, // 0x006 - 0x0C80 (7.5ms - 4000ms)
+  conn_latency : 16,  // Number of connection events
+  supervision_timeout : 16,  // 0x000A to 0x0C80 (100ms to 32s)
+  master_clock_accuracy : MasterClockAccuracy,
+}
+
+struct LeAdvertisingReport {
+  event_type : AdvertisingEventType,
+  address_type : AddressType,
+  address : Address,
+  _size_(advertising_data) : 8,
+  advertising_data : GapData[],
+  rssi : 8,
+}
+
+packet LeAdvertisingReport : LeMetaEvent (subevent_code = ADVERTISING_REPORT) {
+  _count_(advertising_reports) : 8,
+  advertising_reports : LeAdvertisingReport[],
+}
+
+packet LeConnectionUpdateComplete : LeMetaEvent (subevent_code = CONNECTION_UPDATE_COMPLETE) {
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+  conn_interval : 16, // 0x006 - 0x0C80 (7.5ms - 4000ms)
+  conn_latency : 16,  // Number of connection events
+  supervision_timeout : 16,  // 0x000A to 0x0C80 (100ms to 32s)
+}
+
+packet LeReadRemoteFeaturesComplete : LeMetaEvent (subevent_code = READ_REMOTE_FEATURES_COMPLETE) {
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+  le_features : 64,
+}
+
+packet LeLongTermKeyRequest : LeMetaEvent (subevent_code = LONG_TERM_KEY_REQUEST) {
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+  random_number : 64,
+  encrypted_diversifier : 16,
+}
+
+packet LeRemoteConnectionParameterRequest : LeMetaEvent (subevent_code = REMOTE_CONNECTION_PARAMETER_REQUEST) {
+  connection_handle : 12,
+  _reserved_ : 4,
+  interval_min : 16, // 0x006 - 0x0C80 (7.5ms - 4s)
+  interval_max : 16, // 0x006 - 0x0C80 (7.5ms - 4s)
+  latency : 16,  // Number of connection events (0x0000 to 0x01f3 (499)
+  timeout : 16,  // 0x000A to 0x0C80 (100ms to 32s)
+}
+
+packet LeDataLengthChange : LeMetaEvent (subevent_code = DATA_LENGTH_CHANGE) {
+  connection_handle : 12,
+  _reserved_ : 4,
+  max_tx_octets : 16, // 0x001B - 0x00FB
+  max_tx_time : 16, // 0x0148 - 0x4290
+  max_rx_octets : 16, // 0x001B - 0x00FB
+  max_rx_time : 16, // 0x0148 - 0x4290
+}
+
+packet ReadLocalP256PublicKeyComplete : LeMetaEvent (subevent_code = READ_LOCAL_P256_PUBLIC_KEY_COMPLETE) {
+  status : ErrorCode,
+  local_p_256_public_key : 8[64],
+}
+
+packet GenerateDhKeyComplete : LeMetaEvent (subevent_code = GENERATE_DHKEY_COMPLETE) {
+  status : ErrorCode,
+  dh_key : 8[32],
+}
+
+packet LeEnhancedConnectionComplete : LeMetaEvent (subevent_code = ENHANCED_CONNECTION_COMPLETE) {
+  status : ErrorCode,
+  connection_handle : 12,
+  _reserved_ : 4,
+  role : Role,
+  peer_address_type : AddressType,
+  peer_address : Address,
+  local_resolvable_private_address : Address,
+  peer_resolvable_private_address : Address,
+  conn_interval : 16, // 0x006 - 0x0C80 (7.5ms - 4000ms)
+  conn_latency : 16,  // Number of connection events
+  supervision_timeout : 16,  // 0x000A to 0x0C80 (100ms to 32s)
+  master_clock_accuracy : MasterClockAccuracy,
+}
+
+enum DirectAdvertisingAddressType : 8 {
+  PUBLIC_DEVICE_ADDRESS = 0x00,
+  RANDOM_DEVICE_ADDRESS = 0x01,
+  PUBLIC_IDENTITY_ADDRESS = 0x02,
+  RANDOM_IDENTITY_ADDRESS = 0x03,
+  CONTROLLER_UNABLE_TO_RESOLVE = 0xFE,
+  NO_ADDRESS = 0xFF,
+}
+
+enum DirectAdvertisingEventType : 8 {
+  ADV_DIRECT_IND = 0x01,
+}
+
+enum DirectAddressType : 8 {
+  RANDOM_DEVICE_ADDRESS = 0x01,
+}
+
+struct LeDirectedAdvertisingReport {
+  event_type : DirectAdvertisingEventType,
+  address_type : DirectAdvertisingAddressType,
+  address : Address,
+  direct_address_type : DirectAddressType,
+  direct_address : Address,
+  rssi : 8,
+}
+
+packet LeDirectedAdvertisingReport : LeMetaEvent (subevent_code = DIRECTED_ADVERTISING_REPORT) {
+  _count_(advertising_reports) : 8,
+  advertising_reports : LeDirectedAdvertisingReport[],
+}
+
+packet LePhyUpdateComplete : LeMetaEvent (subevent_code = PHY_UPDATE_COMPLETE) {
+  _payload_, // placeholder (unimplemented)
+}
+
+enum DataStatus : 2 {
+  COMPLETE = 0x0,
+  CONTINUING = 0x1,
+  TRUNCATED = 0x2,
+  RESERVED = 0x3,
+}
+
+struct LeExtendedAdvertisingReport {
+  connectable : 1,
+  scannable : 1,
+  directed : 1,
+  scan_response : 1,
+  data_status : DataStatus,
+  _reserved_ : 10,
+  address_type : DirectAdvertisingAddressType,
+  address : Address,
+  primary_phy : PrimaryPhyType,
+  secondary_phy : SecondaryPhyType,
+  advertising_sid : 4, // SID subfield in the ADI field
+  _reserved_ : 4,
+  tx_power : 8,
+  rssi : 8, // -127 to +20 (0x7F means not available)
+  periodic_advertising_interval : 16, // 0x006 to 0xFFFF (7.5 ms to 82s)
+  direct_address_type : DirectAdvertisingAddressType,
+  direct_address : Address,
+  _size_(advertising_data) : 8,
+  advertising_data : GapData[],
+}
+
+packet LeExtendedAdvertisingReport : LeMetaEvent (subevent_code = EXTENDED_ADVERTISING_REPORT) {
+  _count_(advertising_reports) : 8,
+  advertising_reports : LeExtendedAdvertisingReport[],
+}
+
+packet LePeriodicAdvertisingSyncEstablished : LeMetaEvent (subevent_code = PERIODIC_ADVERTISING_SYNC_ESTABLISHED) {
+  _payload_, // placeholder (unimplemented)
+}
+
+packet LePeriodicAdvertisingReport : LeMetaEvent (subevent_code = PERIODIC_ADVERTISING_REPORT) {
+  _payload_, // placeholder (unimplemented)
+}
+
+packet LePeriodicAdvertisingSyncLost : LeMetaEvent (subevent_code = PERIODIC_ADVERTISING_SYNC_LOST) {
+  _payload_, // placeholder (unimplemented)
+}
+
+packet LeScanTimeout : LeMetaEvent (subevent_code = SCAN_TIMEOUT) {
+}
+
+packet LeAdvertisingSetTerminated : LeMetaEvent (subevent_code = ADVERTISING_SET_TERMINATED) {
+  status : ErrorCode,
+  advertising_handle : 8,
+  connection_handle : 12,
+  _reserved_ : 4,
+  num_completed_extended_advertising_events : 8,
+}
+
+packet LeScanRequestReceived : LeMetaEvent (subevent_code = SCAN_REQUEST_RECEIVED) {
+  advertising_handle : 8,
+  scanner_address_type : AddressType,
+  scanner_address : Address,
+}
+
+// Vendor specific events
+
+packet VendorSpecificEvent : EventPacket (event_code = VENDOR_SPECIFIC) {
+  subevent_code : VseSubeventCode,
+  _payload_,
+}
+
+enum qualityReportId : 8 {
+  MONITOR_MODE = 0x01,
+  APPROACH_LSTO = 0x02,
+  A2DP_AUDIO_CHOPPY = 0x03,
+  SCO_VOICE_CHOPPY = 0x04,
+  ROOT_INFLAMMATION = 0x05,
+  LMP_LL_MESSAGE_TRACE = 0x11,
+  BT_SCHEDULING_TRACE = 0x12,
+  CONTROLLER_DBG_INFO = 0x13,
+}
+
+packet BqrEvent : VendorSpecificEvent (subevent_code = BQR_EVENT) {
+  quality_report_id : qualityReportId,
+  _payload_,
+}
+
+enum BqrPacketType : 8 {
+  TYPE_ID = 0x01,
+  TYPE_NULL = 0x02,
+  TYPE_POLL = 0x03,
+  TYPE_FHS = 0x04,
+  TYPE_HV1 = 0x05,
+  TYPE_HV2 = 0x06,
+  TYPE_HV3 = 0x07,
+  TYPE_DV = 0x08,
+  TYPE_EV3 = 0x09,
+  TYPE_EV4 = 0x0A,
+  TYPE_EV5 = 0x0B,
+  TYPE_2EV3 = 0x0C,
+  TYPE_2EV5 = 0x0D,
+  TYPE_3EV3 = 0x0E,
+  TYPE_3EV5 = 0x0F,
+  TYPE_DM1 = 0x10,
+  TYPE_DH1 = 0x11,
+  TYPE_DM3 = 0x12,
+  TYPE_DH3 = 0x13,
+  TYPE_DM5 = 0x14,
+  TYPE_DH5 = 0x15,
+  TYPE_AUX1 = 0x16,
+  TYPE_2DH1 = 0x17,
+  TYPE_2DH3 = 0x18,
+  TYPE_2DH5 = 0x19,
+  TYPE_3DH1 = 0x1A,
+  TYPE_3DH3 = 0x1B,
+  TYPE_3DH5 = 0x1C,
+}
+
+packet BqrLinkQualityEvent : BqrEvent {
+  packet_type : BqrPacketType,
+  connection_handle : 12,
+  _reserved_ : 4,
+  connection_role : Role,
+  tx_power_level : 8,
+  rssi : 8,
+  snr : 8,
+  unused_afh_channel_count : 8,
+  afh_select_unideal_channel_count : 8,
+  lsto : 16,
+  connection_piconet_clock : 32,
+  retransmission_count : 32,
+  no_rx_count : 32,
+  nak_count : 32,
+  last_tx_ack_timestamp : 32,
+  flow_off_count : 32,
+  last_flow_on_timestamp : 32,
+  buffer_overflow_bytes : 32,
+  buffer_underflow_bytes : 32,
+  _payload_,
+}
+
+packet BqrMonitorModeEvent : BqrLinkQualityEvent (quality_report_id = MONITOR_MODE) {
+ _payload_, // vendor specific parameter
+}
+
+packet BqrApproachLstoEvent : BqrLinkQualityEvent (quality_report_id = APPROACH_LSTO) {
+ _payload_, // vendor specific parameter
+}
+
+packet BqrA2dpAudioChoppyEvent : BqrLinkQualityEvent (quality_report_id = A2DP_AUDIO_CHOPPY) {
+  _payload_, // vendor specific parameter
+}
+
+packet BqrScoVoiceChoppyEvent : BqrLinkQualityEvent (quality_report_id = SCO_VOICE_CHOPPY) {
+ _payload_, // vendor specific parameter
+}
+
+packet BqrRootInflammationEvent : BqrEvent (quality_report_id = ROOT_INFLAMMATION) {
+  error_code : 8,
+  vendor_specific_error_code : 8,
+  _payload_, // vendor specific parameter
+}
+
+packet BqrLogDumpEvent : BqrEvent {
+  connection_handle : 12,
+  _reserved_ : 4,
+  _payload_,
+}
+
+packet BqrLmpLlMessageTraceEvent : BqrLogDumpEvent (quality_report_id = LMP_LL_MESSAGE_TRACE) {
+  _payload_, // vendor specific parameter
+}
+
+packet BqrBtSchedulingTraceEvent : BqrLogDumpEvent (quality_report_id = BT_SCHEDULING_TRACE) {
+ _payload_, // vendor specific parameter
+}
+
+packet BqrControllerDbgInfoEvent : BqrLogDumpEvent (quality_report_id = CONTROLLER_DBG_INFO) {
+ _payload_, // vendor specific parameter
+}
diff --git a/gd/hci/hci_packets_fuzz_test.cc b/gd/hci/hci_packets_fuzz_test.cc
new file mode 100644
index 0000000..5ef3ef6
--- /dev/null
+++ b/gd/hci/hci_packets_fuzz_test.cc
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define PACKET_FUZZ_TESTING
+#include "hci/hci_packets.h"
+
+#include <memory>
+
+#include "os/log.h"
+#include "packet/bit_inserter.h"
+#include "packet/raw_builder.h"
+
+using bluetooth::packet::BitInserter;
+using bluetooth::packet::RawBuilder;
+using std::vector;
+
+namespace bluetooth {
+namespace hci {
+
+std::vector<void (*)(const uint8_t*, size_t)> hci_packet_fuzz_tests;
+
+DEFINE_AND_REGISTER_ResetReflectionFuzzTest(hci_packet_fuzz_tests);
+
+DEFINE_AND_REGISTER_ResetCompleteReflectionFuzzTest(hci_packet_fuzz_tests);
+
+DEFINE_AND_REGISTER_ReadBufferSizeReflectionFuzzTest(hci_packet_fuzz_tests);
+
+DEFINE_AND_REGISTER_ReadBufferSizeCompleteReflectionFuzzTest(hci_packet_fuzz_tests);
+
+DEFINE_AND_REGISTER_HostBufferSizeReflectionFuzzTest(hci_packet_fuzz_tests);
+
+DEFINE_AND_REGISTER_HostBufferSizeCompleteReflectionFuzzTest(hci_packet_fuzz_tests);
+
+DEFINE_AND_REGISTER_ReadLocalVersionInformationReflectionFuzzTest(hci_packet_fuzz_tests);
+
+DEFINE_AND_REGISTER_ReadLocalVersionInformationCompleteReflectionFuzzTest(hci_packet_fuzz_tests);
+
+DEFINE_AND_REGISTER_ReadBdAddrReflectionFuzzTest(hci_packet_fuzz_tests);
+
+DEFINE_AND_REGISTER_ReadBdAddrCompleteReflectionFuzzTest(hci_packet_fuzz_tests);
+
+DEFINE_AND_REGISTER_ReadLocalSupportedCommandsReflectionFuzzTest(hci_packet_fuzz_tests);
+
+DEFINE_AND_REGISTER_ReadLocalSupportedCommandsCompleteReflectionFuzzTest(hci_packet_fuzz_tests);
+
+DEFINE_AND_REGISTER_WriteSimplePairingModeReflectionFuzzTest(hci_packet_fuzz_tests);
+
+DEFINE_AND_REGISTER_WriteSimplePairingModeCompleteReflectionFuzzTest(hci_packet_fuzz_tests);
+
+DEFINE_AND_REGISTER_WriteLeHostSupportReflectionFuzzTest(hci_packet_fuzz_tests);
+
+DEFINE_AND_REGISTER_WriteLeHostSupportCompleteReflectionFuzzTest(hci_packet_fuzz_tests);
+
+DEFINE_AND_REGISTER_ReadLocalExtendedFeaturesReflectionFuzzTest(hci_packet_fuzz_tests);
+
+DEFINE_AND_REGISTER_ReadLocalExtendedFeaturesCompleteReflectionFuzzTest(hci_packet_fuzz_tests);
+
+DEFINE_AND_REGISTER_WriteSecureConnectionsHostSupportReflectionFuzzTest(hci_packet_fuzz_tests);
+
+DEFINE_AND_REGISTER_WriteSecureConnectionsHostSupportCompleteReflectionFuzzTest(hci_packet_fuzz_tests);
+
+DEFINE_AND_REGISTER_LeReadWhiteListSizeReflectionFuzzTest(hci_packet_fuzz_tests);
+
+DEFINE_AND_REGISTER_LeReadWhiteListSizeCompleteReflectionFuzzTest(hci_packet_fuzz_tests);
+
+DEFINE_AND_REGISTER_LeReadBufferSizeReflectionFuzzTest(hci_packet_fuzz_tests);
+
+DEFINE_AND_REGISTER_LeReadBufferSizeCompleteReflectionFuzzTest(hci_packet_fuzz_tests);
+
+DEFINE_AND_REGISTER_WriteCurrentIacLapReflectionFuzzTest(hci_packet_fuzz_tests);
+
+DEFINE_AND_REGISTER_WriteCurrentIacLapCompleteReflectionFuzzTest(hci_packet_fuzz_tests);
+
+DEFINE_AND_REGISTER_WriteInquiryScanActivityReflectionFuzzTest(hci_packet_fuzz_tests);
+
+DEFINE_AND_REGISTER_WriteInquiryScanActivityCompleteReflectionFuzzTest(hci_packet_fuzz_tests);
+
+DEFINE_AND_REGISTER_ReadInquiryScanActivityReflectionFuzzTest(hci_packet_fuzz_tests);
+
+DEFINE_AND_REGISTER_ReadInquiryScanActivityCompleteReflectionFuzzTest(hci_packet_fuzz_tests);
+
+DEFINE_AND_REGISTER_ReadCurrentIacLapReflectionFuzzTest(hci_packet_fuzz_tests);
+
+DEFINE_AND_REGISTER_ReadCurrentIacLapCompleteReflectionFuzzTest(hci_packet_fuzz_tests);
+
+DEFINE_AND_REGISTER_ReadNumberOfSupportedIacReflectionFuzzTest(hci_packet_fuzz_tests);
+
+DEFINE_AND_REGISTER_ReadNumberOfSupportedIacCompleteReflectionFuzzTest(hci_packet_fuzz_tests);
+
+DEFINE_AND_REGISTER_ReadPageTimeoutReflectionFuzzTest(hci_packet_fuzz_tests);
+
+DEFINE_AND_REGISTER_ReadPageTimeoutCompleteReflectionFuzzTest(hci_packet_fuzz_tests);
+
+DEFINE_AND_REGISTER_WritePageTimeoutReflectionFuzzTest(hci_packet_fuzz_tests);
+
+DEFINE_AND_REGISTER_WritePageTimeoutCompleteReflectionFuzzTest(hci_packet_fuzz_tests);
+
+DEFINE_AND_REGISTER_InquiryReflectionFuzzTest(hci_packet_fuzz_tests);
+
+DEFINE_AND_REGISTER_InquiryStatusReflectionFuzzTest(hci_packet_fuzz_tests);
+
+DEFINE_AND_REGISTER_InquiryCancelReflectionFuzzTest(hci_packet_fuzz_tests);
+
+DEFINE_AND_REGISTER_InquiryCancelCompleteReflectionFuzzTest(hci_packet_fuzz_tests);
+
+DEFINE_AND_REGISTER_PeriodicInquiryModeReflectionFuzzTest(hci_packet_fuzz_tests);
+
+DEFINE_AND_REGISTER_PeriodicInquiryModeCompleteReflectionFuzzTest(hci_packet_fuzz_tests);
+
+DEFINE_AND_REGISTER_ExitPeriodicInquiryModeReflectionFuzzTest(hci_packet_fuzz_tests);
+
+DEFINE_AND_REGISTER_ExitPeriodicInquiryModeCompleteReflectionFuzzTest(hci_packet_fuzz_tests);
+
+}  // namespace hci
+}  // namespace bluetooth
+
+void RunHciPacketFuzzTest(const uint8_t* data, size_t size) {
+  if (data == nullptr) return;
+  for (auto test_function : bluetooth::hci::hci_packet_fuzz_tests) {
+    test_function(data, size);
+  }
+}
\ No newline at end of file
diff --git a/gd/hci/hci_packets_test.cc b/gd/hci/hci_packets_test.cc
new file mode 100644
index 0000000..02e7d70
--- /dev/null
+++ b/gd/hci/hci_packets_test.cc
@@ -0,0 +1,717 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define PACKET_TESTING
+#include "hci/hci_packets.h"
+
+#include <gtest/gtest.h>
+#include <forward_list>
+#include <memory>
+
+#include "os/log.h"
+#include "packet/bit_inserter.h"
+#include "packet/raw_builder.h"
+
+using bluetooth::packet::BitInserter;
+using bluetooth::packet::RawBuilder;
+using std::vector;
+
+namespace bluetooth {
+namespace hci {
+
+std::vector<uint8_t> reset = {0x03, 0x0c, 0x00};
+DEFINE_AND_INSTANTIATE_ResetReflectionTest(reset);
+
+std::vector<uint8_t> reset_complete = {0x0e, 0x04, 0x01, 0x03, 0x0c, 0x00};
+DEFINE_AND_INSTANTIATE_ResetCompleteReflectionTest(reset_complete);
+
+std::vector<uint8_t> read_buffer_size = {0x05, 0x10, 0x00};
+DEFINE_AND_INSTANTIATE_ReadBufferSizeReflectionTest(read_buffer_size);
+
+std::vector<uint8_t> read_buffer_size_complete = {0x0e, 0x0b, 0x01, 0x05, 0x10, 0x00, 0x00,
+                                                  0x04, 0x3c, 0x07, 0x00, 0x08, 0x00};
+DEFINE_AND_INSTANTIATE_ReadBufferSizeCompleteReflectionTest(read_buffer_size_complete);
+
+std::vector<uint8_t> host_buffer_size = {0x33, 0x0c, 0x07, 0x9b, 0x06, 0xff, 0x14, 0x00, 0x0a, 0x00};
+DEFINE_AND_INSTANTIATE_HostBufferSizeReflectionTest(host_buffer_size);
+
+std::vector<uint8_t> host_buffer_size_complete = {0x0e, 0x04, 0x01, 0x33, 0x0c, 0x00};
+DEFINE_AND_INSTANTIATE_HostBufferSizeCompleteReflectionTest(host_buffer_size_complete);
+
+std::vector<uint8_t> read_local_version_information = {0x01, 0x10, 0x00};
+DEFINE_AND_INSTANTIATE_ReadLocalVersionInformationReflectionTest(read_local_version_information);
+
+std::vector<uint8_t> read_local_version_information_complete = {0x0e, 0x0c, 0x01, 0x01, 0x10, 0x00, 0x09,
+                                                                0x00, 0x00, 0x09, 0x1d, 0x00, 0xbe, 0x02};
+DEFINE_AND_INSTANTIATE_ReadLocalVersionInformationCompleteReflectionTest(read_local_version_information_complete);
+
+std::vector<uint8_t> read_bd_addr = {0x09, 0x10, 0x00};
+DEFINE_AND_INSTANTIATE_ReadBdAddrReflectionTest(read_bd_addr);
+
+std::vector<uint8_t> read_bd_addr_complete = {0x0e, 0x0a, 0x01, 0x09, 0x10, 0x00, 0x14, 0x8e, 0x61, 0x5f, 0x36, 0x88};
+DEFINE_AND_INSTANTIATE_ReadBdAddrCompleteReflectionTest(read_bd_addr_complete);
+
+std::vector<uint8_t> read_local_supported_commands = {0x02, 0x10, 0x00};
+DEFINE_AND_INSTANTIATE_ReadLocalSupportedCommandsReflectionTest(read_local_supported_commands);
+
+std::vector<uint8_t> read_local_supported_commands_complete = {
+    0x0e, 0x44, 0x01, 0x02, 0x10, 0x00, /* Supported commands start here (total 64 bytes) */
+    0xff, 0xff, 0xff, 0x03, 0xce, 0xff, 0xef, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xf2, 0x0f, 0xe8, 0xfe,
+    0x3f, 0xf7, 0x83, 0xff, 0x1c, 0x00, 0x00, 0x00, 0x61, 0xff, 0xff, 0xff, 0x7f, 0xbe, 0x20, 0xf5,
+    0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+DEFINE_AND_INSTANTIATE_ReadLocalSupportedCommandsCompleteReflectionTest(read_local_supported_commands_complete);
+
+std::vector<uint8_t> read_local_extended_features_0 = {0x04, 0x10, 0x01, 0x00};
+
+std::vector<uint8_t> read_local_extended_features_complete_0 = {0x0e, 0x0e, 0x01, 0x04, 0x10, 0x00, 0x00, 0x02,
+                                                                0xff, 0xfe, 0x8f, 0xfe, 0xd8, 0x3f, 0x5b, 0x87};
+
+std::vector<uint8_t> write_simple_paring_mode = {0x56, 0x0c, 0x01, 0x01};
+DEFINE_AND_INSTANTIATE_WriteSimplePairingModeReflectionTest(write_simple_paring_mode);
+
+std::vector<uint8_t> write_simple_paring_mode_complete = {0x0e, 0x04, 0x01, 0x56, 0x0c, 0x00};
+DEFINE_AND_INSTANTIATE_WriteSimplePairingModeCompleteReflectionTest(write_simple_paring_mode_complete);
+
+std::vector<uint8_t> write_le_host_supported = {0x6d, 0x0c, 0x02, 0x01, 0x01};
+DEFINE_AND_INSTANTIATE_WriteLeHostSupportReflectionTest(write_le_host_supported);
+
+std::vector<uint8_t> write_le_host_supported_complete = {0x0e, 0x04, 0x01, 0x6d, 0x0c, 0x00};
+DEFINE_AND_INSTANTIATE_WriteLeHostSupportCompleteReflectionTest(write_le_host_supported_complete);
+
+std::vector<uint8_t> read_local_extended_features_1 = {0x04, 0x10, 0x01, 0x01};
+
+std::vector<uint8_t> read_local_extended_features_complete_1 = {0x0e, 0x0e, 0x01, 0x04, 0x10, 0x00, 0x01, 0x02,
+                                                                0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+std::vector<uint8_t> read_local_extended_features_2 = {0x04, 0x10, 0x01, 0x02};
+DEFINE_AND_INSTANTIATE_ReadLocalExtendedFeaturesReflectionTest(read_local_extended_features_0,
+                                                               read_local_extended_features_1,
+                                                               read_local_extended_features_2);
+
+std::vector<uint8_t> read_local_extended_features_complete_2 = {0x0e, 0x0e, 0x01, 0x04, 0x10, 0x00, 0x02, 0x02,
+                                                                0x45, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+DEFINE_AND_INSTANTIATE_ReadLocalExtendedFeaturesCompleteReflectionTest(read_local_extended_features_complete_0,
+                                                                       read_local_extended_features_complete_1,
+                                                                       read_local_extended_features_complete_2);
+
+std::vector<uint8_t> write_secure_connections_host_support = {0x7a, 0x0c, 0x01, 0x01};
+DEFINE_AND_INSTANTIATE_WriteSecureConnectionsHostSupportReflectionTest(write_secure_connections_host_support);
+
+std::vector<uint8_t> write_secure_connections_host_support_complete = {0x0e, 0x04, 0x01, 0x7a, 0x0c, 0x00};
+DEFINE_AND_INSTANTIATE_WriteSecureConnectionsHostSupportCompleteReflectionTest(
+    write_secure_connections_host_support_complete);
+
+std::vector<uint8_t> le_read_white_list_size = {0x0f, 0x20, 0x00};
+DEFINE_AND_INSTANTIATE_LeReadWhiteListSizeReflectionTest(le_read_white_list_size);
+
+std::vector<uint8_t> le_read_white_list_size_complete = {0x0e, 0x05, 0x01, 0x0f, 0x20, 0x00, 0x80};
+DEFINE_AND_INSTANTIATE_LeReadWhiteListSizeCompleteReflectionTest(le_read_white_list_size_complete);
+
+std::vector<uint8_t> le_read_buffer_size = {0x02, 0x20, 0x00};
+DEFINE_AND_INSTANTIATE_LeReadBufferSizeReflectionTest(le_read_buffer_size);
+
+std::vector<uint8_t> le_read_buffer_size_complete = {0x0e, 0x07, 0x01, 0x02, 0x20, 0x00, 0xfb, 0x00, 0x10};
+DEFINE_AND_INSTANTIATE_LeReadBufferSizeCompleteReflectionTest(le_read_buffer_size_complete);
+
+std::vector<uint8_t> write_current_iac_laps = {0x3a, 0x0c, 0x07, 0x02, 0x11, 0x8b, 0x9e, 0x22, 0x8b, 0x9e};
+DEFINE_AND_INSTANTIATE_WriteCurrentIacLapReflectionTest(write_current_iac_laps);
+
+std::vector<uint8_t> write_current_iac_laps_complete = {0x0e, 0x04, 0x01, 0x3a, 0x0c, 0x00};
+DEFINE_AND_INSTANTIATE_WriteCurrentIacLapCompleteReflectionTest(write_current_iac_laps_complete);
+
+std::vector<uint8_t> write_inquiry_scan_activity = {0x1e, 0x0c, 0x04, 0x00, 0x08, 0x12, 0x00};
+DEFINE_AND_INSTANTIATE_WriteInquiryScanActivityReflectionTest(write_inquiry_scan_activity);
+
+std::vector<uint8_t> write_inquiry_scan_activity_complete = {0x0e, 0x04, 0x01, 0x1e, 0x0c, 0x00};
+DEFINE_AND_INSTANTIATE_WriteInquiryScanActivityCompleteReflectionTest(write_inquiry_scan_activity_complete);
+
+std::vector<uint8_t> read_inquiry_scan_activity = {0x1d, 0x0c, 0x00};
+DEFINE_AND_INSTANTIATE_ReadInquiryScanActivityReflectionTest(read_inquiry_scan_activity);
+
+std::vector<uint8_t> read_inquiry_scan_activity_complete = {0x0e, 0x08, 0x01, 0x1d, 0x0c, 0x00, 0xaa, 0xbb, 0xcc, 0xdd};
+DEFINE_AND_INSTANTIATE_ReadInquiryScanActivityCompleteReflectionTest(read_inquiry_scan_activity_complete);
+
+std::vector<uint8_t> read_current_iac_lap = {0x39, 0x0c, 0x00};
+DEFINE_AND_INSTANTIATE_ReadCurrentIacLapReflectionTest(read_current_iac_lap);
+
+std::vector<uint8_t> read_current_iac_lap_complete = {0x0e, 0x0b, 0x01, 0x39, 0x0c, 0x00, 0x02,
+                                                      0x11, 0x8b, 0x9e, 0x22, 0x8b, 0x9e};
+DEFINE_AND_INSTANTIATE_ReadCurrentIacLapCompleteReflectionTest(read_current_iac_lap_complete);
+
+std::vector<uint8_t> read_number_of_supported_iac = {0x38, 0x0c, 0x00};
+DEFINE_AND_INSTANTIATE_ReadNumberOfSupportedIacReflectionTest(read_number_of_supported_iac);
+
+std::vector<uint8_t> read_number_of_supported_iac_complete = {0x0e, 0x05, 0x01, 0x38, 0x0c, 0x00, 0x99};
+DEFINE_AND_INSTANTIATE_ReadNumberOfSupportedIacCompleteReflectionTest(read_number_of_supported_iac_complete);
+
+std::vector<uint8_t> read_page_timeout = {0x17, 0x0c, 0x00};
+DEFINE_AND_INSTANTIATE_ReadPageTimeoutReflectionTest(read_page_timeout);
+
+std::vector<uint8_t> read_page_timeout_complete = {0x0e, 0x06, 0x01, 0x17, 0x0c, 0x00, 0x11, 0x22};
+DEFINE_AND_INSTANTIATE_ReadPageTimeoutCompleteReflectionTest(read_page_timeout_complete);
+
+std::vector<uint8_t> write_page_timeout = {0x18, 0x0c, 0x02, 0x00, 0x20};
+DEFINE_AND_INSTANTIATE_WritePageTimeoutReflectionTest(write_page_timeout);
+
+std::vector<uint8_t> write_page_timeout_complete = {0x0e, 0x04, 0x01, 0x18, 0x0c, 0x00};
+DEFINE_AND_INSTANTIATE_WritePageTimeoutCompleteReflectionTest(write_page_timeout_complete);
+
+std::vector<uint8_t> inquiry = {0x01, 0x04, 0x05, 0x33, 0x8b, 0x9e, 0xaa, 0xbb};
+DEFINE_AND_INSTANTIATE_InquiryReflectionTest(inquiry);
+
+std::vector<uint8_t> inquiry_status = {0x0f, 0x04, 0x00, 0x01, 0x01, 0x04};
+DEFINE_AND_INSTANTIATE_InquiryStatusReflectionTest(inquiry_status);
+
+std::vector<uint8_t> inquiry_cancel = {0x02, 0x04, 0x00};
+DEFINE_AND_INSTANTIATE_InquiryCancelReflectionTest(inquiry_cancel);
+
+std::vector<uint8_t> inquiry_cancel_complete = {0x0e, 0x04, 0x01, 0x02, 0x04, 0x00};
+DEFINE_AND_INSTANTIATE_InquiryCancelCompleteReflectionTest(inquiry_cancel_complete);
+
+std::vector<uint8_t> periodic_inquiry_mode = {0x03, 0x04, 0x09, 0x12, 0x34, 0x56, 0x78, 0x11, 0x8b, 0x9e, 0x9a, 0xbc};
+DEFINE_AND_INSTANTIATE_PeriodicInquiryModeReflectionTest(periodic_inquiry_mode);
+
+std::vector<uint8_t> periodic_inquiry_mode_complete = {0x0e, 0x04, 0x01, 0x03, 0x04, 0x00};
+DEFINE_AND_INSTANTIATE_PeriodicInquiryModeCompleteReflectionTest(periodic_inquiry_mode_complete);
+
+std::vector<uint8_t> exit_periodic_inquiry_mode = {0x04, 0x04, 0x00};
+DEFINE_AND_INSTANTIATE_ExitPeriodicInquiryModeReflectionTest(exit_periodic_inquiry_mode);
+
+std::vector<uint8_t> exit_periodic_inquiry_mode_complete = {0x0e, 0x04, 0x01, 0x04, 0x04, 0x00};
+DEFINE_AND_INSTANTIATE_ExitPeriodicInquiryModeCompleteReflectionTest(exit_periodic_inquiry_mode_complete);
+
+std::vector<uint8_t> pixel_3_xl_write_extended_inquiry_response{
+    0x52, 0x0c, 0xf1, 0x01, 0x0b, 0x09, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x20, 0x33, 0x20, 0x58, 0x4c, 0x19, 0x03, 0x05,
+    0x11, 0x0a, 0x11, 0x0c, 0x11, 0x0e, 0x11, 0x12, 0x11, 0x15, 0x11, 0x16, 0x11, 0x1f, 0x11, 0x2d, 0x11, 0x2f, 0x11,
+    0x00, 0x12, 0x32, 0x11, 0x01, 0x05, 0x81, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+std::vector<uint8_t> pixel_3_xl_write_extended_inquiry_response_no_uuids{
+    0x52, 0x0c, 0xf1, 0x01, 0x0b, 0x09, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x20, 0x33, 0x20, 0x58, 0x4c, 0x01, 0x03, 0x01,
+    0x05, 0x81, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+std::vector<uint8_t> pixel_3_xl_write_extended_inquiry_response_no_uuids_just_eir{
+    pixel_3_xl_write_extended_inquiry_response_no_uuids.begin() + 4,  // skip command, size, and fec_required
+    pixel_3_xl_write_extended_inquiry_response_no_uuids.end()};
+
+TEST(HciPacketsTest, testWriteExtendedInquiryResponse) {
+  std::shared_ptr<std::vector<uint8_t>> view_bytes =
+      std::make_shared<std::vector<uint8_t>>(pixel_3_xl_write_extended_inquiry_response);
+
+  PacketView<kLittleEndian> packet_bytes_view(view_bytes);
+  auto view = WriteExtendedInquiryResponseView::Create(CommandPacketView::Create(packet_bytes_view));
+  ASSERT_TRUE(view.IsValid());
+  auto gap_data = view.GetExtendedInquiryResponse();
+  ASSERT_GE(gap_data.size(), 4);
+  ASSERT_EQ(gap_data[0].data_type_, GapDataType::COMPLETE_LOCAL_NAME);
+  ASSERT_EQ(gap_data[0].data_.size(), 10);
+  ASSERT_EQ(gap_data[1].data_type_, GapDataType::COMPLETE_LIST_16_BIT_UUIDS);
+  ASSERT_EQ(gap_data[1].data_.size(), 24);
+  ASSERT_EQ(gap_data[2].data_type_, GapDataType::COMPLETE_LIST_32_BIT_UUIDS);
+  ASSERT_EQ(gap_data[2].data_.size(), 0);
+  ASSERT_EQ(gap_data[3].data_type_, GapDataType::COMPLETE_LIST_128_BIT_UUIDS);
+  ASSERT_EQ(gap_data[3].data_.size(), 128);
+
+  std::vector<GapData> no_padding{gap_data.begin(), gap_data.begin() + 4};
+  auto builder = WriteExtendedInquiryResponseBuilder::Create(view.GetFecRequired(), no_padding);
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  builder->Serialize(it);
+
+  EXPECT_EQ(packet_bytes->size(), view_bytes->size());
+  for (size_t i = 0; i < view_bytes->size(); i++) {
+    ASSERT_EQ(packet_bytes->at(i), view_bytes->at(i));
+  }
+}
+
+//  TODO: Revisit reflection tests for EIR
+// DEFINE_AND_INSTANTIATE_WriteExtendedInquiryResponseReflectionTest(pixel_3_xl_write_extended_inquiry_response,
+// pixel_3_xl_write_extended_inquiry_response_no_uuids);
+
+std::vector<uint8_t> le_set_scan_parameters{
+    0x0b, 0x20, 0x07, 0x01, 0x12, 0x00, 0x12, 0x00, 0x01, 0x00,
+};
+TEST(HciPacketsTest, testLeSetScanParameters) {
+  PacketView<kLittleEndian> packet_bytes_view(std::make_shared<std::vector<uint8_t>>(le_set_scan_parameters));
+  auto view =
+      LeSetScanParametersView::Create(LeScanningCommandView::Create(CommandPacketView::Create(packet_bytes_view)));
+
+  ASSERT(view.IsValid());
+  ASSERT_EQ(LeScanType::ACTIVE, view.GetLeScanType());
+  ASSERT_EQ(0x12, view.GetLeScanInterval());
+  ASSERT_EQ(0x12, view.GetLeScanWindow());
+  ASSERT_EQ(AddressType::RANDOM_DEVICE_ADDRESS, view.GetOwnAddressType());
+  ASSERT_EQ(LeSetScanningFilterPolicy::ACCEPT_ALL, view.GetScanningFilterPolicy());
+}
+
+DEFINE_AND_INSTANTIATE_LeSetScanParametersReflectionTest(le_set_scan_parameters);
+
+std::vector<uint8_t> le_set_scan_enable{
+    0x0c, 0x20, 0x02, 0x01, 0x00,
+};
+TEST(HciPacketsTest, testLeSetScanEnable) {
+  PacketView<kLittleEndian> packet_bytes_view(std::make_shared<std::vector<uint8_t>>(le_set_scan_enable));
+  auto view = LeSetScanEnableView::Create(LeScanningCommandView::Create(CommandPacketView::Create(packet_bytes_view)));
+
+  ASSERT(view.IsValid());
+  ASSERT_EQ(Enable::ENABLED, view.GetLeScanEnable());
+  ASSERT_EQ(Enable::DISABLED, view.GetFilterDuplicates());
+}
+
+DEFINE_AND_INSTANTIATE_LeSetScanEnableReflectionTest(le_set_scan_enable);
+
+std::vector<uint8_t> le_get_vendor_capabilities{
+    0x53,
+    0xfd,
+    0x00,
+};
+TEST(HciPacketsTest, testLeGetVendorCapabilities) {
+  PacketView<kLittleEndian> packet_bytes_view(std::make_shared<std::vector<uint8_t>>(le_get_vendor_capabilities));
+  auto view =
+      LeGetVendorCapabilitiesView::Create(VendorCommandView::Create(CommandPacketView::Create(packet_bytes_view)));
+
+  ASSERT(view.IsValid());
+}
+
+DEFINE_AND_INSTANTIATE_LeGetVendorCapabilitiesReflectionTest(le_get_vendor_capabilities);
+
+std::vector<uint8_t> le_get_vendor_capabilities_complete{
+    0x0e, 0x0c, 0x01, 0x53, 0xfd, 0x00, 0x05, 0x01, 0x00, 0x04, 0x80, 0x01, 0x10, 0x01,
+};
+TEST(HciPacketsTest, testLeGetVendorCapabilitiesComplete) {
+  PacketView<kLittleEndian> packet_bytes_view(
+      std::make_shared<std::vector<uint8_t>>(le_get_vendor_capabilities_complete));
+  auto view = LeGetVendorCapabilitiesCompleteView::Create(
+      CommandCompleteView::Create(EventPacketView::Create(packet_bytes_view)));
+
+  ASSERT(view.IsValid());
+  auto base_capabilities = view.GetBaseVendorCapabilities();
+  ASSERT_EQ(5, base_capabilities.max_advt_instances_);
+  ASSERT_EQ(1, base_capabilities.offloaded_resolution_of_private_address_);
+  ASSERT_EQ(1024, base_capabilities.total_scan_results_storage_);
+  ASSERT_EQ(128, base_capabilities.max_irk_list_sz_);
+  ASSERT_EQ(1, base_capabilities.filtering_support_);
+  ASSERT_EQ(16, base_capabilities.max_filter_);
+  ASSERT_EQ(1, base_capabilities.activity_energy_info_support_);
+}
+
+DEFINE_AND_INSTANTIATE_LeGetVendorCapabilitiesCompleteReflectionTest(le_get_vendor_capabilities_complete);
+
+std::vector<uint8_t> le_set_extended_scan_parameters{
+    0x41, 0x20, 0x08, 0x01, 0x00, 0x01, 0x01, 0x12, 0x00, 0x12, 0x00,
+};
+
+TEST(HciPacketsTest, testLeSetExtendedScanParameters) {
+  PacketView<kLittleEndian> packet_bytes_view(std::make_shared<std::vector<uint8_t>>(le_set_extended_scan_parameters));
+  auto view = LeSetExtendedScanParametersView::Create(
+      LeScanningCommandView::Create(CommandPacketView::Create(packet_bytes_view)));
+
+  ASSERT(view.IsValid());
+  ASSERT_EQ(1, view.GetScanningPhys());
+  auto params = view.GetParameters();
+  ASSERT_EQ(1, params.size());
+  ASSERT_EQ(LeScanType::ACTIVE, params[0].le_scan_type_);
+  ASSERT_EQ(18, params[0].le_scan_interval_);
+  ASSERT_EQ(18, params[0].le_scan_window_);
+}
+
+std::vector<uint8_t> le_set_extended_scan_parameters_6553{
+    0x41, 0x20, 0x08, 0x01, 0x00, 0x01, 0x01, 0x99, 0x19, 0x99, 0x19,
+};
+
+TEST(HciPacketsTest, testLeSetExtendedScanParameters_6553) {
+  PacketView<kLittleEndian> packet_bytes_view(
+      std::make_shared<std::vector<uint8_t>>(le_set_extended_scan_parameters_6553));
+  auto view = LeSetExtendedScanParametersView::Create(
+      LeScanningCommandView::Create(CommandPacketView::Create(packet_bytes_view)));
+
+  ASSERT(view.IsValid());
+  ASSERT_EQ(1, view.GetScanningPhys());
+  auto params = view.GetParameters();
+  ASSERT_EQ(1, params.size());
+  ASSERT_EQ(LeScanType::ACTIVE, params[0].le_scan_type_);
+  ASSERT_EQ(6553, params[0].le_scan_interval_);
+  ASSERT_EQ(6553, params[0].le_scan_window_);
+}
+
+DEFINE_AND_INSTANTIATE_LeSetExtendedScanParametersReflectionTest(le_set_extended_scan_parameters,
+                                                                 le_set_extended_scan_parameters_6553);
+
+std::vector<uint8_t> le_set_extended_scan_parameters_complete{
+    0x0e, 0x04, 0x01, 0x41, 0x20, 0x00,
+};
+DEFINE_AND_INSTANTIATE_LeSetExtendedScanParametersCompleteReflectionTest(le_set_extended_scan_parameters_complete);
+
+std::vector<uint8_t> le_set_extended_scan_enable{
+    0x42, 0x20, 0x06, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+TEST(HciPacketsTest, testLeSetExtendedScanEnable) {
+  PacketView<kLittleEndian> packet_bytes_view(std::make_shared<std::vector<uint8_t>>(le_set_extended_scan_enable));
+  auto view =
+      LeSetExtendedScanEnableView::Create(LeScanningCommandView::Create(CommandPacketView::Create(packet_bytes_view)));
+
+  ASSERT(view.IsValid());
+  ASSERT_EQ(FilterDuplicates::DISABLED, view.GetFilterDuplicates());
+  ASSERT_EQ(Enable::ENABLED, view.GetEnable());
+  ASSERT_EQ(0, view.GetDuration());
+  ASSERT_EQ(0, view.GetPeriod());
+}
+
+std::vector<uint8_t> le_set_extended_scan_enable_disable{
+    0x42, 0x20, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+};
+
+TEST(HciPacketsTest, testLeSetExtendedScanEnableDisable) {
+  PacketView<kLittleEndian> packet_bytes_view(
+      std::make_shared<std::vector<uint8_t>>(le_set_extended_scan_enable_disable));
+  auto view =
+      LeSetExtendedScanEnableView::Create(LeScanningCommandView::Create(CommandPacketView::Create(packet_bytes_view)));
+
+  ASSERT(view.IsValid());
+  ASSERT_EQ(FilterDuplicates::ENABLED, view.GetFilterDuplicates());
+  ASSERT_EQ(Enable::DISABLED, view.GetEnable());
+  ASSERT_EQ(0, view.GetDuration());
+  ASSERT_EQ(0, view.GetPeriod());
+}
+
+DEFINE_AND_INSTANTIATE_LeSetExtendedScanEnableReflectionTest(le_set_extended_scan_enable,
+                                                             le_set_extended_scan_enable_disable);
+
+std::vector<uint8_t> le_set_extended_scan_enable_complete{
+    0x0e, 0x04, 0x01, 0x42, 0x20, 0x00,
+};
+DEFINE_AND_INSTANTIATE_LeSetExtendedScanEnableCompleteReflectionTest(le_set_extended_scan_enable_complete);
+
+std::vector<uint8_t> le_extended_create_connection = {
+    0x43, 0x20, 0x2a, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x08,
+    0x30, 0x00, 0x18, 0x00, 0x28, 0x00, 0x00, 0x00, 0xf4, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x08, 0x30, 0x00, 0x18, 0x00, 0x28, 0x00, 0x00, 0x00, 0xf4, 0x01, 0x00, 0x00, 0x00, 0x00};
+DEFINE_AND_INSTANTIATE_LeExtendedCreateConnectionReflectionTest(le_extended_create_connection);
+
+TEST(HciPacketsTest, testLeExtendedCreateConnection) {
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes =
+      std::make_shared<std::vector<uint8_t>>(le_extended_create_connection);
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  auto view = LeExtendedCreateConnectionView::Create(
+      LeConnectionManagementCommandView::Create(CommandPacketView::Create(packet_bytes_view)));
+  ASSERT_TRUE(view.IsValid());
+}
+
+std::vector<uint8_t> le_set_extended_advertising_random_address = {
+    0x35, 0x20, 0x07, 0x00, 0x77, 0x58, 0xeb, 0xd3, 0x1c, 0x6e,
+};
+
+TEST(HciPacketsTest, testLeSetExtendedAdvertisingRandomAddress) {
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes =
+      std::make_shared<std::vector<uint8_t>>(le_set_extended_advertising_random_address);
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  auto view = LeSetExtendedAdvertisingRandomAddressView::Create(
+      LeAdvertisingCommandView::Create(CommandPacketView::Create(packet_bytes_view)));
+  ASSERT_TRUE(view.IsValid());
+  uint8_t random_address_bytes[] = {0x77, 0x58, 0xeb, 0xd3, 0x1c, 0x6e};
+  ASSERT_EQ(0, view.GetAdvertisingHandle());
+  ASSERT_EQ(Address(random_address_bytes), view.GetAdvertisingRandomAddress());
+}
+DEFINE_AND_INSTANTIATE_LeSetExtendedAdvertisingRandomAddressReflectionTest(le_set_extended_advertising_random_address);
+
+std::vector<uint8_t> le_set_extended_advertising_random_address_complete{
+    0x0e, 0x04, 0x01, 0x35, 0x20, 0x00,
+};
+DEFINE_AND_INSTANTIATE_LeSetExtendedAdvertisingRandomAddressCompleteReflectionTest(
+    le_set_extended_advertising_random_address_complete);
+
+std::vector<uint8_t> le_set_extended_advertising_data{
+    0x37, 0x20, 0x12, 0x00, 0x03, 0x01, 0x0e, 0x02, 0x01, 0x02, 0x0a,
+    0x09, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x20, 0x33, 0x20, 0x58,
+};
+TEST(HciPacketsTest, testLeSetExtendedAdvertisingData) {
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes =
+      std::make_shared<std::vector<uint8_t>>(le_set_extended_advertising_data);
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  auto view = LeSetExtendedAdvertisingDataRawView::Create(
+      LeAdvertisingCommandView::Create(CommandPacketView::Create(packet_bytes_view)));
+  ASSERT_TRUE(view.IsValid());
+  ASSERT_EQ(0, view.GetAdvertisingHandle());
+  ASSERT_EQ(Operation::COMPLETE_ADVERTISEMENT, view.GetOperation());
+  ASSERT_EQ(FragmentPreference::CONTROLLER_SHOULD_NOT, view.GetFragmentPreference());
+  std::vector<uint8_t> advertising_data{
+      0x02, 0x01, 0x02, 0x0a, 0x09, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x20, 0x33, 0x20, 0x58,
+  };
+  ASSERT_EQ(advertising_data, view.GetAdvertisingData());
+}
+
+DEFINE_AND_INSTANTIATE_LeSetExtendedAdvertisingDataRawReflectionTest(le_set_extended_advertising_data);
+
+std::vector<uint8_t> le_set_extended_advertising_data_complete{
+    0x0e, 0x04, 0x01, 0x37, 0x20, 0x00,
+};
+DEFINE_AND_INSTANTIATE_LeSetExtendedAdvertisingDataCompleteReflectionTest(le_set_extended_advertising_data_complete);
+
+std::vector<uint8_t> le_set_extended_advertising_parameters_set_0{
+    0x36, 0x20, 0x19, 0x00, 0x13, 0x00, 0x90, 0x01, 0x00, 0xc2, 0x01, 0x00, 0x07, 0x01,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x01, 0x00, 0x01, 0x01, 0x00,
+};
+TEST(HciPacketsTest, testLeSetExtendedAdvertisingParametersLegacySet0) {
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes =
+      std::make_shared<std::vector<uint8_t>>(le_set_extended_advertising_parameters_set_0);
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  auto view = LeSetExtendedAdvertisingLegacyParametersView::Create(
+      LeAdvertisingCommandView::Create(CommandPacketView::Create(packet_bytes_view)));
+  ASSERT_TRUE(view.IsValid());
+  ASSERT_EQ(0, view.GetAdvertisingHandle());
+  ASSERT_EQ(400, view.GetPrimaryAdvertisingIntervalMin());
+  ASSERT_EQ(450, view.GetPrimaryAdvertisingIntervalMax());
+  ASSERT_EQ(0x7, view.GetPrimaryAdvertisingChannelMap());
+  ASSERT_EQ(OwnAddressType::RANDOM_DEVICE_ADDRESS, view.GetOwnAddressType());
+  ASSERT_EQ(PeerAddressType::PUBLIC_DEVICE_OR_IDENTITY_ADDRESS, view.GetPeerAddressType());
+  ASSERT_EQ(Address::kEmpty, view.GetPeerAddress());
+  ASSERT_EQ(AdvertisingFilterPolicy::ALL_DEVICES, view.GetAdvertisingFilterPolicy());
+  ASSERT_EQ(1, view.GetAdvertisingSid());
+  ASSERT_EQ(Enable::DISABLED, view.GetScanRequestNotificationEnable());
+}
+
+std::vector<uint8_t> le_set_extended_advertising_parameters_set_1{
+    0x36, 0x20, 0x19, 0x01, 0x13, 0x00, 0x90, 0x01, 0x00, 0xc2, 0x01, 0x00, 0x07, 0x01,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x01, 0x00, 0x01, 0x01, 0x00,
+};
+TEST(HciPacketsTest, testLeSetExtendedAdvertisingParametersSet1) {
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes =
+      std::make_shared<std::vector<uint8_t>>(le_set_extended_advertising_parameters_set_1);
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  auto view = LeSetExtendedAdvertisingLegacyParametersView::Create(
+      LeAdvertisingCommandView::Create(CommandPacketView::Create(packet_bytes_view)));
+  ASSERT_TRUE(view.IsValid());
+  ASSERT_EQ(1, view.GetAdvertisingHandle());
+  ASSERT_EQ(400, view.GetPrimaryAdvertisingIntervalMin());
+  ASSERT_EQ(450, view.GetPrimaryAdvertisingIntervalMax());
+  ASSERT_EQ(0x7, view.GetPrimaryAdvertisingChannelMap());
+  ASSERT_EQ(OwnAddressType::RANDOM_DEVICE_ADDRESS, view.GetOwnAddressType());
+  ASSERT_EQ(PeerAddressType::PUBLIC_DEVICE_OR_IDENTITY_ADDRESS, view.GetPeerAddressType());
+  ASSERT_EQ(Address::kEmpty, view.GetPeerAddress());
+  ASSERT_EQ(AdvertisingFilterPolicy::ALL_DEVICES, view.GetAdvertisingFilterPolicy());
+  ASSERT_EQ(1, view.GetAdvertisingSid());
+  ASSERT_EQ(Enable::DISABLED, view.GetScanRequestNotificationEnable());
+}
+
+DEFINE_AND_INSTANTIATE_LeSetExtendedAdvertisingLegacyParametersReflectionTest(
+    le_set_extended_advertising_parameters_set_0, le_set_extended_advertising_parameters_set_1);
+
+std::vector<uint8_t> le_set_extended_advertising_parameters_complete{0x0e, 0x05, 0x01, 0x36, 0x20, 0x00, 0xf5};
+TEST(HciPacketsTest, testLeSetExtendedAdvertisingParametersComplete) {
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes =
+      std::make_shared<std::vector<uint8_t>>(le_set_extended_advertising_parameters_complete);
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  auto view = LeSetExtendedAdvertisingParametersCompleteView::Create(
+      CommandCompleteView::Create(EventPacketView::Create(packet_bytes_view)));
+  ASSERT_TRUE(view.IsValid());
+  ASSERT_EQ(static_cast<uint8_t>(-11), view.GetSelectedTxPower());
+}
+
+DEFINE_AND_INSTANTIATE_LeSetExtendedAdvertisingParametersCompleteReflectionTest(
+    le_set_extended_advertising_parameters_complete);
+
+std::vector<uint8_t> le_remove_advertising_set_1{
+    0x3c,
+    0x20,
+    0x01,
+    0x01,
+};
+TEST(HciPacketsTest, testLeRemoveAdvertisingSet1) {
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes =
+      std::make_shared<std::vector<uint8_t>>(le_remove_advertising_set_1);
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  auto view = LeRemoveAdvertisingSetView::Create(
+      LeAdvertisingCommandView::Create(CommandPacketView::Create(packet_bytes_view)));
+  ASSERT_TRUE(view.IsValid());
+  ASSERT_EQ(1, view.GetAdvertisingHandle());
+}
+
+DEFINE_AND_INSTANTIATE_LeRemoveAdvertisingSetReflectionTest(le_remove_advertising_set_1);
+
+std::vector<uint8_t> le_remove_advertising_set_complete{
+    0x0e, 0x04, 0x01, 0x3c, 0x20, 0x00,
+};
+DEFINE_AND_INSTANTIATE_LeRemoveAdvertisingSetCompleteReflectionTest(le_remove_advertising_set_complete);
+
+std::vector<uint8_t> le_set_extended_advertising_disable_1{
+    0x39, 0x20, 0x06, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00,
+};
+TEST(HciPacketsTest, testLeSetExtendedAdvertisingDisable1) {
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes =
+      std::make_shared<std::vector<uint8_t>>(le_set_extended_advertising_disable_1);
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  auto view = LeSetExtendedAdvertisingDisableView::Create(
+      LeAdvertisingCommandView::Create(CommandPacketView::Create(packet_bytes_view)));
+  ASSERT_TRUE(view.IsValid());
+  auto disabled_set = view.GetDisabledSets();
+  ASSERT_EQ(1, disabled_set.size());
+  ASSERT_EQ(1, disabled_set[0].advertising_handle_);
+}
+
+DEFINE_AND_INSTANTIATE_LeSetExtendedAdvertisingDisableReflectionTest(le_set_extended_advertising_disable_1);
+
+std::vector<uint8_t> le_set_extended_advertising_enable_complete{
+    0x0e, 0x04, 0x01, 0x39, 0x20, 0x00,
+};
+DEFINE_AND_INSTANTIATE_LeSetExtendedAdvertisingEnableCompleteReflectionTest(
+    le_set_extended_advertising_enable_complete);
+
+TEST(HciPacketsTest, testLeSetAdvertisingDataBuilderLength) {
+  GapData gap_data;
+  gap_data.data_type_ = GapDataType::COMPLETE_LOCAL_NAME;
+  gap_data.data_ = std::vector<uint8_t>({'A', ' ', 'g', 'o', 'o', 'd', ' ', 'n', 'a', 'm', 'e'});
+  auto builder = LeSetAdvertisingDataBuilder::Create({gap_data});
+  ASSERT_EQ(2 /*opcode*/ + 1 /* parameter size */ + 1 /* data_length */ + 31 /* data */, builder->size());
+
+  auto packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  packet_bytes->reserve(builder->size());
+  BitInserter bit_inserter(*packet_bytes);
+  builder->Serialize(bit_inserter);
+  auto command_view = LeAdvertisingCommandView::Create(CommandPacketView::Create(packet_bytes));
+  ASSERT(command_view.IsValid());
+  ASSERT_EQ(1 /* data_length */ + 31 /* data */, command_view.GetPayload().size());
+  auto view = LeSetAdvertisingDataView::Create(command_view);
+  ASSERT(view.IsValid());
+}
+
+TEST(HciPacketsTest, testLeSetScanResponseDataBuilderLength) {
+  GapData gap_data;
+  gap_data.data_type_ = GapDataType::COMPLETE_LOCAL_NAME;
+  gap_data.data_ = std::vector<uint8_t>({'A', ' ', 'g', 'o', 'o', 'd', ' ', 'n', 'a', 'm', 'e'});
+  auto builder = LeSetScanResponseDataBuilder::Create({gap_data});
+  ASSERT_EQ(2 /*opcode*/ + 1 /* parameter size */ + 1 /*data_length */ + 31 /* data */, builder->size());
+
+  auto packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  packet_bytes->reserve(builder->size());
+  BitInserter bit_inserter(*packet_bytes);
+  builder->Serialize(bit_inserter);
+  auto command_view = LeAdvertisingCommandView::Create(CommandPacketView::Create(packet_bytes));
+  ASSERT(command_view.IsValid());
+  ASSERT_EQ(1 /* data_length */ + 31 /* data */, command_view.GetPayload().size());
+  auto view = LeSetScanResponseDataView::Create(command_view);
+  ASSERT(view.IsValid());
+}
+
+TEST(HciPacketsTest, testLeMultiAdvSetAdvertisingDataBuilderLength) {
+  GapData gap_data;
+  gap_data.data_type_ = GapDataType::COMPLETE_LOCAL_NAME;
+  gap_data.data_ = std::vector<uint8_t>({'A', ' ', 'g', 'o', 'o', 'd', ' ', 'n', 'a', 'm', 'e'});
+  uint8_t set = 3;
+  auto builder = LeMultiAdvtSetDataBuilder::Create({gap_data}, set);
+  ASSERT_EQ(2 /*opcode*/ + 1 /* parameter size */ + 1 /* data_length */ + 31 /* data */ + 1 /* set */, builder->size());
+
+  auto packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  packet_bytes->reserve(builder->size());
+  BitInserter bit_inserter(*packet_bytes);
+  builder->Serialize(bit_inserter);
+  auto command_view =
+      LeMultiAdvtView::Create(LeAdvertisingCommandView::Create(CommandPacketView::Create(packet_bytes)));
+  ASSERT(command_view.IsValid());
+  EXPECT_EQ(1 /* data_length */ + 31 /* data */ + 1 /* set */, command_view.GetPayload().size());
+  auto view = LeMultiAdvtSetDataView::Create(command_view);
+  ASSERT(view.IsValid());
+}
+
+TEST(HciPacketsTest, testLeMultiAdvSetScanResponseDataBuilderLength) {
+  GapData gap_data;
+  gap_data.data_type_ = GapDataType::COMPLETE_LOCAL_NAME;
+  gap_data.data_ = std::vector<uint8_t>({'A', ' ', 'g', 'o', 'o', 'd', ' ', 'n', 'a', 'm', 'e'});
+  uint8_t set = 3;
+  auto builder = LeMultiAdvtSetScanRespBuilder::Create({gap_data}, set);
+  EXPECT_EQ(2 /*opcode*/ + 1 /* parameter size */ + 1 /*data_length */ + 31 /* data */ + 1 /* set */, builder->size());
+
+  auto packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  packet_bytes->reserve(builder->size());
+  BitInserter bit_inserter(*packet_bytes);
+  builder->Serialize(bit_inserter);
+  auto command_view =
+      LeMultiAdvtView::Create(LeAdvertisingCommandView::Create(CommandPacketView::Create(packet_bytes)));
+  ASSERT(command_view.IsValid());
+  ASSERT_EQ(1 /* data_length */ + 31 /* data */ + 1 /* set */, command_view.GetPayload().size());
+  auto view = LeMultiAdvtSetScanRespView::Create(command_view);
+  ASSERT(view.IsValid());
+}
+
+std::vector<uint8_t> controller_bqr = {0x5e, 0xfd, 0x07, 0x00, 0x1f, 0x00, 0x07, 0x00, 0x88, 0x13};
+DEFINE_AND_INSTANTIATE_ControllerBqrReflectionTest(controller_bqr);
+
+std::vector<uint8_t> controller_bqr_complete = {0x0e, 0x08, 0x01, 0x5e, 0xfd, 0x00, 0x1f, 0x00, 0x07, 0x00};
+DEFINE_AND_INSTANTIATE_ControllerBqrCompleteReflectionTest(controller_bqr_complete);
+
+std::vector<uint8_t> bqr_monitor_mode_event = {
+    0xff, 0x31, 0x58, 0x01, 0x10, 0x02, 0x00, 0x00, 0x07, 0xd5, 0x00, 0x14, 0x00, 0x40, 0x1f, 0xed, 0x41,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x3c, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+DEFINE_AND_INSTANTIATE_BqrMonitorModeEventReflectionTest(bqr_monitor_mode_event);
+
+std::vector<uint8_t> bqr_approach_lsto_event = {
+    0xff, 0x48, 0x58, 0x02, 0x10, 0x02, 0x00, 0x01, 0x09, 0xaf, 0x00, 0x2d, 0x00, 0x00, 0x7d, 0x94, 0xe9, 0x03, 0x01,
+    0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x30, 0xa8, 0x0f, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x81, 0x9b, 0xf2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xcc, 0xcc, 0xcc, 0xcc, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x4e, 0x11, 0x00, 0x0c, 0x54, 0x10, 0x00};
+DEFINE_AND_INSTANTIATE_BqrApproachLstoEventReflectionTest(bqr_approach_lsto_event);
+
+std::vector<uint8_t> bqr_a2dp_audio_choppy_event = {
+    0xff, 0x41, 0x58, 0x03, 0x19, 0x09, 0x00, 0x00, 0x07, 0xcb, 0x00, 0x3a, 0x01, 0x40, 0x1f, 0x7e, 0xce,
+    0x58, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x7e, 0xce, 0x58,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0xd1, 0x57, 0x00, 0x30, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x0d, 0xce, 0x58, 0x00, 0x3a, 0xce, 0x58, 0x00, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x01};
+DEFINE_AND_INSTANTIATE_BqrA2dpAudioChoppyEventReflectionTest(bqr_a2dp_audio_choppy_event);
+
+std::vector<uint8_t> bqr_sco_voice_choppy_event = {
+    0xff, 0x4a, 0x58, 0x04, 0x09, 0x08, 0x00, 0x00, 0x08, 0xbf, 0x00, 0x03, 0x00, 0x40, 0x1f, 0x92, 0x6c, 0x0a, 0x0d,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x06, 0x02, 0x02, 0x0b,
+    0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x00};
+DEFINE_AND_INSTANTIATE_BqrScoVoiceChoppyEventReflectionTest(bqr_sco_voice_choppy_event);
+
+std::vector<uint8_t> bqr_root_inflammation_event = {0xff, 0x04, 0x58, 0x05, 0x00, 0xfe};
+DEFINE_AND_INSTANTIATE_BqrRootInflammationEventReflectionTest(bqr_root_inflammation_event);
+
+std::vector<uint8_t> bqr_lmp_ll_message_trace_event = {0xff, 0x11, 0x58, 0x11, 0x03, 0x00, 0x01, 0xff, 0x11, 0x55,
+                                                       0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55};
+DEFINE_AND_INSTANTIATE_BqrLmpLlMessageTraceEventReflectionTest(bqr_lmp_ll_message_trace_event);
+
+std::vector<uint8_t> bqr_bt_scheduling_trace_event = {0xff, 0x1d, 0x58, 0x12, 0x05, 0x00, 0x02, 0xd9, 0xae, 0x08, 0x01,
+                                                      0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11,
+                                                      0x00, 0x01, 0x0c, 0x00, 0x36, 0x3c, 0x00, 0x00, 0x00};
+DEFINE_AND_INSTANTIATE_BqrBtSchedulingTraceEventReflectionTest(bqr_bt_scheduling_trace_event);
+
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/le_advertising_interface.h b/gd/hci/le_advertising_interface.h
new file mode 100644
index 0000000..f915c02
--- /dev/null
+++ b/gd/hci/le_advertising_interface.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "common/callback.h"
+#include "hci/hci_packets.h"
+#include "os/handler.h"
+#include "os/utils.h"
+
+namespace bluetooth {
+namespace hci {
+
+class LeAdvertisingInterface {
+ public:
+  LeAdvertisingInterface() = default;
+  virtual ~LeAdvertisingInterface() = default;
+  DISALLOW_COPY_AND_ASSIGN(LeAdvertisingInterface);
+
+  virtual void EnqueueCommand(std::unique_ptr<LeAdvertisingCommandBuilder> command,
+                              common::OnceCallback<void(CommandCompleteView)> on_complete, os::Handler* handler) = 0;
+
+  virtual void EnqueueCommand(std::unique_ptr<LeAdvertisingCommandBuilder> command,
+                              common::OnceCallback<void(CommandStatusView)> on_status, os::Handler* handler) = 0;
+
+  static constexpr hci::SubeventCode LeAdvertisingEvents[] = {
+      hci::SubeventCode::SCAN_REQUEST_RECEIVED,
+      hci::SubeventCode::ADVERTISING_SET_TERMINATED,
+  };
+};
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/le_advertising_manager.cc b/gd/hci/le_advertising_manager.cc
new file mode 100644
index 0000000..6a99dcc
--- /dev/null
+++ b/gd/hci/le_advertising_manager.cc
@@ -0,0 +1,439 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <memory>
+#include <mutex>
+
+#include "hci/controller.h"
+#include "hci/hci_layer.h"
+#include "hci/hci_packets.h"
+#include "hci/le_advertising_interface.h"
+#include "hci/le_advertising_manager.h"
+#include "module.h"
+#include "os/handler.h"
+#include "os/log.h"
+
+namespace bluetooth {
+namespace hci {
+
+const ModuleFactory LeAdvertisingManager::Factory = ModuleFactory([]() { return new LeAdvertisingManager(); });
+
+enum class AdvertisingApiType {
+  LE_4_0 = 1,
+  ANDROID_HCI = 2,
+  LE_5_0 = 3,
+};
+
+struct Advertiser {
+  os::Handler* handler;
+  common::Callback<void(Address, AddressType)> scan_callback;
+  common::Callback<void(ErrorCode, uint8_t, uint8_t)> set_terminated_callback;
+};
+
+ExtendedAdvertisingConfig::ExtendedAdvertisingConfig(const AdvertisingConfig& config) : AdvertisingConfig(config) {
+  switch (config.event_type) {
+    case AdvertisingEventType::ADV_IND:
+      connectable = true;
+      scannable = true;
+      break;
+    case AdvertisingEventType::ADV_DIRECT_IND:
+      connectable = true;
+      directed = true;
+      high_duty_directed_connectable = true;
+      break;
+    case AdvertisingEventType::ADV_SCAN_IND:
+      scannable = true;
+      break;
+    case AdvertisingEventType::ADV_NONCONN_IND:
+      break;
+    case AdvertisingEventType::ADV_DIRECT_IND_LOW:
+      connectable = true;
+      directed = true;
+      break;
+    default:
+      LOG_WARN("Unknown event type");
+      break;
+  }
+  if (config.address_type == AddressType::PUBLIC_DEVICE_ADDRESS) {
+    own_address_type = OwnAddressType::PUBLIC_DEVICE_ADDRESS;
+  } else if (config.address_type == AddressType::RANDOM_DEVICE_ADDRESS) {
+    own_address_type = OwnAddressType::RANDOM_DEVICE_ADDRESS;
+  }
+  // TODO(b/149221472): Support fragmentation
+  operation = Operation::COMPLETE_ADVERTISEMENT;
+}
+
+struct LeAdvertisingManager::impl {
+  impl(Module* module) : module_(module), le_advertising_interface_(nullptr), num_instances_(0) {}
+
+  void start(os::Handler* handler, hci::HciLayer* hci_layer, hci::Controller* controller) {
+    module_handler_ = handler;
+    hci_layer_ = hci_layer;
+    controller_ = controller;
+    le_advertising_interface_ = hci_layer_->GetLeAdvertisingInterface(
+        common::Bind(&LeAdvertisingManager::impl::handle_event, common::Unretained(this)), module_handler_);
+    num_instances_ = controller_->GetControllerLeNumberOfSupportedAdverisingSets();
+    enabled_sets_ = std::vector<EnabledSet>(num_instances_);
+    if (controller_->IsSupported(hci::OpCode::LE_SET_EXTENDED_ADVERTISING_PARAMETERS)) {
+      advertising_api_type_ = AdvertisingApiType::LE_5_0;
+    } else if (controller_->IsSupported(hci::OpCode::LE_MULTI_ADVT)) {
+      advertising_api_type_ = AdvertisingApiType::ANDROID_HCI;
+    } else {
+      advertising_api_type_ = AdvertisingApiType::LE_4_0;
+    }
+  }
+
+  size_t GetNumberOfAdvertisingInstances() const {
+    return num_instances_;
+  }
+
+  void handle_event(LeMetaEventView event) {
+    switch (event.GetSubeventCode()) {
+      case hci::SubeventCode::SCAN_REQUEST_RECEIVED:
+        handle_scan_request(LeScanRequestReceivedView::Create(event));
+        break;
+      case hci::SubeventCode::ADVERTISING_SET_TERMINATED:
+        handle_set_terminated(LeAdvertisingSetTerminatedView::Create(event));
+        break;
+      default:
+        LOG_INFO("Unknown subevent in scanner %s", hci::SubeventCodeText(event.GetSubeventCode()).c_str());
+    }
+  }
+
+  void handle_scan_request(LeScanRequestReceivedView event_view) {
+    if (!event_view.IsValid()) {
+      LOG_INFO("Dropping invalid scan request event");
+      return;
+    }
+    registered_handler_->Post(
+        common::BindOnce(scan_callback_, event_view.GetScannerAddress(), event_view.GetScannerAddressType()));
+  }
+
+  void handle_set_terminated(LeAdvertisingSetTerminatedView event_view) {
+    if (!event_view.IsValid()) {
+      LOG_INFO("Dropping invalid advertising event");
+      return;
+    }
+    registered_handler_->Post(common::BindOnce(set_terminated_callback_, event_view.GetStatus(),
+                                               event_view.GetAdvertisingHandle(),
+                                               event_view.GetNumCompletedExtendedAdvertisingEvents()));
+  }
+
+  AdvertiserId allocate_advertiser() {
+    AdvertiserId id = 0;
+    {
+      std::unique_lock lock(id_mutex_);
+      while (id < num_instances_ && advertising_sets_.count(id) != 0) {
+        id++;
+      }
+    }
+    if (id == num_instances_) {
+      return kInvalidId;
+    }
+    return id;
+  }
+
+  void remove_advertiser(AdvertiserId id) {
+    stop_advertising(id);
+    std::unique_lock lock(id_mutex_);
+    if (advertising_sets_.count(id) == 0) {
+      return;
+    }
+    advertising_sets_.erase(id);
+  }
+
+  void create_advertiser(AdvertiserId id, const AdvertisingConfig& config,
+                         const common::Callback<void(Address, AddressType)>& scan_callback,
+                         const common::Callback<void(ErrorCode, uint8_t, uint8_t)>& set_terminated_callback,
+                         os::Handler* handler) {
+    advertising_sets_[id].scan_callback = scan_callback;
+    advertising_sets_[id].set_terminated_callback = set_terminated_callback;
+    advertising_sets_[id].handler = handler;
+    switch (advertising_api_type_) {
+      case (AdvertisingApiType::LE_4_0):
+        le_advertising_interface_->EnqueueCommand(
+            hci::LeSetAdvertisingParametersBuilder::Create(
+                config.interval_min, config.interval_max, config.event_type, config.address_type,
+                config.peer_address_type, config.peer_address, config.channel_map, config.filter_policy),
+            common::BindOnce(impl::check_status<LeSetAdvertisingParametersCompleteView>), module_handler_);
+        le_advertising_interface_->EnqueueCommand(hci::LeSetRandomAddressBuilder::Create(config.random_address),
+                                                  common::BindOnce(impl::check_status<LeSetRandomAddressCompleteView>),
+                                                  module_handler_);
+        if (!config.scan_response.empty()) {
+          le_advertising_interface_->EnqueueCommand(
+              hci::LeSetScanResponseDataBuilder::Create(config.scan_response),
+              common::BindOnce(impl::check_status<LeSetScanResponseDataCompleteView>), module_handler_);
+        }
+        le_advertising_interface_->EnqueueCommand(
+            hci::LeSetAdvertisingDataBuilder::Create(config.advertisement),
+            common::BindOnce(impl::check_status<LeSetAdvertisingDataCompleteView>), module_handler_);
+        le_advertising_interface_->EnqueueCommand(
+            hci::LeSetAdvertisingEnableBuilder::Create(Enable::ENABLED),
+            common::BindOnce(impl::check_status<LeSetAdvertisingEnableCompleteView>), module_handler_);
+        break;
+      case (AdvertisingApiType::ANDROID_HCI):
+        le_advertising_interface_->EnqueueCommand(
+            hci::LeMultiAdvtParamBuilder::Create(config.interval_min, config.interval_max, config.event_type,
+                                                 config.address_type, config.peer_address_type, config.peer_address,
+                                                 config.channel_map, config.filter_policy, id, config.tx_power),
+            common::BindOnce(impl::check_status<LeMultiAdvtCompleteView>), module_handler_);
+        le_advertising_interface_->EnqueueCommand(hci::LeMultiAdvtSetDataBuilder::Create(config.advertisement, id),
+                                                  common::BindOnce(impl::check_status<LeMultiAdvtCompleteView>),
+                                                  module_handler_);
+        if (!config.scan_response.empty()) {
+          le_advertising_interface_->EnqueueCommand(
+              hci::LeMultiAdvtSetScanRespBuilder::Create(config.scan_response, id),
+              common::BindOnce(impl::check_status<LeMultiAdvtCompleteView>), module_handler_);
+        }
+        le_advertising_interface_->EnqueueCommand(
+            hci::LeMultiAdvtSetRandomAddrBuilder::Create(config.random_address, id),
+            common::BindOnce(impl::check_status<LeMultiAdvtCompleteView>), module_handler_);
+        le_advertising_interface_->EnqueueCommand(hci::LeMultiAdvtSetEnableBuilder::Create(Enable::ENABLED, id),
+                                                  common::BindOnce(impl::check_status<LeMultiAdvtCompleteView>),
+                                                  module_handler_);
+        break;
+      case (AdvertisingApiType::LE_5_0): {
+        ExtendedAdvertisingConfig new_config = config;
+        new_config.legacy_pdus = true;
+        create_extended_advertiser(id, new_config, scan_callback, set_terminated_callback, handler);
+      } break;
+    }
+  }
+
+  void create_extended_advertiser(AdvertiserId id, const ExtendedAdvertisingConfig& config,
+                                  const common::Callback<void(Address, AddressType)>& scan_callback,
+                                  const common::Callback<void(ErrorCode, uint8_t, uint8_t)>& set_terminated_callback,
+                                  os::Handler* handler) {
+    if (advertising_api_type_ != AdvertisingApiType::LE_5_0) {
+      create_advertiser(id, config, scan_callback, set_terminated_callback, handler);
+      return;
+    }
+
+    if (config.legacy_pdus) {
+      LegacyAdvertisingProperties legacy_properties = LegacyAdvertisingProperties::ADV_IND;
+      if (config.connectable && config.directed) {
+        if (config.high_duty_directed_connectable) {
+          legacy_properties = LegacyAdvertisingProperties::ADV_DIRECT_IND_HIGH;
+        } else {
+          legacy_properties = LegacyAdvertisingProperties::ADV_DIRECT_IND_LOW;
+        }
+      }
+      if (config.scannable && !config.connectable) {
+        legacy_properties = LegacyAdvertisingProperties::ADV_SCAN_IND;
+      }
+      if (!config.scannable && !config.connectable) {
+        legacy_properties = LegacyAdvertisingProperties::ADV_NONCONN_IND;
+      }
+
+      le_advertising_interface_->EnqueueCommand(
+          LeSetExtendedAdvertisingLegacyParametersBuilder::Create(
+              id, legacy_properties, config.interval_min, config.interval_max, config.channel_map,
+              config.own_address_type, config.peer_address_type, config.peer_address, config.filter_policy,
+              config.tx_power, config.sid, config.enable_scan_request_notifications),
+          common::BindOnce(impl::check_status<LeSetExtendedAdvertisingParametersCompleteView>), module_handler_);
+    } else {
+      uint8_t legacy_properties = (config.connectable ? 0x1 : 0x00) | (config.scannable ? 0x2 : 0x00) |
+                                  (config.directed ? 0x4 : 0x00) | (config.high_duty_directed_connectable ? 0x8 : 0x00);
+      uint8_t extended_properties = (config.anonymous ? 0x20 : 0x00) | (config.include_tx_power ? 0x40 : 0x00);
+
+      le_advertising_interface_->EnqueueCommand(
+          hci::LeSetExtendedAdvertisingParametersBuilder::Create(
+              id, legacy_properties, extended_properties, config.interval_min, config.interval_max, config.channel_map,
+              config.own_address_type, config.peer_address_type, config.peer_address, config.filter_policy,
+              config.tx_power, (config.use_le_coded_phy ? PrimaryPhyType::LE_CODED : PrimaryPhyType::LE_1M),
+              config.secondary_max_skip, config.secondary_advertising_phy, config.sid,
+              config.enable_scan_request_notifications),
+          common::BindOnce(impl::check_status<LeSetExtendedAdvertisingParametersCompleteView>), module_handler_);
+    }
+
+    le_advertising_interface_->EnqueueCommand(
+        hci::LeSetExtendedAdvertisingRandomAddressBuilder::Create(id, config.random_address),
+        common::BindOnce(impl::check_status<LeSetExtendedAdvertisingRandomAddressCompleteView>), module_handler_);
+    if (!config.scan_response.empty()) {
+      le_advertising_interface_->EnqueueCommand(
+          hci::LeSetExtendedAdvertisingScanResponseBuilder::Create(id, config.operation, config.fragment_preference,
+                                                                   config.scan_response),
+          common::BindOnce(impl::check_status<LeSetExtendedAdvertisingScanResponseCompleteView>), module_handler_);
+    }
+    le_advertising_interface_->EnqueueCommand(
+        hci::LeSetExtendedAdvertisingDataBuilder::Create(id, config.operation, config.fragment_preference,
+                                                         config.advertisement),
+        common::BindOnce(impl::check_status<LeSetExtendedAdvertisingDataCompleteView>), module_handler_);
+
+    EnabledSet curr_set;
+    curr_set.advertising_handle_ = id;
+    curr_set.duration_ = 0;                         // TODO: 0 means until the host disables it
+    curr_set.max_extended_advertising_events_ = 0;  // TODO: 0 is no maximum
+    std::vector<EnabledSet> enabled_sets = {curr_set};
+
+    enabled_sets_[id] = curr_set;
+    le_advertising_interface_->EnqueueCommand(
+        hci::LeSetExtendedAdvertisingEnableBuilder::Create(Enable::ENABLED, enabled_sets),
+        common::BindOnce(impl::check_status<LeSetExtendedAdvertisingEnableCompleteView>), module_handler_);
+
+    advertising_sets_[id].scan_callback = scan_callback;
+    advertising_sets_[id].set_terminated_callback = set_terminated_callback;
+    advertising_sets_[id].handler = handler;
+  }
+
+  void stop_advertising(AdvertiserId advertising_set) {
+    if (advertising_sets_.find(advertising_set) == advertising_sets_.end()) {
+      LOG_INFO("Unknown advertising set %u", advertising_set);
+      return;
+    }
+    switch (advertising_api_type_) {
+      case (AdvertisingApiType::LE_4_0):
+        le_advertising_interface_->EnqueueCommand(
+            hci::LeSetAdvertisingEnableBuilder::Create(Enable::DISABLED),
+            common::BindOnce(impl::check_status<LeSetAdvertisingEnableCompleteView>), module_handler_);
+        break;
+      case (AdvertisingApiType::ANDROID_HCI):
+        le_advertising_interface_->EnqueueCommand(
+            hci::LeMultiAdvtSetEnableBuilder::Create(Enable::DISABLED, advertising_set),
+            common::BindOnce(impl::check_status<LeMultiAdvtCompleteView>), module_handler_);
+        break;
+      case (AdvertisingApiType::LE_5_0): {
+        EnabledSet curr_set;
+        curr_set.advertising_handle_ = advertising_set;
+        std::vector<EnabledSet> enabled_vector{curr_set};
+        le_advertising_interface_->EnqueueCommand(
+            hci::LeSetExtendedAdvertisingEnableBuilder::Create(Enable::DISABLED, enabled_vector),
+            common::BindOnce(impl::check_status<LeSetExtendedAdvertisingEnableCompleteView>), module_handler_);
+      } break;
+    }
+
+    std::unique_lock lock(id_mutex_);
+    enabled_sets_[advertising_set].advertising_handle_ = -1;
+    advertising_sets_.erase(advertising_set);
+  }
+
+  common::Callback<void(Address, AddressType)> scan_callback_;
+  common::Callback<void(ErrorCode, uint8_t, uint8_t)> set_terminated_callback_;
+  os::Handler* registered_handler_{nullptr};
+  Module* module_;
+  os::Handler* module_handler_;
+  hci::HciLayer* hci_layer_;
+  hci::Controller* controller_;
+  hci::LeAdvertisingInterface* le_advertising_interface_;
+  std::map<AdvertiserId, Advertiser> advertising_sets_;
+
+  std::mutex id_mutex_;
+  size_t num_instances_;
+  std::vector<hci::EnabledSet> enabled_sets_;
+
+  AdvertisingApiType advertising_api_type_{0};
+
+  template <class View>
+  static void check_status(CommandCompleteView view) {
+    ASSERT(view.IsValid());
+    auto status_view = View::Create(view);
+    ASSERT(status_view.IsValid());
+    if (status_view.GetStatus() != ErrorCode::SUCCESS) {
+      LOG_INFO("SetEnable returned status %s", ErrorCodeText(status_view.GetStatus()).c_str());
+    }
+  }
+};
+
+LeAdvertisingManager::LeAdvertisingManager() {
+  pimpl_ = std::make_unique<impl>(this);
+}
+
+void LeAdvertisingManager::ListDependencies(ModuleList* list) {
+  list->add<hci::HciLayer>();
+  list->add<hci::Controller>();
+}
+
+void LeAdvertisingManager::Start() {
+  pimpl_->start(GetHandler(), GetDependency<hci::HciLayer>(), GetDependency<hci::Controller>());
+}
+
+void LeAdvertisingManager::Stop() {
+  pimpl_.reset();
+}
+
+std::string LeAdvertisingManager::ToString() const {
+  return "Le Advertising Manager";
+}
+
+size_t LeAdvertisingManager::GetNumberOfAdvertisingInstances() const {
+  return pimpl_->GetNumberOfAdvertisingInstances();
+}
+
+AdvertiserId LeAdvertisingManager::CreateAdvertiser(
+    const AdvertisingConfig& config, const common::Callback<void(Address, AddressType)>& scan_callback,
+    const common::Callback<void(ErrorCode, uint8_t, uint8_t)>& set_terminated_callback, os::Handler* handler) {
+  if (config.peer_address == Address::kEmpty) {
+    if (config.address_type == hci::AddressType::PUBLIC_IDENTITY_ADDRESS ||
+        config.address_type == hci::AddressType::RANDOM_IDENTITY_ADDRESS) {
+      LOG_WARN("Peer address can not be empty");
+      return kInvalidId;
+    }
+    if (config.event_type == hci::AdvertisingEventType::ADV_DIRECT_IND ||
+        config.event_type == hci::AdvertisingEventType::ADV_DIRECT_IND_LOW) {
+      LOG_WARN("Peer address can not be empty for directed advertising");
+      return kInvalidId;
+    }
+  }
+  AdvertiserId id = pimpl_->allocate_advertiser();
+  if (id == kInvalidId) {
+    return id;
+  }
+  GetHandler()->Post(common::BindOnce(&impl::create_advertiser, common::Unretained(pimpl_.get()), id, config,
+                                      scan_callback, set_terminated_callback, handler));
+  return id;
+}
+
+AdvertiserId LeAdvertisingManager::ExtendedCreateAdvertiser(
+    const ExtendedAdvertisingConfig& config, const common::Callback<void(Address, AddressType)>& scan_callback,
+    const common::Callback<void(ErrorCode, uint8_t, uint8_t)>& set_terminated_callback, os::Handler* handler) {
+  if (config.directed) {
+    if (config.peer_address == Address::kEmpty) {
+      LOG_INFO("Peer address can not be empty for directed advertising");
+      return kInvalidId;
+    }
+  }
+  if (config.channel_map == 0) {
+    LOG_INFO("At least one channel must be set in the map");
+    return kInvalidId;
+  }
+  if (!config.legacy_pdus) {
+    if (config.connectable && config.scannable) {
+      LOG_INFO("Extended advertising PDUs can not be connectable and scannable");
+      return kInvalidId;
+    }
+    if (config.high_duty_directed_connectable) {
+      LOG_INFO("Extended advertising PDUs can not be high duty cycle");
+      return kInvalidId;
+    }
+  }
+  if (config.interval_min > config.interval_max) {
+    LOG_INFO("Advertising interval: min (%hu) > max (%hu)", config.interval_min, config.interval_max);
+    return kInvalidId;
+  }
+  AdvertiserId id = pimpl_->allocate_advertiser();
+  if (id == kInvalidId) {
+    return id;
+  }
+  GetHandler()->Post(common::BindOnce(&impl::create_extended_advertiser, common::Unretained(pimpl_.get()), id, config,
+                                      scan_callback, set_terminated_callback, handler));
+  return id;
+}
+
+void LeAdvertisingManager::RemoveAdvertiser(AdvertiserId id) {
+  GetHandler()->Post(common::BindOnce(&impl::remove_advertiser, common::Unretained(pimpl_.get()), id));
+}
+
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/le_advertising_manager.h b/gd/hci/le_advertising_manager.h
new file mode 100644
index 0000000..d410dec
--- /dev/null
+++ b/gd/hci/le_advertising_manager.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <memory>
+
+#include "hci/hci_packets.h"
+#include "module.h"
+
+namespace bluetooth {
+namespace hci {
+
+class AdvertisingConfig {
+ public:
+  std::vector<GapData> advertisement;
+  std::vector<GapData> scan_response;
+  Address random_address;
+  uint16_t interval_min;
+  uint16_t interval_max;
+  AdvertisingEventType event_type;
+  AddressType address_type;
+  PeerAddressType peer_address_type;
+  Address peer_address;
+  uint8_t channel_map;
+  AdvertisingFilterPolicy filter_policy;
+  uint8_t tx_power;  // -127 to +20 (0x7f is no preference)
+};
+
+class ExtendedAdvertisingConfig : public AdvertisingConfig {
+ public:
+  bool connectable = false;
+  bool scannable = false;
+  bool directed = false;
+  bool high_duty_directed_connectable = false;
+  bool legacy_pdus = false;
+  bool anonymous = false;
+  bool include_tx_power = false;
+  bool use_le_coded_phy;       // Primary advertisement PHY is LE Coded
+  uint8_t secondary_max_skip;  // maximum advertising events to be skipped, 0x0 send AUX_ADV_IND prior ot the next event
+  SecondaryPhyType secondary_advertising_phy;
+  uint8_t sid = 0x00;
+  Enable enable_scan_request_notifications = Enable::DISABLED;
+  OwnAddressType own_address_type;
+  Operation operation;  // TODO(b/149221472): Support fragmentation
+  FragmentPreference fragment_preference = FragmentPreference::CONTROLLER_SHOULD_NOT;
+  ExtendedAdvertisingConfig() = default;
+  ExtendedAdvertisingConfig(const AdvertisingConfig& config);
+};
+
+using AdvertiserId = int32_t;
+
+class LeAdvertisingManager : public bluetooth::Module {
+ public:
+  static constexpr AdvertiserId kInvalidId = -1;
+  LeAdvertisingManager();
+
+  size_t GetNumberOfAdvertisingInstances() const;
+
+  // Return -1 if the advertiser was not created, otherwise the advertiser ID.
+  AdvertiserId CreateAdvertiser(const AdvertisingConfig& config,
+                                const common::Callback<void(Address, AddressType)>& scan_callback,
+                                const common::Callback<void(ErrorCode, uint8_t, uint8_t)>& set_terminated_callback,
+                                os::Handler* handler);
+  AdvertiserId ExtendedCreateAdvertiser(
+      const ExtendedAdvertisingConfig& config, const common::Callback<void(Address, AddressType)>& scan_callback,
+      const common::Callback<void(ErrorCode, uint8_t, uint8_t)>& set_terminated_callback, os::Handler* handler);
+
+  void RemoveAdvertiser(AdvertiserId id);
+
+  static const ModuleFactory Factory;
+
+ protected:
+  void ListDependencies(ModuleList* list) override;
+
+  void Start() override;
+
+  void Stop() override;
+
+  std::string ToString() const override;
+
+ private:
+  struct impl;
+  std::unique_ptr<impl> pimpl_;
+  DISALLOW_COPY_AND_ASSIGN(LeAdvertisingManager);
+};
+
+}  // namespace hci
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/hci/le_advertising_manager_test.cc b/gd/hci/le_advertising_manager_test.cc
new file mode 100644
index 0000000..d2a5b7b
--- /dev/null
+++ b/gd/hci/le_advertising_manager_test.cc
@@ -0,0 +1,427 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "hci/le_advertising_manager.h"
+
+#include <algorithm>
+#include <chrono>
+#include <future>
+#include <map>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "common/bind.h"
+#include "hci/address.h"
+#include "hci/controller.h"
+#include "hci/hci_layer.h"
+#include "os/thread.h"
+#include "packet/raw_builder.h"
+
+namespace bluetooth {
+namespace hci {
+namespace {
+
+using packet::kLittleEndian;
+using packet::PacketView;
+using packet::RawBuilder;
+
+PacketView<kLittleEndian> GetPacketView(std::unique_ptr<packet::BasePacketBuilder> packet) {
+  auto bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter i(*bytes);
+  bytes->reserve(packet->size());
+  packet->Serialize(i);
+  return packet::PacketView<packet::kLittleEndian>(bytes);
+}
+
+class TestController : public Controller {
+ public:
+  bool IsSupported(OpCode op_code) const override {
+    return supported_opcodes_.count(op_code) == 1;
+  }
+
+  void AddSupported(OpCode op_code) {
+    supported_opcodes_.insert(op_code);
+  }
+
+  uint8_t GetControllerLeNumberOfSupportedAdverisingSets() const override {
+    return num_advertisers;
+  }
+
+  uint8_t num_advertisers{0};
+
+ protected:
+  void Start() override {}
+  void Stop() override {}
+  void ListDependencies(ModuleList* list) override {}
+
+ private:
+  std::set<OpCode> supported_opcodes_{};
+};
+
+class TestHciLayer : public HciLayer {
+ public:
+  TestHciLayer() {
+    RegisterEventHandler(EventCode::COMMAND_COMPLETE,
+                         base::Bind(&TestHciLayer::CommandCompleteCallback, common::Unretained(this)), nullptr);
+    RegisterEventHandler(EventCode::COMMAND_STATUS,
+                         base::Bind(&TestHciLayer::CommandStatusCallback, common::Unretained(this)), nullptr);
+  }
+
+  void EnqueueCommand(std::unique_ptr<CommandPacketBuilder> command,
+                      common::OnceCallback<void(CommandStatusView)> on_status, os::Handler* handler) override {
+    auto packet_view = CommandPacketView::Create(GetPacketView(std::move(command)));
+    ASSERT(packet_view.IsValid());
+    command_queue_.push_back(packet_view);
+    command_status_callbacks.push_back(std::move(on_status));
+    if (command_promise_ != nullptr &&
+        (command_op_code_ == OpCode::NONE || command_op_code_ == packet_view.GetOpCode())) {
+      if (command_op_code_ == OpCode::LE_MULTI_ADVT && command_sub_ocf_ != SubOcf::SET_ENABLE) {
+        return;
+      }
+      command_promise_->set_value(command_queue_.size());
+      command_promise_.reset();
+    }
+  }
+
+  void EnqueueCommand(std::unique_ptr<CommandPacketBuilder> command,
+                      common::OnceCallback<void(CommandCompleteView)> on_complete, os::Handler* handler) override {
+    auto packet_view = CommandPacketView::Create(GetPacketView(std::move(command)));
+    ASSERT(packet_view.IsValid());
+    command_queue_.push_back(packet_view);
+    command_complete_callbacks.push_back(std::move(on_complete));
+    if (command_promise_ != nullptr &&
+        (command_op_code_ == OpCode::NONE || command_op_code_ == packet_view.GetOpCode())) {
+      if (command_op_code_ == OpCode::LE_MULTI_ADVT) {
+        auto sub_view = LeMultiAdvtView::Create(LeAdvertisingCommandView::Create(packet_view));
+        ASSERT(sub_view.IsValid());
+        if (sub_view.GetSubCmd() != command_sub_ocf_) {
+          return;
+        }
+      }
+      command_promise_->set_value(command_queue_.size());
+      command_promise_.reset();
+    }
+  }
+
+  std::future<size_t> GetCommandFuture(OpCode op_code = OpCode::NONE) {
+    ASSERT_LOG(command_promise_ == nullptr, "Promises promises ... Only one at a time");
+    command_op_code_ = op_code;
+    command_promise_ = std::make_unique<std::promise<size_t>>();
+    return command_promise_->get_future();
+  }
+
+  std::future<size_t> GetSubCommandFuture(SubOcf sub_ocf) {
+    ASSERT_LOG(command_promise_ == nullptr, "Promises promises ... Only one at a time");
+    command_op_code_ = OpCode::LE_MULTI_ADVT;
+    command_sub_ocf_ = sub_ocf;
+    command_promise_ = std::make_unique<std::promise<size_t>>();
+    return command_promise_->get_future();
+  }
+
+  ConnectionManagementCommandView GetCommandPacket(OpCode op_code) {
+    ASSERT(!command_queue_.empty());
+    CommandPacketView command_packet_view = CommandPacketView::Create(command_queue_.front());
+    command_queue_.pop_front();
+    ConnectionManagementCommandView command = ConnectionManagementCommandView::Create(command_packet_view);
+    ASSERT(command.IsValid());
+    EXPECT_EQ(command.GetOpCode(), op_code);
+
+    return command;
+  }
+
+  void RegisterEventHandler(EventCode event_code, common::Callback<void(EventPacketView)> event_handler,
+                            os::Handler* handler) override {
+    registered_events_[event_code] = event_handler;
+  }
+
+  void RegisterLeEventHandler(SubeventCode subevent_code, common::Callback<void(LeMetaEventView)> event_handler,
+                              os::Handler* handler) override {
+    registered_le_events_[subevent_code] = event_handler;
+  }
+
+  void IncomingEvent(std::unique_ptr<EventPacketBuilder> event_builder) {
+    auto packet = GetPacketView(std::move(event_builder));
+    EventPacketView event = EventPacketView::Create(packet);
+    ASSERT_TRUE(event.IsValid());
+    EventCode event_code = event.GetEventCode();
+    ASSERT_TRUE(registered_events_.find(event_code) != registered_events_.end()) << EventCodeText(event_code);
+    registered_events_[event_code].Run(event);
+  }
+
+  void IncomingLeMetaEvent(std::unique_ptr<LeMetaEventBuilder> event_builder) {
+    auto packet = GetPacketView(std::move(event_builder));
+    EventPacketView event = EventPacketView::Create(packet);
+    LeMetaEventView meta_event_view = LeMetaEventView::Create(event);
+    ASSERT_TRUE(meta_event_view.IsValid());
+    SubeventCode subevent_code = meta_event_view.GetSubeventCode();
+    ASSERT_TRUE(registered_le_events_.find(subevent_code) != registered_le_events_.end())
+        << SubeventCodeText(subevent_code);
+    registered_le_events_[subevent_code].Run(meta_event_view);
+  }
+
+  void CommandCompleteCallback(EventPacketView event) {
+    CommandCompleteView complete_view = CommandCompleteView::Create(event);
+    ASSERT(complete_view.IsValid());
+    std::move(command_complete_callbacks.front()).Run(complete_view);
+    command_complete_callbacks.pop_front();
+  }
+
+  void CommandStatusCallback(EventPacketView event) {
+    CommandStatusView status_view = CommandStatusView::Create(event);
+    ASSERT(status_view.IsValid());
+    std::move(command_status_callbacks.front()).Run(status_view);
+    command_status_callbacks.pop_front();
+  }
+
+  void ListDependencies(ModuleList* list) override {}
+  void Start() override {}
+  void Stop() override {}
+
+ private:
+  std::map<EventCode, common::Callback<void(EventPacketView)>> registered_events_;
+  std::map<SubeventCode, common::Callback<void(LeMetaEventView)>> registered_le_events_;
+  std::list<base::OnceCallback<void(CommandCompleteView)>> command_complete_callbacks;
+  std::list<base::OnceCallback<void(CommandStatusView)>> command_status_callbacks;
+
+  std::list<CommandPacketView> command_queue_;
+  mutable std::mutex mutex_;
+  std::unique_ptr<std::promise<size_t>> command_promise_{};
+  OpCode command_op_code_;
+  SubOcf command_sub_ocf_;
+};
+
+class LeAdvertisingManagerTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    test_hci_layer_ = new TestHciLayer;  // Ownership is transferred to registry
+    test_controller_ = new TestController;
+    test_controller_->AddSupported(param_opcode_);
+    fake_registry_.InjectTestModule(&HciLayer::Factory, test_hci_layer_);
+    fake_registry_.InjectTestModule(&Controller::Factory, test_controller_);
+    client_handler_ = fake_registry_.GetTestModuleHandler(&HciLayer::Factory);
+    ASSERT_NE(client_handler_, nullptr);
+    test_controller_->num_advertisers = 1;
+    fake_registry_.Start<LeAdvertisingManager>(&thread_);
+    le_advertising_manager_ =
+        static_cast<LeAdvertisingManager*>(fake_registry_.GetModuleUnderTest(&LeAdvertisingManager::Factory));
+  }
+
+  void TearDown() override {
+    fake_registry_.SynchronizeModuleHandler(&LeAdvertisingManager::Factory, std::chrono::milliseconds(20));
+    fake_registry_.StopAll();
+  }
+
+  TestModuleRegistry fake_registry_;
+  TestHciLayer* test_hci_layer_ = nullptr;
+  TestController* test_controller_ = nullptr;
+  os::Thread& thread_ = fake_registry_.GetTestThread();
+  LeAdvertisingManager* le_advertising_manager_ = nullptr;
+  os::Handler* client_handler_ = nullptr;
+
+  const common::Callback<void(Address, AddressType)> scan_callback =
+      common::Bind(&LeAdvertisingManagerTest::on_scan, common::Unretained(this));
+  const common::Callback<void(ErrorCode, uint8_t, uint8_t)> set_terminated_callback =
+      common::Bind(&LeAdvertisingManagerTest::on_set_terminated, common::Unretained(this));
+
+  std::future<Address> GetOnScanPromise() {
+    ASSERT_LOG(address_promise_ == nullptr, "Promises promises ... Only one at a time");
+    address_promise_ = std::make_unique<std::promise<Address>>();
+    return address_promise_->get_future();
+  }
+  void on_scan(Address address, AddressType address_type) {
+    if (address_promise_ == nullptr) {
+      return;
+    }
+    address_promise_->set_value(address);
+    address_promise_.reset();
+  }
+
+  std::future<ErrorCode> GetSetTerminatedPromise() {
+    ASSERT_LOG(set_terminated_promise_ == nullptr, "Promises promises ... Only one at a time");
+    set_terminated_promise_ = std::make_unique<std::promise<ErrorCode>>();
+    return set_terminated_promise_->get_future();
+  }
+  void on_set_terminated(ErrorCode error_code, uint8_t, uint8_t) {
+    if (set_terminated_promise_ != nullptr) {
+      return;
+    }
+    set_terminated_promise_->set_value(error_code);
+    set_terminated_promise_.reset();
+  }
+
+  std::unique_ptr<std::promise<Address>> address_promise_{};
+  std::unique_ptr<std::promise<ErrorCode>> set_terminated_promise_{};
+
+  OpCode param_opcode_{OpCode::LE_SET_ADVERTISING_PARAMETERS};
+};
+
+class LeAndroidHciAdvertisingManagerTest : public LeAdvertisingManagerTest {
+ protected:
+  void SetUp() override {
+    param_opcode_ = OpCode::LE_MULTI_ADVT;
+    LeAdvertisingManagerTest::SetUp();
+    test_controller_->num_advertisers = 3;
+  }
+};
+
+class LeExtendedAdvertisingManagerTest : public LeAdvertisingManagerTest {
+ protected:
+  void SetUp() override {
+    param_opcode_ = OpCode::LE_SET_EXTENDED_ADVERTISING_PARAMETERS;
+    LeAdvertisingManagerTest::SetUp();
+    test_controller_->num_advertisers = 5;
+  }
+};
+
+TEST_F(LeAdvertisingManagerTest, startup_teardown) {}
+
+TEST_F(LeAndroidHciAdvertisingManagerTest, startup_teardown) {}
+
+TEST_F(LeExtendedAdvertisingManagerTest, startup_teardown) {}
+
+TEST_F(LeAdvertisingManagerTest, create_advertiser_test) {
+  AdvertisingConfig advertising_config{};
+  advertising_config.event_type = AdvertisingEventType::ADV_IND;
+  advertising_config.address_type = AddressType::PUBLIC_DEVICE_ADDRESS;
+  std::vector<GapData> gap_data{};
+  GapData data_item{};
+  data_item.data_type_ = GapDataType::FLAGS;
+  data_item.data_ = {0x34};
+  gap_data.push_back(data_item);
+  data_item.data_type_ = GapDataType::COMPLETE_LOCAL_NAME;
+  data_item.data_ = {'r', 'a', 'n', 'd', 'o', 'm', ' ', 'd', 'e', 'v', 'i', 'c', 'e'};
+  gap_data.push_back(data_item);
+  advertising_config.advertisement = gap_data;
+  advertising_config.scan_response = gap_data;
+
+  auto last_command_future = test_hci_layer_->GetCommandFuture(OpCode::LE_SET_ADVERTISING_ENABLE);
+  auto id = le_advertising_manager_->CreateAdvertiser(advertising_config, scan_callback, set_terminated_callback,
+                                                      client_handler_);
+  ASSERT_NE(LeAdvertisingManager::kInvalidId, id);
+  std::vector<OpCode> adv_opcodes = {
+      OpCode::LE_SET_ADVERTISING_PARAMETERS, OpCode::LE_SET_RANDOM_ADDRESS,     OpCode::LE_SET_SCAN_RESPONSE_DATA,
+      OpCode::LE_SET_ADVERTISING_DATA,       OpCode::LE_SET_ADVERTISING_ENABLE,
+  };
+  std::vector<uint8_t> success_vector{static_cast<uint8_t>(ErrorCode::SUCCESS)};
+  auto result = last_command_future.wait_for(std::chrono::duration(std::chrono::milliseconds(100)));
+  ASSERT_EQ(std::future_status::ready, result);
+  for (size_t i = 0; i < adv_opcodes.size(); i++) {
+    auto packet_view = test_hci_layer_->GetCommandPacket(adv_opcodes[i]);
+    CommandPacketView command_packet_view = CommandPacketView::Create(packet_view);
+    ConnectionManagementCommandView command = ConnectionManagementCommandView::Create(command_packet_view);
+    test_hci_layer_->IncomingEvent(
+        CommandCompleteBuilder::Create(uint8_t{1}, adv_opcodes[i], std::make_unique<RawBuilder>(success_vector)));
+  }
+  // Disable the advertiser
+  last_command_future = test_hci_layer_->GetCommandFuture(OpCode::LE_SET_ADVERTISING_ENABLE);
+  le_advertising_manager_->RemoveAdvertiser(id);
+  result = last_command_future.wait_for(std::chrono::duration(std::chrono::milliseconds(100)));
+  ASSERT_EQ(std::future_status::ready, result);
+  test_hci_layer_->IncomingEvent(LeSetAdvertisingEnableCompleteBuilder::Create(uint8_t{1}, ErrorCode::SUCCESS));
+}
+
+TEST_F(LeAndroidHciAdvertisingManagerTest, create_advertiser_test) {
+  AdvertisingConfig advertising_config{};
+  advertising_config.event_type = AdvertisingEventType::ADV_IND;
+  advertising_config.address_type = AddressType::PUBLIC_DEVICE_ADDRESS;
+  std::vector<GapData> gap_data{};
+  GapData data_item{};
+  data_item.data_type_ = GapDataType::FLAGS;
+  data_item.data_ = {0x34};
+  gap_data.push_back(data_item);
+  data_item.data_type_ = GapDataType::COMPLETE_LOCAL_NAME;
+  data_item.data_ = {'r', 'a', 'n', 'd', 'o', 'm', ' ', 'd', 'e', 'v', 'i', 'c', 'e'};
+  gap_data.push_back(data_item);
+  advertising_config.advertisement = gap_data;
+  advertising_config.scan_response = gap_data;
+
+  auto next_command_future = test_hci_layer_->GetSubCommandFuture(SubOcf::SET_ENABLE);
+  auto id = le_advertising_manager_->CreateAdvertiser(advertising_config, scan_callback, set_terminated_callback,
+                                                      client_handler_);
+  ASSERT_NE(LeAdvertisingManager::kInvalidId, id);
+  std::vector<SubOcf> sub_ocf = {
+      SubOcf::SET_PARAM, SubOcf::SET_DATA, SubOcf::SET_SCAN_RESP, SubOcf::SET_RANDOM_ADDR, SubOcf::SET_ENABLE,
+  };
+  auto result = next_command_future.wait_for(std::chrono::duration(std::chrono::milliseconds(100)));
+  ASSERT_EQ(std::future_status::ready, result);
+  size_t num_commands = next_command_future.get();
+  for (size_t i = 0; i < sub_ocf.size(); i++) {
+    auto packet = test_hci_layer_->GetCommandPacket(OpCode::LE_MULTI_ADVT);
+    auto sub_packet = LeMultiAdvtView::Create(LeAdvertisingCommandView::Create(packet));
+    ASSERT(sub_packet.IsValid());
+    test_hci_layer_->IncomingEvent(LeMultiAdvtCompleteBuilder::Create(uint8_t{1}, ErrorCode::SUCCESS, sub_ocf[i]));
+    num_commands -= 1;
+  }
+  ASSERT_EQ(0, num_commands);
+  // Disable the advertiser
+  next_command_future = test_hci_layer_->GetSubCommandFuture(SubOcf::SET_ENABLE);
+  le_advertising_manager_->RemoveAdvertiser(id);
+  result = next_command_future.wait_for(std::chrono::duration(std::chrono::milliseconds(100)));
+  ASSERT_EQ(std::future_status::ready, result);
+  test_hci_layer_->IncomingEvent(LeMultiAdvtSetEnableCompleteBuilder::Create(uint8_t{1}, ErrorCode::SUCCESS));
+}
+
+TEST_F(LeExtendedAdvertisingManagerTest, create_advertiser_test) {
+  ExtendedAdvertisingConfig advertising_config{};
+  advertising_config.event_type = AdvertisingEventType::ADV_IND;
+  advertising_config.address_type = AddressType::PUBLIC_DEVICE_ADDRESS;
+  std::vector<GapData> gap_data{};
+  GapData data_item{};
+  data_item.data_type_ = GapDataType::FLAGS;
+  data_item.data_ = {0x34};
+  gap_data.push_back(data_item);
+  data_item.data_type_ = GapDataType::COMPLETE_LOCAL_NAME;
+  data_item.data_ = {'r', 'a', 'n', 'd', 'o', 'm', ' ', 'd', 'e', 'v', 'i', 'c', 'e'};
+  gap_data.push_back(data_item);
+  advertising_config.advertisement = gap_data;
+  advertising_config.scan_response = gap_data;
+  advertising_config.channel_map = 1;
+
+  auto last_command_future = test_hci_layer_->GetCommandFuture(OpCode::LE_SET_EXTENDED_ADVERTISING_ENABLE);
+  auto id = le_advertising_manager_->ExtendedCreateAdvertiser(advertising_config, scan_callback,
+                                                              set_terminated_callback, client_handler_);
+  ASSERT_NE(LeAdvertisingManager::kInvalidId, id);
+  std::vector<OpCode> adv_opcodes = {
+      OpCode::LE_SET_EXTENDED_ADVERTISING_PARAMETERS,    OpCode::LE_SET_EXTENDED_ADVERTISING_RANDOM_ADDRESS,
+      OpCode::LE_SET_EXTENDED_ADVERTISING_SCAN_RESPONSE, OpCode::LE_SET_EXTENDED_ADVERTISING_DATA,
+      OpCode::LE_SET_EXTENDED_ADVERTISING_ENABLE,
+  };
+  auto result = last_command_future.wait_for(std::chrono::duration(std::chrono::milliseconds(100)));
+  std::vector<uint8_t> success_vector{static_cast<uint8_t>(ErrorCode::SUCCESS)};
+  ASSERT_EQ(std::future_status::ready, result);
+  for (size_t i = 0; i < adv_opcodes.size(); i++) {
+    auto packet_view = test_hci_layer_->GetCommandPacket(adv_opcodes[i]);
+    CommandPacketView command_packet_view = CommandPacketView::Create(packet_view);
+    ConnectionManagementCommandView command = ConnectionManagementCommandView::Create(command_packet_view);
+    if (adv_opcodes[i] == OpCode::LE_SET_EXTENDED_ADVERTISING_PARAMETERS) {
+      test_hci_layer_->IncomingEvent(LeSetExtendedAdvertisingParametersCompleteBuilder::Create(
+          uint8_t{1}, ErrorCode::SUCCESS, static_cast<uint8_t>(-23)));
+    } else {
+      test_hci_layer_->IncomingEvent(
+          CommandCompleteBuilder::Create(uint8_t{1}, adv_opcodes[i], std::make_unique<RawBuilder>(success_vector)));
+    }
+  }
+  // Disable the advertiser
+  last_command_future = test_hci_layer_->GetCommandFuture(OpCode::LE_SET_EXTENDED_ADVERTISING_ENABLE);
+  le_advertising_manager_->RemoveAdvertiser(id);
+  result = last_command_future.wait_for(std::chrono::duration(std::chrono::milliseconds(100)));
+  ASSERT_EQ(std::future_status::ready, result);
+  test_hci_layer_->IncomingEvent(LeSetExtendedAdvertisingEnableCompleteBuilder::Create(uint8_t{1}, ErrorCode::SUCCESS));
+}
+}  // namespace
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/le_device.h b/gd/hci/le_device.h
new file mode 100644
index 0000000..35a620b
--- /dev/null
+++ b/gd/hci/le_device.h
@@ -0,0 +1,89 @@
+/******************************************************************************
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+#pragma once
+
+#include "hci/device.h"
+
+namespace bluetooth::hci {
+
+/**
+ * TODO(optedoblivion): Build out AddressType getter/setter
+ */
+// enum AddressType {};
+
+/**
+ * A device representing a LE device.
+ *
+ * <p>This can be a LE only or a piece of a DUAL MODE device.
+ *
+ * <p>LE specific public address logic goes here.
+ */
+class LeDevice : public Device {
+ public:
+  void SetPublicAddress(Address public_address) {
+    public_address_ = public_address;
+  }
+
+  Address GetPublicAddress() {
+    return public_address_;
+  }
+
+  void SetIrk(uint8_t irk) {
+    irk_ = irk;
+    // TODO(optedoblivion): Set derived Address
+  }
+
+  uint8_t GetIrk() {
+    return irk_;
+  }
+
+ protected:
+  friend class DeviceDatabase;
+  // TODO(optedoblivion): How to set public address.  Do I set it when no IRK is known?
+  // Right now my thought is to do this:
+  // 1. Construct LeDevice with address of all 0s
+  // 2. IF NO IRK AND NO PRIVATE ADDRESS: (i.e. nothing in disk cache)
+  //  a. Hopefully pairing will happen
+  //  b. Pending successful pairing get the IRK and Private Address
+  //  c. Set Both to device.
+  //  (d). If available set IRK to the controller (later iteration)
+  // [#3 should indicate we have a bug]
+  // 3. IF YES IRK AND NO PRIVATE ADDRESS: (Partial Disk Cache Information)
+  //  a. Set IRK
+  //  b. Generate Private Address
+  //  c. Set Private Address to device
+  //  (d). If available set IRK to the controller (later iteration)
+  // 4. IF YES IRK AND YES PRIVATE ADDRESS: (i.e. Disk cache hit)
+  //  a. Construct with private address
+  //  b. Set IRK
+  //  (c). If available set IRK to the controller (later iteration)
+  // 5. IF NO IRK AND YES PRIVATE ADDRESS (we have a bug)
+  //  a1. -Construct with private address-
+  //  b. -Indicate we need to repair or query for IRK?-
+  //
+  //  or
+  //
+  //  a2. Don't use class
+  explicit LeDevice(Address address) : Device(address, DeviceType::LE), public_address_(), irk_(0) {}
+
+ private:
+  Address public_address_;
+  uint8_t irk_;
+};
+
+}  // namespace bluetooth::hci
diff --git a/gd/hci/le_report.h b/gd/hci/le_report.h
new file mode 100644
index 0000000..7beeed6
--- /dev/null
+++ b/gd/hci/le_report.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <memory>
+
+#include "hci/hci_packets.h"
+
+namespace bluetooth::hci {
+
+class LeReport {
+ public:
+  explicit LeReport(const LeAdvertisingReport& advertisement)
+      : report_type_(ReportType::ADVERTISING_EVENT), advertising_event_type_(advertisement.event_type_),
+        address_(advertisement.address_), address_type_(advertisement.address_type_), rssi_(advertisement.rssi_),
+        gap_data_(advertisement.advertising_data_) {}
+  explicit LeReport(const LeDirectedAdvertisingReport& advertisement)
+      : report_type_(ReportType::DIRECTED_ADVERTISING_EVENT), address_(advertisement.address_),
+        rssi_(advertisement.rssi_) {}
+  explicit LeReport(const LeExtendedAdvertisingReport& advertisement)
+      : report_type_(ReportType::EXTENDED_ADVERTISING_EVENT), address_(advertisement.address_),
+        rssi_(advertisement.rssi_), gap_data_(advertisement.advertising_data_) {}
+  virtual ~LeReport() = default;
+
+  enum class ReportType {
+    ADVERTISING_EVENT = 1,
+    DIRECTED_ADVERTISING_EVENT = 2,
+    EXTENDED_ADVERTISING_EVENT = 3,
+  };
+  const ReportType report_type_;
+
+  ReportType GetReportType() const {
+    return report_type_;
+  }
+
+  // Advertising Event
+  const AdvertisingEventType advertising_event_type_{};
+  const Address address_{};
+  const AddressType address_type_{};
+  const int8_t rssi_;
+  const std::vector<GapData> gap_data_{};
+};
+
+class DirectedLeReport : public LeReport {
+ public:
+  explicit DirectedLeReport(const LeDirectedAdvertisingReport& advertisement)
+      : LeReport(advertisement), direct_address_type_(advertisement.address_type_),
+        direct_address_(advertisement.direct_address_) {}
+  explicit DirectedLeReport(const LeExtendedAdvertisingReport& advertisement)
+      : LeReport(advertisement), direct_address_type_(advertisement.address_type_),
+        direct_address_(advertisement.direct_address_) {}
+
+  const DirectAdvertisingAddressType direct_address_type_{};
+  const Address direct_address_{};
+};
+
+class ExtendedLeReport : public DirectedLeReport {
+ public:
+  explicit ExtendedLeReport(const LeExtendedAdvertisingReport& advertisement)
+      : DirectedLeReport(advertisement), connectable_(advertisement.connectable_), scannable_(advertisement.scannable_),
+        directed_(advertisement.directed_), scan_response_(advertisement.scan_response_),
+        complete_(advertisement.data_status_ == DataStatus::COMPLETE),
+        truncated_(advertisement.data_status_ == DataStatus::TRUNCATED) {}
+
+  // Extended
+  bool connectable_;
+  bool scannable_;
+  bool directed_;
+  bool scan_response_;
+  bool complete_;
+  bool truncated_;
+};
+}  // namespace bluetooth::hci
\ No newline at end of file
diff --git a/gd/hci/le_scanning_interface.h b/gd/hci/le_scanning_interface.h
new file mode 100644
index 0000000..97a6766
--- /dev/null
+++ b/gd/hci/le_scanning_interface.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "common/callback.h"
+#include "hci/hci_packets.h"
+#include "os/handler.h"
+#include "os/utils.h"
+
+namespace bluetooth {
+namespace hci {
+
+class LeScanningInterface {
+ public:
+  LeScanningInterface() = default;
+  virtual ~LeScanningInterface() = default;
+  DISALLOW_COPY_AND_ASSIGN(LeScanningInterface);
+
+  virtual void EnqueueCommand(std::unique_ptr<LeScanningCommandBuilder> command,
+                              common::OnceCallback<void(CommandCompleteView)> on_complete, os::Handler* handler) = 0;
+
+  virtual void EnqueueCommand(std::unique_ptr<LeScanningCommandBuilder> command,
+                              common::OnceCallback<void(CommandStatusView)> on_status, os::Handler* handler) = 0;
+
+  static constexpr hci::SubeventCode LeScanningEvents[] = {
+      hci::SubeventCode::SCAN_TIMEOUT,
+      hci::SubeventCode::ADVERTISING_REPORT,
+      hci::SubeventCode::DIRECTED_ADVERTISING_REPORT,
+      hci::SubeventCode::EXTENDED_ADVERTISING_REPORT,
+      hci::SubeventCode::PERIODIC_ADVERTISING_REPORT,
+      hci::SubeventCode::PERIODIC_ADVERTISING_SYNC_ESTABLISHED,
+      hci::SubeventCode::PERIODIC_ADVERTISING_SYNC_LOST,
+  };
+};
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/le_scanning_manager.cc b/gd/hci/le_scanning_manager.cc
new file mode 100644
index 0000000..c3b96d9
--- /dev/null
+++ b/gd/hci/le_scanning_manager.cc
@@ -0,0 +1,261 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <memory>
+#include <mutex>
+#include <set>
+
+#include "hci/controller.h"
+#include "hci/hci_layer.h"
+#include "hci/hci_packets.h"
+#include "hci/le_scanning_interface.h"
+#include "hci/le_scanning_manager.h"
+#include "module.h"
+#include "os/handler.h"
+#include "os/log.h"
+
+namespace bluetooth {
+namespace hci {
+
+constexpr uint16_t kDefaultLeScanWindow = 4800;
+constexpr uint16_t kDefaultLeScanInterval = 4800;
+
+const ModuleFactory LeScanningManager::Factory = ModuleFactory([]() { return new LeScanningManager(); });
+
+enum class ScanApiType {
+  LE_4_0 = 1,
+  ANDROID_HCI = 2,
+  LE_5_0 = 3,
+};
+
+struct LeScanningManager::impl {
+  impl(Module* module) : module_(module), le_scanning_interface_(nullptr) {}
+
+  void start(os::Handler* handler, hci::HciLayer* hci_layer, hci::Controller* controller) {
+    module_handler_ = handler;
+    hci_layer_ = hci_layer;
+    controller_ = controller;
+    le_scanning_interface_ = hci_layer_->GetLeScanningInterface(
+        common::Bind(&LeScanningManager::impl::handle_scan_results, common::Unretained(this)), module_handler_);
+    if (controller_->IsSupported(OpCode::LE_SET_EXTENDED_SCAN_PARAMETERS)) {
+      api_type_ = ScanApiType::LE_5_0;
+    } else if (controller_->IsSupported(OpCode::LE_EXTENDED_SCAN_PARAMS)) {
+      api_type_ = ScanApiType::ANDROID_HCI;
+    } else {
+      api_type_ = ScanApiType::LE_4_0;
+    }
+    configure_scan();
+  }
+
+  void handle_scan_results(LeMetaEventView event) {
+    switch (event.GetSubeventCode()) {
+      case hci::SubeventCode::ADVERTISING_REPORT:
+        handle_advertising_report<LeAdvertisingReportView, LeAdvertisingReport, LeReport>(
+            LeAdvertisingReportView::Create(event));
+        break;
+      case hci::SubeventCode::DIRECTED_ADVERTISING_REPORT:
+        handle_advertising_report<LeDirectedAdvertisingReportView, LeDirectedAdvertisingReport, DirectedLeReport>(
+            LeDirectedAdvertisingReportView::Create(event));
+        break;
+      case hci::SubeventCode::EXTENDED_ADVERTISING_REPORT:
+        handle_advertising_report<LeExtendedAdvertisingReportView, LeExtendedAdvertisingReport, ExtendedLeReport>(
+            LeExtendedAdvertisingReportView::Create(event));
+        break;
+      case hci::SubeventCode::SCAN_TIMEOUT:
+        if (registered_callback_ != nullptr) {
+          registered_callback_->Handler()->Post(
+              common::BindOnce(&LeScanningManagerCallbacks::on_timeout, common::Unretained(registered_callback_)));
+          registered_callback_ = nullptr;
+        }
+        break;
+      default:
+        LOG_ALWAYS_FATAL("Unknown advertising subevent %s", hci::SubeventCodeText(event.GetSubeventCode()).c_str());
+    }
+  }
+
+  template <class EventType, class ReportStructType, class ReportType>
+  void handle_advertising_report(EventType event_view) {
+    if (registered_callback_ == nullptr) {
+      LOG_INFO("Dropping advertising event (no registered handler)");
+      return;
+    }
+    if (!event_view.IsValid()) {
+      LOG_INFO("Dropping invalid advertising event");
+      return;
+    }
+    std::vector<ReportStructType> report_vector = event_view.GetAdvertisingReports();
+    if (report_vector.empty()) {
+      LOG_INFO("Zero results in advertising event");
+      return;
+    }
+    std::vector<std::shared_ptr<LeReport>> param;
+    param.reserve(report_vector.size());
+    for (const ReportStructType& report : report_vector) {
+      param.push_back(std::shared_ptr<LeReport>(static_cast<LeReport*>(new ReportType(report))));
+    }
+    registered_callback_->Handler()->Post(common::BindOnce(&LeScanningManagerCallbacks::on_advertisements,
+                                                           common::Unretained(registered_callback_), param));
+  }
+
+  void configure_scan() {
+    std::vector<PhyScanParameters> parameter_vector;
+    PhyScanParameters phy_scan_parameters;
+    phy_scan_parameters.le_scan_window_ = kDefaultLeScanWindow;
+    phy_scan_parameters.le_scan_interval_ = kDefaultLeScanInterval;
+    phy_scan_parameters.le_scan_type_ = LeScanType::ACTIVE;
+    parameter_vector.push_back(phy_scan_parameters);
+    uint8_t phys_in_use = 1;
+
+    switch (api_type_) {
+      case ScanApiType::LE_5_0:
+        le_scanning_interface_->EnqueueCommand(hci::LeSetExtendedScanParametersBuilder::Create(
+                                                   own_address_type_, filter_policy_, phys_in_use, parameter_vector),
+                                               common::BindOnce(impl::check_status), module_handler_);
+        break;
+      case ScanApiType::ANDROID_HCI:
+        le_scanning_interface_->EnqueueCommand(
+            hci::LeExtendedScanParamsBuilder::Create(LeScanType::ACTIVE, interval_ms_, window_ms_, own_address_type_,
+                                                     filter_policy_),
+            common::BindOnce(impl::check_status), module_handler_);
+
+        break;
+      case ScanApiType::LE_4_0:
+        le_scanning_interface_->EnqueueCommand(
+            hci::LeSetScanParametersBuilder::Create(LeScanType::ACTIVE, interval_ms_, window_ms_, own_address_type_,
+                                                    filter_policy_),
+            common::BindOnce(impl::check_status), module_handler_);
+        break;
+    }
+  }
+
+  void start_scan(LeScanningManagerCallbacks* le_scanning_manager_callbacks) {
+    registered_callback_ = le_scanning_manager_callbacks;
+    switch (api_type_) {
+      case ScanApiType::LE_5_0:
+        le_scanning_interface_->EnqueueCommand(
+            hci::LeSetExtendedScanEnableBuilder::Create(Enable::ENABLED,
+                                                        FilterDuplicates::DISABLED /* filter duplicates */, 0, 0),
+            common::BindOnce(impl::check_status), module_handler_);
+        break;
+      case ScanApiType::ANDROID_HCI:
+      case ScanApiType::LE_4_0:
+        le_scanning_interface_->EnqueueCommand(
+            hci::LeSetScanEnableBuilder::Create(Enable::ENABLED, Enable::DISABLED /* filter duplicates */),
+            common::BindOnce(impl::check_status), module_handler_);
+        break;
+    }
+  }
+
+  void stop_scan(common::Callback<void()> on_stopped) {
+    if (registered_callback_ == nullptr) {
+      return;
+    }
+    registered_callback_->Handler()->Post(std::move(on_stopped));
+    switch (api_type_) {
+      case ScanApiType::LE_5_0:
+        le_scanning_interface_->EnqueueCommand(
+            hci::LeSetExtendedScanEnableBuilder::Create(Enable::DISABLED,
+                                                        FilterDuplicates::DISABLED /* filter duplicates */, 0, 0),
+            common::BindOnce(impl::check_status), module_handler_);
+        registered_callback_ = nullptr;
+        break;
+      case ScanApiType::ANDROID_HCI:
+      case ScanApiType::LE_4_0:
+        le_scanning_interface_->EnqueueCommand(
+            hci::LeSetScanEnableBuilder::Create(Enable::DISABLED, Enable::DISABLED /* filter duplicates */),
+            common::BindOnce(impl::check_status), module_handler_);
+        registered_callback_ = nullptr;
+        break;
+    }
+  }
+
+  ScanApiType api_type_;
+
+  LeScanningManagerCallbacks* registered_callback_;
+  Module* module_;
+  os::Handler* module_handler_;
+  hci::HciLayer* hci_layer_;
+  hci::Controller* controller_;
+  hci::LeScanningInterface* le_scanning_interface_;
+
+  uint32_t interval_ms_{1000};
+  uint16_t window_ms_{1000};
+  AddressType own_address_type_{AddressType::PUBLIC_DEVICE_ADDRESS};
+  LeSetScanningFilterPolicy filter_policy_{LeSetScanningFilterPolicy::ACCEPT_ALL};
+
+  static void check_status(CommandCompleteView view) {
+    switch (view.GetCommandOpCode()) {
+      case (OpCode::LE_SET_SCAN_ENABLE): {
+        auto status_view = LeSetScanEnableCompleteView::Create(view);
+        ASSERT(status_view.IsValid());
+        ASSERT(status_view.GetStatus() == ErrorCode::SUCCESS);
+      } break;
+      case (OpCode::LE_SET_EXTENDED_SCAN_ENABLE): {
+        auto status_view = LeSetExtendedScanEnableCompleteView::Create(view);
+        ASSERT(status_view.IsValid());
+        ASSERT(status_view.GetStatus() == ErrorCode::SUCCESS);
+      } break;
+      case (OpCode::LE_SET_SCAN_PARAMETERS): {
+        auto status_view = LeSetScanParametersCompleteView::Create(view);
+        ASSERT(status_view.IsValid());
+        ASSERT(status_view.GetStatus() == ErrorCode::SUCCESS);
+      } break;
+      case (OpCode::LE_EXTENDED_SCAN_PARAMS): {
+        auto status_view = LeExtendedScanParamsCompleteView::Create(view);
+        ASSERT(status_view.IsValid());
+        ASSERT(status_view.GetStatus() == ErrorCode::SUCCESS);
+      } break;
+      case (OpCode::LE_SET_EXTENDED_SCAN_PARAMETERS): {
+        auto status_view = LeSetExtendedScanParametersCompleteView::Create(view);
+        ASSERT(status_view.IsValid());
+        ASSERT(status_view.GetStatus() == ErrorCode::SUCCESS);
+      } break;
+      default:
+        LOG_ALWAYS_FATAL("Unhandled event %s", OpCodeText(view.GetCommandOpCode()).c_str());
+    }
+  }
+};
+
+LeScanningManager::LeScanningManager() {
+  pimpl_ = std::make_unique<impl>(this);
+}
+
+void LeScanningManager::ListDependencies(ModuleList* list) {
+  list->add<hci::HciLayer>();
+  list->add<hci::Controller>();
+}
+
+void LeScanningManager::Start() {
+  pimpl_->start(GetHandler(), GetDependency<hci::HciLayer>(), GetDependency<hci::Controller>());
+}
+
+void LeScanningManager::Stop() {
+  pimpl_.reset();
+}
+
+std::string LeScanningManager::ToString() const {
+  return "Le Scanning Manager";
+}
+
+void LeScanningManager::StartScan(LeScanningManagerCallbacks* callbacks) {
+  GetHandler()->Post(common::Bind(&impl::start_scan, common::Unretained(pimpl_.get()), callbacks));
+}
+
+void LeScanningManager::StopScan(common::Callback<void()> on_stopped) {
+  GetHandler()->Post(common::Bind(&impl::stop_scan, common::Unretained(pimpl_.get()), on_stopped));
+}
+
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/le_scanning_manager.h b/gd/hci/le_scanning_manager.h
new file mode 100644
index 0000000..2f348d2
--- /dev/null
+++ b/gd/hci/le_scanning_manager.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <memory>
+
+#include "common/callback.h"
+#include "hci/hci_packets.h"
+#include "hci/le_report.h"
+#include "module.h"
+
+namespace bluetooth {
+namespace hci {
+
+class LeScanningManagerCallbacks {
+ public:
+  virtual ~LeScanningManagerCallbacks() = default;
+  virtual void on_advertisements(std::vector<std::shared_ptr<LeReport>>) = 0;
+  virtual void on_timeout() = 0;
+  virtual os::Handler* Handler() = 0;
+};
+
+class LeScanningManager : public bluetooth::Module {
+ public:
+  LeScanningManager();
+
+  void StartScan(LeScanningManagerCallbacks* callbacks);
+
+  void StopScan(common::Callback<void()> on_stopped);
+
+  static const ModuleFactory Factory;
+
+ protected:
+  void ListDependencies(ModuleList* list) override;
+
+  void Start() override;
+
+  void Stop() override;
+
+  std::string ToString() const override;
+
+ private:
+  struct impl;
+  std::unique_ptr<impl> pimpl_;
+  DISALLOW_COPY_AND_ASSIGN(LeScanningManager);
+};
+
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/le_scanning_manager_test.cc b/gd/hci/le_scanning_manager_test.cc
new file mode 100644
index 0000000..fe98159
--- /dev/null
+++ b/gd/hci/le_scanning_manager_test.cc
@@ -0,0 +1,343 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <algorithm>
+#include <chrono>
+#include <future>
+#include <map>
+
+#include "common/bind.h"
+#include "hci/address.h"
+#include "hci/controller.h"
+#include "hci/hci_layer.h"
+#include "hci/le_scanning_manager.h"
+#include "os/thread.h"
+#include "packet/raw_builder.h"
+
+namespace bluetooth {
+namespace hci {
+namespace {
+
+using packet::kLittleEndian;
+using packet::PacketView;
+using packet::RawBuilder;
+
+PacketView<kLittleEndian> GetPacketView(std::unique_ptr<packet::BasePacketBuilder> packet) {
+  auto bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter i(*bytes);
+  bytes->reserve(packet->size());
+  packet->Serialize(i);
+  return packet::PacketView<packet::kLittleEndian>(bytes);
+}
+
+class TestController : public Controller {
+ public:
+  bool IsSupported(OpCode op_code) const override {
+    return supported_opcodes_.count(op_code) == 1;
+  }
+
+  void AddSupported(OpCode op_code) {
+    supported_opcodes_.insert(op_code);
+  }
+
+ protected:
+  void Start() override {}
+  void Stop() override {}
+  void ListDependencies(ModuleList* list) override {}
+
+ private:
+  std::set<OpCode> supported_opcodes_{};
+};
+
+class TestHciLayer : public HciLayer {
+ public:
+  TestHciLayer() {
+    RegisterEventHandler(EventCode::COMMAND_COMPLETE,
+                         base::Bind(&TestHciLayer::CommandCompleteCallback, common::Unretained(this)), nullptr);
+    RegisterEventHandler(EventCode::COMMAND_STATUS,
+                         base::Bind(&TestHciLayer::CommandStatusCallback, common::Unretained(this)), nullptr);
+  }
+
+  void EnqueueCommand(std::unique_ptr<CommandPacketBuilder> command,
+                      common::OnceCallback<void(CommandStatusView)> on_status, os::Handler* handler) override {
+    command_queue_.push(std::move(command));
+    command_status_callbacks.push_front(std::move(on_status));
+    if (command_promise_ != nullptr) {
+      command_promise_->set_value();
+      command_promise_.reset();
+    }
+  }
+
+  void EnqueueCommand(std::unique_ptr<CommandPacketBuilder> command,
+                      common::OnceCallback<void(CommandCompleteView)> on_complete, os::Handler* handler) override {
+    command_queue_.push(std::move(command));
+    command_complete_callbacks.push_front(std::move(on_complete));
+    if (command_promise_ != nullptr) {
+      command_promise_->set_value();
+      command_promise_.reset();
+    }
+  }
+
+  std::future<void> GetCommandFuture() {
+    ASSERT_LOG(command_promise_ == nullptr, "Promises promises ... Only one at a time");
+    command_promise_ = std::make_unique<std::promise<void>>();
+    return command_promise_->get_future();
+  }
+
+  std::unique_ptr<CommandPacketBuilder> GetLastCommand() {
+    ASSERT(!command_queue_.empty());
+    auto last = std::move(command_queue_.front());
+    command_queue_.pop();
+    return last;
+  }
+
+  ConnectionManagementCommandView GetCommandPacket(OpCode op_code) {
+    auto packet_view = GetPacketView(GetLastCommand());
+    CommandPacketView command_packet_view = CommandPacketView::Create(packet_view);
+    ConnectionManagementCommandView command = ConnectionManagementCommandView::Create(command_packet_view);
+    ASSERT(command.IsValid());
+    EXPECT_EQ(command.GetOpCode(), op_code);
+
+    return command;
+  }
+
+  void RegisterEventHandler(EventCode event_code, common::Callback<void(EventPacketView)> event_handler,
+                            os::Handler* handler) override {
+    registered_events_[event_code] = event_handler;
+  }
+
+  void RegisterLeEventHandler(SubeventCode subevent_code, common::Callback<void(LeMetaEventView)> event_handler,
+                              os::Handler* handler) override {
+    registered_le_events_[subevent_code] = event_handler;
+  }
+
+  void IncomingEvent(std::unique_ptr<EventPacketBuilder> event_builder) {
+    auto packet = GetPacketView(std::move(event_builder));
+    EventPacketView event = EventPacketView::Create(packet);
+    ASSERT_TRUE(event.IsValid());
+    EventCode event_code = event.GetEventCode();
+    ASSERT_TRUE(registered_events_.find(event_code) != registered_events_.end()) << EventCodeText(event_code);
+    registered_events_[event_code].Run(event);
+  }
+
+  void IncomingLeMetaEvent(std::unique_ptr<LeMetaEventBuilder> event_builder) {
+    auto packet = GetPacketView(std::move(event_builder));
+    EventPacketView event = EventPacketView::Create(packet);
+    LeMetaEventView meta_event_view = LeMetaEventView::Create(event);
+    ASSERT_TRUE(meta_event_view.IsValid());
+    SubeventCode subevent_code = meta_event_view.GetSubeventCode();
+    ASSERT_TRUE(registered_le_events_.find(subevent_code) != registered_le_events_.end())
+        << SubeventCodeText(subevent_code);
+    registered_le_events_[subevent_code].Run(meta_event_view);
+  }
+
+  void CommandCompleteCallback(EventPacketView event) {
+    CommandCompleteView complete_view = CommandCompleteView::Create(event);
+    ASSERT(complete_view.IsValid());
+    std::move(command_complete_callbacks.front()).Run(complete_view);
+    command_complete_callbacks.pop_front();
+  }
+
+  void CommandStatusCallback(EventPacketView event) {
+    CommandStatusView status_view = CommandStatusView::Create(event);
+    ASSERT(status_view.IsValid());
+    std::move(command_status_callbacks.front()).Run(status_view);
+    command_status_callbacks.pop_front();
+  }
+
+  void ListDependencies(ModuleList* list) override {}
+  void Start() override {}
+  void Stop() override {}
+
+ private:
+  std::map<EventCode, common::Callback<void(EventPacketView)>> registered_events_;
+  std::map<SubeventCode, common::Callback<void(LeMetaEventView)>> registered_le_events_;
+  std::list<base::OnceCallback<void(CommandCompleteView)>> command_complete_callbacks;
+  std::list<base::OnceCallback<void(CommandStatusView)>> command_status_callbacks;
+
+  std::queue<std::unique_ptr<CommandPacketBuilder>> command_queue_;
+  mutable std::mutex mutex_;
+  std::unique_ptr<std::promise<void>> command_promise_{};
+};
+
+class LeScanningManagerTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    test_hci_layer_ = new TestHciLayer;  // Ownership is transferred to registry
+    test_controller_ = new TestController;
+    test_controller_->AddSupported(param_opcode_);
+    fake_registry_.InjectTestModule(&HciLayer::Factory, test_hci_layer_);
+    fake_registry_.InjectTestModule(&Controller::Factory, test_controller_);
+    client_handler_ = fake_registry_.GetTestModuleHandler(&HciLayer::Factory);
+    ASSERT_NE(client_handler_, nullptr);
+    mock_callbacks_.handler_ = client_handler_;
+    std::future<void> config_future = test_hci_layer_->GetCommandFuture();
+    fake_registry_.Start<LeScanningManager>(&thread_);
+    le_scanning_manager =
+        static_cast<LeScanningManager*>(fake_registry_.GetModuleUnderTest(&LeScanningManager::Factory));
+    auto result = config_future.wait_for(std::chrono::duration(std::chrono::milliseconds(1000)));
+    ASSERT_EQ(std::future_status::ready, result);
+    HandleConfiguration();
+  }
+
+  void TearDown() override {
+    fake_registry_.SynchronizeModuleHandler(&LeScanningManager::Factory, std::chrono::milliseconds(20));
+    fake_registry_.StopAll();
+  }
+
+  virtual void HandleConfiguration() {
+    auto packet = test_hci_layer_->GetCommandPacket(OpCode::LE_SET_SCAN_PARAMETERS);
+    test_hci_layer_->IncomingEvent(LeSetScanParametersCompleteBuilder::Create(1, ErrorCode::SUCCESS));
+  }
+
+  TestModuleRegistry fake_registry_;
+  TestHciLayer* test_hci_layer_ = nullptr;
+  TestController* test_controller_ = nullptr;
+  os::Thread& thread_ = fake_registry_.GetTestThread();
+  LeScanningManager* le_scanning_manager = nullptr;
+  os::Handler* client_handler_ = nullptr;
+
+  class MockLeScanningManagerCallbacks : public LeScanningManagerCallbacks {
+   public:
+    MOCK_METHOD(void, on_advertisements, (std::vector<std::shared_ptr<LeReport>>), (override));
+    MOCK_METHOD(void, on_timeout, (), (override));
+    os::Handler* Handler() {
+      return handler_;
+    }
+    os::Handler* handler_{nullptr};
+  } mock_callbacks_;
+
+  OpCode param_opcode_{OpCode::LE_SET_ADVERTISING_PARAMETERS};
+};
+
+class LeAndroidHciScanningManagerTest : public LeScanningManagerTest {
+ protected:
+  void SetUp() override {
+    param_opcode_ = OpCode::LE_EXTENDED_SCAN_PARAMS;
+    LeScanningManagerTest::SetUp();
+  }
+
+  void HandleConfiguration() override {
+    auto packet = test_hci_layer_->GetCommandPacket(OpCode::LE_EXTENDED_SCAN_PARAMS);
+    test_hci_layer_->IncomingEvent(LeExtendedScanParamsCompleteBuilder::Create(1, ErrorCode::SUCCESS));
+  }
+};
+
+class LeExtendedScanningManagerTest : public LeScanningManagerTest {
+ protected:
+  void SetUp() override {
+    param_opcode_ = OpCode::LE_SET_EXTENDED_SCAN_PARAMETERS;
+    LeScanningManagerTest::SetUp();
+  }
+
+  void HandleConfiguration() override {
+    auto packet = test_hci_layer_->GetCommandPacket(OpCode::LE_SET_EXTENDED_SCAN_PARAMETERS);
+    test_hci_layer_->IncomingEvent(LeSetExtendedScanParametersCompleteBuilder::Create(1, ErrorCode::SUCCESS));
+  }
+};
+
+TEST_F(LeScanningManagerTest, startup_teardown) {}
+
+TEST_F(LeScanningManagerTest, start_scan_test) {
+  auto next_command_future = test_hci_layer_->GetCommandFuture();
+  le_scanning_manager->StartScan(&mock_callbacks_);
+
+  auto result = next_command_future.wait_for(std::chrono::duration(std::chrono::milliseconds(100)));
+  ASSERT_EQ(std::future_status::ready, result);
+  test_hci_layer_->IncomingEvent(LeSetScanEnableCompleteBuilder::Create(uint8_t{1}, ErrorCode::SUCCESS));
+
+  LeAdvertisingReport report{};
+  report.event_type_ = AdvertisingEventType::ADV_IND;
+  report.address_type_ = AddressType::PUBLIC_DEVICE_ADDRESS;
+  Address::FromString("12:34:56:78:9a:bc", report.address_);
+  std::vector<GapData> gap_data{};
+  GapData data_item{};
+  data_item.data_type_ = GapDataType::FLAGS;
+  data_item.data_ = {0x34};
+  gap_data.push_back(data_item);
+  data_item.data_type_ = GapDataType::COMPLETE_LOCAL_NAME;
+  data_item.data_ = {'r', 'a', 'n', 'd', 'o', 'm', ' ', 'd', 'e', 'v', 'i', 'c', 'e'};
+  gap_data.push_back(data_item);
+  report.advertising_data_ = gap_data;
+
+  EXPECT_CALL(mock_callbacks_, on_advertisements);
+
+  test_hci_layer_->IncomingLeMetaEvent(LeAdvertisingReportBuilder::Create({report}));
+}
+
+TEST_F(LeAndroidHciScanningManagerTest, start_scan_test) {
+  auto next_command_future = test_hci_layer_->GetCommandFuture();
+  le_scanning_manager->StartScan(&mock_callbacks_);
+
+  auto result = next_command_future.wait_for(std::chrono::duration(std::chrono::milliseconds(100)));
+  ASSERT_EQ(std::future_status::ready, result);
+  test_hci_layer_->IncomingEvent(LeSetScanEnableCompleteBuilder::Create(uint8_t{1}, ErrorCode::SUCCESS));
+
+  LeAdvertisingReport report{};
+  report.event_type_ = AdvertisingEventType::ADV_IND;
+  report.address_type_ = AddressType::PUBLIC_DEVICE_ADDRESS;
+  Address::FromString("12:34:56:78:9a:bc", report.address_);
+  std::vector<GapData> gap_data{};
+  GapData data_item{};
+  data_item.data_type_ = GapDataType::FLAGS;
+  data_item.data_ = {0x34};
+  gap_data.push_back(data_item);
+  data_item.data_type_ = GapDataType::COMPLETE_LOCAL_NAME;
+  data_item.data_ = {'r', 'a', 'n', 'd', 'o', 'm', ' ', 'd', 'e', 'v', 'i', 'c', 'e'};
+  gap_data.push_back(data_item);
+  report.advertising_data_ = gap_data;
+
+  EXPECT_CALL(mock_callbacks_, on_advertisements);
+
+  test_hci_layer_->IncomingLeMetaEvent(LeAdvertisingReportBuilder::Create({report}));
+}
+
+TEST_F(LeExtendedScanningManagerTest, start_scan_test) {
+  auto next_command_future = test_hci_layer_->GetCommandFuture();
+  le_scanning_manager->StartScan(&mock_callbacks_);
+
+  auto result = next_command_future.wait_for(std::chrono::duration(std::chrono::milliseconds(100)));
+  ASSERT_EQ(std::future_status::ready, result);
+  auto packet = test_hci_layer_->GetCommandPacket(OpCode::LE_SET_EXTENDED_SCAN_ENABLE);
+
+  test_hci_layer_->IncomingEvent(LeSetScanEnableCompleteBuilder::Create(uint8_t{1}, ErrorCode::SUCCESS));
+
+  LeExtendedAdvertisingReport report{};
+  report.connectable_ = 1;
+  report.scannable_ = 1;
+  report.address_type_ = DirectAdvertisingAddressType::PUBLIC_DEVICE_ADDRESS;
+  Address::FromString("12:34:56:78:9a:bc", report.address_);
+  std::vector<GapData> gap_data{};
+  GapData data_item{};
+  data_item.data_type_ = GapDataType::FLAGS;
+  data_item.data_ = {0x34};
+  gap_data.push_back(data_item);
+  data_item.data_type_ = GapDataType::COMPLETE_LOCAL_NAME;
+  data_item.data_ = {'r', 'a', 'n', 'd', 'o', 'm', ' ', 'd', 'e', 'v', 'i', 'c', 'e'};
+  gap_data.push_back(data_item);
+  report.advertising_data_ = gap_data;
+
+  EXPECT_CALL(mock_callbacks_, on_advertisements);
+
+  test_hci_layer_->IncomingLeMetaEvent(LeExtendedAdvertisingReportBuilder::Create({report}));
+}
+
+}  // namespace
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/le_security_interface.h b/gd/hci/le_security_interface.h
new file mode 100644
index 0000000..13c3ef3
--- /dev/null
+++ b/gd/hci/le_security_interface.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "common/callback.h"
+#include "hci/hci_packets.h"
+#include "os/handler.h"
+#include "os/utils.h"
+
+namespace bluetooth {
+namespace hci {
+
+class LeSecurityInterface {
+ public:
+  LeSecurityInterface() = default;
+  virtual ~LeSecurityInterface() = default;
+  DISALLOW_COPY_AND_ASSIGN(LeSecurityInterface);
+
+  virtual void EnqueueCommand(std::unique_ptr<LeSecurityCommandBuilder> command,
+                              common::OnceCallback<void(CommandCompleteView)> on_complete, os::Handler* handler) = 0;
+
+  virtual void EnqueueCommand(std::unique_ptr<LeSecurityCommandBuilder> command,
+                              common::OnceCallback<void(CommandStatusView)> on_status, os::Handler* handler) = 0;
+
+  static constexpr hci::SubeventCode LeSecurityEvents[] = {
+      hci::SubeventCode::LONG_TERM_KEY_REQUEST,
+      hci::SubeventCode::READ_LOCAL_P256_PUBLIC_KEY_COMPLETE,
+      hci::SubeventCode::GENERATE_DHKEY_COMPLETE,
+  };
+};
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/hci/security_interface.h b/gd/hci/security_interface.h
new file mode 100644
index 0000000..1258122
--- /dev/null
+++ b/gd/hci/security_interface.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "common/callback.h"
+#include "hci/hci_packets.h"
+#include "os/utils.h"
+
+namespace bluetooth {
+namespace hci {
+
+class SecurityInterface {
+ public:
+  SecurityInterface() = default;
+  virtual ~SecurityInterface() = default;
+  DISALLOW_COPY_AND_ASSIGN(SecurityInterface);
+
+  virtual void EnqueueCommand(std::unique_ptr<SecurityCommandBuilder> command,
+                              common::OnceCallback<void(CommandCompleteView)> on_complete, os::Handler* handler) = 0;
+
+  virtual void EnqueueCommand(std::unique_ptr<SecurityCommandBuilder> command,
+                              common::OnceCallback<void(CommandStatusView)> on_status, os::Handler* handler) = 0;
+
+  static constexpr hci::EventCode SecurityEvents[] = {
+      hci::EventCode::CHANGE_CONNECTION_LINK_KEY_COMPLETE,
+      hci::EventCode::MASTER_LINK_KEY_COMPLETE,
+      hci::EventCode::RETURN_LINK_KEYS,
+      hci::EventCode::PIN_CODE_REQUEST,
+      hci::EventCode::LINK_KEY_REQUEST,
+      hci::EventCode::LINK_KEY_NOTIFICATION,
+      hci::EventCode::ENCRYPTION_KEY_REFRESH_COMPLETE,
+      hci::EventCode::IO_CAPABILITY_REQUEST,
+      hci::EventCode::IO_CAPABILITY_RESPONSE,
+      hci::EventCode::REMOTE_OOB_DATA_REQUEST,
+      hci::EventCode::SIMPLE_PAIRING_COMPLETE,
+      hci::EventCode::USER_PASSKEY_NOTIFICATION,
+      hci::EventCode::KEYPRESS_NOTIFICATION,
+      hci::EventCode::USER_CONFIRMATION_REQUEST,
+      hci::EventCode::USER_PASSKEY_REQUEST,
+      hci::EventCode::REMOTE_HOST_SUPPORTED_FEATURES_NOTIFICATION,
+  };
+};
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/gd/l2cap/Android.bp b/gd/l2cap/Android.bp
new file mode 100644
index 0000000..c04a9d3
--- /dev/null
+++ b/gd/l2cap/Android.bp
@@ -0,0 +1,82 @@
+filegroup {
+    name: "BluetoothL2capSources",
+    srcs: [
+        "fcs.cc",
+        "classic/dynamic_channel_manager.cc",
+        "classic/dynamic_channel_service.cc",
+        "classic/fixed_channel.cc",
+        "classic/fixed_channel_manager.cc",
+        "classic/fixed_channel_service.cc",
+        "classic/internal/dynamic_channel_service_manager_impl.cc",
+        "classic/internal/fixed_channel_impl.cc",
+        "classic/internal/fixed_channel_service_manager_impl.cc",
+        "classic/internal/link.cc",
+        "classic/internal/link_manager.cc",
+        "classic/internal/signalling_manager.cc",
+        "classic/l2cap_classic_module.cc",
+        "dynamic_channel.cc",
+        "internal/basic_mode_channel_data_controller.cc",
+        "internal/data_pipeline_manager.cc",
+        "internal/dynamic_channel_allocator.cc",
+        "internal/dynamic_channel_impl.cc",
+        "internal/enhanced_retransmission_mode_channel_data_controller.cc",
+        "internal/le_credit_based_channel_data_controller.cc",
+        "internal/receiver.cc",
+        "internal/scheduler_fifo.cc",
+        "internal/sender.cc",
+        "le/dynamic_channel_manager.cc",
+        "le/dynamic_channel_service.cc",
+        "le/fixed_channel.cc",
+        "le/fixed_channel_manager.cc",
+        "le/fixed_channel_service.cc",
+        "le/internal/dynamic_channel_service_manager_impl.cc",
+        "le/internal/fixed_channel_impl.cc",
+        "le/internal/fixed_channel_service_manager_impl.cc",
+        "le/internal/link.cc",
+        "le/internal/link_manager.cc",
+        "le/internal/signalling_manager.cc",
+        "le/l2cap_le_module.cc",
+    ],
+}
+
+filegroup {
+    name: "BluetoothL2capTestSources",
+    srcs: [
+        "classic/internal/dynamic_channel_service_manager_test.cc",
+        "classic/internal/fixed_channel_impl_test.cc",
+        "classic/internal/fixed_channel_service_manager_test.cc",
+        "classic/internal/link_test.cc",
+        "classic/internal/link_manager_test.cc",
+        "classic/internal/signalling_manager_test.cc",
+        "internal/basic_mode_channel_data_controller_test.cc",
+        "internal/dynamic_channel_allocator_test.cc",
+        "internal/dynamic_channel_impl_test.cc",
+        "internal/enhanced_retransmission_mode_channel_data_controller_test.cc",
+        "internal/fixed_channel_allocator_test.cc",
+        "internal/le_credit_based_channel_data_controller_test.cc",
+        "internal/receiver_test.cc",
+        "internal/scheduler_fifo_test.cc",
+        "internal/sender_test.cc",
+        "l2cap_packet_test.cc",
+        "le/internal/dynamic_channel_service_manager_test.cc",
+        "le/internal/fixed_channel_impl_test.cc",
+        "le/internal/fixed_channel_service_manager_test.cc",
+        "le/internal/link_manager_test.cc",
+        "signal_id_test.cc",
+    ],
+}
+
+filegroup {
+    name: "BluetoothFacade_l2cap_layer",
+    srcs: [
+        "classic/facade.cc",
+    ],
+}
+
+filegroup {
+    name: "BluetoothL2capFuzzTestSources",
+    srcs: [
+        "internal/dynamic_channel_allocator_fuzz_test.cc",
+        "l2cap_packet_fuzz_test.cc",
+    ],
+}
diff --git a/gd/l2cap/cid.h b/gd/l2cap/cid.h
new file mode 100644
index 0000000..729272c
--- /dev/null
+++ b/gd/l2cap/cid.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+
+namespace bluetooth {
+namespace l2cap {
+
+using Cid = uint16_t;
+
+constexpr Cid kInvalidCid = 0;
+constexpr Cid kFirstFixedChannel = 1;
+constexpr Cid kLastFixedChannel = 63;
+constexpr Cid kFirstDynamicChannel = kLastFixedChannel + 1;
+constexpr Cid kLastDynamicChannel = (uint16_t)(0xffff);
+
+constexpr Cid kClassicSignallingCid = 1;
+constexpr Cid kConnectionlessCid = 2;
+constexpr Cid kLeAttributeCid = 4;
+constexpr Cid kLeSignallingCid = 5;
+constexpr Cid kSmpCid = 6;
+constexpr Cid kSmpBrCid = 7;
+
+constexpr Cid kClassicPairingTriggerCid = kLastFixedChannel - 1;
+
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/classic/cert/l2cap_test.py b/gd/l2cap/classic/cert/l2cap_test.py
new file mode 100644
index 0000000..3fcb4f1
--- /dev/null
+++ b/gd/l2cap/classic/cert/l2cap_test.py
@@ -0,0 +1,2045 @@
+#
+#   Copyright 2019 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import time
+from datetime import timedelta
+from mobly import asserts
+
+from cert.gd_base_test_facade_only import GdFacadeOnlyBaseTestClass
+from cert.event_asserts import EventAsserts
+from cert.event_callback_stream import EventCallbackStream
+from facade import common_pb2
+from facade import rootservice_pb2 as facade_rootservice
+from google.protobuf import empty_pb2 as empty_proto
+from l2cap.classic import facade_pb2 as l2cap_facade_pb2
+from neighbor.facade import facade_pb2 as neighbor_facade
+from hci.facade import acl_manager_facade_pb2 as acl_manager_facade
+import bluetooth_packets_python3 as bt_packets
+from bluetooth_packets_python3 import hci_packets, l2cap_packets
+
+# Assemble a sample packet. TODO: Use RawBuilder
+SAMPLE_PACKET = l2cap_packets.CommandRejectNotUnderstoodBuilder(1)
+
+
+class L2capTest(GdFacadeOnlyBaseTestClass):
+
+    def setup_test(self):
+        self.device_under_test.rootservice.StartStack(
+            facade_rootservice.StartStackRequest(
+                module_under_test=facade_rootservice.BluetoothModule.Value(
+                    'L2CAP'),))
+        self.cert_device.rootservice.StartStack(
+            facade_rootservice.StartStackRequest(
+                module_under_test=facade_rootservice.BluetoothModule.Value(
+                    'HCI_INTERFACES'),))
+
+        self.device_under_test.wait_channel_ready()
+        self.cert_device.wait_channel_ready()
+
+        self.device_under_test.address = self.device_under_test.hci_controller.GetMacAddress(
+            empty_proto.Empty()).address
+        cert_address = self.cert_device.controller_read_only_property.ReadLocalAddress(
+            empty_proto.Empty()).address
+        self.cert_device.address = cert_address
+        self.dut_address = common_pb2.BluetoothAddress(
+            address=self.device_under_test.address)
+        self.cert_address = common_pb2.BluetoothAddress(
+            address=self.cert_device.address)
+
+        self.device_under_test.neighbor.EnablePageScan(
+            neighbor_facade.EnableMsg(enabled=True))
+
+        self.cert_acl_handle = 0
+
+        self.on_connection_request = None
+        self.on_connection_response = None
+        self.on_configuration_request = None
+        self.on_configuration_response = None
+        self.on_disconnection_request = None
+        self.on_disconnection_response = None
+        self.on_information_request = None
+        self.on_information_response = None
+
+        self.scid_to_dcid = {}
+        self.ertm_tx_window_size = 10
+        self.ertm_max_transmit = 20
+
+    def _on_connection_request_default(self, l2cap_control_view):
+        connection_request_view = l2cap_packets.ConnectionRequestView(
+            l2cap_control_view)
+        sid = connection_request_view.GetIdentifier()
+        cid = connection_request_view.GetSourceCid()
+
+        self.scid_to_dcid[cid] = cid
+
+        connection_response = l2cap_packets.ConnectionResponseBuilder(
+            sid, cid, cid, l2cap_packets.ConnectionResponseResult.SUCCESS,
+            l2cap_packets.ConnectionResponseStatus.
+            NO_FURTHER_INFORMATION_AVAILABLE)
+        connection_response_l2cap = l2cap_packets.BasicFrameBuilder(
+            1, connection_response)
+        self.cert_device.hci_acl_manager.SendAclData(
+            acl_manager_facade.AclData(
+                handle=self.cert_acl_handle,
+                payload=bytes(connection_response_l2cap.Serialize())))
+        return True
+
+    def _on_connection_response_default(self, l2cap_control_view):
+        connection_response_view = l2cap_packets.ConnectionResponseView(
+            l2cap_control_view)
+        sid = connection_response_view.GetIdentifier()
+        scid = connection_response_view.GetSourceCid()
+        dcid = connection_response_view.GetDestinationCid()
+        self.scid_to_dcid[scid] = dcid
+
+        config_request = l2cap_packets.ConfigurationRequestBuilder(
+            sid + 1, dcid, l2cap_packets.Continuation.END, [])
+        config_request_l2cap = l2cap_packets.BasicFrameBuilder(
+            1, config_request)
+        self.cert_device.hci_acl_manager.SendAclData(
+            acl_manager_facade.AclData(
+                handle=self.cert_acl_handle,
+                payload=bytes(config_request_l2cap.Serialize())))
+        return True
+
+    def _on_connection_response_use_ertm(self, l2cap_control_view):
+        connection_response_view = l2cap_packets.ConnectionResponseView(
+            l2cap_control_view)
+        sid = connection_response_view.GetIdentifier()
+        scid = connection_response_view.GetSourceCid()
+        dcid = connection_response_view.GetDestinationCid()
+        self.scid_to_dcid[scid] = dcid
+
+        # FIXME: This doesn't work!
+        ertm_option = l2cap_packets.RetransmissionAndFlowControlConfigurationOption(
+        )
+        ertm_option.mode = l2cap_packets.RetransmissionAndFlowControlModeOption.L2CAP_BASIC
+        ertm_option.tx_window_size = self.ertm_tx_window_size
+        ertm_option.max_transmit = self.ertm_max_transmit
+        ertm_option.retransmission_time_out = 2000
+        ertm_option.monitor_time_out = 12000
+        ertm_option.maximum_pdu_size = 1010
+
+        options = [ertm_option]
+
+        config_request = l2cap_packets.ConfigurationRequestBuilder(
+            sid + 1, dcid, l2cap_packets.Continuation.END, options)
+
+        config_request_l2cap = l2cap_packets.BasicFrameBuilder(
+            1, config_request)
+
+        config_packet = bytearray([
+            0x1a,
+            0x00,
+            0x01,
+            0x00,
+            0x04,
+            sid + 1,
+            0x16,
+            0x00,
+            dcid & 0xff,
+            dcid >> 8,
+            0x00,
+            0x00,
+            0x01,
+            0x02,
+            0xa0,
+            0x02,  # MTU
+            0x04,
+            0x09,
+            0x03,
+            self.ertm_tx_window_size,
+            self.ertm_max_transmit,
+            0xd0,
+            0x07,
+            0xe0,
+            0x2e,
+            0xf2,
+            0x03,  # ERTM
+            0x05,
+            0x01,
+            0x00  # FCS
+        ])
+
+        self.cert_device.hci_acl_manager.SendAclData(
+            acl_manager_facade.AclData(
+                handle=self.cert_acl_handle, payload=bytes(config_packet)))
+        return True
+
+    def _on_connection_response_use_ertm_and_fcs(self, l2cap_control_view):
+        connection_response_view = l2cap_packets.ConnectionResponseView(
+            l2cap_control_view)
+        sid = connection_response_view.GetIdentifier()
+        scid = connection_response_view.GetSourceCid()
+        dcid = connection_response_view.GetDestinationCid()
+        self.scid_to_dcid[scid] = dcid
+
+        # FIXME: This doesn't work!
+        ertm_option = l2cap_packets.RetransmissionAndFlowControlConfigurationOption(
+        )
+        ertm_option.mode = l2cap_packets.RetransmissionAndFlowControlModeOption.L2CAP_BASIC
+        ertm_option.tx_window_size = self.ertm_tx_window_size
+        ertm_option.max_transmit = self.ertm_max_transmit
+        ertm_option.retransmission_time_out = 2000
+        ertm_option.monitor_time_out = 12000
+        ertm_option.maximum_pdu_size = 1010
+
+        options = [ertm_option]
+
+        config_request = l2cap_packets.ConfigurationRequestBuilder(
+            sid + 1, dcid, l2cap_packets.Continuation.END, options)
+
+        config_request_l2cap = l2cap_packets.BasicFrameBuilder(
+            1, config_request)
+
+        config_packet = bytearray([
+            0x1a,
+            0x00,
+            0x01,
+            0x00,
+            0x04,
+            sid + 1,
+            0x16,
+            0x00,
+            dcid & 0xff,
+            dcid >> 8,
+            0x00,
+            0x00,
+            0x01,
+            0x02,
+            0xa0,
+            0x02,  # MTU
+            0x04,
+            0x09,
+            0x03,
+            self.ertm_tx_window_size,
+            self.ertm_max_transmit,
+            0xd0,
+            0x07,
+            0xe0,
+            0x2e,
+            0xf2,
+            0x03,  # ERTM
+            0x05,
+            0x01,
+            0x01  # FCS
+        ])
+
+        self.cert_device.hci_acl_manager.SendAclData(
+            acl_manager_facade.AclData(
+                handle=self.cert_acl_handle, payload=bytes(config_packet)))
+        return True
+
+    def _on_configuration_request_default(self, l2cap_control_view):
+        configuration_request = l2cap_packets.ConfigurationRequestView(
+            l2cap_control_view)
+        sid = configuration_request.GetIdentifier()
+        dcid = configuration_request.GetDestinationCid()
+        config_response = l2cap_packets.ConfigurationResponseBuilder(
+            sid, self.scid_to_dcid.get(dcid, 0), l2cap_packets.Continuation.END,
+            l2cap_packets.ConfigurationResponseResult.SUCCESS, [])
+        config_response_l2cap = l2cap_packets.BasicFrameBuilder(
+            1, config_response)
+        self.cert_send_b_frame(config_response_l2cap)
+
+    def _on_configuration_response_default(self, l2cap_control_view):
+        configuration_response = l2cap_packets.ConfigurationResponseView(
+            l2cap_control_view)
+        sid = configuration_response.GetIdentifier()
+
+    def _on_disconnection_request_default(self, l2cap_control_view):
+        disconnection_request = l2cap_packets.DisconnectionRequestView(
+            l2cap_control_view)
+        sid = disconnection_request.GetIdentifier()
+        scid = disconnection_request.GetSourceCid()
+        dcid = disconnection_request.GetDestinationCid()
+        disconnection_response = l2cap_packets.DisconnectionResponseBuilder(
+            sid, dcid, scid)
+        disconnection_response_l2cap = l2cap_packets.BasicFrameBuilder(
+            1, disconnection_response)
+        self.cert_device.hci_acl_manager.SendAclData(
+            acl_manager_facade.AclData(
+                handle=self.cert_acl_handle,
+                payload=bytes(disconnection_response_l2cap.Serialize())))
+
+    def _on_disconnection_response_default(self, l2cap_control_view):
+        disconnection_response = l2cap_packets.DisconnectionResponseView(
+            l2cap_control_view)
+
+    def _on_information_request_default(self, l2cap_control_view):
+        information_request = l2cap_packets.InformationRequestView(
+            l2cap_control_view)
+        sid = information_request.GetIdentifier()
+        information_type = information_request.GetInfoType()
+        if information_type == l2cap_packets.InformationRequestInfoType.CONNECTIONLESS_MTU:
+            response = l2cap_packets.InformationResponseConnectionlessMtuBuilder(
+                sid, l2cap_packets.InformationRequestResult.SUCCESS, 100)
+            response_l2cap = l2cap_packets.BasicFrameBuilder(1, response)
+            self.cert_device.hci_acl_manager.SendAclData(
+                acl_manager_facade.AclData(
+                    handle=self.cert_acl_handle,
+                    payload=bytes(response_l2cap.Serialize())))
+            return
+        if information_type == l2cap_packets.InformationRequestInfoType.EXTENDED_FEATURES_SUPPORTED:
+            response = l2cap_packets.InformationResponseExtendedFeaturesBuilder(
+                sid, l2cap_packets.InformationRequestResult.SUCCESS, 0, 0, 0, 1,
+                0, 1, 0, 0, 0, 0)
+            response_l2cap = l2cap_packets.BasicFrameBuilder(1, response)
+            self.cert_device.hci_acl_manager.SendAclData(
+                acl_manager_facade.AclData(
+                    handle=self.cert_acl_handle,
+                    payload=bytes(response_l2cap.Serialize())))
+            return
+        if information_type == l2cap_packets.InformationRequestInfoType.FIXED_CHANNELS_SUPPORTED:
+            response = l2cap_packets.InformationResponseFixedChannelsBuilder(
+                sid, l2cap_packets.InformationRequestResult.SUCCESS, 2)
+            response_l2cap = l2cap_packets.BasicFrameBuilder(1, response)
+            self.cert_device.hci_acl_manager.SendAclData(
+                acl_manager_facade.AclData(
+                    handle=self.cert_acl_handle,
+                    payload=bytes(response_l2cap.Serialize())))
+            return
+
+    def _on_information_response_default(self, l2cap_control_view):
+        information_response = l2cap_packets.InformationResponseView(
+            l2cap_control_view)
+
+    def teardown_test(self):
+        self.device_under_test.rootservice.StopStack(
+            facade_rootservice.StopStackRequest())
+        self.cert_device.rootservice.StopStack(
+            facade_rootservice.StopStackRequest())
+
+    def _handle_control_packet(self, l2cap_packet):
+        packet_bytes = l2cap_packet.payload
+        l2cap_view = l2cap_packets.BasicFrameView(
+            bt_packets.PacketViewLittleEndian(list(packet_bytes)))
+        if l2cap_view.GetChannelId() != 1:
+            return
+        l2cap_control_view = l2cap_packets.ControlView(l2cap_view.GetPayload())
+        if l2cap_control_view.GetCode(
+        ) == l2cap_packets.CommandCode.CONNECTION_REQUEST:
+            return self.on_connection_request(
+                l2cap_control_view
+            ) if self.on_connection_request else self._on_connection_request_default(
+                l2cap_control_view)
+        if l2cap_control_view.GetCode(
+        ) == l2cap_packets.CommandCode.CONNECTION_RESPONSE:
+            return self.on_connection_response(
+                l2cap_control_view
+            ) if self.on_connection_response else self._on_connection_response_default(
+                l2cap_control_view)
+        if l2cap_control_view.GetCode(
+        ) == l2cap_packets.CommandCode.CONFIGURATION_REQUEST:
+            return self.on_configuration_request(
+                l2cap_control_view
+            ) if self.on_configuration_request else self._on_configuration_request_default(
+                l2cap_control_view)
+        if l2cap_control_view.GetCode(
+        ) == l2cap_packets.CommandCode.CONFIGURATION_RESPONSE:
+            return self.on_configuration_response(
+                l2cap_control_view
+            ) if self.on_configuration_response else self._on_configuration_response_default(
+                l2cap_control_view)
+        if l2cap_control_view.GetCode(
+        ) == l2cap_packets.CommandCode.DISCONNECTION_REQUEST:
+            return self.on_disconnection_request(
+                l2cap_control_view
+            ) if self.on_disconnection_request else self._on_disconnection_request_default(
+                l2cap_control_view)
+        if l2cap_control_view.GetCode(
+        ) == l2cap_packets.CommandCode.DISCONNECTION_RESPONSE:
+            return self.on_disconnection_response(
+                l2cap_control_view
+            ) if self.on_disconnection_response else self._on_disconnection_response_default(
+                l2cap_control_view)
+        if l2cap_control_view.GetCode(
+        ) == l2cap_packets.CommandCode.INFORMATION_REQUEST:
+            return self.on_information_request(
+                l2cap_control_view
+            ) if self.on_information_request else self._on_information_request_default(
+                l2cap_control_view)
+        if l2cap_control_view.GetCode(
+        ) == l2cap_packets.CommandCode.INFORMATION_RESPONSE:
+            return self.on_information_response(
+                l2cap_control_view
+            ) if self.on_information_response else self._on_information_response_default(
+                l2cap_control_view)
+        return
+
+    def is_correct_connection_request(self, l2cap_packet):
+        packet_bytes = l2cap_packet.payload
+        l2cap_view = l2cap_packets.BasicFrameView(
+            bt_packets.PacketViewLittleEndian(list(packet_bytes)))
+        if l2cap_view.GetChannelId() != 1:
+            return False
+        l2cap_control_view = l2cap_packets.ControlView(l2cap_view.GetPayload())
+        return l2cap_control_view.GetCode(
+        ) == l2cap_packets.CommandCode.CONNECTION_REQUEST
+
+    def is_correct_configuration_response(self, l2cap_packet):
+        packet_bytes = l2cap_packet.payload
+        l2cap_view = l2cap_packets.BasicFrameView(
+            bt_packets.PacketViewLittleEndian(list(packet_bytes)))
+        if l2cap_view.GetChannelId() != 1:
+            return False
+        l2cap_control_view = l2cap_packets.ControlView(l2cap_view.GetPayload())
+        if l2cap_control_view.GetCode(
+        ) != l2cap_packets.CommandCode.CONFIGURATION_RESPONSE:
+            return False
+        configuration_response_view = l2cap_packets.ConfigurationResponseView(
+            l2cap_control_view)
+        return configuration_response_view.GetResult(
+        ) == l2cap_packets.ConfigurationResponseResult.SUCCESS
+
+    def is_correct_configuration_request(self, l2cap_packet):
+        packet_bytes = l2cap_packet.payload
+        l2cap_view = l2cap_packets.BasicFrameView(
+            bt_packets.PacketViewLittleEndian(list(packet_bytes)))
+        if l2cap_view.GetChannelId() != 1:
+            return False
+        l2cap_control_view = l2cap_packets.ControlView(l2cap_view.GetPayload())
+        return l2cap_control_view.GetCode(
+        ) == l2cap_packets.CommandCode.CONFIGURATION_REQUEST
+
+    def is_correct_disconnection_request(self, l2cap_packet):
+        packet_bytes = l2cap_packet.payload
+        l2cap_view = l2cap_packets.BasicFrameView(
+            bt_packets.PacketViewLittleEndian(list(packet_bytes)))
+        if l2cap_view.GetChannelId() != 1:
+            return False
+        l2cap_control_view = l2cap_packets.ControlView(l2cap_view.GetPayload())
+        return l2cap_control_view.GetCode(
+        ) == l2cap_packets.CommandCode.DISCONNECTION_REQUEST
+
+    def cert_send_b_frame(self, b_frame):
+        self.cert_device.hci_acl_manager.SendAclData(
+            acl_manager_facade.AclData(
+                handle=self.cert_acl_handle,
+                payload=bytes(b_frame.Serialize())))
+
+    def get_req_seq_from_ertm_s_frame(self, scid, packet):
+        packet_bytes = packet.payload
+        l2cap_view = l2cap_packets.BasicFrameView(
+            bt_packets.PacketViewLittleEndian(list(packet_bytes)))
+        if l2cap_view.GetChannelId() != scid:
+            return False
+        standard_view = l2cap_packets.StandardFrameView(l2cap_view)
+        if standard_view.GetFrameType() == l2cap_packets.FrameType.I_FRAME:
+            return False
+        s_frame = l2cap_packets.EnhancedSupervisoryFrameView(standard_view)
+        return s_frame.GetReqSeq()
+
+    def get_s_from_ertm_s_frame(self, scid, packet):
+        packet_bytes = packet.payload
+        l2cap_view = l2cap_packets.BasicFrameView(
+            bt_packets.PacketViewLittleEndian(list(packet_bytes)))
+        if l2cap_view.GetChannelId() != scid:
+            return False
+        standard_view = l2cap_packets.StandardFrameView(l2cap_view)
+        if standard_view.GetFrameType() == l2cap_packets.FrameType.I_FRAME:
+            return False
+        s_frame = l2cap_packets.EnhancedSupervisoryFrameView(standard_view)
+        return s_frame.GetS()
+
+    def get_p_from_ertm_s_frame(self, scid, packet):
+        packet_bytes = packet.payload
+        l2cap_view = l2cap_packets.BasicFrameView(
+            bt_packets.PacketViewLittleEndian(list(packet_bytes)))
+        if l2cap_view.GetChannelId() != scid:
+            return False
+        standard_view = l2cap_packets.StandardFrameView(l2cap_view)
+        if standard_view.GetFrameType() == l2cap_packets.FrameType.I_FRAME:
+            return False
+        s_frame = l2cap_packets.EnhancedSupervisoryFrameView(standard_view)
+        return s_frame.GetP()
+
+    def get_f_from_ertm_s_frame(self, scid, packet):
+        packet_bytes = packet.payload
+        l2cap_view = l2cap_packets.BasicFrameView(
+            bt_packets.PacketViewLittleEndian(list(packet_bytes)))
+        if l2cap_view.GetChannelId() != scid:
+            return False
+        standard_view = l2cap_packets.StandardFrameView(l2cap_view)
+        if standard_view.GetFrameType() == l2cap_packets.FrameType.I_FRAME:
+            return False
+        s_frame = l2cap_packets.EnhancedSupervisoryFrameView(standard_view)
+        return s_frame.GetF()
+
+    def get_tx_seq_from_ertm_i_frame(self, scid, packet):
+        packet_bytes = packet.payload
+        l2cap_view = l2cap_packets.BasicFrameView(
+            bt_packets.PacketViewLittleEndian(list(packet_bytes)))
+        if l2cap_view.GetChannelId() != scid:
+            return False
+        standard_view = l2cap_packets.StandardFrameView(l2cap_view)
+        if standard_view.GetFrameType() == l2cap_packets.FrameType.S_FRAME:
+            return False
+        i_frame = l2cap_packets.EnhancedInformationFrameView(standard_view)
+        return i_frame.GetTxSeq()
+
+    def get_payload_from_ertm_i_frame(self, scid, packet):
+        packet_bytes = packet.payload
+        l2cap_view = l2cap_packets.BasicFrameView(
+            bt_packets.PacketViewLittleEndian(list(packet_bytes)))
+        if l2cap_view.GetChannelId() != scid:
+            return False
+        standard_view = l2cap_packets.StandardFrameView(l2cap_view)
+        if standard_view.GetFrameType() == l2cap_packets.FrameType.S_FRAME:
+            return False
+        i_frame = l2cap_packets.EnhancedInformationFrameView(standard_view)
+        return i_frame.GetPayload()  # TODO(mylesgw): This doesn't work!
+
+    def _setup_link_from_cert(self):
+
+        self.device_under_test.neighbor.EnablePageScan(
+            neighbor_facade.EnableMsg(enabled=True))
+
+        with EventCallbackStream(
+                self.cert_device.hci_acl_manager.CreateConnection(
+                    acl_manager_facade.ConnectionMsg(
+                        address_type=int(
+                            hci_packets.AddressType.PUBLIC_DEVICE_ADDRESS),
+                        address=bytes(self.dut_address.address)))
+        ) as connection_event_stream:
+
+            connection_event_asserts = EventAsserts(connection_event_stream)
+
+            # Cert gets ConnectionComplete with a handle and sends ACL data
+            handle = 0xfff
+
+            def get_handle(packet):
+                packet_bytes = packet.event
+                if b'\x03\x0b\x00' in packet_bytes:
+                    nonlocal handle
+                    cc_view = hci_packets.ConnectionCompleteView(
+                        hci_packets.EventPacketView(
+                            bt_packets.PacketViewLittleEndian(
+                                list(packet_bytes))))
+                    handle = cc_view.GetConnectionHandle()
+                    return True
+                return False
+
+            connection_event_asserts.assert_event_occurs(get_handle)
+
+        self.cert_acl_handle = handle
+        return handle
+
+    def _open_channel(
+            self,
+            cert_acl_data_stream,
+            signal_id=1,
+            cert_acl_handle=0x1,
+            scid=0x0101,
+            psm=0x33,
+            mode=l2cap_facade_pb2.RetransmissionFlowControlMode.BASIC):
+        cert_acl_data_asserts = EventAsserts(cert_acl_data_stream)
+
+        self.device_under_test.l2cap.SetDynamicChannel(
+            l2cap_facade_pb2.SetEnableDynamicChannelRequest(
+                psm=psm, retransmission_mode=mode))
+        open_channel = l2cap_packets.ConnectionRequestBuilder(
+            signal_id, psm, scid)
+        open_channel_l2cap = l2cap_packets.BasicFrameBuilder(1, open_channel)
+        self.cert_send_b_frame(open_channel_l2cap)
+
+        def verify_connection_response(packet):
+            packet_bytes = packet.payload
+            l2cap_view = l2cap_packets.BasicFrameView(
+                bt_packets.PacketViewLittleEndian(list(packet_bytes)))
+            l2cap_control_view = l2cap_packets.ControlView(
+                l2cap_view.GetPayload())
+            if l2cap_control_view.GetCode(
+            ) != l2cap_packets.CommandCode.CONNECTION_RESPONSE:
+                return False
+            connection_response_view = l2cap_packets.ConnectionResponseView(
+                l2cap_control_view)
+            return connection_response_view.GetSourceCid(
+            ) == scid and connection_response_view.GetResult(
+            ) == l2cap_packets.ConnectionResponseResult.SUCCESS and connection_response_view.GetDestinationCid(
+            ) != 0
+
+        cert_acl_data_asserts.assert_event_occurs(verify_connection_response)
+
+    def test_connect_dynamic_channel_and_send_data(self):
+        cert_acl_handle = self._setup_link_from_cert()
+        with EventCallbackStream(
+                self.cert_device.hci_acl_manager.FetchAclData(
+                    empty_proto.Empty())) as cert_acl_data_stream:
+            cert_acl_data_asserts = EventAsserts(cert_acl_data_stream)
+            cert_acl_data_stream.register_callback(self._handle_control_packet)
+            psm = 0x33
+            scid = 0x41
+            self._open_channel(cert_acl_data_stream, 1, cert_acl_handle, scid,
+                               psm)
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=0x33, payload=b'abc'))
+            cert_acl_data_asserts = EventAsserts(cert_acl_data_stream)
+            cert_acl_data_asserts.assert_event_occurs(
+                lambda packet: b'abc' in packet.payload)
+
+    def test_fixed_channel(self):
+        cert_acl_handle = self._setup_link_from_cert()
+        self.device_under_test.l2cap.RegisterChannel(
+            l2cap_facade_pb2.RegisterChannelRequest(channel=2))
+        asserts.skip("FIXME: Not working")
+        with EventCallbackStream(
+                self.cert_device.hci_acl_manager.FetchAclData(
+                    empty_proto.Empty())) as cert_acl_data_stream:
+            cert_acl_data_asserts = EventAsserts(cert_acl_data_stream)
+            self.device_under_test.l2cap.SendL2capPacket(
+                l2cap_facade_pb2.L2capPacket(channel=2, payload=b"123"))
+            cert_acl_data_asserts.assert_event_occurs(
+                lambda packet: b'123' in packet.payload)
+
+    def test_open_two_channels(self):
+        cert_acl_handle = self._setup_link_from_cert()
+        with EventCallbackStream(
+                self.cert_device.hci_acl_manager.FetchAclData(
+                    empty_proto.Empty())) as cert_acl_data_stream:
+            cert_acl_data_stream.register_callback(self._handle_control_packet)
+            self._open_channel(cert_acl_data_stream, 1, cert_acl_handle, 0x41,
+                               0x41)
+            self._open_channel(cert_acl_data_stream, 2, cert_acl_handle, 0x43,
+                               0x43)
+
+    def test_connect_and_send_data_ertm_no_segmentation(self):
+        cert_acl_handle = self._setup_link_from_cert()
+        with EventCallbackStream(
+                self.cert_device.hci_acl_manager.FetchAclData(
+                    empty_proto.Empty())) as cert_acl_data_stream:
+            cert_acl_data_asserts = EventAsserts(cert_acl_data_stream)
+            cert_acl_data_stream.register_callback(self._handle_control_packet)
+            self.on_connection_response = self._on_connection_response_use_ertm
+
+            psm = 0x33
+            scid = 0x41
+            self._open_channel(
+                cert_acl_data_stream,
+                1,
+                cert_acl_handle,
+                scid,
+                psm,
+                mode=l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM)
+
+            # FIXME: Order shouldn't matter here
+            cert_acl_data_asserts.assert_event_occurs(
+                self.is_correct_configuration_response)
+            cert_acl_data_asserts.assert_event_occurs(
+                self.is_correct_configuration_request)
+
+            dcid = self.scid_to_dcid[scid]
+
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(
+                    psm=psm, payload=b'abc' * 34))
+            cert_acl_data_asserts.assert_event_occurs(
+                lambda packet: b'abc' * 34 in packet.payload)
+
+            i_frame = l2cap_packets.EnhancedInformationFrameBuilder(
+                dcid, 0, l2cap_packets.Final.NOT_SET, 1,
+                l2cap_packets.SegmentationAndReassembly.UNSEGMENTED,
+                SAMPLE_PACKET)
+            self.cert_send_b_frame(i_frame)
+
+    def test_basic_operation_request_connection(self):
+        """
+        L2CAP/COS/CED/BV-01-C [Request Connection]
+        Verify that the IUT is able to request the connection establishment for an L2CAP data channel and
+        initiate the configuration procedure.
+        """
+        cert_acl_handle = self._setup_link_from_cert()
+
+        with EventCallbackStream(
+                self.cert_device.hci_acl_manager.FetchAclData(
+                    empty_proto.Empty())) as cert_acl_data_stream:
+            cert_acl_data_asserts = EventAsserts(cert_acl_data_stream)
+            cert_acl_data_stream.register_callback(self._handle_control_packet)
+            psm = 0x33
+            # TODO: Use another test case
+            self.device_under_test.l2cap.OpenChannel(
+                l2cap_facade_pb2.OpenChannelRequest(
+                    remote=self.cert_address, psm=psm))
+            cert_acl_data_asserts.assert_event_occurs(
+                self.is_correct_connection_request)
+
+    def test_accept_disconnect(self):
+        """
+        L2CAP/COS/CED/BV-07-C
+        """
+        cert_acl_handle = self._setup_link_from_cert()
+
+        with EventCallbackStream(
+                self.cert_device.hci_acl_manager.FetchAclData(
+                    empty_proto.Empty())) as cert_acl_data_stream:
+            cert_acl_data_asserts = EventAsserts(cert_acl_data_stream)
+            cert_acl_data_stream.register_callback(self._handle_control_packet)
+
+            scid = 0x41
+            psm = 0x33
+            self._open_channel(cert_acl_data_stream, 1, cert_acl_handle, scid,
+                               psm)
+
+            dcid = self.scid_to_dcid[scid]
+
+            close_channel = l2cap_packets.DisconnectionRequestBuilder(
+                1, dcid, scid)
+            close_channel_l2cap = l2cap_packets.BasicFrameBuilder(
+                1, close_channel)
+            self.cert_send_b_frame(close_channel_l2cap)
+
+            def verify_disconnection_response(packet):
+                packet_bytes = packet.payload
+                l2cap_view = l2cap_packets.BasicFrameView(
+                    bt_packets.PacketViewLittleEndian(list(packet_bytes)))
+                l2cap_control_view = l2cap_packets.ControlView(
+                    l2cap_view.GetPayload())
+                if l2cap_control_view.GetCode(
+                ) != l2cap_packets.CommandCode.DISCONNECTION_RESPONSE:
+                    return False
+                disconnection_response_view = l2cap_packets.DisconnectionResponseView(
+                    l2cap_control_view)
+                return disconnection_response_view.GetSourceCid(
+                ) == scid and disconnection_response_view.GetDestinationCid(
+                ) == dcid
+
+            cert_acl_data_asserts.assert_event_occurs(
+                verify_disconnection_response)
+
+    def test_disconnect_on_timeout(self):
+        """
+        L2CAP/COS/CED/BV-08-C
+        """
+        cert_acl_handle = self._setup_link_from_cert()
+
+        with EventCallbackStream(
+                self.cert_device.hci_acl_manager.FetchAclData(
+                    empty_proto.Empty())) as cert_acl_data_stream:
+            cert_acl_data_asserts = EventAsserts(cert_acl_data_stream)
+            scid = 0x41
+            psm = 0x33
+            cert_acl_data_stream.register_callback(self._handle_control_packet)
+
+            # Don't send configuration request or response back
+            self.on_configuration_request = lambda _: True
+            self.on_connection_response = lambda _: True
+
+            self._open_channel(cert_acl_data_stream, 1, cert_acl_handle, scid,
+                               psm)
+
+            def is_configuration_response(l2cap_packet):
+                packet_bytes = l2cap_packet.payload
+                l2cap_view = l2cap_packets.BasicFrameView(
+                    bt_packets.PacketViewLittleEndian(list(packet_bytes)))
+                if l2cap_view.GetChannelId() != 1:
+                    return False
+                l2cap_control_view = l2cap_packets.ControlView(
+                    l2cap_view.GetPayload())
+                return l2cap_control_view.GetCode(
+                ) == l2cap_packets.CommandCode.CONFIGURATION_RESPONSE
+
+            cert_acl_data_asserts.assert_none_matching(
+                is_configuration_response)
+
+    def test_respond_to_echo_request(self):
+        """
+        L2CAP/COS/ECH/BV-01-C [Respond to Echo Request]
+        Verify that the IUT responds to an echo request.
+        """
+        cert_acl_handle = self._setup_link_from_cert()
+        with EventCallbackStream(
+                self.cert_device.hci_acl_manager.FetchAclData(
+                    empty_proto.Empty())) as cert_acl_data_stream:
+            cert_acl_data_asserts = EventAsserts(cert_acl_data_stream)
+            cert_acl_data_stream.register_callback(self._handle_control_packet)
+
+            echo_request = l2cap_packets.EchoRequestBuilder(
+                100, l2cap_packets.DisconnectionRequestBuilder(1, 2, 3))
+            echo_request_l2cap = l2cap_packets.BasicFrameBuilder(
+                1, echo_request)
+            self.cert_send_b_frame(echo_request_l2cap)
+
+            cert_acl_data_asserts.assert_event_occurs(
+                lambda packet: b"\x06\x01\x04\x00\x02\x00\x03\x00" in packet.payload
+            )
+
+    def test_reject_unknown_command(self):
+        """
+        L2CAP/COS/CED/BI-01-C
+        """
+        cert_acl_handle = self._setup_link_from_cert()
+        with EventCallbackStream(
+                self.cert_device.hci_acl_manager.FetchAclData(
+                    empty_proto.Empty())) as cert_acl_data_stream:
+            cert_acl_data_asserts = EventAsserts(cert_acl_data_stream)
+            cert_acl_data_stream.register_callback(self._handle_control_packet)
+
+            invalid_command_packet = b"\x04\x00\x01\x00\xff\x01\x00\x00"
+            self.cert_device.hci_acl_manager.SendAclData(
+                acl_manager_facade.AclData(
+                    handle=cert_acl_handle,
+                    payload=bytes(invalid_command_packet)))
+
+            def is_command_reject(l2cap_packet):
+                packet_bytes = l2cap_packet.payload
+                l2cap_view = l2cap_packets.BasicFrameView(
+                    bt_packets.PacketViewLittleEndian(list(packet_bytes)))
+                if l2cap_view.GetChannelId() != 1:
+                    return False
+                l2cap_control_view = l2cap_packets.ControlView(
+                    l2cap_view.GetPayload())
+                return l2cap_control_view.GetCode(
+                ) == l2cap_packets.CommandCode.COMMAND_REJECT
+
+            cert_acl_data_asserts.assert_event_occurs(is_command_reject)
+
+    def test_query_for_1_2_features(self):
+        """
+        L2CAP/COS/IEX/BV-01-C [Query for 1.2 Features]
+        """
+        cert_acl_handle = self._setup_link_from_cert()
+        with EventCallbackStream(
+                self.cert_device.hci_acl_manager.FetchAclData(
+                    empty_proto.Empty())) as cert_acl_data_stream:
+            cert_acl_data_asserts = EventAsserts(cert_acl_data_stream)
+            cert_acl_data_asserts_alt = EventAsserts(cert_acl_data_stream)
+            cert_acl_data_stream.register_callback(self._handle_control_packet)
+            signal_id = 3
+            information_request = l2cap_packets.InformationRequestBuilder(
+                signal_id, l2cap_packets.InformationRequestInfoType.
+                EXTENDED_FEATURES_SUPPORTED)
+            information_request_l2cap = l2cap_packets.BasicFrameBuilder(
+                1, information_request)
+            self.cert_send_b_frame(information_request_l2cap)
+
+            def is_correct_information_response(l2cap_packet):
+                packet_bytes = l2cap_packet.payload
+                l2cap_view = l2cap_packets.BasicFrameView(
+                    bt_packets.PacketViewLittleEndian(list(packet_bytes)))
+                if l2cap_view.GetChannelId() != 1:
+                    return False
+                l2cap_control_view = l2cap_packets.ControlView(
+                    l2cap_view.GetPayload())
+                if l2cap_control_view.GetCode(
+                ) != l2cap_packets.CommandCode.INFORMATION_RESPONSE:
+                    return False
+                information_response_view = l2cap_packets.InformationResponseView(
+                    l2cap_control_view)
+                return information_response_view.GetInfoType(
+                ) == l2cap_packets.InformationRequestInfoType.EXTENDED_FEATURES_SUPPORTED
+
+            cert_acl_data_asserts.assert_event_occurs(
+                is_correct_information_response)
+
+            def is_correct_information_request(l2cap_packet):
+                packet_bytes = l2cap_packet.payload
+                l2cap_view = l2cap_packets.BasicFrameView(
+                    bt_packets.PacketViewLittleEndian(list(packet_bytes)))
+                if l2cap_view.GetChannelId() != 1:
+                    return False
+                l2cap_control_view = l2cap_packets.ControlView(
+                    l2cap_view.GetPayload())
+                if l2cap_control_view.GetCode(
+                ) != l2cap_packets.CommandCode.INFORMATION_REQUEST:
+                    return False
+                information_request_view = l2cap_packets.InformationRequestView(
+                    l2cap_control_view)
+                return information_request_view.GetInfoType(
+                ) == l2cap_packets.InformationRequestInfoType.EXTENDED_FEATURES_SUPPORTED
+
+            cert_acl_data_asserts_alt.assert_event_occurs(
+                is_correct_information_request)
+
+    def test_extended_feature_info_response_ertm(self):
+        """
+        L2CAP/EXF/BV-01-C [Extended Features Information Response for Enhanced
+        Retransmission Mode]
+        """
+        cert_acl_handle = self._setup_link_from_cert()
+        with EventCallbackStream(
+                self.cert_device.hci_acl_manager.FetchAclData(
+                    empty_proto.Empty())) as cert_acl_data_stream:
+            cert_acl_data_asserts = EventAsserts(cert_acl_data_stream)
+            cert_acl_data_stream.register_callback(self._handle_control_packet)
+
+            signal_id = 3
+            information_request = l2cap_packets.InformationRequestBuilder(
+                signal_id, l2cap_packets.InformationRequestInfoType.
+                EXTENDED_FEATURES_SUPPORTED)
+            information_request_l2cap = l2cap_packets.BasicFrameBuilder(
+                1, information_request)
+            self.cert_send_b_frame(information_request_l2cap)
+
+            def is_correct_information_response(l2cap_packet):
+                packet_bytes = l2cap_packet.payload
+                l2cap_view = l2cap_packets.BasicFrameView(
+                    bt_packets.PacketViewLittleEndian(list(packet_bytes)))
+                if l2cap_view.GetChannelId() != 1:
+                    return False
+                l2cap_control_view = l2cap_packets.ControlView(
+                    l2cap_view.GetPayload())
+                if l2cap_control_view.GetCode(
+                ) != l2cap_packets.CommandCode.INFORMATION_RESPONSE:
+                    return False
+                information_response_view = l2cap_packets.InformationResponseView(
+                    l2cap_control_view)
+                if information_response_view.GetInfoType(
+                ) != l2cap_packets.InformationRequestInfoType.EXTENDED_FEATURES_SUPPORTED:
+                    return False
+                extended_features_view = l2cap_packets.InformationResponseExtendedFeaturesView(
+                    information_response_view)
+                return extended_features_view.GetEnhancedRetransmissionMode()
+
+            cert_acl_data_asserts.assert_event_occurs(
+                is_correct_information_response)
+
+    def test_extended_feature_info_response_fcs(self):
+        """
+        L2CAP/EXF/BV-03-C [Extended Features Information Response for FCS Option]
+        Note: This is not mandated by L2CAP Spec
+        """
+        cert_acl_handle = self._setup_link_from_cert()
+        with EventCallbackStream(
+                self.cert_device.hci_acl_manager.FetchAclData(
+                    empty_proto.Empty())) as cert_acl_data_stream:
+            cert_acl_data_asserts = EventAsserts(cert_acl_data_stream)
+            cert_acl_data_stream.register_callback(self._handle_control_packet)
+
+            signal_id = 3
+            information_request = l2cap_packets.InformationRequestBuilder(
+                signal_id, l2cap_packets.InformationRequestInfoType.
+                EXTENDED_FEATURES_SUPPORTED)
+            information_request_l2cap = l2cap_packets.BasicFrameBuilder(
+                1, information_request)
+            self.cert_send_b_frame(information_request_l2cap)
+
+            def is_correct_information_response(l2cap_packet):
+                packet_bytes = l2cap_packet.payload
+                l2cap_view = l2cap_packets.BasicFrameView(
+                    bt_packets.PacketViewLittleEndian(list(packet_bytes)))
+                if l2cap_view.GetChannelId() != 1:
+                    return False
+                l2cap_control_view = l2cap_packets.ControlView(
+                    l2cap_view.GetPayload())
+                if l2cap_control_view.GetCode(
+                ) != l2cap_packets.CommandCode.INFORMATION_RESPONSE:
+                    return False
+                information_response_view = l2cap_packets.InformationResponseView(
+                    l2cap_control_view)
+                if information_response_view.GetInfoType(
+                ) != l2cap_packets.InformationRequestInfoType.EXTENDED_FEATURES_SUPPORTED:
+                    return False
+                extended_features_view = l2cap_packets.InformationResponseExtendedFeaturesView(
+                    information_response_view)
+                return extended_features_view.GetFcsOption()
+
+            cert_acl_data_asserts.assert_event_occurs(
+                is_correct_information_response)
+
+    def test_config_channel_not_use_FCS(self):
+        """
+        L2CAP/FOC/BV-01-C [IUT Initiated Configuration of the FCS Option]
+        Verify the IUT can configure a channel to not use FCS in I/S-frames.
+        """
+        cert_acl_handle = self._setup_link_from_cert()
+        with EventCallbackStream(
+                self.cert_device.hci_acl_manager.FetchAclData(
+                    empty_proto.Empty())) as cert_acl_data_stream:
+            cert_acl_data_asserts = EventAsserts(cert_acl_data_stream)
+            cert_acl_data_stream.register_callback(self._handle_control_packet)
+
+            self.on_connection_response = self._on_connection_response_use_ertm
+
+            psm = 0x33
+            scid = 0x41
+            self._open_channel(
+                cert_acl_data_stream,
+                1,
+                cert_acl_handle,
+                scid,
+                psm,
+                mode=l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM)
+            # FIXME: Order shouldn't matter here
+            cert_acl_data_asserts.assert_event_occurs(
+                self.is_correct_configuration_response)
+            cert_acl_data_asserts.assert_event_occurs(
+                self.is_correct_configuration_request)
+
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+            cert_acl_data_asserts.assert_event_occurs(
+                lambda packet: b"abc" in packet.payload)
+
+    def test_explicitly_request_use_FCS(self):
+        """
+        L2CAP/FOC/BV-02-C [Lower Tester Explicitly Requests FCS should be Used]
+        Verify the IUT will include the FCS in I/S-frames if the Lower Tester explicitly requests that FCS
+        should be used.
+        """
+
+        cert_acl_handle = self._setup_link_from_cert()
+        with EventCallbackStream(
+                self.cert_device.hci_acl_manager.FetchAclData(
+                    empty_proto.Empty())) as cert_acl_data_stream:
+            cert_acl_data_asserts = EventAsserts(cert_acl_data_stream)
+            cert_acl_data_stream.register_callback(self._handle_control_packet)
+
+            self.on_connection_response = self._on_connection_response_use_ertm_and_fcs
+            psm = 0x33
+            scid = 0x41
+            self._open_channel(
+                cert_acl_data_stream,
+                1,
+                cert_acl_handle,
+                scid,
+                psm,
+                mode=l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM)
+            # FIXME: Order shouldn't matter here
+            cert_acl_data_asserts.assert_event_occurs(
+                self.is_correct_configuration_response)
+            cert_acl_data_asserts.assert_event_occurs(
+                self.is_correct_configuration_request)
+
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+            cert_acl_data_asserts.assert_event_occurs(
+                lambda packet: b"abc\x4f\xa3" in packet.payload
+            )  # TODO: Use packet parser
+
+    def test_implicitly_request_use_FCS(self):
+        """
+        L2CAP/FOC/BV-03-C [Lower Tester Implicitly Requests FCS should be Used]
+        TODO: Update this test case. What's the difference between this one and test_explicitly_request_use_FCS?
+        """
+        cert_acl_handle = self._setup_link_from_cert()
+        with EventCallbackStream(
+                self.cert_device.hci_acl_manager.FetchAclData(
+                    empty_proto.Empty())) as cert_acl_data_stream:
+            cert_acl_data_asserts = EventAsserts(cert_acl_data_stream)
+            cert_acl_data_stream.register_callback(self._handle_control_packet)
+
+            self.on_connection_response = self._on_connection_response_use_ertm_and_fcs
+            psm = 0x33
+            scid = 0x41
+            self._open_channel(
+                cert_acl_data_stream,
+                1,
+                cert_acl_handle,
+                scid,
+                psm,
+                mode=l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM)
+            # FIXME: Order shouldn't matter here
+            cert_acl_data_asserts.assert_event_occurs(
+                self.is_correct_configuration_response)
+            cert_acl_data_asserts.assert_event_occurs(
+                self.is_correct_configuration_request)
+
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+            cert_acl_data_asserts.assert_event_occurs(
+                lambda packet: b"abc\x4f\xa3" in packet.payload
+            )  # TODO: Use packet parser
+
+    def test_transmit_i_frames(self):
+        """
+        L2CAP/ERM/BV-01-C [Transmit I-frames]
+        """
+        cert_acl_handle = self._setup_link_from_cert()
+        with EventCallbackStream(
+                self.cert_device.hci_acl_manager.FetchAclData(
+                    empty_proto.Empty())) as cert_acl_data_stream:
+            cert_acl_data_asserts = EventAsserts(cert_acl_data_stream)
+            cert_acl_data_stream.register_callback(self._handle_control_packet)
+
+            self.on_connection_response = self._on_connection_response_use_ertm
+            psm = 0x33
+            scid = 0x41
+            self._open_channel(
+                cert_acl_data_stream,
+                1,
+                cert_acl_handle,
+                scid,
+                psm,
+                mode=l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM)
+
+            dcid = self.scid_to_dcid[scid]
+
+            # FIXME: Order shouldn't matter here
+            cert_acl_data_asserts.assert_event_occurs(
+                self.is_correct_configuration_response)
+            cert_acl_data_asserts.assert_event_occurs(
+                self.is_correct_configuration_request)
+
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+            cert_acl_data_asserts.assert_event_occurs(
+                lambda packet: b"abc" in packet.payload)
+
+            # Assemble a sample packet. TODO: Use RawBuilder
+            SAMPLE_PACKET = l2cap_packets.CommandRejectNotUnderstoodBuilder(1)
+
+            i_frame = l2cap_packets.EnhancedInformationFrameBuilder(
+                dcid, 0, l2cap_packets.Final.NOT_SET, 1,
+                l2cap_packets.SegmentationAndReassembly.UNSEGMENTED,
+                SAMPLE_PACKET)
+            self.cert_send_b_frame(i_frame)
+
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+            cert_acl_data_asserts.assert_event_occurs(
+                lambda packet: b"abc" in packet.payload)
+
+            i_frame = l2cap_packets.EnhancedInformationFrameBuilder(
+                dcid, 1, l2cap_packets.Final.NOT_SET, 2,
+                l2cap_packets.SegmentationAndReassembly.UNSEGMENTED,
+                SAMPLE_PACKET)
+            self.cert_send_b_frame(i_frame)
+
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+            cert_acl_data_asserts.assert_event_occurs(
+                lambda packet: b"abc" in packet.payload)
+
+            i_frame = l2cap_packets.EnhancedInformationFrameBuilder(
+                dcid, 2, l2cap_packets.Final.NOT_SET, 3,
+                l2cap_packets.SegmentationAndReassembly.UNSEGMENTED,
+                SAMPLE_PACKET)
+            self.cert_send_b_frame(i_frame)
+
+    def test_receive_i_frames(self):
+        """
+        L2CAP/ERM/BV-02-C [Receive I-Frames]
+        Verify the IUT can receive in-sequence valid I-frames and deliver L2CAP SDUs to the Upper Tester
+        """
+        cert_acl_handle = self._setup_link_from_cert()
+        with EventCallbackStream(
+                self.cert_device.hci_acl_manager.FetchAclData(
+                    empty_proto.Empty())) as cert_acl_data_stream:
+            cert_acl_data_asserts = EventAsserts(cert_acl_data_stream)
+            cert_acl_data_stream.register_callback(self._handle_control_packet)
+            self.on_connection_response = self._on_connection_response_use_ertm
+
+            psm = 0x33
+            scid = 0x41
+            self._open_channel(
+                cert_acl_data_stream,
+                1,
+                cert_acl_handle,
+                scid,
+                psm,
+                mode=l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM)
+
+            dcid = self.scid_to_dcid[scid]
+
+            # FIXME: Order shouldn't matter here
+            cert_acl_data_asserts.assert_event_occurs(
+                self.is_correct_configuration_response)
+            cert_acl_data_asserts.assert_event_occurs(
+                self.is_correct_configuration_request)
+
+            for i in range(3):
+                i_frame = l2cap_packets.EnhancedInformationFrameBuilder(
+                    dcid, i, l2cap_packets.Final.NOT_SET, 0,
+                    l2cap_packets.SegmentationAndReassembly.UNSEGMENTED,
+                    SAMPLE_PACKET)
+                self.cert_send_b_frame(i_frame)
+                cert_acl_data_asserts.assert_event_occurs(
+                    lambda packet: self.get_req_seq_from_ertm_s_frame(scid, packet) == i + 1
+                )
+
+            i_frame = l2cap_packets.EnhancedInformationFrameBuilder(
+                dcid, 3, l2cap_packets.Final.NOT_SET, 0,
+                l2cap_packets.SegmentationAndReassembly.START, SAMPLE_PACKET)
+            self.cert_send_b_frame(i_frame)
+            cert_acl_data_asserts.assert_event_occurs(
+                lambda packet: self.get_req_seq_from_ertm_s_frame(scid, packet) == 4
+            )
+
+            i_frame = l2cap_packets.EnhancedInformationFrameBuilder(
+                dcid, 4, l2cap_packets.Final.NOT_SET, 0,
+                l2cap_packets.SegmentationAndReassembly.CONTINUATION,
+                SAMPLE_PACKET)
+            self.cert_send_b_frame(i_frame)
+            cert_acl_data_asserts.assert_event_occurs(
+                lambda packet: self.get_req_seq_from_ertm_s_frame(scid, packet) == 5
+            )
+
+            i_frame = l2cap_packets.EnhancedInformationFrameBuilder(
+                dcid, 5, l2cap_packets.Final.NOT_SET, 0,
+                l2cap_packets.SegmentationAndReassembly.END, SAMPLE_PACKET)
+            self.cert_send_b_frame(i_frame)
+            cert_acl_data_asserts.assert_event_occurs(
+                lambda packet: self.get_req_seq_from_ertm_s_frame(scid, packet) == 6
+            )
+
+    def test_acknowledging_received_i_frames(self):
+        """
+        L2CAP/ERM/BV-03-C [Acknowledging Received I-Frames]
+        Verify the IUT sends S-frame [RR] with the Poll bit not set to acknowledge data received from the
+        Lower Tester
+        """
+        cert_acl_handle = self._setup_link_from_cert()
+        with EventCallbackStream(
+                self.cert_device.hci_acl_manager.FetchAclData(
+                    empty_proto.Empty())) as cert_acl_data_stream:
+            cert_acl_data_asserts = EventAsserts(cert_acl_data_stream)
+            cert_acl_data_stream.register_callback(self._handle_control_packet)
+            self.on_connection_response = self._on_connection_response_use_ertm
+
+            psm = 0x33
+            scid = 0x41
+            self._open_channel(
+                cert_acl_data_stream,
+                1,
+                cert_acl_handle,
+                scid,
+                psm,
+                mode=l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM)
+
+            dcid = self.scid_to_dcid[scid]
+
+            # FIXME: Order shouldn't matter here
+            cert_acl_data_asserts.assert_event_occurs(
+                self.is_correct_configuration_response)
+            cert_acl_data_asserts.assert_event_occurs(
+                self.is_correct_configuration_request)
+
+            for i in range(3):
+                i_frame = l2cap_packets.EnhancedInformationFrameBuilder(
+                    dcid, i, l2cap_packets.Final.NOT_SET, 0,
+                    l2cap_packets.SegmentationAndReassembly.UNSEGMENTED,
+                    SAMPLE_PACKET)
+                self.cert_send_b_frame(i_frame)
+                cert_acl_data_asserts.assert_event_occurs(
+                    lambda packet: self.get_req_seq_from_ertm_s_frame(scid, packet) == i + 1
+                )
+
+            cert_acl_data_asserts.assert_none_matching(
+                lambda packet: self.get_req_seq_from_ertm_s_frame(scid, packet) == 4,
+                timedelta(seconds=1))
+
+    def test_resume_transmitting_when_received_rr(self):
+        """
+        L2CAP/ERM/BV-05-C [Resume Transmitting I-Frames when an S-Frame [RR] is Received]
+        Verify the IUT will cease transmission of I-frames when the negotiated TxWindow is full. Verify the
+        IUT will resume transmission of I-frames when an S-frame [RR] is received that acknowledges
+        previously sent I-frames.
+        """
+        self.ertm_tx_window_size = 1
+        cert_acl_handle = self._setup_link_from_cert()
+        with EventCallbackStream(
+                self.cert_device.hci_acl_manager.FetchAclData(
+                    empty_proto.Empty())) as cert_acl_data_stream:
+            cert_acl_data_asserts = EventAsserts(cert_acl_data_stream)
+            cert_acl_data_stream.register_callback(self._handle_control_packet)
+            self.on_connection_response = self._on_connection_response_use_ertm
+
+            psm = 0x33
+            scid = 0x41
+            self._open_channel(
+                cert_acl_data_stream,
+                1,
+                cert_acl_handle,
+                scid,
+                psm,
+                mode=l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM)
+
+            dcid = self.scid_to_dcid[scid]
+
+            # FIXME: Order shouldn't matter here
+            cert_acl_data_asserts.assert_event_occurs(
+                self.is_correct_configuration_response)
+            cert_acl_data_asserts.assert_event_occurs(
+                self.is_correct_configuration_request)
+
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'def'))
+
+            # TODO: Besides checking TxSeq, we also want to check payload, once we can get it from packet view
+            cert_acl_data_asserts.assert_event_occurs(
+                lambda packet: self.get_tx_seq_from_ertm_i_frame(scid, packet) == 0
+            )
+            cert_acl_data_asserts.assert_none_matching(
+                lambda packet: self.get_tx_seq_from_ertm_i_frame(scid, packet) == 1,
+            )
+            s_frame = l2cap_packets.EnhancedSupervisoryFrameBuilder(
+                dcid, l2cap_packets.SupervisoryFunction.RECEIVER_READY,
+                l2cap_packets.Poll.NOT_SET, l2cap_packets.Final.POLL_RESPONSE,
+                1)
+            self.cert_send_b_frame(s_frame)
+            cert_acl_data_asserts.assert_event_occurs(
+                lambda packet: self.get_tx_seq_from_ertm_i_frame(scid, packet) == 1
+            )
+
+    def test_resume_transmitting_when_acknowledge_previously_sent(self):
+        """
+        L2CAP/ERM/BV-06-C [Resume Transmitting I-Frames when an I-Frame is Received]
+        Verify the IUT will cease transmission of I-frames when the negotiated TxWindow is full. Verify the
+        IUT will resume transmission of I-frames when an I-frame is received that acknowledges previously
+        sent I-frames.
+        """
+        self.ertm_tx_window_size = 1
+        cert_acl_handle = self._setup_link_from_cert()
+        with EventCallbackStream(
+                self.cert_device.hci_acl_manager.FetchAclData(
+                    empty_proto.Empty())) as cert_acl_data_stream:
+            cert_acl_data_asserts = EventAsserts(cert_acl_data_stream)
+            cert_acl_data_stream.register_callback(self._handle_control_packet)
+            self.on_connection_response = self._on_connection_response_use_ertm
+
+            psm = 0x33
+            scid = 0x41
+            self._open_channel(
+                cert_acl_data_stream,
+                1,
+                cert_acl_handle,
+                scid,
+                psm,
+                mode=l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM)
+
+            dcid = self.scid_to_dcid[scid]
+
+            # FIXME: Order shouldn't matter here
+            cert_acl_data_asserts.assert_event_occurs(
+                self.is_correct_configuration_response)
+            cert_acl_data_asserts.assert_event_occurs(
+                self.is_correct_configuration_request)
+
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'def'))
+
+            cert_acl_data_asserts.assert_event_occurs(
+                lambda packet: self.get_tx_seq_from_ertm_i_frame(scid, packet) == 0
+            )
+            # TODO: If 1 second is greater than their retransmit timeout, use a smaller timeout
+            cert_acl_data_asserts.assert_none_matching(
+                lambda packet: self.get_tx_seq_from_ertm_i_frame(scid, packet) == 1,
+                timedelta(seconds=1))
+
+            i_frame = l2cap_packets.EnhancedInformationFrameBuilder(
+                dcid, 0, l2cap_packets.Final.NOT_SET, 1,
+                l2cap_packets.SegmentationAndReassembly.UNSEGMENTED,
+                SAMPLE_PACKET)
+            self.cert_send_b_frame(i_frame)
+
+            cert_acl_data_asserts.assert_event_occurs(
+                lambda packet: self.get_tx_seq_from_ertm_i_frame(scid, packet) == 1
+            )
+
+            i_frame = l2cap_packets.EnhancedInformationFrameBuilder(
+                dcid, 1, l2cap_packets.Final.NOT_SET, 2,
+                l2cap_packets.SegmentationAndReassembly.UNSEGMENTED,
+                SAMPLE_PACKET)
+            self.cert_send_b_frame(i_frame)
+
+    def test_transmit_s_frame_rr_with_poll_bit_set(self):
+        """
+        L2CAP/ERM/BV-08-C [Send S-Frame [RR] with Poll Bit Set]
+        Verify the IUT sends an S-frame [RR] with the Poll bit set when its retransmission timer expires.
+        """
+        cert_acl_handle = self._setup_link_from_cert()
+        with EventCallbackStream(
+                self.cert_device.hci_acl_manager.FetchAclData(
+                    empty_proto.Empty())) as cert_acl_data_stream:
+            cert_acl_data_asserts = EventAsserts(cert_acl_data_stream)
+            cert_acl_data_stream.register_callback(self._handle_control_packet)
+            self.on_connection_response = self._on_connection_response_use_ertm
+
+            psm = 0x33
+            scid = 0x41
+            self._open_channel(
+                cert_acl_data_stream,
+                1,
+                cert_acl_handle,
+                scid,
+                psm,
+                mode=l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM)
+
+            # FIXME: Order shouldn't matter here
+            cert_acl_data_asserts.assert_event_occurs(
+                self.is_correct_configuration_response)
+            cert_acl_data_asserts.assert_event_occurs(
+                self.is_correct_configuration_request)
+
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+            # TODO: Always use their retransmission timeout value
+            time.sleep(2)
+            cert_acl_data_asserts.assert_event_occurs(
+                lambda packet: self.get_p_from_ertm_s_frame(scid, packet) == l2cap_packets.Poll.POLL
+            )
+
+    def test_transmit_s_frame_rr_with_final_bit_set(self):
+        """
+        L2CAP/ERM/BV-09-C [Send S-Frame [RR] with Final Bit Set]
+        Verify the IUT responds with an S-frame [RR] with the Final bit set after receiving an S-frame [RR]
+        with the Poll bit set.
+        """
+        cert_acl_handle = self._setup_link_from_cert()
+        with EventCallbackStream(
+                self.cert_device.hci_acl_manager.FetchAclData(
+                    empty_proto.Empty())) as cert_acl_data_stream:
+            cert_acl_data_asserts = EventAsserts(cert_acl_data_stream)
+            cert_acl_data_stream.register_callback(self._handle_control_packet)
+            self.on_connection_response = self._on_connection_response_use_ertm
+
+            psm = 0x33
+            scid = 0x41
+            self._open_channel(
+                cert_acl_data_stream,
+                1,
+                cert_acl_handle,
+                scid,
+                psm,
+                mode=l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM)
+
+            # FIXME: Order shouldn't matter here
+            cert_acl_data_asserts.assert_event_occurs(
+                self.is_correct_configuration_response)
+            cert_acl_data_asserts.assert_event_occurs(
+                self.is_correct_configuration_request)
+
+            dcid = self.scid_to_dcid[scid]
+
+            s_frame = l2cap_packets.EnhancedSupervisoryFrameBuilder(
+                dcid, l2cap_packets.SupervisoryFunction.RECEIVER_READY,
+                l2cap_packets.Poll.POLL, l2cap_packets.Final.NOT_SET, 0)
+            self.cert_send_b_frame(s_frame)
+
+            cert_acl_data_asserts.assert_event_occurs(
+                lambda packet: self.get_f_from_ertm_s_frame(scid, packet) == l2cap_packets.Final.POLL_RESPONSE
+            )
+
+    def test_s_frame_transmissions_exceed_max_transmit(self):
+        """
+        L2CAP/ERM/BV-11-C [S-Frame Transmissions Exceed MaxTransmit]
+        Verify the IUT will close the channel when the Monitor Timer expires.
+        """
+        asserts.skip("Need to configure DUT to have a shorter timer")
+        cert_acl_handle = self._setup_link_from_cert()
+        with EventCallbackStream(
+                self.cert_device.hci_acl_manager.FetchAclData(
+                    empty_proto.Empty())) as cert_acl_data_stream:
+            cert_acl_data_asserts = EventAsserts(cert_acl_data_stream)
+            cert_acl_data_stream.register_callback(self._handle_control_packet)
+            self.on_connection_response = self._on_connection_response_use_ertm
+
+            psm = 0x33
+            scid = 0x41
+            self._open_channel(
+                cert_acl_data_stream,
+                1,
+                cert_acl_handle,
+                scid,
+                psm,
+                mode=l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM)
+
+            # FIXME: Order shouldn't matter here
+            cert_acl_data_asserts.assert_event_occurs(
+                self.is_correct_configuration_response)
+            cert_acl_data_asserts.assert_event_occurs(
+                self.is_correct_configuration_request)
+
+            dcid = self.scid_to_dcid[scid]
+
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+
+            # Retransmission timer = 2, 20 * monitor timer = 360, so total timeout is 362
+            time.sleep(362)
+            cert_acl_data_asserts.assert_event_occurs(
+                self.is_correct_disconnection_request)
+
+    def test_i_frame_transmissions_exceed_max_transmit(self):
+        """
+        L2CAP/ERM/BV-12-C [I-Frame Transmissions Exceed MaxTransmit]
+        Verify the IUT will close the channel when it receives an S-frame [RR] with the final bit set that does
+        not acknowledge the previous I-frame sent by the IUT.
+        """
+        cert_acl_handle = self._setup_link_from_cert()
+        with EventCallbackStream(
+                self.cert_device.hci_acl_manager.FetchAclData(
+                    empty_proto.Empty())) as cert_acl_data_stream:
+            cert_acl_data_asserts = EventAsserts(cert_acl_data_stream)
+            cert_acl_data_stream.register_callback(self._handle_control_packet)
+            self.on_connection_response = self._on_connection_response_use_ertm
+
+            psm = 0x33
+            scid = 0x41
+            self._open_channel(
+                cert_acl_data_stream,
+                1,
+                cert_acl_handle,
+                scid,
+                psm,
+                mode=l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM)
+
+            # FIXME: Order shouldn't matter here
+            cert_acl_data_asserts.assert_event_occurs(
+                self.is_correct_configuration_response)
+            cert_acl_data_asserts.assert_event_occurs(
+                self.is_correct_configuration_request)
+
+            dcid = self.scid_to_dcid[scid]
+
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+
+            cert_acl_data_asserts.assert_event_occurs(
+                lambda packet: self.get_tx_seq_from_ertm_i_frame(scid, packet) == 0
+            )
+
+            s_frame = l2cap_packets.EnhancedSupervisoryFrameBuilder(
+                dcid, l2cap_packets.SupervisoryFunction.RECEIVER_READY,
+                l2cap_packets.Poll.NOT_SET, l2cap_packets.Final.POLL_RESPONSE,
+                0)
+            self.cert_send_b_frame(s_frame)
+
+            cert_acl_data_asserts.assert_event_occurs(
+                self.is_correct_disconnection_request)
+
+    def test_respond_to_rej(self):
+        """
+        L2CAP/ERM/BV-13-C [Respond to S-Frame [REJ]]
+        Verify the IUT retransmits I-frames starting from the sequence number specified in the S-frame [REJ].
+        """
+        self.ertm_tx_window_size = 2
+        self.ertm_max_transmit = 2
+        cert_acl_handle = self._setup_link_from_cert()
+        with EventCallbackStream(
+                self.cert_device.hci_acl_manager.FetchAclData(
+                    empty_proto.Empty())) as cert_acl_data_stream:
+            cert_acl_data_asserts = EventAsserts(cert_acl_data_stream)
+            cert_acl_data_stream.register_callback(self._handle_control_packet)
+            self.on_connection_response = self._on_connection_response_use_ertm
+
+            psm = 0x33
+            scid = 0x41
+            self._open_channel(
+                cert_acl_data_stream,
+                1,
+                cert_acl_handle,
+                scid,
+                psm,
+                mode=l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM)
+
+            # FIXME: Order shouldn't matter here
+            cert_acl_data_asserts.assert_event_occurs(
+                self.is_correct_configuration_response)
+            cert_acl_data_asserts.assert_event_occurs(
+                self.is_correct_configuration_request)
+
+            dcid = self.scid_to_dcid[scid]
+
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+            for i in range(2):
+                cert_acl_data_asserts.assert_event_occurs(
+                    lambda packet: self.get_tx_seq_from_ertm_i_frame(scid, packet) == i,
+                    timeout=timedelta(seconds=0.5))
+
+            s_frame = l2cap_packets.EnhancedSupervisoryFrameBuilder(
+                dcid, l2cap_packets.SupervisoryFunction.REJECT,
+                l2cap_packets.Poll.NOT_SET, l2cap_packets.Final.NOT_SET, 0)
+            self.cert_send_b_frame(s_frame)
+
+            for i in range(2):
+                cert_acl_data_asserts.assert_event_occurs(
+                    lambda packet: self.get_tx_seq_from_ertm_i_frame(scid, packet) == i,
+                    timeout=timedelta(seconds=0.5))
+
+    def test_receive_s_frame_rr_final_bit_set(self):
+        """
+        L2CAP/ERM/BV-18-C [Receive S-Frame [RR] Final Bit = 1]
+        Verify the IUT will retransmit any previously sent I-frames unacknowledged by receipt of an S-Frame
+        [RR] with the Final Bit set.
+        """
+        cert_acl_handle = self._setup_link_from_cert()
+        with EventCallbackStream(
+                self.cert_device.hci_acl_manager.FetchAclData(
+                    empty_proto.Empty())) as cert_acl_data_stream:
+            cert_acl_data_asserts = EventAsserts(cert_acl_data_stream)
+            cert_acl_data_stream.register_callback(self._handle_control_packet)
+            self.on_connection_response = self._on_connection_response_use_ertm
+
+            psm = 0x33
+            scid = 0x41
+            self._open_channel(
+                cert_acl_data_stream,
+                1,
+                cert_acl_handle,
+                scid,
+                psm,
+                mode=l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM)
+
+            # FIXME: Order shouldn't matter here
+            cert_acl_data_asserts.assert_event_occurs(
+                self.is_correct_configuration_response)
+            cert_acl_data_asserts.assert_event_occurs(
+                self.is_correct_configuration_request)
+
+            dcid = self.scid_to_dcid[scid]
+
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+
+            # TODO: Always use their retransmission timeout value
+            time.sleep(2)
+            cert_acl_data_asserts.assert_event_occurs(
+                lambda packet: self.get_p_from_ertm_s_frame(scid, packet) == l2cap_packets.Poll.POLL
+            )
+
+            s_frame = l2cap_packets.EnhancedSupervisoryFrameBuilder(
+                dcid, l2cap_packets.SupervisoryFunction.RECEIVER_READY,
+                l2cap_packets.Poll.NOT_SET, l2cap_packets.Final.POLL_RESPONSE,
+                0)
+            self.cert_send_b_frame(s_frame)
+
+            cert_acl_data_asserts.assert_event_occurs(
+                lambda packet: self.get_tx_seq_from_ertm_i_frame(scid, packet) == 0
+            )
+
+    def test_receive_i_frame_final_bit_set(self):
+        """
+        L2CAP/ERM/BV-19-C [Receive I-Frame Final Bit = 1]
+        Verify the IUT will retransmit any previously sent I-frames unacknowledged by receipt of an I-frame
+        with the final bit set.
+        """
+        cert_acl_handle = self._setup_link_from_cert()
+        with EventCallbackStream(
+                self.cert_device.hci_acl_manager.FetchAclData(
+                    empty_proto.Empty())) as cert_acl_data_stream:
+            cert_acl_data_asserts = EventAsserts(cert_acl_data_stream)
+            cert_acl_data_stream.register_callback(self._handle_control_packet)
+            self.on_connection_response = self._on_connection_response_use_ertm
+
+            psm = 0x33
+            scid = 0x41
+            self._open_channel(
+                cert_acl_data_stream,
+                1,
+                cert_acl_handle,
+                scid,
+                psm,
+                mode=l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM)
+
+            # FIXME: Order shouldn't matter here
+            cert_acl_data_asserts.assert_event_occurs(
+                self.is_correct_configuration_response)
+            cert_acl_data_asserts.assert_event_occurs(
+                self.is_correct_configuration_request)
+
+            dcid = self.scid_to_dcid[scid]
+
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+
+            # TODO: Always use their retransmission timeout value
+            time.sleep(2)
+            cert_acl_data_asserts.assert_event_occurs(
+                lambda packet: self.get_p_from_ertm_s_frame(scid, packet) == l2cap_packets.Poll.POLL
+            )
+
+            i_frame = l2cap_packets.EnhancedInformationFrameBuilder(
+                dcid, 0, l2cap_packets.Final.POLL_RESPONSE, 0,
+                l2cap_packets.SegmentationAndReassembly.UNSEGMENTED,
+                SAMPLE_PACKET)
+            self.cert_send_b_frame(i_frame)
+
+            cert_acl_data_asserts.assert_event_occurs(
+                lambda packet: self.get_tx_seq_from_ertm_i_frame(scid, packet) == 0
+            )
+
+    def test_recieve_rnr(self):
+        """
+        L2CAP/ERM/BV-20-C [Enter Remote Busy Condition]
+        Verify the IUT will not retransmit any I-frames when it receives a remote busy indication from the
+        Lower Tester (S-frame [RNR]).
+        """
+        cert_acl_handle = self._setup_link_from_cert()
+        with EventCallbackStream(
+                self.cert_device.hci_acl_manager.FetchAclData(
+                    empty_proto.Empty())) as cert_acl_data_stream:
+            cert_acl_data_asserts = EventAsserts(cert_acl_data_stream)
+            cert_acl_data_stream.register_callback(self._handle_control_packet)
+            self.on_connection_response = self._on_connection_response_use_ertm
+
+            psm = 0x33
+            scid = 0x41
+            self._open_channel(
+                cert_acl_data_stream,
+                1,
+                cert_acl_handle,
+                scid,
+                psm,
+                mode=l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM)
+
+            # FIXME: Order shouldn't matter here
+            cert_acl_data_asserts.assert_event_occurs(
+                self.is_correct_configuration_response)
+            cert_acl_data_asserts.assert_event_occurs(
+                self.is_correct_configuration_request)
+
+            dcid = self.scid_to_dcid[scid]
+
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=0x33, payload=b'abc'))
+
+            # TODO: Always use their retransmission timeout value
+            time.sleep(2)
+            cert_acl_data_asserts.assert_event_occurs(
+                lambda packet: self.get_p_from_ertm_s_frame(scid, packet) == l2cap_packets.Poll.POLL
+            )
+
+            s_frame = l2cap_packets.EnhancedSupervisoryFrameBuilder(
+                dcid, l2cap_packets.SupervisoryFunction.RECEIVER_NOT_READY,
+                l2cap_packets.Poll.NOT_SET, l2cap_packets.Final.POLL_RESPONSE,
+                0)
+            self.cert_send_b_frame(s_frame)
+
+            cert_acl_data_asserts.assert_none_matching(
+                lambda packet: self.get_tx_seq_from_ertm_i_frame(scid, packet) == 0
+            )
+
+    def test_sent_rej_lost(self):
+        """
+        L2CAP/ERM/BI-01-C [S-Frame [REJ] Lost or Corrupted]
+        Verify the IUT can handle receipt of an S-=frame [RR] Poll = 1 if the S-frame [REJ] sent from the IUT
+        is lost.
+        """
+        self.ertm_tx_window_size = 5
+        cert_acl_handle = self._setup_link_from_cert()
+        with EventCallbackStream(
+                self.cert_device.hci_acl_manager.FetchAclData(
+                    empty_proto.Empty())) as cert_acl_data_stream:
+            cert_acl_data_asserts = EventAsserts(cert_acl_data_stream)
+            cert_acl_data_stream.register_callback(self._handle_control_packet)
+            self.on_connection_response = self._on_connection_response_use_ertm
+
+            psm = 0x33
+            scid = 0x41
+            self._open_channel(
+                cert_acl_data_stream,
+                1,
+                cert_acl_handle,
+                scid,
+                psm,
+                mode=l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM)
+
+            # FIXME: Order shouldn't matter here
+            cert_acl_data_asserts.assert_event_occurs(
+                self.is_correct_configuration_response)
+            cert_acl_data_asserts.assert_event_occurs(
+                self.is_correct_configuration_request)
+
+            dcid = self.scid_to_dcid[scid]
+
+            i_frame = l2cap_packets.EnhancedInformationFrameBuilder(
+                dcid, 0, l2cap_packets.Final.NOT_SET, 0,
+                l2cap_packets.SegmentationAndReassembly.UNSEGMENTED,
+                SAMPLE_PACKET)
+            self.cert_send_b_frame(i_frame)
+            cert_acl_data_asserts.assert_event_occurs(
+                lambda packet: self.get_req_seq_from_ertm_s_frame(scid, packet) == 1
+            )
+
+            i_frame = l2cap_packets.EnhancedInformationFrameBuilder(
+                dcid, self.ertm_tx_window_size - 1, l2cap_packets.Final.NOT_SET,
+                0, l2cap_packets.SegmentationAndReassembly.UNSEGMENTED,
+                SAMPLE_PACKET)
+            self.cert_send_b_frame(i_frame)
+            cert_acl_data_asserts.assert_event_occurs(
+                lambda packet: self.get_s_from_ertm_s_frame(scid, packet) == l2cap_packets.SupervisoryFunction.REJECT
+            )
+
+            s_frame = l2cap_packets.EnhancedSupervisoryFrameBuilder(
+                dcid, l2cap_packets.SupervisoryFunction.RECEIVER_READY,
+                l2cap_packets.Poll.POLL, l2cap_packets.Final.NOT_SET, 0)
+            self.cert_send_b_frame(s_frame)
+
+            cert_acl_data_asserts.assert_event_occurs(
+                lambda packet: self.get_req_seq_from_ertm_s_frame(scid, packet) == 1 and self.get_f_from_ertm_s_frame(scid, packet) == l2cap_packets.Final.POLL_RESPONSE)
+            for i in range(1, self.ertm_tx_window_size):
+                i_frame = l2cap_packets.EnhancedInformationFrameBuilder(
+                    dcid, i, l2cap_packets.Final.NOT_SET, 0,
+                    l2cap_packets.SegmentationAndReassembly.UNSEGMENTED,
+                    SAMPLE_PACKET)
+                self.cert_send_b_frame(i_frame)
+                cert_acl_data_asserts.assert_event_occurs(
+                    lambda packet: self.get_req_seq_from_ertm_s_frame(scid, packet) == i + 1
+                )
+
+    def test_handle_duplicate_srej(self):
+        """
+        L2CAP/ERM/BI-03-C [Handle Duplicate S-Frame [SREJ]]
+        Verify the IUT will only retransmit the requested I-frame once after receiving a duplicate SREJ.
+        """
+        cert_acl_handle = self._setup_link_from_cert()
+        with EventCallbackStream(
+                self.cert_device.hci_acl_manager.FetchAclData(
+                    empty_proto.Empty())) as cert_acl_data_stream:
+            cert_acl_data_asserts = EventAsserts(cert_acl_data_stream)
+            cert_acl_data_stream.register_callback(self._handle_control_packet)
+            self.on_connection_response = self._on_connection_response_use_ertm
+
+            psm = 0x33
+            scid = 0x41
+            self._open_channel(
+                cert_acl_data_stream,
+                1,
+                cert_acl_handle,
+                scid,
+                psm,
+                mode=l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM)
+
+            # FIXME: Order shouldn't matter here
+            cert_acl_data_asserts.assert_event_occurs(
+                self.is_correct_configuration_response)
+            cert_acl_data_asserts.assert_event_occurs(
+                self.is_correct_configuration_request)
+
+            dcid = self.scid_to_dcid[scid]
+
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+            cert_acl_data_asserts.assert_event_occurs(
+                lambda packet: self.get_tx_seq_from_ertm_i_frame(scid, packet) == 0,
+                timeout=timedelta(0.5))
+            cert_acl_data_asserts.assert_event_occurs(
+                lambda packet: self.get_tx_seq_from_ertm_i_frame(scid, packet) == 1,
+                timeout=timedelta(0.5))
+            cert_acl_data_asserts.assert_event_occurs(
+                lambda packet: self.get_p_from_ertm_s_frame(scid, packet) == l2cap_packets.Poll.POLL
+            )
+
+            # Send SREJ with F not set
+            s_frame = l2cap_packets.EnhancedSupervisoryFrameBuilder(
+                dcid, l2cap_packets.SupervisoryFunction.SELECT_REJECT,
+                l2cap_packets.Poll.NOT_SET, l2cap_packets.Final.NOT_SET, 0)
+            self.cert_send_b_frame(s_frame)
+
+            cert_acl_data_asserts.assert_none(timeout=timedelta(seconds=0.5))
+            # Send SREJ with F set
+            s_frame = l2cap_packets.EnhancedSupervisoryFrameBuilder(
+                dcid, l2cap_packets.SupervisoryFunction.SELECT_REJECT,
+                l2cap_packets.Poll.NOT_SET, l2cap_packets.Final.POLL_RESPONSE,
+                0)
+            self.cert_send_b_frame(s_frame)
+
+            cert_acl_data_asserts.assert_event_occurs(
+                lambda packet: self.get_tx_seq_from_ertm_i_frame(scid, packet) == 0
+            )
+
+    def test_handle_receipt_rej_and_rr_with_f_set(self):
+        """
+        L2CAP/ERM/BI-04-C [Handle Receipt of S-Frame [REJ] and S-Frame [RR, F=1] that Both Require Retransmission of the Same I-Frames]
+        Verify the IUT will only retransmit the requested I-frames once after receiving an S-frame [REJ]
+        followed by an S-frame [RR] with the Final bit set that indicates the same I-frames should be
+        retransmitted.
+        """
+        cert_acl_handle = self._setup_link_from_cert()
+        with EventCallbackStream(
+                self.cert_device.hci_acl_manager.FetchAclData(
+                    empty_proto.Empty())) as cert_acl_data_stream:
+            cert_acl_data_asserts = EventAsserts(cert_acl_data_stream)
+            cert_acl_data_stream.register_callback(self._handle_control_packet)
+            self.on_connection_response = self._on_connection_response_use_ertm
+
+            psm = 0x33
+            scid = 0x41
+            self._open_channel(
+                cert_acl_data_stream,
+                1,
+                cert_acl_handle,
+                scid,
+                psm,
+                mode=l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM)
+
+            # FIXME: Order shouldn't matter here
+            cert_acl_data_asserts.assert_event_occurs(
+                self.is_correct_configuration_response)
+            cert_acl_data_asserts.assert_event_occurs(
+                self.is_correct_configuration_request)
+
+            dcid = self.scid_to_dcid[scid]
+
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+            cert_acl_data_asserts.assert_event_occurs(
+                lambda packet: self.get_tx_seq_from_ertm_i_frame(scid, packet) == 0,
+                timeout=timedelta(0.5))
+            cert_acl_data_asserts.assert_event_occurs(
+                lambda packet: self.get_tx_seq_from_ertm_i_frame(scid, packet) == 1,
+                timeout=timedelta(0.5))
+            cert_acl_data_asserts.assert_event_occurs(
+                lambda packet: self.get_p_from_ertm_s_frame(scid, packet) == l2cap_packets.Poll.POLL,
+                timeout=timedelta(2))
+
+            # Send REJ with F not set
+            s_frame = l2cap_packets.EnhancedSupervisoryFrameBuilder(
+                dcid, l2cap_packets.SupervisoryFunction.REJECT,
+                l2cap_packets.Poll.NOT_SET, l2cap_packets.Final.NOT_SET, 0)
+            self.cert_send_b_frame(s_frame)
+
+            cert_acl_data_asserts.assert_none(timeout=timedelta(seconds=0.5))
+
+            # Send RR with F set
+            s_frame = l2cap_packets.EnhancedSupervisoryFrameBuilder(
+                dcid, l2cap_packets.SupervisoryFunction.REJECT,
+                l2cap_packets.Poll.NOT_SET, l2cap_packets.Final.POLL_RESPONSE,
+                0)
+            self.cert_send_b_frame(s_frame)
+
+            cert_acl_data_asserts.assert_event_occurs(
+                lambda packet: self.get_tx_seq_from_ertm_i_frame(scid, packet) == 0
+            )
+            cert_acl_data_asserts.assert_event_occurs(
+                lambda packet: self.get_tx_seq_from_ertm_i_frame(scid, packet) == 1
+            )
+
+    def test_handle_rej_and_i_frame_with_f_set(self):
+        """
+        L2CAP/ERM/BI-05-C [Handle receipt of S-Frame [REJ] and I-Frame [F=1] that Both Require Retransmission of the Same I-Frames]
+        Verify the IUT will only retransmit the requested I-frames once after receiving an S-frame [REJ]
+        followed by an I-frame with the Final bit set that indicates the same I-frames should be retransmitted.
+        """
+        cert_acl_handle = self._setup_link_from_cert()
+        with EventCallbackStream(
+                self.cert_device.hci_acl_manager.FetchAclData(
+                    empty_proto.Empty())) as cert_acl_data_stream:
+            cert_acl_data_asserts = EventAsserts(cert_acl_data_stream)
+            cert_acl_data_stream.register_callback(self._handle_control_packet)
+            self.on_connection_response = self._on_connection_response_use_ertm
+
+            psm = 0x33
+            scid = 0x41
+            self._open_channel(
+                cert_acl_data_stream,
+                1,
+                cert_acl_handle,
+                scid,
+                psm,
+                mode=l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM)
+
+            # FIXME: Order shouldn't matter here
+            cert_acl_data_asserts.assert_event_occurs(
+                self.is_correct_configuration_response)
+            cert_acl_data_asserts.assert_event_occurs(
+                self.is_correct_configuration_request)
+
+            dcid = self.scid_to_dcid[scid]
+
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+            cert_acl_data_asserts.assert_event_occurs(
+                lambda packet: self.get_tx_seq_from_ertm_i_frame(scid, packet) == 0,
+                timeout=timedelta(0.5))
+            cert_acl_data_asserts.assert_event_occurs(
+                lambda packet: self.get_tx_seq_from_ertm_i_frame(scid, packet) == 1,
+                timeout=timedelta(0.5))
+            cert_acl_data_asserts.assert_event_occurs(
+                lambda packet: self.get_p_from_ertm_s_frame(scid, packet) == l2cap_packets.Poll.POLL,
+                timeout=timedelta(2))
+
+            # Send SREJ with F not set
+            s_frame = l2cap_packets.EnhancedSupervisoryFrameBuilder(
+                dcid, l2cap_packets.SupervisoryFunction.SELECT_REJECT,
+                l2cap_packets.Poll.NOT_SET, l2cap_packets.Final.NOT_SET, 0)
+            self.cert_send_b_frame(s_frame)
+
+            cert_acl_data_asserts.assert_none(timeout=timedelta(seconds=0.5))
+
+            i_frame = l2cap_packets.EnhancedInformationFrameBuilder(
+                dcid, 0, l2cap_packets.Final.POLL_RESPONSE, 0,
+                l2cap_packets.SegmentationAndReassembly.UNSEGMENTED,
+                SAMPLE_PACKET)
+            self.cert_send_b_frame(i_frame)
+
+            cert_acl_data_asserts.assert_event_occurs(
+                lambda packet: self.get_tx_seq_from_ertm_i_frame(scid, packet) == 0
+            )
+            cert_acl_data_asserts.assert_event_occurs(
+                lambda packet: self.get_tx_seq_from_ertm_i_frame(scid, packet) == 1
+            )
+
+    def test_initiated_configurtion_request_ertm(self):
+        """
+        L2CAP/CMC/BV-01-C [IUT Initiated Configuration of Enhanced Retransmission Mode]
+        Verify the IUT can send a Configuration Request command containing the F&EC option that specifies
+        Enhanced Retransmission Mode.
+        """
+        cert_acl_handle = self._setup_link_from_cert()
+        with EventCallbackStream(
+                self.cert_device.hci_acl_manager.FetchAclData(
+                    empty_proto.Empty())) as cert_acl_data_stream:
+            cert_acl_data_asserts = EventAsserts(cert_acl_data_stream)
+            cert_acl_data_stream.register_callback(self._handle_control_packet)
+            self.on_connection_response = self._on_connection_response_use_ertm
+
+            psm = 0x33
+            scid = 0x41
+            self._open_channel(
+                cert_acl_data_stream,
+                1,
+                cert_acl_handle,
+                scid,
+                psm,
+                mode=l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM)
+
+            # TODO: Fix this test. It doesn't work so far with PDL struct
+
+            cert_acl_data_asserts.assert_event_occurs(
+                self.is_correct_configuration_request)
+            asserts.skip("Struct not working")
+
+    def test_respond_configuration_request_ertm(self):
+        """
+        L2CAP/CMC/BV-02-C [Lower Tester Initiated Configuration of Enhanced Retransmission Mode]
+        Verify the IUT can accept a Configuration Request from the Lower Tester containing an F&EC option
+        that specifies Enhanced Retransmission Mode.
+        """
+        cert_acl_handle = self._setup_link_from_cert()
+        with EventCallbackStream(
+                self.cert_device.hci_acl_manager.FetchAclData(
+                    empty_proto.Empty())) as cert_acl_data_stream:
+            cert_acl_data_asserts = EventAsserts(cert_acl_data_stream)
+            cert_acl_data_stream.register_callback(self._handle_control_packet)
+            psm = 1
+            scid = 0x0101
+            self.retransmission_mode = l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM
+            self.device_under_test.l2cap.SetDynamicChannel(
+                l2cap_facade_pb2.SetEnableDynamicChannelRequest(
+                    psm=psm, retransmission_mode=self.retransmission_mode))
+
+            open_channel = l2cap_packets.ConnectionRequestBuilder(1, psm, scid)
+            open_channel_l2cap = l2cap_packets.BasicFrameBuilder(
+                1, open_channel)
+            self.cert_send_b_frame(open_channel_l2cap)
+
+            # TODO: Verify that the type should be ERTM
+            cert_acl_data_asserts.assert_event_occurs(
+                self.is_correct_configuration_response)
diff --git a/gd/l2cap/classic/cert/pts_l2cap_test.py b/gd/l2cap/classic/cert/pts_l2cap_test.py
new file mode 100644
index 0000000..68c2751
--- /dev/null
+++ b/gd/l2cap/classic/cert/pts_l2cap_test.py
@@ -0,0 +1,739 @@
+#!/usr/bin/env python3
+#
+#   Copyright 2019 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+from datetime import timedelta
+import time
+
+from cert.pts_base_test import PTSBaseTestClass
+from cert.event_asserts import EventAsserts
+from cert.event_callback_stream import EventCallbackStream
+from facade import common_pb2
+from facade import rootservice_pb2 as facade_rootservice_pb2
+from l2cap.classic import facade_pb2 as l2cap_facade_pb2
+from google.protobuf import empty_pb2
+from neighbor.facade import facade_pb2 as neighbor_facade
+
+
+class PTSL2capTest(PTSBaseTestClass):
+
+    def setup_test(self):
+        self.device_under_test = self.gd_devices[0]
+
+        self.device_under_test.rootservice.StartStack(
+            facade_rootservice_pb2.StartStackRequest(
+                module_under_test=facade_rootservice_pb2.BluetoothModule.Value(
+                    'L2CAP'),))
+
+        self.device_under_test.wait_channel_ready()
+
+        dut_address = self.device_under_test.controller_read_only_property.ReadLocalAddress(
+            empty_pb2.Empty()).address
+        pts_address = self.controller_configs.get('pts_address').lower()
+        self.device_under_test.address = dut_address
+
+        self.dut_address = common_pb2.BluetoothAddress(
+            address=self.device_under_test.address)
+        self.pts_address = common_pb2.BluetoothAddress(
+            address=str.encode(pts_address))
+
+        self.device_under_test.neighbor.EnablePageScan(
+            neighbor_facade.EnableMsg(enabled=True))
+
+    def teardown_test(self):
+        self.device_under_test.rootservice.StopStack(
+            facade_rootservice_pb2.StopStackRequest())
+
+    def _dut_connection_stream(self):
+        return EventCallbackStream(
+            self.device_under_test.l2cap.FetchConnectionComplete(
+                empty_pb2.Empty()))
+
+    def _dut_connection_close_stream(self):
+        return EventCallbackStream(
+            self.device_under_test.l2cap.FetchConnectionClose(
+                empty_pb2.Empty()))
+
+    def _assert_connection_complete(self, due_connection_asserts, timeout=30):
+        due_connection_asserts.assert_event_occurs(
+            lambda device: device.remote.address == self.pts_address.address,
+            timeout=timedelta(seconds=timeout))
+
+    def _assert_connection_close(self, due_connection_close_asserts,
+                                 timeout=30):
+        due_connection_close_asserts.assert_event_occurs(
+            lambda device: device.remote.address == self.pts_address.address,
+            timeout=timedelta(seconds=timeout))
+
+    def test_L2CAP_IEX_BV_01_C(self):
+        """
+        L2CAP/COS/IEX/BV-01-C [Query for 1.2 Features]
+        Verify that the IUT transmits an information request command to solicit if the remote device supports
+        Specification 1.2 features.
+        """
+        psm = 1
+        self.device_under_test.l2cap.OpenChannel(
+            l2cap_facade_pb2.OpenChannelRequest(
+                remote=self.pts_address, psm=psm))
+        time.sleep(5)
+
+    def test_L2CAP_IEX_BV_02_C(self):
+        """
+        L2CAP/COS/IEX/BV-02-C [Respond with 1.2 Features]
+        Verify that the IUT responds to an information request command soliciting for Specification 1.2
+        features.
+        """
+        psm = 1
+        retransmission_mode = l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM
+        self.device_under_test.l2cap.SetDynamicChannel(
+            l2cap_facade_pb2.SetEnableDynamicChannelRequest(
+                psm=psm, retransmission_mode=retransmission_mode))
+        time.sleep(20)
+
+    def test_L2CAP_EXF_BV_01_C(self):
+        """
+        L2CAP/EXF/BV-01-C [Extended Features Information Response for Enhanced Retransmission Mode]
+        Verify the IUT can format an Information Response for the information type of Extended Features that
+        correctly identifies that Enhanced Retransmission Mode is locally supported.
+
+        """
+        psm = 1
+        retransmission_mode = l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM
+        self.device_under_test.l2cap.SetDynamicChannel(
+            l2cap_facade_pb2.SetEnableDynamicChannelRequest(
+                psm=psm, retransmission_mode=retransmission_mode))
+        time.sleep(5)
+
+    def test_L2CAP_EXF_BV_03_C(self):
+        """
+        L2CAP/EXF/BV-03-C [Extended Features Information Response for FCS Option]
+        Verify the IUT can format an Information Response for the information type of Extended Features that
+        correctly identifies that the FCS Option is locally supported.
+        """
+        psm = 1
+        retransmission_mode = l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM
+        self.device_under_test.l2cap.SetDynamicChannel(
+            l2cap_facade_pb2.SetEnableDynamicChannelRequest(
+                psm=psm, retransmission_mode=retransmission_mode))
+        time.sleep(5)
+
+    def test_L2CAP_CMC_BV_01_C(self):
+        """
+        L2CAP/CMC/BV-01-C [IUT Initiated Configuration of Enhanced Retransmission Mode]
+        Verify the IUT can send a Configuration Request command containing the F&EC option that specifies
+        Enhanced Retransmission Mode.
+        """
+        with self._dut_connection_close_stream() as dut_connection_close_stream:
+            due_connection_close_asserts = EventAsserts(
+                dut_connection_close_stream)
+            psm = 1
+            retransmission_mode = l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM
+            self.device_under_test.l2cap.SetDynamicChannel(
+                l2cap_facade_pb2.SetEnableDynamicChannelRequest(
+                    psm=psm, retransmission_mode=retransmission_mode))
+            self._assert_connection_close(due_connection_close_asserts)
+
+    def test_L2CAP_CMC_BV_02_C(self):
+        """
+        L2CAP/CMC/BV-02-C [Lower Tester Initiated Configuration of Enhanced Retransmission Mode]
+        Verify the IUT can accept a Configuration Request from the Lower Tester containing an F&EC option
+        that specifies Enhanced Retransmission Mode.
+        """
+        with self._dut_connection_close_stream() as dut_connection_close_stream:
+            due_connection_close_asserts = EventAsserts(
+                dut_connection_close_stream)
+            psm = 1
+            retransmission_mode = l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM
+            self.device_under_test.l2cap.SetDynamicChannel(
+                l2cap_facade_pb2.SetEnableDynamicChannelRequest(
+                    psm=psm, retransmission_mode=retransmission_mode))
+            self._assert_connection_close(due_connection_close_asserts)
+
+    def test_L2CAP_ERM_BV_01_C(self):
+        """
+        L2CAP/ERM/BV-01-C [Transmit I-frames]
+        Verify the IUT can send correctly formatted sequential I-frames with valid values for the enhanced
+        control fields (SAR, F-bit, ReqSeq, TxSeq).
+        """
+        with self._dut_connection_stream() as dut_connection_stream, \
+            self._dut_connection_close_stream() as dut_connection_close_stream:
+            due_connection_asserts = EventAsserts(dut_connection_stream)
+            due_connection_close_asserts = EventAsserts(
+                dut_connection_close_stream)
+            psm = 1
+            retransmission_mode = l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM
+            self.device_under_test.l2cap.SetDynamicChannel(
+                l2cap_facade_pb2.SetEnableDynamicChannelRequest(
+                    psm=psm, retransmission_mode=retransmission_mode))
+            self._assert_connection_complete(due_connection_asserts)
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+            self._assert_connection_close(due_connection_close_asserts)
+
+    def test_L2CAP_ERM_BV_02_C(self):
+        """
+        L2CAP/ERM/BV-02-C [Receive I-Frames]
+        Verify the IUT can receive in-sequence valid I-frames and deliver L2CAP SDUs to the Upper Tester
+        """
+        with self._dut_connection_stream() as dut_connection_stream, \
+            self._dut_connection_close_stream() as dut_connection_close_stream:
+            due_connection_asserts = EventAsserts(dut_connection_stream)
+            due_connection_close_asserts = EventAsserts(
+                dut_connection_close_stream)
+            psm = 1
+            retransmission_mode = l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM
+            self.device_under_test.l2cap.SetDynamicChannel(
+                l2cap_facade_pb2.SetEnableDynamicChannelRequest(
+                    psm=psm, retransmission_mode=retransmission_mode))
+            self._assert_connection_complete(due_connection_asserts)
+            self._assert_connection_close(due_connection_close_asserts)
+
+    def test_L2CAP_ERM_BV_03_C(self):
+        """
+        L2CAP/ERM/BV-03-C [Acknowledging Received I-Frames]
+        """
+        with self._dut_connection_stream() as dut_connection_stream, \
+            self._dut_connection_close_stream() as dut_connection_close_stream:
+            due_connection_asserts = EventAsserts(dut_connection_stream)
+            due_connection_close_asserts = EventAsserts(
+                dut_connection_close_stream)
+            psm = 1
+            retransmission_mode = l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM
+            self.device_under_test.l2cap.SetDynamicChannel(
+                l2cap_facade_pb2.SetEnableDynamicChannelRequest(
+                    psm=psm, retransmission_mode=retransmission_mode))
+            self._assert_connection_complete(due_connection_asserts)
+            self._assert_connection_close(due_connection_close_asserts)
+
+    def test_L2CAP_ERM_BV_05_C(self):
+        """
+        L2CAP/ERM/BV-05-C [Resume Transmitting I-Frames when an S-Frame [RR] is Received]
+        Verify the IUT will cease transmission of I-frames when the negotiated TxWindow is full. Verify the
+        IUT will resume transmission of I-frames when an S-frame [RR] is received that acknowledges
+        previously sent I-frames.
+        """
+        with self._dut_connection_stream() as dut_connection_stream, \
+            self._dut_connection_close_stream() as dut_connection_close_stream:
+            due_connection_asserts = EventAsserts(dut_connection_stream)
+            due_connection_close_asserts = EventAsserts(
+                dut_connection_close_stream)
+            psm = 1
+
+            self.device_under_test.l2cap.SetDynamicChannel(
+                l2cap_facade_pb2.SetEnableDynamicChannelRequest(
+                    psm=psm,
+                    retransmission_mode=l2cap_facade_pb2.
+                    RetransmissionFlowControlMode.ERTM))
+            self._assert_connection_complete(due_connection_asserts)
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+            self._assert_connection_close(due_connection_close_asserts)
+
+    def test_L2CAP_ERM_BV_06_C(self):
+        """
+        L2CAP/ERM/BV-06-C [Resume Transmitting I-Frames when an I-Frame is Received]
+        Verify the IUT will cease transmission of I-frames when the negotiated TxWindow is full. Verify the
+        IUT will resume transmission of I-frames when an I-frame is received that acknowledges previously
+        sent I-frames.
+        """
+        with self._dut_connection_stream() as dut_connection_stream, \
+            self._dut_connection_close_stream() as dut_connection_close_stream:
+            due_connection_asserts = EventAsserts(dut_connection_stream)
+            due_connection_close_asserts = EventAsserts(
+                dut_connection_close_stream)
+            psm = 1
+
+            self.device_under_test.l2cap.SetDynamicChannel(
+                l2cap_facade_pb2.SetEnableDynamicChannelRequest(
+                    psm=psm,
+                    retransmission_mode=l2cap_facade_pb2.
+                    RetransmissionFlowControlMode.ERTM))
+            self._assert_connection_complete(due_connection_asserts)
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+            self._assert_connection_close(due_connection_close_asserts)
+
+    def test_L2CAP_ERM_BV_08_C(self):
+        """
+        L2CAP/ERM/BV-08-C [Send S-Frame [RR] with Poll Bit Set]
+        Verify the IUT sends an S-frame [RR] with the Poll bit set when its retransmission timer expires.
+        """
+        with self._dut_connection_stream() as dut_connection_stream, \
+            self._dut_connection_close_stream() as dut_connection_close_stream:
+            due_connection_asserts = EventAsserts(dut_connection_stream)
+            due_connection_close_asserts = EventAsserts(
+                dut_connection_close_stream)
+            psm = 1
+            retransmission_mode = l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM
+            self.device_under_test.l2cap.SetDynamicChannel(
+                l2cap_facade_pb2.SetEnableDynamicChannelRequest(
+                    psm=psm, retransmission_mode=retransmission_mode))
+            self._assert_connection_complete(due_connection_asserts)
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+            self._assert_connection_close(due_connection_close_asserts)
+
+    def test_L2CAP_ERM_BV_09_C(self):
+        """
+        L2CAP/ERM/BV-09-C [Send S-frame [RR] with Final Bit Set]
+        Verify the IUT responds with an S-frame [RR] with the Final bit set after receiving an S-frame [RR]
+        with the Poll bit set.
+        """
+        with self._dut_connection_stream() as dut_connection_stream, \
+            self._dut_connection_close_stream() as dut_connection_close_stream:
+            due_connection_asserts = EventAsserts(dut_connection_stream)
+            due_connection_close_asserts = EventAsserts(
+                dut_connection_close_stream)
+            psm = 1
+            retransmission_mode = l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM
+            self.device_under_test.l2cap.SetDynamicChannel(
+                l2cap_facade_pb2.SetEnableDynamicChannelRequest(
+                    psm=psm, retransmission_mode=retransmission_mode))
+            self._assert_connection_complete(due_connection_asserts)
+            self._assert_connection_close(due_connection_close_asserts)
+
+    def test_L2CAP_ERM_BV_10_C(self):
+        """
+      L2CAP/ERM/BV-10-C [Retransmit S-Frame [RR] with Poll Bit Set]
+      Verify the IUT will retransmit the S-frame [RR] with the Poll bit set when the Monitor Timer expires.
+      """
+        with self._dut_connection_stream() as dut_connection_stream, \
+            self._dut_connection_close_stream() as dut_connection_close_stream:
+            due_connection_asserts = EventAsserts(dut_connection_stream)
+            due_connection_close_asserts = EventAsserts(
+                dut_connection_close_stream)
+            psm = 1
+            retransmission_mode = l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM
+            self.device_under_test.l2cap.SetDynamicChannel(
+                l2cap_facade_pb2.SetEnableDynamicChannelRequest(
+                    psm=psm, retransmission_mode=retransmission_mode))
+            self._assert_connection_complete(due_connection_asserts)
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+            self._assert_connection_close(due_connection_close_asserts)
+
+    def test_L2CAP_ERM_BV_13_C(self):
+        """
+        L2CAP/ERM/BV-13-C [Respond to S-Frame [REJ]]
+        """
+        with self._dut_connection_stream() as dut_connection_stream, \
+            self._dut_connection_close_stream() as dut_connection_close_stream:
+            due_connection_asserts = EventAsserts(dut_connection_stream)
+            due_connection_close_asserts = EventAsserts(
+                dut_connection_close_stream)
+            psm = 1
+
+            self.device_under_test.l2cap.SetDynamicChannel(
+                l2cap_facade_pb2.SetEnableDynamicChannelRequest(
+                    psm=psm,
+                    retransmission_mode=l2cap_facade_pb2.
+                    RetransmissionFlowControlMode.ERTM))
+            self._assert_connection_complete(due_connection_asserts)
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+            self._assert_connection_close(due_connection_close_asserts)
+
+    def test_L2CAP_ERM_BV_16_C(self):
+        """
+         L2CAP/ERM/BV-16-C [Send S-Frame [REJ]]
+        Verify the IUT can send an S-frame [REJ] after receiving out of sequence I-frames.
+        """
+        with self._dut_connection_stream() as dut_connection_stream, \
+            self._dut_connection_close_stream() as dut_connection_close_stream:
+            due_connection_asserts = EventAsserts(dut_connection_stream)
+            due_connection_close_asserts = EventAsserts(
+                dut_connection_close_stream)
+            psm = 1
+
+            self.device_under_test.l2cap.SetDynamicChannel(
+                l2cap_facade_pb2.SetEnableDynamicChannelRequest(
+                    psm=psm,
+                    retransmission_mode=l2cap_facade_pb2.
+                    RetransmissionFlowControlMode.ERTM))
+            self._assert_connection_complete(due_connection_asserts)
+            self._assert_connection_close(
+                due_connection_close_asserts, timeout=60)
+
+    def test_L2CAP_ERM_BV_18_C(self):
+        """
+        L2CAP/ERM/BV-18-C [Receive S-Frame [RR] Final Bit = 1]
+        Verify the IUT will retransmit any previously sent I-frames unacknowledged by receipt of an S-Frame
+        [RR] with the Final Bit set.
+        """
+        with self._dut_connection_stream() as dut_connection_stream, \
+            self._dut_connection_close_stream() as dut_connection_close_stream:
+            due_connection_asserts = EventAsserts(dut_connection_stream)
+            due_connection_close_asserts = EventAsserts(
+                dut_connection_close_stream)
+            psm = 1
+            retransmission_mode = l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM
+            self.device_under_test.l2cap.SetDynamicChannel(
+                l2cap_facade_pb2.SetEnableDynamicChannelRequest(
+                    psm=psm, retransmission_mode=retransmission_mode))
+            self._assert_connection_complete(due_connection_asserts)
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+            self._assert_connection_close(due_connection_close_asserts)
+
+    def test_L2CAP_ERM_BV_19_C(self):
+        """
+        L2CAP/ERM/BV-19-C [Receive I-Frame Final Bit = 1]
+        Verify the IUT will retransmit any previously sent I-frames unacknowledged by receipt of an I-frame
+        with the final bit set.
+        """
+        with self._dut_connection_stream() as dut_connection_stream, \
+            self._dut_connection_close_stream() as dut_connection_close_stream:
+            due_connection_asserts = EventAsserts(dut_connection_stream)
+            due_connection_close_asserts = EventAsserts(
+                dut_connection_close_stream)
+            psm = 1
+            retransmission_mode = l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM
+            self.device_under_test.l2cap.SetDynamicChannel(
+                l2cap_facade_pb2.SetEnableDynamicChannelRequest(
+                    psm=psm, retransmission_mode=retransmission_mode))
+            self._assert_connection_complete(due_connection_asserts)
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+            self._assert_connection_close(due_connection_close_asserts)
+
+    def test_L2CAP_ERM_BV_20_C(self):
+        """
+        L2CAP/ERM/BV-20-C [Enter Remote Busy Condition]
+        Verify the IUT will not retransmit any I-frames when it receives a remote busy indication from the
+        Lower Tester (S-frame [RNR]).
+        """
+        with self._dut_connection_stream() as dut_connection_stream, \
+            self._dut_connection_close_stream() as dut_connection_close_stream:
+            due_connection_asserts = EventAsserts(dut_connection_stream)
+            due_connection_close_asserts = EventAsserts(
+                dut_connection_close_stream)
+            psm = 1
+            retransmission_mode = l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM
+            self.device_under_test.l2cap.SetDynamicChannel(
+                l2cap_facade_pb2.SetEnableDynamicChannelRequest(
+                    psm=psm, retransmission_mode=retransmission_mode))
+            self._assert_connection_complete(due_connection_asserts)
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+            self._assert_connection_close(due_connection_close_asserts)
+
+    def test_L2CAP_COS_CED_BV_01_C(self):
+        """
+        L2CAP/COS/CED/BV-01-C [Request Connection]
+        Verify that the IUT is able to request the connection establishment for an L2CAP data channel and
+        initiate the configuration procedure.
+        """
+        with self._dut_connection_stream() as dut_connection_stream, \
+            self._dut_connection_close_stream() as dut_connection_close_stream:
+            due_connection_asserts = EventAsserts(dut_connection_stream)
+            due_connection_close_asserts = EventAsserts(
+                dut_connection_close_stream)
+            psm = 1
+            self.device_under_test.l2cap.OpenChannel(
+                l2cap_facade_pb2.OpenChannelRequest(
+                    remote=self.pts_address, psm=psm))
+            self._assert_connection_complete(due_connection_asserts)
+
+            self.device_under_test.l2cap.CloseChannel(
+                l2cap_facade_pb2.CloseChannelRequest(psm=psm))
+            self._assert_connection_close(due_connection_close_asserts)
+
+    def test_L2CAP_COS_CED_BV_03_C(self):
+        """
+        L2CAP/COS/CED/BV-03-C [Send Data]
+        Verify that the IUT is able to send DATA.
+        """
+        with self._dut_connection_stream() as dut_connection_stream, \
+            self._dut_connection_close_stream() as dut_connection_close_stream:
+            due_connection_asserts = EventAsserts(dut_connection_stream)
+            due_connection_close_asserts = EventAsserts(
+                dut_connection_close_stream)
+            psm = 1
+            retransmission_mode = l2cap_facade_pb2.RetransmissionFlowControlMode.BASIC
+            self.device_under_test.l2cap.SetDynamicChannel(
+                l2cap_facade_pb2.SetEnableDynamicChannelRequest(
+                    psm=psm, retransmission_mode=retransmission_mode))
+            self._assert_connection_complete(due_connection_asserts)
+
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(
+                    psm=psm, payload=b'abc' * 34))
+            self._assert_connection_close(due_connection_close_asserts)
+
+    def test_L2CAP_COS_CED_BV_04_C(self):
+        """
+        L2CAP/COS/CED/BV-04-C [Disconnect]
+        Verify that the IUT is able to disconnect the data channel.
+        """
+        with self._dut_connection_stream() as dut_connection_stream, \
+            self._dut_connection_close_stream() as dut_connection_close_stream:
+            due_connection_asserts = EventAsserts(dut_connection_stream)
+            due_connection_close_asserts = EventAsserts(
+                dut_connection_close_stream)
+            psm = 1
+            retransmission_mode = l2cap_facade_pb2.RetransmissionFlowControlMode.BASIC
+            self.device_under_test.l2cap.SetDynamicChannel(
+                l2cap_facade_pb2.SetEnableDynamicChannelRequest(
+                    psm=psm, retransmission_mode=retransmission_mode))
+            self._assert_connection_complete(due_connection_asserts)
+            time.sleep(2)
+            self.device_under_test.l2cap.CloseChannel(
+                l2cap_facade_pb2.CloseChannelRequest(psm=psm))
+            self._assert_connection_close(due_connection_close_asserts)
+
+    def test_L2CAP_COS_CED_BV_05_C(self):
+        """
+        L2CAP/COS/CED/BV-05-C [Accept Connection]
+        Verify that the IUT is able to disconnect the data channel.
+        """
+        with self._dut_connection_stream() as dut_connection_stream, \
+            self._dut_connection_close_stream() as dut_connection_close_stream:
+            due_connection_asserts = EventAsserts(dut_connection_stream)
+            due_connection_close_asserts = EventAsserts(
+                dut_connection_close_stream)
+            psm = 1
+            retransmission_mode = l2cap_facade_pb2.RetransmissionFlowControlMode.BASIC
+            self.device_under_test.l2cap.SetDynamicChannel(
+                l2cap_facade_pb2.SetEnableDynamicChannelRequest(
+                    psm=psm, retransmission_mode=retransmission_mode))
+            self._assert_connection_complete(due_connection_asserts)
+            self._assert_connection_close(due_connection_close_asserts)
+
+    def test_L2CAP_COS_CED_BV_07_C(self):
+        """
+        L2CAP/COS/CED/BV-07-C [Accept Disconnect]
+        Verify that the IUT is able to respond to the request to disconnect the data channel.
+        """
+        with self._dut_connection_stream() as dut_connection_stream, \
+            self._dut_connection_close_stream() as dut_connection_close_stream:
+            due_connection_asserts = EventAsserts(dut_connection_stream)
+            due_connection_close_asserts = EventAsserts(
+                dut_connection_close_stream)
+            psm = 1
+            retransmission_mode = l2cap_facade_pb2.RetransmissionFlowControlMode.BASIC
+            self.device_under_test.l2cap.SetDynamicChannel(
+                l2cap_facade_pb2.SetEnableDynamicChannelRequest(
+                    psm=psm, retransmission_mode=retransmission_mode))
+            self._assert_connection_complete(due_connection_asserts)
+            self._assert_connection_close(due_connection_close_asserts)
+
+    def test_L2CAP_COS_CED_BV_08_C(self):
+        """
+        L2CAP/COS/CED/BV-08-C [Disconnect on Timeout]
+        Verify that the IUT disconnects the data channel and shuts down this channel if no response occurs
+        """
+        with self._dut_connection_stream() as dut_connection_stream, \
+            self._dut_connection_close_stream() as dut_connection_close_stream:
+            due_connection_asserts = EventAsserts(dut_connection_stream)
+            due_connection_close_asserts = EventAsserts(
+                dut_connection_close_stream)
+            psm = 1
+            retransmission_mode = l2cap_facade_pb2.RetransmissionFlowControlMode.BASIC
+            self.device_under_test.l2cap.SetDynamicChannel(
+                l2cap_facade_pb2.SetEnableDynamicChannelRequest(
+                    psm=psm, retransmission_mode=retransmission_mode))
+            time.sleep(120)
+
+    def test_L2CAP_COS_CED_BV_09_C(self):
+        """
+        L2CAP/COS/CED/BV-09-C [Receive Multi-Command Packet]
+        Verify that the IUT is able to receive more than one signaling command in one L2CAP packet.
+        """
+        with self._dut_connection_stream() as dut_connection_stream, \
+            self._dut_connection_close_stream() as dut_connection_close_stream:
+            due_connection_asserts = EventAsserts(dut_connection_stream)
+            due_connection_close_asserts = EventAsserts(
+                dut_connection_close_stream)
+            psm = 1
+            retransmission_mode = l2cap_facade_pb2.RetransmissionFlowControlMode.BASIC
+            self.device_under_test.l2cap.SetDynamicChannel(
+                l2cap_facade_pb2.SetEnableDynamicChannelRequest(
+                    psm=psm, retransmission_mode=retransmission_mode))
+            self._assert_connection_complete(due_connection_asserts)
+            self._assert_connection_close(due_connection_close_asserts)
+
+    def test_L2CAP_COS_CED_BV_11_C(self):
+        """
+        L2CAP/COS/CED/BV-11-C [Configure MTU Size]
+        Verify that the IUT is able to configure the supported MTU size
+        """
+        with self._dut_connection_stream() as dut_connection_stream, \
+            self._dut_connection_close_stream() as dut_connection_close_stream:
+            due_connection_asserts = EventAsserts(dut_connection_stream)
+            due_connection_close_asserts = EventAsserts(
+                dut_connection_close_stream)
+            psm = 1
+            retransmission_mode = l2cap_facade_pb2.RetransmissionFlowControlMode.BASIC
+            self.device_under_test.l2cap.SetDynamicChannel(
+                l2cap_facade_pb2.SetEnableDynamicChannelRequest(
+                    psm=psm, retransmission_mode=retransmission_mode))
+            self._assert_connection_complete(due_connection_asserts)
+            self._assert_connection_close(due_connection_close_asserts)
+
+    def test_L2CAP_COS_CED_BI_01_C(self):
+        """
+        L2CAP/COS/CED/BI-01-C [Reject Unknown Command]
+        Verify that the IUT rejects an unknown signaling command.
+        """
+        with self._dut_connection_stream() as dut_connection_stream, \
+            self._dut_connection_close_stream() as dut_connection_close_stream:
+            due_connection_asserts = EventAsserts(dut_connection_stream)
+            due_connection_close_asserts = EventAsserts(
+                dut_connection_close_stream)
+            psm = 1
+            retransmission_mode = l2cap_facade_pb2.RetransmissionFlowControlMode.BASIC
+            self.device_under_test.l2cap.SetDynamicChannel(
+                l2cap_facade_pb2.SetEnableDynamicChannelRequest(
+                    psm=psm, retransmission_mode=retransmission_mode))
+            self._assert_connection_complete(due_connection_asserts)
+            time.sleep(5)
+
+    def test_L2CAP_COS_CFD_BV_03_C(self):
+        """
+        L2CAP/COS/CFD/BV-03-C [Send Requested Options]
+        Verify that the IUT can receive a configuration request with no options and send the requested
+        options to the Lower Tester
+        """
+        with self._dut_connection_stream() as dut_connection_stream, \
+            self._dut_connection_close_stream() as dut_connection_close_stream:
+            due_connection_asserts = EventAsserts(dut_connection_stream)
+            due_connection_close_asserts = EventAsserts(
+                dut_connection_close_stream)
+            psm = 1
+            retransmission_mode = l2cap_facade_pb2.RetransmissionFlowControlMode.BASIC
+            self.device_under_test.l2cap.SetDynamicChannel(
+                l2cap_facade_pb2.SetEnableDynamicChannelRequest(
+                    psm=psm, retransmission_mode=retransmission_mode))
+            self._assert_connection_close(due_connection_close_asserts)
+
+    def test_L2CAP_COS_CFD_BV_08_C(self):
+        """
+        L2CAP/COS/CFD/BV-08-C [Non-blocking Config Response]
+        Verify that the IUT does not block transmitting L2CAP_ConfigRsp while waiting for L2CAP_ConfigRsp
+        from the Lower Tester.
+        """
+        with self._dut_connection_stream() as dut_connection_stream, \
+            self._dut_connection_close_stream() as dut_connection_close_stream:
+            due_connection_asserts = EventAsserts(dut_connection_stream)
+            due_connection_close_asserts = EventAsserts(
+                dut_connection_close_stream)
+            psm = 1
+
+            self.device_under_test.l2cap.OpenChannel(
+                l2cap_facade_pb2.OpenChannelRequest(
+                    remote=self.pts_address, psm=psm))
+            self._assert_connection_complete(due_connection_asserts)
+            self.device_under_test.l2cap.CloseChannel(
+                l2cap_facade_pb2.CloseChannelRequest(psm=psm))
+            self._assert_connection_close(due_connection_close_asserts)
+
+    def test_L2CAP_ERM_BI_01_C(self):
+        """
+        L2CAP/ERM/BI-01-C [S-Frame [REJ] Lost or Corrupted]
+        Verify the IUT can handle receipt of an S-=frame [RR] Poll = 1 if the S-frame [REJ] sent from the IUT
+        is lost.
+        """
+        with self._dut_connection_stream() as dut_connection_stream, \
+            self._dut_connection_close_stream() as dut_connection_close_stream:
+            due_connection_asserts = EventAsserts(dut_connection_stream)
+            due_connection_close_asserts = EventAsserts(
+                dut_connection_close_stream)
+            psm = 1
+            retransmission_mode = l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM
+            self.device_under_test.l2cap.SetDynamicChannel(
+                l2cap_facade_pb2.SetEnableDynamicChannelRequest(
+                    psm=psm, retransmission_mode=retransmission_mode))
+            self._assert_connection_complete(due_connection_asserts)
+            self._assert_connection_close(due_connection_close_asserts)
+
+    def test_L2CAP_ERM_BI_03_C(self):
+        """
+        L2CAP/ERM/BI-03-C [Handle Duplicate S-Frame [SREJ]]
+        """
+        with self._dut_connection_stream() as dut_connection_stream, \
+            self._dut_connection_close_stream() as dut_connection_close_stream:
+            due_connection_asserts = EventAsserts(dut_connection_stream)
+            due_connection_close_asserts = EventAsserts(
+                dut_connection_close_stream)
+            psm = 1
+
+            self.device_under_test.l2cap.SetDynamicChannel(
+                l2cap_facade_pb2.SetEnableDynamicChannelRequest(
+                    psm=psm,
+                    retransmission_mode=l2cap_facade_pb2.
+                    RetransmissionFlowControlMode.ERTM))
+            self._assert_connection_complete(due_connection_asserts)
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+            self._assert_connection_close(due_connection_close_asserts)
+
+    def test_L2CAP_ERM_BI_04_C(self):
+        """
+        L2CAP/ERM/BI-04-C [Handle Receipt of S-Frame [REJ] and S-Frame [RR, F=1]
+        that Both Require Retransmission of the Same I-Frames]
+        """
+        with self._dut_connection_stream() as dut_connection_stream, \
+            self._dut_connection_close_stream() as dut_connection_close_stream:
+            due_connection_asserts = EventAsserts(dut_connection_stream)
+            due_connection_close_asserts = EventAsserts(
+                dut_connection_close_stream)
+            psm = 1
+
+            self.device_under_test.l2cap.SetDynamicChannel(
+                l2cap_facade_pb2.SetEnableDynamicChannelRequest(
+                    psm=psm,
+                    retransmission_mode=l2cap_facade_pb2.
+                    RetransmissionFlowControlMode.ERTM))
+            self._assert_connection_complete(due_connection_asserts)
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+            self._assert_connection_close(due_connection_close_asserts)
+
+    def test_L2CAP_ERM_BI_05_C(self):
+        """
+        L2CAP/ERM/BI-05-C [Handle receipt of S-Frame [REJ] and I-Frame [F=1] that
+        Both Require Retransmission of the Same I-Frames]
+        """
+        with self._dut_connection_stream() as dut_connection_stream, \
+            self._dut_connection_close_stream() as dut_connection_close_stream:
+            due_connection_asserts = EventAsserts(dut_connection_stream)
+            due_connection_close_asserts = EventAsserts(
+                dut_connection_close_stream)
+            psm = 1
+
+            self.device_under_test.l2cap.SetDynamicChannel(
+                l2cap_facade_pb2.SetEnableDynamicChannelRequest(
+                    psm=psm,
+                    retransmission_mode=l2cap_facade_pb2.
+                    RetransmissionFlowControlMode.ERTM))
+            self._assert_connection_complete(due_connection_asserts)
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+            self.device_under_test.l2cap.SendDynamicChannelPacket(
+                l2cap_facade_pb2.DynamicChannelPacket(psm=psm, payload=b'abc'))
+            self._assert_connection_close(due_connection_close_asserts)
diff --git a/gd/l2cap/classic/dynamic_channel.h b/gd/l2cap/classic/dynamic_channel.h
new file mode 100644
index 0000000..0bd5a82
--- /dev/null
+++ b/gd/l2cap/classic/dynamic_channel.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "l2cap/dynamic_channel.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace classic {
+
+using DynamicChannel = l2cap::DynamicChannel;
+
+}  // namespace classic
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/classic/dynamic_channel_configuration_option.h b/gd/l2cap/classic/dynamic_channel_configuration_option.h
new file mode 100644
index 0000000..3f3cc02
--- /dev/null
+++ b/gd/l2cap/classic/dynamic_channel_configuration_option.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "l2cap/mtu.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace classic {
+
+/**
+ * Configuration Option specified by L2CAP Channel user on a dynamic channel. L2CAP module will configure the channel
+ * based on user provided option.
+ */
+struct DynamicChannelConfigurationOption {
+  enum class RetransmissionAndFlowControlMode {
+    L2CAP_BASIC,
+    ENHANCED_RETRANSMISSION,
+  };
+  /**
+   * Retransmission and flow control mode. Currently L2CAP_BASIC and ENHANCED_RETRANSMISSION.
+   * If the remote doesn't support a mode, it might fall back to basic, as this is a negotiable option.
+   */
+  RetransmissionAndFlowControlMode channel_mode = RetransmissionAndFlowControlMode::L2CAP_BASIC;
+
+  /**
+   * Maximum SDU size that the L2CAP Channel user is able to process.
+   */
+  Mtu incoming_mtu = kDefaultClassicMtu;
+};
+
+}  // namespace classic
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/classic/dynamic_channel_manager.cc b/gd/l2cap/classic/dynamic_channel_manager.cc
new file mode 100644
index 0000000..dbbd465
--- /dev/null
+++ b/gd/l2cap/classic/dynamic_channel_manager.cc
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "l2cap/classic/dynamic_channel_manager.h"
+#include "l2cap/classic/internal/dynamic_channel_service_impl.h"
+#include "l2cap/classic/internal/dynamic_channel_service_manager_impl.h"
+#include "l2cap/classic/internal/link.h"
+#include "l2cap/classic/internal/link_manager.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace classic {
+
+bool DynamicChannelManager::ConnectChannel(hci::Address device, DynamicChannelConfigurationOption configuration_option,
+                                           Psm psm, OnConnectionOpenCallback on_connection_open,
+                                           OnConnectionFailureCallback on_fail_callback, os::Handler* handler) {
+  internal::Link::PendingDynamicChannelConnection pending_dynamic_channel_connection{
+      .handler_ = handler,
+      .on_open_callback_ = std::move(on_connection_open),
+      .on_fail_callback_ = std::move(on_fail_callback),
+      .configuration_ = configuration_option,
+  };
+  l2cap_layer_handler_->Post(common::BindOnce(&internal::LinkManager::ConnectDynamicChannelServices,
+                                              common::Unretained(link_manager_), device,
+                                              std::move(pending_dynamic_channel_connection), psm));
+
+  return true;
+}
+
+bool DynamicChannelManager::RegisterService(Psm psm, DynamicChannelConfigurationOption configuration_option,
+                                            const SecurityPolicy& security_policy,
+                                            OnRegistrationCompleteCallback on_registration_complete,
+                                            OnConnectionOpenCallback on_connection_open, os::Handler* handler) {
+  internal::DynamicChannelServiceImpl::PendingRegistration pending_registration{
+      .user_handler_ = handler,
+      .security_policy_ = security_policy,
+      .on_registration_complete_callback_ = std::move(on_registration_complete),
+      .on_connection_open_callback_ = std::move(on_connection_open),
+      .configuration_ = configuration_option,
+  };
+  l2cap_layer_handler_->Post(common::BindOnce(&internal::DynamicChannelServiceManagerImpl::Register,
+                                              common::Unretained(service_manager_), psm,
+                                              std::move(pending_registration)));
+
+  return true;
+}
+
+}  // namespace classic
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/classic/dynamic_channel_manager.h b/gd/l2cap/classic/dynamic_channel_manager.h
new file mode 100644
index 0000000..f459f31
--- /dev/null
+++ b/gd/l2cap/classic/dynamic_channel_manager.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <string>
+
+#include "hci/acl_manager.h"
+#include "hci/address.h"
+#include "l2cap/classic/dynamic_channel.h"
+#include "l2cap/classic/dynamic_channel_configuration_option.h"
+#include "l2cap/classic/dynamic_channel_service.h"
+#include "l2cap/l2cap_packets.h"
+#include "l2cap/psm.h"
+#include "l2cap/security_policy.h"
+#include "os/handler.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace classic {
+
+class L2capClassicModule;
+
+namespace internal {
+class LinkManager;
+class DynamicChannelServiceManagerImpl;
+}  // namespace internal
+
+class DynamicChannelManager {
+ public:
+  enum class ConnectionResultCode {
+    SUCCESS = 0,
+    FAIL_NO_SERVICE_REGISTERED = 1,  // No service is registered
+    FAIL_HCI_ERROR = 2,              // See hci_error
+    FAIL_L2CAP_ERROR = 3,            // See l2cap_connection_response_result
+  };
+
+  struct ConnectionResult {
+    ConnectionResultCode connection_result_code = ConnectionResultCode::SUCCESS;
+    hci::ErrorCode hci_error = hci::ErrorCode::SUCCESS;
+    ConnectionResponseResult l2cap_connection_response_result = ConnectionResponseResult::SUCCESS;
+  };
+  /**
+   * OnConnectionFailureCallback(std::string failure_reason);
+   */
+  using OnConnectionFailureCallback = common::OnceCallback<void(ConnectionResult result)>;
+
+  /**
+   * OnConnectionOpenCallback(DynamicChannel channel);
+   */
+  using OnConnectionOpenCallback = common::Callback<void(std::unique_ptr<DynamicChannel>)>;
+
+  enum class RegistrationResult {
+    SUCCESS = 0,
+    FAIL_DUPLICATE_SERVICE = 1,  // Duplicate service registration for the same PSM
+    FAIL_INVALID_SERVICE = 2,    // Invalid PSM
+  };
+
+  /**
+   * OnRegistrationFailureCallback(RegistrationResult result, DynamicChannelService service);
+   */
+  using OnRegistrationCompleteCallback =
+      common::OnceCallback<void(RegistrationResult, std::unique_ptr<DynamicChannelService>)>;
+
+  /**
+   * Connect to a Dynamic channel on a remote device
+   *
+   * - This method is asynchronous
+   * - When false is returned, the connection fails immediately
+   * - When true is returned, method caller should wait for on_fail_callback or on_open_callback
+   * - If an ACL connection does not exist, this method will create an ACL connection
+   * - If HCI connection failed, on_fail_callback will be triggered with FAIL_HCI_ERROR
+   * - If Dynamic channel on a remote device is already reported as connected via on_open_callback, it won't be
+   *   reported again
+   *
+   * @param device: Remote device to make this connection.
+   * @param psm: Service PSM to connect. PSM is defined in Core spec Vol 3 Part A 4.2.
+   * @param on_open_callback: A callback to indicate success of a connection initiated from a remote device.
+   * @param on_fail_callback: A callback to indicate connection failure along with a status code.
+   * @param handler: The handler context in which to execute the @callback parameters.
+   * @param configuration_option: The configuration options for this channel
+   *
+   * Returns: true if connection was able to be initiated, false otherwise.
+   */
+  virtual bool ConnectChannel(hci::Address device, DynamicChannelConfigurationOption configuration_option, Psm psm,
+                              OnConnectionOpenCallback on_connection_open, OnConnectionFailureCallback on_fail_callback,
+                              os::Handler* handler);
+
+  /**
+   * Register a service to receive incoming connections bound to a specific channel.
+   *
+   * - This method is asynchronous.
+   * - When false is returned, the registration fails immediately.
+   * - When true is returned, method caller should wait for on_service_registered callback that contains a
+   *   DynamicChannelService object. The registered service can be managed from that object.
+   * - If a PSM is already registered or some other error happens, on_registration_complete will be triggered with a
+   *   non-SUCCESS value
+   * - After a service is registered, a DynamicChannel is delivered through on_open_callback when the remote
+   *   initiates a channel open and channel is opened successfully
+   * - on_open_callback, will only be triggered after on_service_registered callback
+   *
+   * @param security_policy: The security policy used for the connection.
+   * @param psm: Service PSM to register. PSM is defined in Core spec Vol 3 Part A 4.2.
+   * @param on_registration_complete: A callback to indicate the service setup has completed. If the return status is
+   *        not SUCCESS, it means service is not registered due to reasons like PSM already take
+   * @param on_open_callback: A callback to indicate success of a connection initiated from a remote device.
+   * @param handler: The handler context in which to execute the @callback parameter.
+   * @param configuration_option: The configuration options for this channel
+   */
+  virtual bool RegisterService(Psm psm, DynamicChannelConfigurationOption configuration_option,
+                               const SecurityPolicy& security_policy,
+                               OnRegistrationCompleteCallback on_registration_complete,
+                               OnConnectionOpenCallback on_connection_open, os::Handler* handler);
+
+  friend class L2capClassicModule;
+
+  virtual ~DynamicChannelManager() = default;
+
+ protected:
+  DynamicChannelManager() = default;
+
+ private:
+  // The constructor is not to be used by user code
+  DynamicChannelManager(internal::DynamicChannelServiceManagerImpl* service_manager,
+                        internal::LinkManager* link_manager, os::Handler* l2cap_layer_handler)
+      : service_manager_(service_manager), link_manager_(link_manager), l2cap_layer_handler_(l2cap_layer_handler) {
+    ASSERT(service_manager_ != nullptr);
+    ASSERT(link_manager_ != nullptr);
+    ASSERT(l2cap_layer_handler_ != nullptr);
+  }
+  internal::DynamicChannelServiceManagerImpl* service_manager_ = nullptr;
+  internal::LinkManager* link_manager_ = nullptr;
+  os::Handler* l2cap_layer_handler_ = nullptr;
+  DISALLOW_COPY_AND_ASSIGN(DynamicChannelManager);
+};
+
+}  // namespace classic
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/classic/dynamic_channel_service.cc b/gd/l2cap/classic/dynamic_channel_service.cc
new file mode 100644
index 0000000..ee97d37
--- /dev/null
+++ b/gd/l2cap/classic/dynamic_channel_service.cc
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "l2cap/classic/dynamic_channel_service.h"
+#include "common/bind.h"
+#include "l2cap/classic/internal/dynamic_channel_service_manager_impl.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace classic {
+
+void DynamicChannelService::Unregister(OnUnregisteredCallback on_unregistered, os::Handler* on_unregistered_handler) {
+  ASSERT_LOG(manager_ != nullptr, "this service is invalid");
+  l2cap_layer_handler_->Post(common::BindOnce(&internal::DynamicChannelServiceManagerImpl::Unregister,
+                                              common::Unretained(manager_), psm_, std::move(on_unregistered),
+                                              on_unregistered_handler));
+}
+
+Psm DynamicChannelService::GetPsm() const {
+  return psm_;
+}
+
+}  // namespace classic
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/classic/dynamic_channel_service.h b/gd/l2cap/classic/dynamic_channel_service.h
new file mode 100644
index 0000000..24b7403
--- /dev/null
+++ b/gd/l2cap/classic/dynamic_channel_service.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "common/callback.h"
+#include "l2cap/psm.h"
+#include "os/handler.h"
+#include "os/log.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace classic {
+
+namespace internal {
+class DynamicChannelServiceManagerImpl;
+}
+
+class DynamicChannelService {
+ public:
+  DynamicChannelService() = default;
+
+  using OnUnregisteredCallback = common::OnceCallback<void()>;
+
+  /**
+   * Unregister a service from L2CAP module. This operation cannot fail.
+   * All channels opened for this service will be closed.
+   *
+   * @param on_unregistered will be triggered when unregistration is complete
+   */
+  void Unregister(OnUnregisteredCallback on_unregistered, os::Handler* on_unregistered_handler);
+
+  friend internal::DynamicChannelServiceManagerImpl;
+
+  Psm GetPsm() const;
+
+ protected:
+  DynamicChannelService(Psm psm, internal::DynamicChannelServiceManagerImpl* manager, os::Handler* handler)
+      : psm_(psm), manager_(manager), l2cap_layer_handler_(handler) {
+    ASSERT(IsPsmValid(psm));
+    ASSERT(manager_ != nullptr);
+    ASSERT(l2cap_layer_handler_ != nullptr);
+  }
+
+ private:
+  Psm psm_ = kDefaultPsm;
+  internal::DynamicChannelServiceManagerImpl* manager_ = nullptr;
+  os::Handler* l2cap_layer_handler_;
+  DISALLOW_COPY_AND_ASSIGN(DynamicChannelService);
+};
+
+}  // namespace classic
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/classic/facade.cc b/gd/l2cap/classic/facade.cc
new file mode 100644
index 0000000..eabeb08
--- /dev/null
+++ b/gd/l2cap/classic/facade.cc
@@ -0,0 +1,426 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <condition_variable>
+#include <cstdint>
+#include <unordered_map>
+
+#include "common/bidi_queue.h"
+#include "common/bind.h"
+#include "grpc/grpc_event_queue.h"
+#include "hci/address.h"
+#include "l2cap/classic/facade.grpc.pb.h"
+#include "l2cap/classic/facade.h"
+#include "l2cap/classic/l2cap_classic_module.h"
+#include "l2cap/l2cap_packets.h"
+#include "os/log.h"
+#include "packet/raw_builder.h"
+
+using ::grpc::ServerAsyncResponseWriter;
+using ::grpc::ServerAsyncWriter;
+using ::grpc::ServerContext;
+
+using ::bluetooth::packet::RawBuilder;
+
+namespace bluetooth {
+namespace l2cap {
+namespace classic {
+
+class L2capClassicModuleFacadeService : public L2capClassicModuleFacade::Service {
+ public:
+  L2capClassicModuleFacadeService(L2capClassicModule* l2cap_layer, os::Handler* facade_handler)
+      : l2cap_layer_(l2cap_layer), facade_handler_(facade_handler) {
+    ASSERT(l2cap_layer_ != nullptr);
+    ASSERT(facade_handler_ != nullptr);
+  }
+
+  ::grpc::Status FetchConnectionComplete(::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
+                                         ::grpc::ServerWriter<classic::ConnectionCompleteEvent>* writer) override {
+    return pending_connection_complete_.RunLoop(context, writer);
+  }
+
+  ::grpc::Status FetchConnectionClose(::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
+                                      ::grpc::ServerWriter<classic::ConnectionCloseEvent>* writer) override {
+    return pending_connection_close_.RunLoop(context, writer);
+  }
+
+  ::grpc::Status Connect(::grpc::ServerContext* context, const facade::BluetoothAddress* request,
+                         ::google::protobuf::Empty* response) override {
+    auto fixed_channel_manager = l2cap_layer_->GetFixedChannelManager();
+    hci::Address peer;
+    ASSERT(hci::Address::FromString(request->address(), peer));
+    fixed_channel_manager->ConnectServices(peer, common::BindOnce([](FixedChannelManager::ConnectionResult) {}),
+                                           facade_handler_);
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status SendL2capPacket(::grpc::ServerContext* context, const classic::L2capPacket* request,
+                                 SendL2capPacketResult* response) override {
+    std::unique_lock<std::mutex> lock(channel_map_mutex_);
+    if (fixed_channel_helper_map_.find(request->channel()) == fixed_channel_helper_map_.end()) {
+      return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Channel not registered");
+    }
+    std::vector<uint8_t> packet(request->payload().begin(), request->payload().end());
+    if (!fixed_channel_helper_map_[request->channel()]->SendPacket(packet)) {
+      return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Channel not open");
+    }
+    response->set_result_type(SendL2capPacketResultType::OK);
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status SendDynamicChannelPacket(::grpc::ServerContext* context, const DynamicChannelPacket* request,
+                                          ::google::protobuf::Empty* response) override {
+    std::unique_lock<std::mutex> lock(channel_map_mutex_);
+    if (dynamic_channel_helper_map_.find(request->psm()) == dynamic_channel_helper_map_.end()) {
+      return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Psm not registered");
+    }
+    std::vector<uint8_t> packet(request->payload().begin(), request->payload().end());
+    if (!dynamic_channel_helper_map_[request->psm()]->SendPacket(packet)) {
+      return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Channel not open");
+    }
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status OpenChannel(::grpc::ServerContext* context,
+                             const ::bluetooth::l2cap::classic::OpenChannelRequest* request,
+                             ::google::protobuf::Empty* response) override {
+    std::unique_lock<std::mutex> lock(channel_map_mutex_);
+    auto psm = request->psm();
+    auto mode = request->mode();
+    dynamic_channel_helper_map_.emplace(
+        psm, std::make_unique<L2capDynamicChannelHelper>(this, l2cap_layer_, facade_handler_, psm, mode));
+    hci::Address peer;
+    ASSERT(hci::Address::FromString(request->remote().address(), peer));
+    dynamic_channel_helper_map_[psm]->Connect(peer);
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status CloseChannel(::grpc::ServerContext* context,
+                              const ::bluetooth::l2cap::classic::CloseChannelRequest* request,
+                              ::google::protobuf::Empty* response) override {
+    auto psm = request->psm();
+    if (dynamic_channel_helper_map_.find(request->psm()) == dynamic_channel_helper_map_.end()) {
+      return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Psm not registered");
+    }
+    dynamic_channel_helper_map_[psm]->disconnect();
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status FetchL2capData(::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
+                                ::grpc::ServerWriter<classic::L2capPacket>* writer) override {
+    {
+      std::unique_lock<std::mutex> lock(channel_map_mutex_);
+
+      for (auto& connection : fixed_channel_helper_map_) {
+        if (connection.second->channel_ != nullptr) {
+          connection.second->channel_->GetQueueUpEnd()->RegisterDequeue(
+              facade_handler_,
+              common::Bind(&L2capFixedChannelHelper::on_incoming_packet, common::Unretained(connection.second.get())));
+        }
+      }
+
+      for (auto& connection : dynamic_channel_helper_map_) {
+        if (connection.second->channel_ != nullptr) {
+          connection.second->channel_->GetQueueUpEnd()->RegisterDequeue(
+              facade_handler_, common::Bind(&L2capDynamicChannelHelper::on_incoming_packet,
+                                            common::Unretained(connection.second.get())));
+        }
+      }
+
+      fetch_l2cap_data_ = true;
+    }
+
+    auto status = pending_l2cap_data_.RunLoop(context, writer);
+
+    {
+      std::unique_lock<std::mutex> lock(channel_map_mutex_);
+
+      fetch_l2cap_data_ = false;
+
+      for (auto& connection : fixed_channel_helper_map_) {
+        if (connection.second->channel_ != nullptr) {
+          connection.second->channel_->GetQueueUpEnd()->RegisterDequeue(
+              facade_handler_,
+              common::Bind(&L2capFixedChannelHelper::on_incoming_packet, common::Unretained(connection.second.get())));
+        }
+      }
+
+      for (auto& connection : dynamic_channel_helper_map_) {
+        if (connection.second->channel_ != nullptr) {
+          connection.second->channel_->GetQueueUpEnd()->RegisterDequeue(
+              facade_handler_, common::Bind(&L2capDynamicChannelHelper::on_incoming_packet,
+                                            common::Unretained(connection.second.get())));
+        }
+      }
+    }
+
+    return status;
+  }
+
+  ::grpc::Status RegisterChannel(::grpc::ServerContext* context, const classic::RegisterChannelRequest* request,
+                                 ::google::protobuf::Empty* response) override {
+    std::unique_lock<std::mutex> lock(channel_map_mutex_);
+    if (fixed_channel_helper_map_.find(request->channel()) != fixed_channel_helper_map_.end()) {
+      return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Already registered");
+    }
+    fixed_channel_helper_map_.emplace(request->channel(), std::make_unique<L2capFixedChannelHelper>(
+                                                              this, l2cap_layer_, facade_handler_, request->channel()));
+
+    return ::grpc::Status::OK;
+  }
+
+  class L2capFixedChannelHelper {
+   public:
+    L2capFixedChannelHelper(L2capClassicModuleFacadeService* service, L2capClassicModule* l2cap_layer,
+                            os::Handler* handler, Cid cid)
+        : facade_service_(service), l2cap_layer_(l2cap_layer), handler_(handler), cid_(cid) {
+      fixed_channel_manager_ = l2cap_layer_->GetFixedChannelManager();
+      fixed_channel_manager_->RegisterService(
+          cid, {},
+          common::BindOnce(&L2capFixedChannelHelper::on_l2cap_service_registration_complete, common::Unretained(this)),
+          common::Bind(&L2capFixedChannelHelper::on_connection_open, common::Unretained(this)), handler_);
+    }
+
+    void on_l2cap_service_registration_complete(FixedChannelManager::RegistrationResult registration_result,
+                                                std::unique_ptr<FixedChannelService> service) {
+      service_ = std::move(service);
+    }
+
+    void on_connection_open(std::unique_ptr<FixedChannel> channel) {
+      ConnectionCompleteEvent event;
+      event.mutable_remote()->set_address(channel->GetDevice().ToString());
+      facade_service_->pending_connection_complete_.OnIncomingEvent(event);
+      channel_ = std::move(channel);
+      channel_->RegisterOnCloseCallback(
+          facade_service_->facade_handler_,
+          common::BindOnce(&L2capFixedChannelHelper::on_close_callback, common::Unretained(this)));
+      {
+        std::unique_lock<std::mutex> lock(facade_service_->channel_map_mutex_);
+        if (facade_service_->fetch_l2cap_data_) {
+          channel_->GetQueueUpEnd()->RegisterDequeue(
+              facade_service_->facade_handler_,
+              common::Bind(&L2capFixedChannelHelper::on_incoming_packet, common::Unretained(this)));
+        }
+      }
+    }
+
+    bool SendPacket(const std::vector<uint8_t>& packet) {
+      if (channel_ == nullptr) {
+        LOG_WARN("Channel is not open");
+        return false;
+      }
+      channel_->GetQueueUpEnd()->RegisterEnqueue(
+          handler_, common::Bind(&L2capFixedChannelHelper::enqueue_callback, common::Unretained(this), packet));
+      return true;
+    }
+
+    void on_close_callback(hci::ErrorCode error_code) {
+      {
+        std::unique_lock<std::mutex> lock(facade_service_->channel_map_mutex_);
+        if (facade_service_->fetch_l2cap_data_) {
+          channel_->GetQueueUpEnd()->UnregisterDequeue();
+        }
+      }
+      channel_ = nullptr;
+      classic::ConnectionCloseEvent event;
+      event.mutable_remote()->set_address(channel_->GetDevice().ToString());
+      event.set_reason(static_cast<uint32_t>(error_code));
+      facade_service_->pending_connection_close_.OnIncomingEvent(event);
+    }
+
+    void on_incoming_packet() {
+      auto packet = channel_->GetQueueUpEnd()->TryDequeue();
+      std::string data = std::string(packet->begin(), packet->end());
+      L2capPacket l2cap_data;
+      l2cap_data.set_channel(cid_);
+      l2cap_data.set_payload(data);
+      facade_service_->pending_l2cap_data_.OnIncomingEvent(l2cap_data);
+    }
+
+    std::unique_ptr<packet::BasePacketBuilder> enqueue_callback(const std::vector<uint8_t>& packet) {
+      auto packet_one = std::make_unique<packet::RawBuilder>();
+      packet_one->AddOctets(packet);
+      channel_->GetQueueUpEnd()->UnregisterEnqueue();
+      return packet_one;
+    };
+
+    L2capClassicModuleFacadeService* facade_service_;
+    L2capClassicModule* l2cap_layer_;
+    os::Handler* handler_;
+    std::unique_ptr<FixedChannelManager> fixed_channel_manager_;
+    std::unique_ptr<FixedChannelService> service_;
+    std::unique_ptr<FixedChannel> channel_ = nullptr;
+    Cid cid_;
+  };
+
+  ::grpc::Status SetDynamicChannel(::grpc::ServerContext* context, const SetEnableDynamicChannelRequest* request,
+                                   google::protobuf::Empty* response) override {
+    dynamic_channel_helper_map_.emplace(
+        request->psm(), std::make_unique<L2capDynamicChannelHelper>(this, l2cap_layer_, facade_handler_, request->psm(),
+                                                                    request->retransmission_mode()));
+    return ::grpc::Status::OK;
+  }
+
+  class L2capDynamicChannelHelper {
+   public:
+    L2capDynamicChannelHelper(L2capClassicModuleFacadeService* service, L2capClassicModule* l2cap_layer,
+                              os::Handler* handler, Psm psm, RetransmissionFlowControlMode mode)
+        : facade_service_(service), l2cap_layer_(l2cap_layer), handler_(handler), psm_(psm) {
+      dynamic_channel_manager_ = l2cap_layer_->GetDynamicChannelManager();
+      DynamicChannelConfigurationOption configuration_option = {};
+      if (mode == RetransmissionFlowControlMode::BASIC) {
+        configuration_option.channel_mode =
+            DynamicChannelConfigurationOption::RetransmissionAndFlowControlMode::L2CAP_BASIC;
+      } else if (mode == RetransmissionFlowControlMode::ERTM) {
+        configuration_option.channel_mode =
+            DynamicChannelConfigurationOption::RetransmissionAndFlowControlMode::ENHANCED_RETRANSMISSION;
+      }
+      dynamic_channel_manager_->RegisterService(
+          psm, configuration_option, {},
+          common::BindOnce(&L2capDynamicChannelHelper::on_l2cap_service_registration_complete,
+                           common::Unretained(this)),
+          common::Bind(&L2capDynamicChannelHelper::on_connection_open, common::Unretained(this)), handler_);
+    }
+
+    void Connect(hci::Address address) {
+      // TODO: specify channel mode
+      dynamic_channel_manager_->ConnectChannel(
+          address, {}, psm_, common::Bind(&L2capDynamicChannelHelper::on_connection_open, common::Unretained(this)),
+          common::Bind(&L2capDynamicChannelHelper::on_connect_fail, common::Unretained(this)), handler_);
+    }
+
+    void disconnect() {
+      channel_->Close();
+    }
+
+    void on_l2cap_service_registration_complete(DynamicChannelManager::RegistrationResult registration_result,
+                                                std::unique_ptr<DynamicChannelService> service) {}
+
+    void on_connection_open(std::unique_ptr<DynamicChannel> channel) {
+      ConnectionCompleteEvent event;
+      event.mutable_remote()->set_address(channel->GetDevice().ToString());
+      facade_service_->pending_connection_complete_.OnIncomingEvent(event);
+      {
+        std::unique_lock<std::mutex> lock(channel_open_cv_mutex_);
+        channel_ = std::move(channel);
+      }
+      channel_open_cv_.notify_all();
+      channel_->RegisterOnCloseCallback(
+          facade_service_->facade_handler_,
+          common::BindOnce(&L2capDynamicChannelHelper::on_close_callback, common::Unretained(this)));
+      {
+        std::unique_lock<std::mutex> lock(facade_service_->channel_map_mutex_);
+        if (facade_service_->fetch_l2cap_data_) {
+          channel_->GetQueueUpEnd()->RegisterDequeue(
+              facade_service_->facade_handler_,
+              common::Bind(&L2capDynamicChannelHelper::on_incoming_packet, common::Unretained(this)));
+        }
+      }
+    }
+
+    void on_close_callback(hci::ErrorCode error_code) {
+      {
+        std::unique_lock<std::mutex> lock(channel_open_cv_mutex_);
+        if (facade_service_->fetch_l2cap_data_) {
+          channel_->GetQueueUpEnd()->UnregisterDequeue();
+        }
+      }
+      classic::ConnectionCloseEvent event;
+      event.mutable_remote()->set_address(channel_->GetDevice().ToString());
+      channel_ = nullptr;
+      event.set_reason(static_cast<uint32_t>(error_code));
+      facade_service_->pending_connection_close_.OnIncomingEvent(event);
+    }
+
+    void on_connect_fail(DynamicChannelManager::ConnectionResult result) {}
+
+    void on_incoming_packet() {
+      auto packet = channel_->GetQueueUpEnd()->TryDequeue();
+      std::string data = std::string(packet->begin(), packet->end());
+      L2capPacket l2cap_data;
+      //      l2cap_data.set_channel(cid_);
+      l2cap_data.set_payload(data);
+      facade_service_->pending_l2cap_data_.OnIncomingEvent(l2cap_data);
+    }
+
+    bool SendPacket(std::vector<uint8_t> packet) {
+      if (channel_ == nullptr) {
+        std::unique_lock<std::mutex> lock(channel_open_cv_mutex_);
+        if (!channel_open_cv_.wait_for(lock, std::chrono::seconds(1), [this] { return channel_ != nullptr; })) {
+          LOG_WARN("Channel is not open");
+          return false;
+        }
+      }
+      channel_->GetQueueUpEnd()->RegisterEnqueue(
+          handler_, common::Bind(&L2capDynamicChannelHelper::enqueue_callback, common::Unretained(this), packet));
+      return true;
+    }
+
+    std::unique_ptr<packet::BasePacketBuilder> enqueue_callback(std::vector<uint8_t> packet) {
+      auto packet_one = std::make_unique<packet::RawBuilder>(2000);
+      packet_one->AddOctets(packet);
+      channel_->GetQueueUpEnd()->UnregisterEnqueue();
+      return packet_one;
+    };
+
+    L2capClassicModuleFacadeService* facade_service_;
+    L2capClassicModule* l2cap_layer_;
+    os::Handler* handler_;
+    std::unique_ptr<DynamicChannelManager> dynamic_channel_manager_;
+    std::unique_ptr<DynamicChannelService> service_;
+    std::unique_ptr<DynamicChannel> channel_ = nullptr;
+    Psm psm_;
+    std::condition_variable channel_open_cv_;
+    std::mutex channel_open_cv_mutex_;
+  };
+
+  L2capClassicModule* l2cap_layer_;
+  ::bluetooth::os::Handler* facade_handler_;
+  std::mutex channel_map_mutex_;
+  std::map<Cid, std::unique_ptr<L2capFixedChannelHelper>> fixed_channel_helper_map_;
+  std::map<Psm, std::unique_ptr<L2capDynamicChannelHelper>> dynamic_channel_helper_map_;
+  bool fetch_l2cap_data_ = false;
+  ::bluetooth::grpc::GrpcEventQueue<classic::ConnectionCompleteEvent> pending_connection_complete_{
+      "FetchConnectionComplete"};
+  ::bluetooth::grpc::GrpcEventQueue<classic::ConnectionCloseEvent> pending_connection_close_{"FetchConnectionClose"};
+  ::bluetooth::grpc::GrpcEventQueue<L2capPacket> pending_l2cap_data_{"FetchL2capData"};
+};
+
+void L2capClassicModuleFacadeModule::ListDependencies(ModuleList* list) {
+  ::bluetooth::grpc::GrpcFacadeModule::ListDependencies(list);
+  list->add<l2cap::classic::L2capClassicModule>();
+}
+
+void L2capClassicModuleFacadeModule::Start() {
+  ::bluetooth::grpc::GrpcFacadeModule::Start();
+  service_ = new L2capClassicModuleFacadeService(GetDependency<l2cap::classic::L2capClassicModule>(), GetHandler());
+}
+
+void L2capClassicModuleFacadeModule::Stop() {
+  delete service_;
+  ::bluetooth::grpc::GrpcFacadeModule::Stop();
+}
+
+::grpc::Service* L2capClassicModuleFacadeModule::GetService() const {
+  return service_;
+}
+
+const ModuleFactory L2capClassicModuleFacadeModule::Factory =
+    ::bluetooth::ModuleFactory([]() { return new L2capClassicModuleFacadeModule(); });
+
+}  // namespace classic
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/classic/facade.h b/gd/l2cap/classic/facade.h
new file mode 100644
index 0000000..ebaee0d
--- /dev/null
+++ b/gd/l2cap/classic/facade.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <grpc++/grpc++.h>
+
+#include "grpc/grpc_module.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace classic {
+
+class L2capClassicModuleFacadeService;
+
+class L2capClassicModuleFacadeModule : public ::bluetooth::grpc::GrpcFacadeModule {
+ public:
+  static const ModuleFactory Factory;
+
+  void ListDependencies(ModuleList* list) override;
+  void Start() override;
+  void Stop() override;
+
+  ::grpc::Service* GetService() const override;
+
+ private:
+  L2capClassicModuleFacadeService* service_;
+};
+
+}  // namespace classic
+}  // namespace l2cap
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/l2cap/classic/facade.proto b/gd/l2cap/classic/facade.proto
new file mode 100644
index 0000000..b5faf03
--- /dev/null
+++ b/gd/l2cap/classic/facade.proto
@@ -0,0 +1,97 @@
+syntax = "proto3";
+
+package bluetooth.l2cap.classic;
+
+import "google/protobuf/empty.proto";
+import "facade/common.proto";
+
+service L2capClassicModuleFacade {
+  rpc RegisterChannel(RegisterChannelRequest) returns (google.protobuf.Empty) {
+    // Testing Android Bluetooth stack only. Optional for other stack.
+  }
+  rpc FetchConnectionComplete(google.protobuf.Empty) returns (stream ConnectionCompleteEvent) {
+    // Testing Android Bluetooth stack only. Optional for other stack.
+  }
+  rpc FetchConnectionClose(google.protobuf.Empty) returns (stream ConnectionCloseEvent) {
+    // Testing Android Bluetooth stack only. Optional for other stack.
+  }
+  rpc Connect(facade.BluetoothAddress) returns (google.protobuf.Empty) {}
+  rpc OpenChannel(OpenChannelRequest) returns (google.protobuf.Empty) {}
+  rpc CloseChannel(CloseChannelRequest) returns (google.protobuf.Empty) {}
+  rpc ConfigureChannel(ConfigureChannelRequest) returns (google.protobuf.Empty) {}
+  rpc SendL2capPacket(L2capPacket) returns (SendL2capPacketResult) {}
+  rpc FetchL2capData(google.protobuf.Empty) returns (stream L2capPacket) {}
+  rpc SetDynamicChannel(SetEnableDynamicChannelRequest) returns (google.protobuf.Empty) {}
+  rpc SendDynamicChannelPacket(DynamicChannelPacket) returns (google.protobuf.Empty) {}
+}
+
+message RegisterChannelRequest {
+  uint32 channel = 1;
+}
+
+message ConnectionCompleteEvent {
+  facade.BluetoothAddress remote = 1;
+}
+
+message ConnectionCloseEvent {
+  facade.BluetoothAddress remote = 1;
+  uint32 reason = 2;
+}
+
+enum RetransmissionFlowControlMode {
+  BASIC = 0;
+  ERTM = 3;
+}
+
+message OpenChannelRequest {
+  facade.BluetoothAddress remote = 1;
+  uint32 psm = 2;
+  RetransmissionFlowControlMode mode = 3;
+}
+
+message ConfigureChannelRequest {
+  facade.BluetoothAddress remote = 1;
+  // Config
+}
+
+message CloseChannelRequest {
+  uint32 psm = 1;
+}
+
+enum ChannelSignalEventType {
+  OPEN = 0;
+  CLOSE = 1;
+  CONFIGURE = 2;
+}
+
+message ChannelSignalEvent {
+  uint32 cid = 1;
+  ChannelSignalEventType type = 2;
+}
+
+enum SendL2capPacketResultType {
+  OK = 0;
+  BAD_CID = 1;
+}
+
+message SendL2capPacketResult {
+  SendL2capPacketResultType result_type = 1;
+}
+
+message L2capPacket {
+  facade.BluetoothAddress remote = 1;
+  uint32 channel = 2;
+  bytes payload = 3;
+}
+
+message SetEnableDynamicChannelRequest {
+  uint32 psm = 1;
+  bool enable = 2;
+  RetransmissionFlowControlMode retransmission_mode = 3;
+}
+
+message DynamicChannelPacket {
+  facade.BluetoothAddress remote = 1;
+  uint32 psm = 2;
+  bytes payload = 3;
+}
diff --git a/gd/l2cap/classic/fixed_channel.cc b/gd/l2cap/classic/fixed_channel.cc
new file mode 100644
index 0000000..a90d119
--- /dev/null
+++ b/gd/l2cap/classic/fixed_channel.cc
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "l2cap/classic/fixed_channel.h"
+#include "common/bind.h"
+#include "l2cap/classic/internal/fixed_channel_impl.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace classic {
+
+hci::Address FixedChannel::GetDevice() const {
+  return impl_->GetDevice();
+}
+
+void FixedChannel::RegisterOnCloseCallback(os::Handler* user_handler, FixedChannel::OnCloseCallback on_close_callback) {
+  l2cap_handler_->Post(common::BindOnce(&internal::FixedChannelImpl::RegisterOnCloseCallback, impl_, user_handler,
+                                        std::move(on_close_callback)));
+}
+
+void FixedChannel::Acquire() {
+  l2cap_handler_->Post(common::BindOnce(&internal::FixedChannelImpl::Acquire, impl_));
+}
+
+void FixedChannel::Release() {
+  l2cap_handler_->Post(common::BindOnce(&internal::FixedChannelImpl::Release, impl_));
+}
+
+common::BidiQueueEnd<packet::BasePacketBuilder, packet::PacketView<packet::kLittleEndian>>*
+FixedChannel::GetQueueUpEnd() const {
+  return impl_->GetQueueUpEnd();
+}
+}  // namespace classic
+}  // namespace l2cap
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/l2cap/classic/fixed_channel.h b/gd/l2cap/classic/fixed_channel.h
new file mode 100644
index 0000000..bf88e3d
--- /dev/null
+++ b/gd/l2cap/classic/fixed_channel.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include "common/bidi_queue.h"
+#include "common/callback.h"
+#include "hci/acl_manager.h"
+#include "l2cap/cid.h"
+#include "os/handler.h"
+#include "packet/base_packet_builder.h"
+#include "packet/packet_view.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace classic {
+
+namespace internal {
+class FixedChannelImpl;
+}  // namespace internal
+
+/**
+ * L2CAP fixed channel object. When a new object is created, it must be
+ * acquired through calling {@link FixedChannel#Acquire()} within X seconds.
+ * Otherwise, {@link FixedChannel#Release()} will be called automatically.
+ *
+ */
+class FixedChannel {
+ public:
+  // Should only be constructed by modules that have access to LinkManager
+  FixedChannel(std::shared_ptr<internal::FixedChannelImpl> impl, os::Handler* l2cap_handler)
+      : impl_(std::move(impl)), l2cap_handler_(l2cap_handler) {
+    ASSERT(impl_ != nullptr);
+    ASSERT(l2cap_handler_ != nullptr);
+  }
+
+  hci::Address GetDevice() const;
+
+  /**
+   * Register close callback. If close callback is registered, when a channel is closed, the channel's resource will
+   * only be freed after on_close callback is invoked. Otherwise, if no on_close callback is registered, the channel's
+   * resource will be freed immediately after closing.
+   *
+   * @param user_handler The handler used to invoke the callback on
+   * @param on_close_callback The callback invoked upon channel closing.
+   */
+  using OnCloseCallback = common::OnceCallback<void(hci::ErrorCode)>;
+  void RegisterOnCloseCallback(os::Handler* user_handler, OnCloseCallback on_close_callback);
+
+  /**
+   * Indicate that this Fixed Channel is being used. This will prevent ACL connection from being disconnected.
+   */
+  void Acquire();
+
+  /**
+   * Indicate that this Fixed Channel is no longer being used. ACL connection will be disconnected after
+   * kLinkIdleDisconnectTimeout if no other DynamicChannel is connected or no other Fixed Channel is  using this
+   * ACL connection. However a module can still receive data on this channel as long as it remains open.
+   */
+  void Release();
+
+  /**
+   * This method will retrieve the data channel queue to send and receive packets.
+   *
+   * {@see BidiQueueEnd}
+   *
+   * @return The upper end of a bi-directional queue.
+   */
+  common::BidiQueueEnd<packet::BasePacketBuilder, packet::PacketView<packet::kLittleEndian>>* GetQueueUpEnd() const;
+
+ private:
+  std::shared_ptr<internal::FixedChannelImpl> impl_;
+  os::Handler* l2cap_handler_;
+};
+
+}  // namespace classic
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/classic/fixed_channel_manager.cc b/gd/l2cap/classic/fixed_channel_manager.cc
new file mode 100644
index 0000000..0c3e5c7
--- /dev/null
+++ b/gd/l2cap/classic/fixed_channel_manager.cc
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "l2cap/classic/fixed_channel_manager.h"
+#include "l2cap/classic/internal/fixed_channel_service_impl.h"
+#include "l2cap/classic/internal/fixed_channel_service_manager_impl.h"
+#include "l2cap/classic/internal/link_manager.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace classic {
+
+bool FixedChannelManager::ConnectServices(hci::Address device, OnConnectionFailureCallback on_fail_callback,
+                                          os::Handler* handler) {
+  internal::LinkManager::PendingFixedChannelConnection pending_fixed_channel_connection{
+      .handler_ = handler,
+      .on_fail_callback_ = std::move(on_fail_callback),
+  };
+  l2cap_layer_handler_->Post(common::BindOnce(&internal::LinkManager::ConnectFixedChannelServices,
+                                              common::Unretained(link_manager_), device,
+                                              std::move(pending_fixed_channel_connection)));
+  return true;
+}
+
+bool FixedChannelManager::RegisterService(Cid cid, const SecurityPolicy& security_policy,
+                                          OnRegistrationCompleteCallback on_registration_complete,
+                                          OnConnectionOpenCallback on_connection_open, os::Handler* handler) {
+  internal::FixedChannelServiceImpl::PendingRegistration pending_registration{
+      .user_handler_ = handler,
+      .on_registration_complete_callback_ = std::move(on_registration_complete),
+      .on_connection_open_callback_ = std::move(on_connection_open)};
+  l2cap_layer_handler_->Post(common::BindOnce(&internal::FixedChannelServiceManagerImpl::Register,
+                                              common::Unretained(service_manager_), cid,
+                                              std::move(pending_registration)));
+  return true;
+}
+
+}  // namespace classic
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/classic/fixed_channel_manager.h b/gd/l2cap/classic/fixed_channel_manager.h
new file mode 100644
index 0000000..65c0b73
--- /dev/null
+++ b/gd/l2cap/classic/fixed_channel_manager.h
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <string>
+
+#include "hci/acl_manager.h"
+#include "hci/address.h"
+#include "l2cap/cid.h"
+#include "l2cap/classic/fixed_channel.h"
+#include "l2cap/classic/fixed_channel_service.h"
+#include "l2cap/security_policy.h"
+#include "os/handler.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace classic {
+
+class L2capClassicModule;
+
+namespace testing {
+class MockFixedChannelManager;
+}
+
+namespace internal {
+class LinkManager;
+class FixedChannelServiceManagerImpl;
+}  // namespace internal
+
+class FixedChannelManager {
+ public:
+  enum class ConnectionResultCode {
+    SUCCESS = 0,
+    FAIL_NO_SERVICE_REGISTERED = 1,      // No service is registered
+    FAIL_ALL_SERVICES_HAVE_CHANNEL = 2,  // All registered services already have a channel
+    FAIL_HCI_ERROR = 3,                  // See hci_error
+  };
+
+  struct ConnectionResult {
+    ConnectionResultCode connection_result_code = ConnectionResultCode::SUCCESS;
+    hci::ErrorCode hci_error = hci::ErrorCode::SUCCESS;
+  };
+  /**
+   * OnConnectionFailureCallback(std::string failure_reason);
+   */
+  using OnConnectionFailureCallback = common::OnceCallback<void(ConnectionResult result)>;
+
+  /**
+   * OnConnectionOpenCallback(FixedChannel channel);
+   */
+  using OnConnectionOpenCallback = common::Callback<void(std::unique_ptr<FixedChannel>)>;
+
+  enum class RegistrationResult {
+    SUCCESS = 0,
+    FAIL_DUPLICATE_SERVICE = 1,  // Duplicate service registration for the same CID
+    FAIL_INVALID_SERVICE = 2,    // Invalid CID
+  };
+
+  /**
+   * OnRegistrationFailureCallback(RegistrationResult result, FixedChannelService service);
+   */
+  using OnRegistrationCompleteCallback =
+      common::OnceCallback<void(RegistrationResult, std::unique_ptr<FixedChannelService>)>;
+
+  /**
+   * Connect to ALL fixed channels on a remote device
+   *
+   * - This method is asynchronous
+   * - When false is returned, the connection fails immediately
+   * - When true is returned, method caller should wait for on_fail_callback or on_open_callback registered through
+   *   RegisterService() API.
+   * - If an ACL connection does not exist, this method will create an ACL connection. As a result, on_open_callback
+   *   supplied through RegisterService() will be triggered to provide the actual FixedChannel objects
+   * - If HCI connection failed, on_fail_callback will be triggered with FAIL_HCI_ERROR
+   * - If fixed channel on a remote device is already reported as connected via on_open_callback and has been acquired
+   *   via FixedChannel#Acquire() API, it won't be reported again
+   * - If no service is registered, on_fail_callback will be triggered with FAIL_NO_SERVICE_REGISTERED
+   * - If there is an ACL connection and channels for each service is allocated, on_fail_callback will be triggered with
+   *   FAIL_ALL_SERVICES_HAVE_CHANNEL
+   *
+   * NOTE:
+   * This call will initiate an effort to connect all fixed channel services on a remote device.
+   * Due to the connectionless nature of fixed channels, all fixed channels will be connected together.
+   * If a fixed channel service does not need a particular fixed channel. It should release the received
+   * channel immediately after receiving on_open_callback via FixedChannel#Release()
+   *
+   * A module calling ConnectServices() must have called RegisterService() before.
+   * The callback will come back from on_open_callback in the service that is registered
+   *
+   * @param device: Remote device to make this connection.
+   * @param on_fail_callback: A callback to indicate connection failure along with a status code.
+   * @param handler: The handler context in which to execute the @callback parameters.
+   *
+   * Returns: true if connection was able to be initiated, false otherwise.
+   */
+  virtual bool ConnectServices(hci::Address device, OnConnectionFailureCallback on_fail_callback, os::Handler* handler);
+
+  /**
+   * Register a service to receive incoming connections bound to a specific channel.
+   *
+   * - This method is asynchronous.
+   * - When false is returned, the registration fails immediately.
+   * - When true is returned, method caller should wait for on_service_registered callback that contains a
+   *   FixedChannelService object. The registered service can be managed from that object.
+   * - If a CID is already registered or some other error happens, on_registration_complete will be triggered with a
+   *   non-SUCCESS value
+   * - After a service is registered, any classic ACL connection will create a FixedChannel object that is
+   *   delivered through on_open_callback
+   * - on_open_callback, will only be triggered after on_service_registered callback
+   *
+   * @param cid:  cid used to receive incoming connections
+   * @param security_policy: The security policy used for the connection.
+   * @param on_registration_complete: A callback to indicate the service setup has completed. If the return status is
+   *        not SUCCESS, it means service is not registered due to reasons like CID already take
+   * @param on_open_callback: A callback to indicate success of a connection initiated from a remote device.
+   * @param handler: The handler context in which to execute the @callback parameter.
+   */
+  virtual bool RegisterService(Cid cid, const SecurityPolicy& security_policy,
+                               OnRegistrationCompleteCallback on_registration_complete,
+                               OnConnectionOpenCallback on_connection_open, os::Handler* handler);
+
+  virtual ~FixedChannelManager() = default;
+
+  friend class L2capClassicModule;
+  friend class testing::MockFixedChannelManager;
+
+ private:
+  // The constructor is not to be used by user code
+  FixedChannelManager(internal::FixedChannelServiceManagerImpl* service_manager, internal::LinkManager* link_manager,
+                      os::Handler* l2cap_layer_handler)
+      : service_manager_(service_manager), link_manager_(link_manager), l2cap_layer_handler_(l2cap_layer_handler) {}
+
+  internal::FixedChannelServiceManagerImpl* service_manager_ = nullptr;
+  internal::LinkManager* link_manager_ = nullptr;
+  os::Handler* l2cap_layer_handler_ = nullptr;
+  DISALLOW_COPY_AND_ASSIGN(FixedChannelManager);
+};
+
+}  // namespace classic
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/classic/fixed_channel_manager_mock.h b/gd/l2cap/classic/fixed_channel_manager_mock.h
new file mode 100644
index 0000000..5ead957
--- /dev/null
+++ b/gd/l2cap/classic/fixed_channel_manager_mock.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include "l2cap/classic/fixed_channel_manager.h"
+
+#include <gmock/gmock.h>
+
+// Unit test interfaces
+namespace bluetooth {
+namespace l2cap {
+namespace classic {
+namespace testing {
+
+class MockFixedChannelManager : public FixedChannelManager {
+ public:
+  MockFixedChannelManager() : FixedChannelManager(nullptr, nullptr, nullptr){};
+  MOCK_METHOD(bool, ConnectServices,
+              (hci::Address device, OnConnectionFailureCallback on_fail_callback, os::Handler* handler), (override));
+  MOCK_METHOD(bool, RegisterService,
+              (Cid cid, const SecurityPolicy& security_policy, OnRegistrationCompleteCallback on_registration_complete,
+               OnConnectionOpenCallback on_connection_open, os::Handler* handler),
+              (override));
+};
+
+}  // namespace testing
+}  // namespace classic
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/classic/fixed_channel_mock.h b/gd/l2cap/classic/fixed_channel_mock.h
new file mode 100644
index 0000000..417e776
--- /dev/null
+++ b/gd/l2cap/classic/fixed_channel_mock.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include "l2cap/classic/fixed_channel.h"
+#include "l2cap/classic/internal/fixed_channel_impl_mock.h"
+#include "os/handler.h"
+
+#include <gmock/gmock.h>
+
+#include <utility>
+
+// Unit test interfaces
+namespace bluetooth {
+namespace l2cap {
+namespace classic {
+namespace testing {
+
+class MockFixedChannel : public FixedChannel {
+ public:
+  MockFixedChannel() : FixedChannel(nullptr, nullptr){};
+  MOCK_METHOD(void, Acquire, ());
+  MOCK_METHOD(void, Release, ());
+  MOCK_METHOD(void, RegisterOnCloseCallback, (os::Handler * handler, OnCloseCallback on_close_callback));
+};
+
+}  // namespace testing
+}  // namespace classic
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/classic/fixed_channel_service.cc b/gd/l2cap/classic/fixed_channel_service.cc
new file mode 100644
index 0000000..49a977d
--- /dev/null
+++ b/gd/l2cap/classic/fixed_channel_service.cc
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "l2cap/classic/fixed_channel_service.h"
+#include "common/bind.h"
+#include "l2cap/classic/internal/fixed_channel_service_manager_impl.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace classic {
+
+void FixedChannelService::Unregister(OnUnregisteredCallback on_unregistered, os::Handler* on_unregistered_handler) {
+  ASSERT_LOG(manager_ != nullptr, "this service is invalid");
+  l2cap_layer_handler_->Post(common::BindOnce(&internal::FixedChannelServiceManagerImpl::Unregister,
+                                              common::Unretained(manager_), cid_, std::move(on_unregistered),
+                                              on_unregistered_handler));
+}
+
+}  // namespace classic
+}  // namespace l2cap
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/l2cap/classic/fixed_channel_service.h b/gd/l2cap/classic/fixed_channel_service.h
new file mode 100644
index 0000000..d5d213b
--- /dev/null
+++ b/gd/l2cap/classic/fixed_channel_service.h
@@ -0,0 +1,59 @@
+
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include "common/callback.h"
+#include "hci/address.h"
+#include "l2cap/cid.h"
+#include "os/handler.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace classic {
+
+namespace internal {
+class FixedChannelServiceManagerImpl;
+}  // namespace internal
+
+class FixedChannelService {
+ public:
+  FixedChannelService() = default;
+
+  using OnUnregisteredCallback = common::OnceCallback<void()>;
+
+  /**
+   * Unregister a service from L2CAP module. This operation cannot fail.
+   * All channels opened for this service will be invalidated.
+   *
+   * @param on_unregistered will be triggered when unregistration is complete
+   */
+  void Unregister(OnUnregisteredCallback on_unregistered, os::Handler* on_unregistered_handler);
+
+  friend internal::FixedChannelServiceManagerImpl;
+
+ private:
+  FixedChannelService(Cid cid, internal::FixedChannelServiceManagerImpl* manager, os::Handler* handler)
+      : cid_(cid), manager_(manager), l2cap_layer_handler_(handler) {}
+  Cid cid_ = kInvalidCid;
+  internal::FixedChannelServiceManagerImpl* manager_ = nullptr;
+  os::Handler* l2cap_layer_handler_;
+  DISALLOW_COPY_AND_ASSIGN(FixedChannelService);
+};
+
+}  // namespace classic
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/classic/fixed_channel_service_mock.h b/gd/l2cap/classic/fixed_channel_service_mock.h
new file mode 100644
index 0000000..6695cd1
--- /dev/null
+++ b/gd/l2cap/classic/fixed_channel_service_mock.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include "l2cap/classic/fixed_channel_service.h"
+
+#include <gmock/gmock.h>
+
+// Unit test interfaces
+namespace bluetooth {
+namespace l2cap {
+namespace classic {
+namespace testing {
+
+class MockFixedChannelService : public FixedChannelService {
+ public:
+  MockFixedChannelService() : FixedChannelService(){};
+  MOCK_METHOD(void, Unregister, (OnUnregisteredCallback on_unregistered, os::Handler* on_unregistered_handler));
+};
+
+}  // namespace testing
+}  // namespace classic
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/classic/internal/channel_configuration_state.h b/gd/l2cap/classic/internal/channel_configuration_state.h
new file mode 100644
index 0000000..b0dead5
--- /dev/null
+++ b/gd/l2cap/classic/internal/channel_configuration_state.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "l2cap/l2cap_packets.h"
+#include "l2cap/mtu.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace classic {
+namespace internal {
+
+struct ChannelConfigurationState {
+ public:
+  enum State {
+    /**
+     * for the initiator path, a request has been sent but a positive response has not yet been received, and for the
+     * acceptor path, a request with acceptable options has not yet been received.
+     */
+    WAIT_CONFIG_REQ_RSP,
+    /**
+     * the acceptor path is complete after having responded to acceptable options, but for the initiator path, a
+     * positive response on the recent request has not yet been received.
+     */
+    WAIT_CONFIG_RSP,
+    /**
+     * the initiator path is complete after having received a positive response, but for the acceptor path, a request
+     * with acceptable options has not yet been received.
+     */
+    WAIT_CONFIG_REQ,
+    /**
+     * Configuration is complete
+     */
+    CONFIGURED,
+  };
+  State state_ = State::WAIT_CONFIG_REQ_RSP;
+
+  Mtu incoming_mtu_ = kDefaultClassicMtu;
+  Mtu outgoing_mtu_ = kDefaultClassicMtu;
+  RetransmissionAndFlowControlModeOption retransmission_and_flow_control_mode_;
+  RetransmissionAndFlowControlConfigurationOption local_retransmission_and_flow_control_;
+  RetransmissionAndFlowControlConfigurationOption remote_retransmission_and_flow_control_;
+  FcsType fcs_type_ = FcsType::DEFAULT;
+};
+}  // namespace internal
+}  // namespace classic
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/classic/internal/dynamic_channel_service_impl.h b/gd/l2cap/classic/internal/dynamic_channel_service_impl.h
new file mode 100644
index 0000000..3aae9ba
--- /dev/null
+++ b/gd/l2cap/classic/internal/dynamic_channel_service_impl.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "common/bind.h"
+
+#include "l2cap/classic/dynamic_channel.h"
+#include "l2cap/classic/dynamic_channel_configuration_option.h"
+#include "l2cap/classic/dynamic_channel_manager.h"
+#include "l2cap/classic/dynamic_channel_service.h"
+#include "l2cap/security_policy.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace classic {
+namespace internal {
+class DynamicChannelServiceImpl {
+ public:
+  virtual ~DynamicChannelServiceImpl() = default;
+
+  struct PendingRegistration {
+    os::Handler* user_handler_ = nullptr;
+    SecurityPolicy security_policy_;
+    DynamicChannelManager::OnRegistrationCompleteCallback on_registration_complete_callback_;
+    DynamicChannelManager::OnConnectionOpenCallback on_connection_open_callback_;
+    DynamicChannelConfigurationOption configuration_;
+  };
+
+  virtual void NotifyChannelCreation(std::unique_ptr<DynamicChannel> channel) {
+    user_handler_->Post(common::BindOnce(on_connection_open_callback_, std::move(channel)));
+  }
+
+  DynamicChannelConfigurationOption GetConfigOption() const {
+    return config_option_;
+  }
+
+  SecurityPolicy GetSecurityPolicy() const {
+    return security_policy_;
+  }
+
+  friend class DynamicChannelServiceManagerImpl;
+
+ protected:
+  // protected access for mocking
+  DynamicChannelServiceImpl(os::Handler* user_handler,
+                            SecurityPolicy security_policy,
+                            DynamicChannelManager::OnConnectionOpenCallback on_connection_open_callback,
+                            DynamicChannelConfigurationOption config_option)
+      : user_handler_(user_handler), security_policy_(security_policy), on_connection_open_callback_(std::move(on_connection_open_callback)),
+        config_option_(config_option) {}
+
+ private:
+  os::Handler* user_handler_ = nullptr;
+  SecurityPolicy security_policy_;
+  DynamicChannelManager::OnConnectionOpenCallback on_connection_open_callback_;
+  DynamicChannelConfigurationOption config_option_;
+};
+
+}  // namespace internal
+}  // namespace classic
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/classic/internal/dynamic_channel_service_manager_impl.cc b/gd/l2cap/classic/internal/dynamic_channel_service_manager_impl.cc
new file mode 100644
index 0000000..ad48ef4
--- /dev/null
+++ b/gd/l2cap/classic/internal/dynamic_channel_service_manager_impl.cc
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "l2cap/classic/internal/dynamic_channel_service_manager_impl.h"
+#include "common/bind.h"
+#include "l2cap/classic/internal/dynamic_channel_service_impl.h"
+#include "l2cap/psm.h"
+#include "os/log.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace classic {
+namespace internal {
+
+void DynamicChannelServiceManagerImpl::Register(Psm psm,
+                                                DynamicChannelServiceImpl::PendingRegistration pending_registration) {
+  if (!IsPsmValid(psm)) {
+    std::unique_ptr<DynamicChannelService> invalid_service(new DynamicChannelService());
+    pending_registration.user_handler_->Post(
+        common::BindOnce(std::move(pending_registration.on_registration_complete_callback_),
+                         DynamicChannelManager::RegistrationResult::FAIL_INVALID_SERVICE, std::move(invalid_service)));
+  } else if (IsServiceRegistered(psm)) {
+    std::unique_ptr<DynamicChannelService> invalid_service(new DynamicChannelService());
+    pending_registration.user_handler_->Post(common::BindOnce(
+        std::move(pending_registration.on_registration_complete_callback_),
+        DynamicChannelManager::RegistrationResult::FAIL_DUPLICATE_SERVICE, std::move(invalid_service)));
+  } else {
+    service_map_.try_emplace(psm,
+                             DynamicChannelServiceImpl(pending_registration.user_handler_,
+                                                       pending_registration.security_policy_,
+                                                       std::move(pending_registration.on_connection_open_callback_),
+                                                       pending_registration.configuration_));
+    std::unique_ptr<DynamicChannelService> user_service(new DynamicChannelService(psm, this, l2cap_layer_handler_));
+    pending_registration.user_handler_->Post(
+        common::BindOnce(std::move(pending_registration.on_registration_complete_callback_),
+                         DynamicChannelManager::RegistrationResult::SUCCESS, std::move(user_service)));
+  }
+}
+
+void DynamicChannelServiceManagerImpl::Unregister(Psm psm, DynamicChannelService::OnUnregisteredCallback callback,
+                                                  os::Handler* handler) {
+  if (IsServiceRegistered(psm)) {
+    service_map_.erase(psm);
+    handler->Post(std::move(callback));
+  } else {
+    LOG_ERROR("service not registered psm:%d", psm);
+  }
+}
+
+bool DynamicChannelServiceManagerImpl::IsServiceRegistered(Psm psm) const {
+  return service_map_.find(psm) != service_map_.end();
+}
+
+DynamicChannelServiceImpl* DynamicChannelServiceManagerImpl::GetService(Psm psm) {
+  ASSERT(IsServiceRegistered(psm));
+  return &service_map_.find(psm)->second;
+}
+
+std::vector<std::pair<Psm, DynamicChannelServiceImpl*>> DynamicChannelServiceManagerImpl::GetRegisteredServices() {
+  std::vector<std::pair<Psm, DynamicChannelServiceImpl*>> results;
+  for (auto& elem : service_map_) {
+    results.emplace_back(elem.first, &elem.second);
+  }
+  return results;
+}
+
+}  // namespace internal
+}  // namespace classic
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/classic/internal/dynamic_channel_service_manager_impl.h b/gd/l2cap/classic/internal/dynamic_channel_service_manager_impl.h
new file mode 100644
index 0000000..c980639
--- /dev/null
+++ b/gd/l2cap/classic/internal/dynamic_channel_service_manager_impl.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <unordered_map>
+
+#include "l2cap/classic/dynamic_channel_service.h"
+#include "l2cap/classic/internal/dynamic_channel_service_impl.h"
+#include "l2cap/psm.h"
+#include "os/handler.h"
+#include "os/log.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace classic {
+namespace internal {
+
+class DynamicChannelServiceManagerImpl {
+ public:
+  explicit DynamicChannelServiceManagerImpl(os::Handler* l2cap_layer_handler)
+      : l2cap_layer_handler_(l2cap_layer_handler) {}
+
+  virtual ~DynamicChannelServiceManagerImpl() = default;
+  //
+  // All APIs must be invoked in L2CAP layer handler
+  //
+  virtual void Register(Psm psm, DynamicChannelServiceImpl::PendingRegistration pending_registration);
+  virtual void Unregister(Psm psm, DynamicChannelService::OnUnregisteredCallback callback, os::Handler* handler);
+  virtual bool IsServiceRegistered(Psm psm) const;
+  virtual DynamicChannelServiceImpl* GetService(Psm psm);
+
+  virtual std::vector<std::pair<Psm, DynamicChannelServiceImpl*>> GetRegisteredServices();
+ private:
+  os::Handler* l2cap_layer_handler_ = nullptr;
+  std::unordered_map<Psm, DynamicChannelServiceImpl> service_map_;
+};
+}  // namespace internal
+}  // namespace classic
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/classic/internal/dynamic_channel_service_manager_impl_mock.h b/gd/l2cap/classic/internal/dynamic_channel_service_manager_impl_mock.h
new file mode 100644
index 0000000..05d4b7f
--- /dev/null
+++ b/gd/l2cap/classic/internal/dynamic_channel_service_manager_impl_mock.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include "l2cap/classic/internal/dynamic_channel_service_manager_impl.h"
+#include "l2cap/internal/dynamic_channel_impl.h"
+
+#include <gmock/gmock.h>
+
+// Unit test interfaces
+namespace bluetooth {
+namespace l2cap {
+namespace classic {
+namespace internal {
+namespace testing {
+
+class MockDynamicChannelServiceManagerImpl : public DynamicChannelServiceManagerImpl {
+ public:
+  MockDynamicChannelServiceManagerImpl() : DynamicChannelServiceManagerImpl(nullptr) {}
+  MOCK_METHOD(void, Register, (Psm psm, DynamicChannelServiceImpl::PendingRegistration pending_registration),
+              (override));
+  MOCK_METHOD(void, Unregister, (Psm psm, DynamicChannelService::OnUnregisteredCallback callback, os::Handler* handler),
+              (override));
+  MOCK_METHOD(bool, IsServiceRegistered, (Psm psm), (const, override));
+  MOCK_METHOD(DynamicChannelServiceImpl*, GetService, (Psm psm), (override));
+  MOCK_METHOD((std::vector<std::pair<Psm, DynamicChannelServiceImpl*>>), GetRegisteredServices, (), (override));
+};
+
+}  // namespace testing
+}  // namespace internal
+}  // namespace classic
+}  // namespace l2cap
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/l2cap/classic/internal/dynamic_channel_service_manager_test.cc b/gd/l2cap/classic/internal/dynamic_channel_service_manager_test.cc
new file mode 100644
index 0000000..4cf32c6
--- /dev/null
+++ b/gd/l2cap/classic/internal/dynamic_channel_service_manager_test.cc
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <future>
+
+#include <gtest/gtest.h>
+
+#include "common/bind.h"
+#include "l2cap/cid.h"
+#include "l2cap/classic/dynamic_channel_manager.h"
+#include "l2cap/classic/dynamic_channel_service.h"
+#include "l2cap/classic/internal/dynamic_channel_service_manager_impl.h"
+#include "os/handler.h"
+#include "os/thread.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace classic {
+namespace internal {
+namespace {
+
+class L2capDynamicServiceManagerTest : public ::testing::Test {
+ public:
+  ~L2capDynamicServiceManagerTest() override = default;
+
+  void OnServiceRegistered(bool expect_success, DynamicChannelManager::RegistrationResult result,
+                           std::unique_ptr<DynamicChannelService> user_service) {
+    EXPECT_EQ(result == DynamicChannelManager::RegistrationResult::SUCCESS, expect_success);
+    service_registered_ = expect_success;
+  }
+
+ protected:
+  void SetUp() override {
+    thread_ = new os::Thread("test_thread", os::Thread::Priority::NORMAL);
+    user_handler_ = new os::Handler(thread_);
+    l2cap_handler_ = new os::Handler(thread_);
+    manager_ = new DynamicChannelServiceManagerImpl{l2cap_handler_};
+  }
+
+  void TearDown() override {
+    delete manager_;
+    l2cap_handler_->Clear();
+    delete l2cap_handler_;
+    user_handler_->Clear();
+    delete user_handler_;
+    delete thread_;
+  }
+
+  void sync_user_handler() {
+    std::promise<void> promise;
+    auto future = promise.get_future();
+    user_handler_->Post(common::BindOnce(&std::promise<void>::set_value, common::Unretained(&promise)));
+    auto future_status = future.wait_for(std::chrono::seconds(1));
+    EXPECT_EQ(future_status, std::future_status::ready);
+  }
+
+  DynamicChannelServiceManagerImpl* manager_ = nullptr;
+  os::Thread* thread_ = nullptr;
+  os::Handler* user_handler_ = nullptr;
+  os::Handler* l2cap_handler_ = nullptr;
+
+  bool service_registered_ = false;
+};
+
+TEST_F(L2capDynamicServiceManagerTest, register_and_unregister_classic_dynamic_channel) {
+  DynamicChannelServiceImpl::PendingRegistration pending_registration{
+      .user_handler_ = user_handler_,
+      .on_registration_complete_callback_ =
+          common::BindOnce(&L2capDynamicServiceManagerTest::OnServiceRegistered, common::Unretained(this), true)};
+  Cid cid = kSmpBrCid;
+  EXPECT_FALSE(manager_->IsServiceRegistered(cid));
+  manager_->Register(cid, std::move(pending_registration));
+  EXPECT_TRUE(manager_->IsServiceRegistered(cid));
+  sync_user_handler();
+  EXPECT_TRUE(service_registered_);
+  manager_->Unregister(cid, common::BindOnce([] {}), user_handler_);
+  EXPECT_FALSE(manager_->IsServiceRegistered(cid));
+}
+
+TEST_F(L2capDynamicServiceManagerTest, register_classic_dynamic_channel_bad_cid) {
+  DynamicChannelServiceImpl::PendingRegistration pending_registration{
+      .user_handler_ = user_handler_,
+      .on_registration_complete_callback_ =
+          common::BindOnce(&L2capDynamicServiceManagerTest::OnServiceRegistered, common::Unretained(this), false)};
+  Cid cid = 0x1000;
+  EXPECT_FALSE(manager_->IsServiceRegistered(cid));
+  manager_->Register(cid, std::move(pending_registration));
+  EXPECT_FALSE(manager_->IsServiceRegistered(cid));
+  sync_user_handler();
+  EXPECT_FALSE(service_registered_);
+}
+
+}  // namespace
+}  // namespace internal
+}  // namespace classic
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/classic/internal/fixed_channel_impl.cc b/gd/l2cap/classic/internal/fixed_channel_impl.cc
new file mode 100644
index 0000000..9bfaef8
--- /dev/null
+++ b/gd/l2cap/classic/internal/fixed_channel_impl.cc
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <unordered_map>
+
+#include "l2cap/cid.h"
+#include "l2cap/classic/internal/fixed_channel_impl.h"
+#include "l2cap/classic/internal/link.h"
+#include "l2cap/security_policy.h"
+#include "os/handler.h"
+#include "os/log.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace classic {
+namespace internal {
+
+FixedChannelImpl::FixedChannelImpl(Cid cid, Link* link, os::Handler* l2cap_handler)
+    : cid_(cid), device_(link->GetDevice()), link_(link), l2cap_handler_(l2cap_handler) {
+  ASSERT_LOG(cid_ >= kFirstFixedChannel && cid_ <= kLastFixedChannel, "Invalid cid: %d", cid_);
+  ASSERT(link_ != nullptr);
+  ASSERT(l2cap_handler_ != nullptr);
+}
+
+void FixedChannelImpl::RegisterOnCloseCallback(os::Handler* user_handler,
+                                               FixedChannel::OnCloseCallback on_close_callback) {
+  ASSERT_LOG(user_handler_ == nullptr, "OnCloseCallback can only be registered once");
+  // If channel is already closed, call the callback immediately without saving it
+  if (closed_) {
+    user_handler->Post(common::BindOnce(std::move(on_close_callback), close_reason_));
+    return;
+  }
+  user_handler_ = user_handler;
+  on_close_callback_ = std::move(on_close_callback);
+}
+
+void FixedChannelImpl::OnClosed(hci::ErrorCode status) {
+  ASSERT_LOG(!closed_, "Device %s Cid 0x%x closed twice, old status 0x%x, new status 0x%x", device_.ToString().c_str(),
+             cid_, static_cast<int>(close_reason_), static_cast<int>(status));
+  closed_ = true;
+  close_reason_ = status;
+  acquired_ = false;
+  link_ = nullptr;
+  l2cap_handler_ = nullptr;
+  if (user_handler_ == nullptr) {
+    return;
+  }
+  // On close callback can only be called once
+  user_handler_->Post(common::BindOnce(std::move(on_close_callback_), status));
+  user_handler_ = nullptr;
+  on_close_callback_.Reset();
+}
+
+void FixedChannelImpl::Acquire() {
+  ASSERT_LOG(user_handler_ != nullptr, "Must register OnCloseCallback before calling any methods");
+  if (closed_) {
+    LOG_WARN("%s is already closed", ToString().c_str());
+    ASSERT(!acquired_);
+    return;
+  }
+  if (acquired_) {
+    LOG_DEBUG("%s was already acquired", ToString().c_str());
+    return;
+  }
+  acquired_ = true;
+  link_->RefreshRefCount();
+}
+
+void FixedChannelImpl::Release() {
+  ASSERT_LOG(user_handler_ != nullptr, "Must register OnCloseCallback before calling any methods");
+  if (closed_) {
+    LOG_WARN("%s is already closed", ToString().c_str());
+    ASSERT(!acquired_);
+    return;
+  }
+  if (!acquired_) {
+    LOG_DEBUG("%s was already released", ToString().c_str());
+    return;
+  }
+  acquired_ = false;
+  link_->RefreshRefCount();
+}
+
+}  // namespace internal
+}  // namespace classic
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/classic/internal/fixed_channel_impl.h b/gd/l2cap/classic/internal/fixed_channel_impl.h
new file mode 100644
index 0000000..4c4e9f1
--- /dev/null
+++ b/gd/l2cap/classic/internal/fixed_channel_impl.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "common/bidi_queue.h"
+#include "l2cap/cid.h"
+#include "l2cap/classic/fixed_channel.h"
+#include "l2cap/internal/channel_impl.h"
+#include "l2cap/l2cap_packets.h"
+#include "os/handler.h"
+#include "os/log.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace classic {
+namespace internal {
+
+class Link;
+
+class FixedChannelImpl : public l2cap::internal::ChannelImpl {
+ public:
+  FixedChannelImpl(Cid cid, Link* link, os::Handler* l2cap_handler);
+
+  virtual ~FixedChannelImpl() = default;
+
+  hci::Address GetDevice() const {
+    return device_.GetAddress();
+  }
+
+  virtual void RegisterOnCloseCallback(os::Handler* user_handler, FixedChannel::OnCloseCallback on_close_callback);
+
+  virtual void Acquire();
+
+  virtual void Release();
+
+  virtual bool IsAcquired() const {
+    return acquired_;
+  }
+
+  virtual void OnClosed(hci::ErrorCode status);
+
+  virtual std::string ToString() {
+    std::ostringstream ss;
+    ss << "Device " << device_ << " Cid 0x" << std::hex << cid_;
+    return ss.str();
+  }
+
+  common::BidiQueueEnd<packet::BasePacketBuilder, packet::PacketView<packet::kLittleEndian>>* GetQueueUpEnd() {
+    return channel_queue_.GetUpEnd();
+  }
+
+  common::BidiQueueEnd<packet::PacketView<packet::kLittleEndian>, packet::BasePacketBuilder>* GetQueueDownEnd() {
+    return channel_queue_.GetDownEnd();
+  }
+
+  Cid GetCid() const {
+    return cid_;
+  }
+
+  Cid GetRemoteCid() const {
+    return cid_;
+  }
+
+ private:
+  // Constructor states
+  // For logging purpose only
+  const Cid cid_;
+  // For logging purpose only
+  const hci::AddressWithType device_;
+  // Needed to handle Acquire() and Release()
+  Link* link_;
+  os::Handler* l2cap_handler_;
+
+  // User supported states
+  os::Handler* user_handler_ = nullptr;
+  FixedChannel::OnCloseCallback on_close_callback_{};
+
+  // Internal states
+  bool acquired_ = false;
+  bool closed_ = false;
+  hci::ErrorCode close_reason_ = hci::ErrorCode::SUCCESS;
+  static constexpr size_t kChannelQueueSize = 10;
+  common::BidiQueue<packet::PacketView<packet::kLittleEndian>, packet::BasePacketBuilder> channel_queue_{
+      kChannelQueueSize};
+
+  DISALLOW_COPY_AND_ASSIGN(FixedChannelImpl);
+};
+
+}  // namespace internal
+}  // namespace classic
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/classic/internal/fixed_channel_impl_mock.h b/gd/l2cap/classic/internal/fixed_channel_impl_mock.h
new file mode 100644
index 0000000..680bf51
--- /dev/null
+++ b/gd/l2cap/classic/internal/fixed_channel_impl_mock.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include "l2cap/classic/internal/fixed_channel_impl.h"
+
+#include <gmock/gmock.h>
+
+// Unit test interfaces
+namespace bluetooth {
+namespace l2cap {
+namespace classic {
+namespace internal {
+namespace testing {
+
+class MockFixedChannelImpl : public FixedChannelImpl {
+ public:
+  MockFixedChannelImpl(Cid cid, Link* link, os::Handler* l2cap_handler) : FixedChannelImpl(cid, link, l2cap_handler) {}
+  MOCK_METHOD(void, RegisterOnCloseCallback,
+              (os::Handler * user_handler, FixedChannel::OnCloseCallback on_close_callback), (override));
+  MOCK_METHOD(void, Acquire, (), (override));
+  MOCK_METHOD(void, Release, (), (override));
+  MOCK_METHOD(bool, IsAcquired, (), (override, const));
+  MOCK_METHOD(void, OnClosed, (hci::ErrorCode status), (override));
+};
+
+}  // namespace testing
+}  // namespace internal
+}  // namespace classic
+}  // namespace l2cap
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/l2cap/classic/internal/fixed_channel_impl_test.cc b/gd/l2cap/classic/internal/fixed_channel_impl_test.cc
new file mode 100644
index 0000000..95817b7
--- /dev/null
+++ b/gd/l2cap/classic/internal/fixed_channel_impl_test.cc
@@ -0,0 +1,328 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "l2cap/classic/internal/fixed_channel_impl.h"
+
+#include "common/testing/bind_test_util.h"
+#include "l2cap/cid.h"
+#include "l2cap/classic/internal/link_mock.h"
+#include "l2cap/internal/parameter_provider_mock.h"
+#include "os/handler.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace bluetooth {
+namespace l2cap {
+namespace classic {
+namespace internal {
+
+using l2cap::internal::testing::MockParameterProvider;
+using ::testing::_;
+using testing::MockLink;
+using ::testing::Return;
+
+class L2capClassicFixedChannelImplTest : public ::testing::Test {
+ public:
+  static void SyncHandler(os::Handler* handler) {
+    std::promise<void> promise;
+    auto future = promise.get_future();
+    handler->Post(common::BindOnce(&std::promise<void>::set_value, common::Unretained(&promise)));
+    future.wait_for(std::chrono::seconds(1));
+  }
+
+ protected:
+  void SetUp() override {
+    thread_ = new os::Thread("test_thread", os::Thread::Priority::NORMAL);
+    l2cap_handler_ = new os::Handler(thread_);
+  }
+
+  void TearDown() override {
+    l2cap_handler_->Clear();
+    delete l2cap_handler_;
+    delete thread_;
+  }
+
+  os::Thread* thread_ = nullptr;
+  os::Handler* l2cap_handler_ = nullptr;
+};
+
+TEST_F(L2capClassicFixedChannelImplTest, get_device) {
+  MockParameterProvider mock_parameter_provider;
+  EXPECT_CALL(mock_parameter_provider, GetClassicLinkIdleDisconnectTimeout())
+      .WillRepeatedly(Return(std::chrono::seconds(5)));
+  testing::MockAclConnection* mock_acl_connection = new testing::MockAclConnection();
+  EXPECT_CALL(*mock_acl_connection, GetAddress()).Times(1);
+  EXPECT_CALL(*mock_acl_connection, GetAddressType()).Times(1);
+  EXPECT_CALL(*mock_acl_connection, RegisterCallbacks(_, l2cap_handler_)).Times(1);
+  EXPECT_CALL(*mock_acl_connection, UnregisterCallbacks(_)).Times(1);
+  MockLink mock_classic_link(l2cap_handler_, &mock_parameter_provider,
+                             std::unique_ptr<testing::MockAclConnection>(mock_acl_connection));
+  hci::AddressWithType device{hci::Address{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}},
+                              hci::AddressType::PUBLIC_IDENTITY_ADDRESS};
+  EXPECT_CALL(mock_classic_link, GetDevice()).WillRepeatedly(Return(device));
+  FixedChannelImpl fixed_channel_impl(kSmpBrCid, &mock_classic_link, l2cap_handler_);
+  EXPECT_EQ(device.GetAddress(), fixed_channel_impl.GetDevice());
+}
+
+TEST_F(L2capClassicFixedChannelImplTest, close_triggers_callback) {
+  MockParameterProvider mock_parameter_provider;
+  EXPECT_CALL(mock_parameter_provider, GetClassicLinkIdleDisconnectTimeout())
+      .WillRepeatedly(Return(std::chrono::seconds(5)));
+  testing::MockAclConnection* mock_acl_connection = new testing::MockAclConnection();
+  EXPECT_CALL(*mock_acl_connection, GetAddress()).Times(1);
+  EXPECT_CALL(*mock_acl_connection, GetAddressType()).Times(1);
+  EXPECT_CALL(*mock_acl_connection, RegisterCallbacks(_, l2cap_handler_)).Times(1);
+  EXPECT_CALL(*mock_acl_connection, UnregisterCallbacks(_)).Times(1);
+  MockLink mock_classic_link(l2cap_handler_, &mock_parameter_provider,
+                             std::unique_ptr<testing::MockAclConnection>(mock_acl_connection));
+  hci::AddressWithType device{hci::Address{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}},
+                              hci::AddressType::PUBLIC_IDENTITY_ADDRESS};
+  EXPECT_CALL(mock_classic_link, GetDevice()).WillRepeatedly(Return(device));
+  FixedChannelImpl fixed_channel_impl(kSmpBrCid, &mock_classic_link, l2cap_handler_);
+
+  // Register on close callback
+  auto user_handler = std::make_unique<os::Handler>(thread_);
+  hci::ErrorCode my_status = hci::ErrorCode::SUCCESS;
+  fixed_channel_impl.RegisterOnCloseCallback(
+      user_handler.get(), common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { my_status = status; }));
+
+  // Channel closure should trigger such callback
+  fixed_channel_impl.OnClosed(hci::ErrorCode::REMOTE_USER_TERMINATED_CONNECTION);
+  SyncHandler(user_handler.get());
+  EXPECT_EQ(hci::ErrorCode::REMOTE_USER_TERMINATED_CONNECTION, my_status);
+
+  user_handler->Clear();
+}
+
+TEST_F(L2capClassicFixedChannelImplTest, register_callback_after_close_should_call_immediately) {
+  MockParameterProvider mock_parameter_provider;
+  EXPECT_CALL(mock_parameter_provider, GetClassicLinkIdleDisconnectTimeout())
+      .WillRepeatedly(Return(std::chrono::seconds(5)));
+  testing::MockAclConnection* mock_acl_connection = new testing::MockAclConnection();
+  EXPECT_CALL(*mock_acl_connection, GetAddress()).Times(1);
+  EXPECT_CALL(*mock_acl_connection, GetAddressType()).Times(1);
+  EXPECT_CALL(*mock_acl_connection, RegisterCallbacks(_, l2cap_handler_)).Times(1);
+  EXPECT_CALL(*mock_acl_connection, UnregisterCallbacks(_)).Times(1);
+  MockLink mock_classic_link(l2cap_handler_, &mock_parameter_provider,
+                             std::unique_ptr<testing::MockAclConnection>(mock_acl_connection));
+  hci::AddressWithType device{hci::Address{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}},
+                              hci::AddressType::PUBLIC_IDENTITY_ADDRESS};
+  EXPECT_CALL(mock_classic_link, GetDevice()).WillRepeatedly(Return(device));
+  FixedChannelImpl fixed_channel_impl(kSmpBrCid, &mock_classic_link, l2cap_handler_);
+
+  // Channel closure should do nothing
+  fixed_channel_impl.OnClosed(hci::ErrorCode::REMOTE_USER_TERMINATED_CONNECTION);
+
+  // Register on close callback should trigger callback immediately
+  auto user_handler = std::make_unique<os::Handler>(thread_);
+  hci::ErrorCode my_status = hci::ErrorCode::SUCCESS;
+  fixed_channel_impl.RegisterOnCloseCallback(
+      user_handler.get(), common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { my_status = status; }));
+  SyncHandler(user_handler.get());
+  EXPECT_EQ(hci::ErrorCode::REMOTE_USER_TERMINATED_CONNECTION, my_status);
+
+  user_handler->Clear();
+}
+
+TEST_F(L2capClassicFixedChannelImplTest, close_twice_should_fail) {
+  ::testing::FLAGS_gtest_death_test_style = "threadsafe";
+  MockParameterProvider mock_parameter_provider;
+  EXPECT_CALL(mock_parameter_provider, GetClassicLinkIdleDisconnectTimeout())
+      .WillRepeatedly(Return(std::chrono::seconds(5)));
+  testing::MockAclConnection* mock_acl_connection = new testing::MockAclConnection();
+  EXPECT_CALL(*mock_acl_connection, GetAddress()).Times(1);
+  EXPECT_CALL(*mock_acl_connection, GetAddressType()).Times(1);
+  EXPECT_CALL(*mock_acl_connection, RegisterCallbacks(_, l2cap_handler_)).Times(1);
+  EXPECT_CALL(*mock_acl_connection, UnregisterCallbacks(_)).Times(1);
+  MockLink mock_classic_link(l2cap_handler_, &mock_parameter_provider,
+                             std::unique_ptr<testing::MockAclConnection>(mock_acl_connection));
+  hci::AddressWithType device{hci::Address{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}},
+                              hci::AddressType::PUBLIC_IDENTITY_ADDRESS};
+  EXPECT_CALL(mock_classic_link, GetDevice()).WillRepeatedly(Return(device));
+  FixedChannelImpl fixed_channel_impl(kSmpBrCid, &mock_classic_link, l2cap_handler_);
+
+  // Register on close callback
+  auto user_handler = std::make_unique<os::Handler>(thread_);
+  hci::ErrorCode my_status = hci::ErrorCode::SUCCESS;
+  fixed_channel_impl.RegisterOnCloseCallback(
+      user_handler.get(), common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { my_status = status; }));
+
+  // Channel closure should trigger such callback
+  fixed_channel_impl.OnClosed(hci::ErrorCode::REMOTE_USER_TERMINATED_CONNECTION);
+  SyncHandler(user_handler.get());
+  EXPECT_EQ(hci::ErrorCode::REMOTE_USER_TERMINATED_CONNECTION, my_status);
+
+  // 2nd OnClose() callback should fail
+  EXPECT_DEATH(fixed_channel_impl.OnClosed(hci::ErrorCode::PAGE_TIMEOUT), ".*assertion \'!closed_\' failed.*");
+
+  user_handler->Clear();
+}
+
+TEST_F(L2capClassicFixedChannelImplTest, multiple_registration_should_fail) {
+  ::testing::FLAGS_gtest_death_test_style = "threadsafe";
+  MockParameterProvider mock_parameter_provider;
+  EXPECT_CALL(mock_parameter_provider, GetClassicLinkIdleDisconnectTimeout())
+      .WillRepeatedly(Return(std::chrono::seconds(5)));
+  testing::MockAclConnection* mock_acl_connection = new testing::MockAclConnection();
+  EXPECT_CALL(*mock_acl_connection, GetAddress()).Times(1);
+  EXPECT_CALL(*mock_acl_connection, GetAddressType()).Times(1);
+  EXPECT_CALL(*mock_acl_connection, RegisterCallbacks(_, l2cap_handler_)).Times(1);
+  EXPECT_CALL(*mock_acl_connection, UnregisterCallbacks(_)).Times(1);
+  MockLink mock_classic_link(l2cap_handler_, &mock_parameter_provider,
+                             std::unique_ptr<testing::MockAclConnection>(mock_acl_connection));
+  hci::AddressWithType device{hci::Address{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}},
+                              hci::AddressType::PUBLIC_IDENTITY_ADDRESS};
+  EXPECT_CALL(mock_classic_link, GetDevice()).WillRepeatedly(Return(device));
+  FixedChannelImpl fixed_channel_impl(kSmpBrCid, &mock_classic_link, l2cap_handler_);
+
+  // Register on close callback
+  auto user_handler = std::make_unique<os::Handler>(thread_);
+  hci::ErrorCode my_status = hci::ErrorCode::SUCCESS;
+  fixed_channel_impl.RegisterOnCloseCallback(
+      user_handler.get(), common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { my_status = status; }));
+
+  EXPECT_DEATH(fixed_channel_impl.RegisterOnCloseCallback(user_handler.get(),
+                                                          common::BindOnce([](hci::ErrorCode status) { FAIL(); })),
+               ".*OnCloseCallback can only be registered once.*");
+
+  user_handler->Clear();
+}
+
+TEST_F(L2capClassicFixedChannelImplTest, call_acquire_before_registration_should_fail) {
+  ::testing::FLAGS_gtest_death_test_style = "threadsafe";
+  MockParameterProvider mock_parameter_provider;
+  EXPECT_CALL(mock_parameter_provider, GetClassicLinkIdleDisconnectTimeout())
+      .WillRepeatedly(Return(std::chrono::seconds(5)));
+  testing::MockAclConnection* mock_acl_connection = new testing::MockAclConnection();
+  EXPECT_CALL(*mock_acl_connection, GetAddress()).Times(1);
+  EXPECT_CALL(*mock_acl_connection, GetAddressType()).Times(1);
+  EXPECT_CALL(*mock_acl_connection, RegisterCallbacks(_, l2cap_handler_)).Times(1);
+  EXPECT_CALL(*mock_acl_connection, UnregisterCallbacks(_)).Times(1);
+  MockLink mock_classic_link(l2cap_handler_, &mock_parameter_provider,
+                             std::unique_ptr<testing::MockAclConnection>(mock_acl_connection));
+  hci::AddressWithType device{hci::Address{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}},
+                              hci::AddressType::PUBLIC_IDENTITY_ADDRESS};
+  EXPECT_CALL(mock_classic_link, GetDevice()).WillRepeatedly(Return(device));
+  FixedChannelImpl fixed_channel_impl(kSmpBrCid, &mock_classic_link, l2cap_handler_);
+  EXPECT_DEATH(fixed_channel_impl.Acquire(), ".*Must register OnCloseCallback before calling any methods.*");
+}
+
+TEST_F(L2capClassicFixedChannelImplTest, call_release_before_registration_should_fail) {
+  ::testing::FLAGS_gtest_death_test_style = "threadsafe";
+  MockParameterProvider mock_parameter_provider;
+  EXPECT_CALL(mock_parameter_provider, GetClassicLinkIdleDisconnectTimeout())
+      .WillRepeatedly(Return(std::chrono::seconds(5)));
+  testing::MockAclConnection* mock_acl_connection = new testing::MockAclConnection();
+  EXPECT_CALL(*mock_acl_connection, GetAddress()).Times(1);
+  EXPECT_CALL(*mock_acl_connection, GetAddressType()).Times(1);
+  EXPECT_CALL(*mock_acl_connection, RegisterCallbacks(_, l2cap_handler_)).Times(1);
+  EXPECT_CALL(*mock_acl_connection, UnregisterCallbacks(_)).Times(1);
+  MockLink mock_classic_link(l2cap_handler_, &mock_parameter_provider,
+                             std::unique_ptr<testing::MockAclConnection>(mock_acl_connection));
+  hci::AddressWithType device{hci::Address{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}},
+                              hci::AddressType::PUBLIC_IDENTITY_ADDRESS};
+  EXPECT_CALL(mock_classic_link, GetDevice()).WillRepeatedly(Return(device));
+  FixedChannelImpl fixed_channel_impl(kSmpBrCid, &mock_classic_link, l2cap_handler_);
+  EXPECT_DEATH(fixed_channel_impl.Release(), ".*Must register OnCloseCallback before calling any methods.*");
+}
+
+TEST_F(L2capClassicFixedChannelImplTest, test_acquire_release_channel) {
+  MockParameterProvider mock_parameter_provider;
+  EXPECT_CALL(mock_parameter_provider, GetClassicLinkIdleDisconnectTimeout())
+      .WillRepeatedly(Return(std::chrono::seconds(5)));
+  EXPECT_CALL(mock_parameter_provider, GetClassicLinkIdleDisconnectTimeout())
+      .WillRepeatedly(Return(std::chrono::seconds(5)));
+  testing::MockAclConnection* mock_acl_connection = new testing::MockAclConnection();
+  EXPECT_CALL(*mock_acl_connection, GetAddress()).Times(1);
+  EXPECT_CALL(*mock_acl_connection, GetAddressType()).Times(1);
+  EXPECT_CALL(*mock_acl_connection, RegisterCallbacks(_, l2cap_handler_)).Times(1);
+  EXPECT_CALL(*mock_acl_connection, UnregisterCallbacks(_)).Times(1);
+  MockLink mock_classic_link(l2cap_handler_, &mock_parameter_provider,
+                             std::unique_ptr<testing::MockAclConnection>(mock_acl_connection));
+  hci::AddressWithType device{hci::Address{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}},
+                              hci::AddressType::PUBLIC_IDENTITY_ADDRESS};
+  EXPECT_CALL(mock_classic_link, GetDevice()).WillRepeatedly(Return(device));
+  FixedChannelImpl fixed_channel_impl(kSmpBrCid, &mock_classic_link, l2cap_handler_);
+
+  // Register on close callback
+  auto user_handler = std::make_unique<os::Handler>(thread_);
+  hci::ErrorCode my_status = hci::ErrorCode::SUCCESS;
+  fixed_channel_impl.RegisterOnCloseCallback(
+      user_handler.get(),
+      common::testing::BindLambdaForTesting([&my_status](hci::ErrorCode status) { my_status = status; }));
+
+  // Default should be false
+  EXPECT_FALSE(fixed_channel_impl.IsAcquired());
+
+  // Should be called 2 times after Acquire() and Release()
+  EXPECT_CALL(mock_classic_link, RefreshRefCount()).Times(2);
+
+  fixed_channel_impl.Acquire();
+  EXPECT_TRUE(fixed_channel_impl.IsAcquired());
+
+  fixed_channel_impl.Release();
+  EXPECT_FALSE(fixed_channel_impl.IsAcquired());
+
+  user_handler->Clear();
+}
+
+TEST_F(L2capClassicFixedChannelImplTest, test_acquire_after_close) {
+  ::testing::FLAGS_gtest_death_test_style = "threadsafe";
+  MockParameterProvider mock_parameter_provider;
+  EXPECT_CALL(mock_parameter_provider, GetClassicLinkIdleDisconnectTimeout())
+      .WillRepeatedly(Return(std::chrono::seconds(5)));
+  testing::MockAclConnection* mock_acl_connection = new testing::MockAclConnection();
+  EXPECT_CALL(*mock_acl_connection, GetAddress()).Times(1);
+  EXPECT_CALL(*mock_acl_connection, GetAddressType()).Times(1);
+  EXPECT_CALL(*mock_acl_connection, RegisterCallbacks(_, l2cap_handler_)).Times(1);
+  EXPECT_CALL(*mock_acl_connection, UnregisterCallbacks(_)).Times(1);
+  MockLink mock_classic_link(l2cap_handler_, &mock_parameter_provider,
+                             std::unique_ptr<testing::MockAclConnection>(mock_acl_connection));
+  hci::AddressWithType device{hci::Address{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}},
+                              hci::AddressType::PUBLIC_IDENTITY_ADDRESS};
+  EXPECT_CALL(mock_classic_link, GetDevice()).WillRepeatedly(Return(device));
+  FixedChannelImpl fixed_channel_impl(kSmpBrCid, &mock_classic_link, l2cap_handler_);
+
+  // Register on close callback
+  auto user_handler = std::make_unique<os::Handler>(thread_);
+  hci::ErrorCode my_status = hci::ErrorCode::SUCCESS;
+  std::promise<void> promise;
+  auto future = promise.get_future();
+  fixed_channel_impl.RegisterOnCloseCallback(user_handler.get(),
+                                             common::testing::BindLambdaForTesting([&](hci::ErrorCode status) {
+                                               my_status = status;
+                                               promise.set_value();
+                                             }));
+
+  // Channel closure should trigger such callback
+  fixed_channel_impl.OnClosed(hci::ErrorCode::REMOTE_USER_TERMINATED_CONNECTION);
+  SyncHandler(user_handler.get());
+  auto future_status = future.wait_for(std::chrono::seconds(1));
+  EXPECT_EQ(future_status, std::future_status::ready);
+  EXPECT_EQ(hci::ErrorCode::REMOTE_USER_TERMINATED_CONNECTION, my_status);
+
+  // Release or Acquire after closing should crash
+  EXPECT_CALL(mock_classic_link, RefreshRefCount()).Times(0);
+  EXPECT_FALSE(fixed_channel_impl.IsAcquired());
+  EXPECT_DEATH(fixed_channel_impl.Acquire(), ".*Must register OnCloseCallback before calling any methods.*");
+
+  user_handler->Clear();
+}
+
+}  // namespace internal
+}  // namespace classic
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/classic/internal/fixed_channel_service_impl.h b/gd/l2cap/classic/internal/fixed_channel_service_impl.h
new file mode 100644
index 0000000..b06983f
--- /dev/null
+++ b/gd/l2cap/classic/internal/fixed_channel_service_impl.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "l2cap/classic/fixed_channel.h"
+#include "l2cap/classic/fixed_channel_manager.h"
+#include "l2cap/classic/fixed_channel_service.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace classic {
+namespace internal {
+
+class FixedChannelServiceImpl {
+ public:
+  virtual ~FixedChannelServiceImpl() = default;
+
+  struct PendingRegistration {
+    os::Handler* user_handler_ = nullptr;
+    FixedChannelManager::OnRegistrationCompleteCallback on_registration_complete_callback_;
+    FixedChannelManager::OnConnectionOpenCallback on_connection_open_callback_;
+  };
+
+  virtual void NotifyChannelCreation(std::unique_ptr<FixedChannel> channel) {
+    user_handler_->Post(common::BindOnce(on_connection_open_callback_, std::move(channel)));
+  }
+
+  friend class FixedChannelServiceManagerImpl;
+
+ protected:
+  // protected access for mocking
+  FixedChannelServiceImpl(os::Handler* user_handler,
+                          FixedChannelManager::OnConnectionOpenCallback on_connection_open_callback)
+      : user_handler_(user_handler), on_connection_open_callback_(std::move(on_connection_open_callback)) {}
+
+ private:
+  os::Handler* user_handler_ = nullptr;
+  FixedChannelManager::OnConnectionOpenCallback on_connection_open_callback_;
+};
+
+}  // namespace internal
+}  // namespace classic
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/classic/internal/fixed_channel_service_impl_mock.h b/gd/l2cap/classic/internal/fixed_channel_service_impl_mock.h
new file mode 100644
index 0000000..7aeb094
--- /dev/null
+++ b/gd/l2cap/classic/internal/fixed_channel_service_impl_mock.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include "l2cap/classic/internal/fixed_channel_impl.h"
+#include "l2cap/classic/internal/fixed_channel_service_manager_impl.h"
+
+#include <gmock/gmock.h>
+
+// Unit test interfaces
+namespace bluetooth {
+namespace l2cap {
+namespace classic {
+namespace internal {
+namespace testing {
+
+class MockFixedChannelServiceImpl : public FixedChannelServiceImpl {
+ public:
+  MockFixedChannelServiceImpl() : FixedChannelServiceImpl(nullptr, FixedChannelManager::OnConnectionOpenCallback()) {}
+  MOCK_METHOD(void, NotifyChannelCreation, (std::unique_ptr<FixedChannel> channel), (override));
+};
+
+}  // namespace testing
+}  // namespace internal
+}  // namespace classic
+}  // namespace l2cap
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/l2cap/classic/internal/fixed_channel_service_manager_impl.cc b/gd/l2cap/classic/internal/fixed_channel_service_manager_impl.cc
new file mode 100644
index 0000000..b557970
--- /dev/null
+++ b/gd/l2cap/classic/internal/fixed_channel_service_manager_impl.cc
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "l2cap/classic/internal/fixed_channel_service_manager_impl.h"
+
+#include "common/bind.h"
+#include "l2cap/cid.h"
+#include "l2cap/classic/internal/fixed_channel_service_impl.h"
+#include "os/log.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace classic {
+namespace internal {
+
+void FixedChannelServiceManagerImpl::Register(Cid cid,
+                                              FixedChannelServiceImpl::PendingRegistration pending_registration) {
+  if (cid < kFirstFixedChannel || cid > kLastFixedChannel || cid == kClassicSignallingCid) {
+    std::unique_ptr<FixedChannelService> invalid_service(new FixedChannelService());
+    pending_registration.user_handler_->Post(
+        common::BindOnce(std::move(pending_registration.on_registration_complete_callback_),
+                         FixedChannelManager::RegistrationResult::FAIL_INVALID_SERVICE, std::move(invalid_service)));
+  } else if (IsServiceRegistered(cid)) {
+    std::unique_ptr<FixedChannelService> invalid_service(new FixedChannelService());
+    pending_registration.user_handler_->Post(
+        common::BindOnce(std::move(pending_registration.on_registration_complete_callback_),
+                         FixedChannelManager::RegistrationResult::FAIL_DUPLICATE_SERVICE, std::move(invalid_service)));
+  } else {
+    service_map_.try_emplace(cid,
+                             FixedChannelServiceImpl(pending_registration.user_handler_,
+                                                     std::move(pending_registration.on_connection_open_callback_)));
+    std::unique_ptr<FixedChannelService> user_service(new FixedChannelService(cid, this, l2cap_layer_handler_));
+    pending_registration.user_handler_->Post(
+        common::BindOnce(std::move(pending_registration.on_registration_complete_callback_),
+                         FixedChannelManager::RegistrationResult::SUCCESS, std::move(user_service)));
+  }
+}
+
+void FixedChannelServiceManagerImpl::Unregister(Cid cid, FixedChannelService::OnUnregisteredCallback callback,
+                                                os::Handler* handler) {
+  if (IsServiceRegistered(cid)) {
+    service_map_.erase(cid);
+    handler->Post(std::move(callback));
+  } else {
+    LOG_ERROR("service not registered cid:%d", cid);
+  }
+}
+
+bool FixedChannelServiceManagerImpl::IsServiceRegistered(Cid cid) const {
+  return service_map_.find(cid) != service_map_.end();
+}
+
+FixedChannelServiceImpl* FixedChannelServiceManagerImpl::GetService(Cid cid) {
+  ASSERT(IsServiceRegistered(cid));
+  return &service_map_.find(cid)->second;
+}
+
+std::vector<std::pair<Cid, FixedChannelServiceImpl*>> FixedChannelServiceManagerImpl::GetRegisteredServices() {
+  std::vector<std::pair<Cid, FixedChannelServiceImpl*>> results;
+  for (auto& elem : service_map_) {
+    results.emplace_back(elem.first, &elem.second);
+  }
+  return results;
+}
+
+namespace {
+constexpr uint64_t kSignallingChannelMask = 0x02;
+constexpr uint64_t kConnectionlessReceptionMask = 0x04;
+constexpr uint64_t kBrEdrSecurityManager = 0x80;
+}  // namespace
+
+uint64_t FixedChannelServiceManagerImpl::GetSupportedFixedChannelMask() {
+  uint64_t result = 0;
+  result |= kSignallingChannelMask;  // Signalling channel is mandatory
+  for (const auto& elem : service_map_) {
+    Cid cid = elem.first;
+    switch (cid) {
+      case kConnectionlessCid:
+        result |= kConnectionlessReceptionMask;
+        continue;
+      case kSmpBrCid:
+        result |= kBrEdrSecurityManager;
+        continue;
+      default:
+        LOG_WARN("Unknown fixed channel is registered: 0x%x", cid);
+        continue;
+    }
+  }
+  return result;
+}
+}  // namespace internal
+}  // namespace classic
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/classic/internal/fixed_channel_service_manager_impl.h b/gd/l2cap/classic/internal/fixed_channel_service_manager_impl.h
new file mode 100644
index 0000000..1e1d21d
--- /dev/null
+++ b/gd/l2cap/classic/internal/fixed_channel_service_manager_impl.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <unordered_map>
+
+#include "l2cap/cid.h"
+#include "l2cap/classic/fixed_channel_service.h"
+#include "l2cap/classic/internal/fixed_channel_service_impl.h"
+#include "os/handler.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace classic {
+namespace internal {
+
+class FixedChannelServiceManagerImpl {
+ public:
+  explicit FixedChannelServiceManagerImpl(os::Handler* l2cap_layer_handler)
+      : l2cap_layer_handler_(l2cap_layer_handler) {}
+  virtual ~FixedChannelServiceManagerImpl() = default;
+
+  // All APIs must be invoked in L2CAP layer handler
+
+  virtual void Register(Cid cid, FixedChannelServiceImpl::PendingRegistration pending_registration);
+  virtual void Unregister(Cid cid, FixedChannelService::OnUnregisteredCallback callback, os::Handler* handler);
+  virtual bool IsServiceRegistered(Cid cid) const;
+  virtual FixedChannelServiceImpl* GetService(Cid cid);
+  virtual std::vector<std::pair<Cid, FixedChannelServiceImpl*>> GetRegisteredServices();
+  virtual uint64_t GetSupportedFixedChannelMask();
+
+ private:
+  os::Handler* l2cap_layer_handler_ = nullptr;
+  std::unordered_map<Cid, FixedChannelServiceImpl> service_map_;
+};
+}  // namespace internal
+}  // namespace classic
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/classic/internal/fixed_channel_service_manager_impl_mock.h b/gd/l2cap/classic/internal/fixed_channel_service_manager_impl_mock.h
new file mode 100644
index 0000000..55aa4cf
--- /dev/null
+++ b/gd/l2cap/classic/internal/fixed_channel_service_manager_impl_mock.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include "l2cap/classic/internal/fixed_channel_impl.h"
+#include "l2cap/classic/internal/fixed_channel_service_manager_impl.h"
+
+#include <gmock/gmock.h>
+
+// Unit test interfaces
+namespace bluetooth {
+namespace l2cap {
+namespace classic {
+namespace internal {
+namespace testing {
+
+class MockFixedChannelServiceManagerImpl : public FixedChannelServiceManagerImpl {
+ public:
+  MockFixedChannelServiceManagerImpl() : FixedChannelServiceManagerImpl(nullptr) {}
+  MOCK_METHOD(void, Register, (Cid cid, FixedChannelServiceImpl::PendingRegistration pending_registration), (override));
+  MOCK_METHOD(void, Unregister, (Cid cid, FixedChannelService::OnUnregisteredCallback callback, os::Handler* handler),
+              (override));
+  MOCK_METHOD(bool, IsServiceRegistered, (Cid cid), (const, override));
+  MOCK_METHOD(FixedChannelServiceImpl*, GetService, (Cid cid), (override));
+  MOCK_METHOD((std::vector<std::pair<Cid, FixedChannelServiceImpl*>>), GetRegisteredServices, (), (override));
+};
+
+}  // namespace testing
+}  // namespace internal
+}  // namespace classic
+}  // namespace l2cap
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/l2cap/classic/internal/fixed_channel_service_manager_test.cc b/gd/l2cap/classic/internal/fixed_channel_service_manager_test.cc
new file mode 100644
index 0000000..d586913
--- /dev/null
+++ b/gd/l2cap/classic/internal/fixed_channel_service_manager_test.cc
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "l2cap/classic/internal/fixed_channel_service_manager_impl.h"
+
+#include <future>
+
+#include "common/bind.h"
+#include "l2cap/cid.h"
+#include "l2cap/classic/fixed_channel_manager.h"
+#include "l2cap/classic/fixed_channel_service.h"
+#include "os/handler.h"
+#include "os/thread.h"
+
+#include <gtest/gtest.h>
+
+namespace bluetooth {
+namespace l2cap {
+namespace classic {
+namespace internal {
+
+class L2capClassicFixedServiceManagerTest : public ::testing::Test {
+ public:
+  ~L2capClassicFixedServiceManagerTest() override = default;
+
+  void OnServiceRegistered(bool expect_success, FixedChannelManager::RegistrationResult result,
+                           std::unique_ptr<FixedChannelService> user_service) {
+    EXPECT_EQ(result == FixedChannelManager::RegistrationResult::SUCCESS, expect_success);
+    service_registered_ = expect_success;
+  }
+
+ protected:
+  void SetUp() override {
+    manager_ = new FixedChannelServiceManagerImpl{nullptr};
+    thread_ = new os::Thread("test_thread", os::Thread::Priority::NORMAL);
+    user_handler_ = new os::Handler(thread_);
+  }
+
+  void TearDown() override {
+    user_handler_->Clear();
+    delete user_handler_;
+    delete thread_;
+    delete manager_;
+  }
+
+  void sync_user_handler() {
+    std::promise<void> promise;
+    auto future = promise.get_future();
+    user_handler_->Post(common::BindOnce(&std::promise<void>::set_value, common::Unretained(&promise)));
+    future.wait_for(std::chrono::milliseconds(3));
+  }
+
+  FixedChannelServiceManagerImpl* manager_ = nullptr;
+  os::Thread* thread_ = nullptr;
+  os::Handler* user_handler_ = nullptr;
+
+  bool service_registered_ = false;
+};
+
+TEST_F(L2capClassicFixedServiceManagerTest, register_and_unregister_classic_fixed_channel) {
+  FixedChannelServiceImpl::PendingRegistration pending_registration{
+      .user_handler_ = user_handler_,
+      .on_registration_complete_callback_ =
+          common::BindOnce(&L2capClassicFixedServiceManagerTest::OnServiceRegistered, common::Unretained(this), true)};
+  Cid cid = kSmpBrCid;
+  EXPECT_FALSE(manager_->IsServiceRegistered(cid));
+  manager_->Register(cid, std::move(pending_registration));
+  EXPECT_TRUE(manager_->IsServiceRegistered(cid));
+  sync_user_handler();
+  EXPECT_TRUE(service_registered_);
+  manager_->Unregister(cid, common::BindOnce([] {}), user_handler_);
+  EXPECT_FALSE(manager_->IsServiceRegistered(cid));
+}
+
+TEST_F(L2capClassicFixedServiceManagerTest, register_classic_fixed_channel_bad_cid) {
+  FixedChannelServiceImpl::PendingRegistration pending_registration{
+      .user_handler_ = user_handler_,
+      .on_registration_complete_callback_ =
+          common::BindOnce(&L2capClassicFixedServiceManagerTest::OnServiceRegistered, common::Unretained(this), false)};
+  Cid cid = 0x1000;
+  EXPECT_FALSE(manager_->IsServiceRegistered(cid));
+  manager_->Register(cid, std::move(pending_registration));
+  EXPECT_FALSE(manager_->IsServiceRegistered(cid));
+  sync_user_handler();
+  EXPECT_FALSE(service_registered_);
+}
+
+}  // namespace internal
+}  // namespace classic
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/classic/internal/link.cc b/gd/l2cap/classic/internal/link.cc
new file mode 100644
index 0000000..135f67f
--- /dev/null
+++ b/gd/l2cap/classic/internal/link.cc
@@ -0,0 +1,333 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <chrono>
+#include <memory>
+
+#include "hci/acl_manager.h"
+#include "l2cap/classic/dynamic_channel_manager.h"
+#include "l2cap/classic/internal/fixed_channel_impl.h"
+#include "l2cap/classic/internal/link.h"
+#include "l2cap/internal/parameter_provider.h"
+#include "os/alarm.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace classic {
+namespace internal {
+
+Link::Link(os::Handler* l2cap_handler, std::unique_ptr<hci::AclConnection> acl_connection,
+           l2cap::internal::ParameterProvider* parameter_provider,
+           DynamicChannelServiceManagerImpl* dynamic_service_manager,
+           FixedChannelServiceManagerImpl* fixed_service_manager)
+    : l2cap_handler_(l2cap_handler), acl_connection_(std::move(acl_connection)),
+      data_pipeline_manager_(l2cap_handler, this, acl_connection_->GetAclQueueEnd()),
+      parameter_provider_(parameter_provider), dynamic_service_manager_(dynamic_service_manager),
+      fixed_service_manager_(fixed_service_manager),
+      signalling_manager_(l2cap_handler_, this, &data_pipeline_manager_, dynamic_service_manager_,
+                          &dynamic_channel_allocator_, fixed_service_manager_) {
+  ASSERT(l2cap_handler_ != nullptr);
+  ASSERT(acl_connection_ != nullptr);
+  ASSERT(parameter_provider_ != nullptr);
+  link_idle_disconnect_alarm_.Schedule(common::BindOnce(&Link::Disconnect, common::Unretained(this)),
+                                       parameter_provider_->GetClassicLinkIdleDisconnectTimeout());
+  acl_connection_->RegisterCallbacks(this, l2cap_handler_);
+}
+
+Link::~Link() {
+  acl_connection_->UnregisterCallbacks(this);
+}
+
+void Link::OnAclDisconnected(hci::ErrorCode status) {
+  signalling_manager_.CancelAlarm();
+  fixed_channel_allocator_.OnAclDisconnected(status);
+  dynamic_channel_allocator_.OnAclDisconnected(status);
+  DynamicChannelManager::ConnectionResult result{
+      .connection_result_code = DynamicChannelManager::ConnectionResultCode::FAIL_HCI_ERROR,
+      .hci_error = status,
+      .l2cap_connection_response_result = ConnectionResponseResult::SUCCESS,
+  };
+  while (!local_cid_to_pending_dynamic_channel_connection_map_.empty()) {
+    auto entry = local_cid_to_pending_dynamic_channel_connection_map_.begin();
+    NotifyChannelFail(entry->first, result);
+  }
+}
+
+void Link::Disconnect() {
+  acl_connection_->Disconnect(hci::DisconnectReason::REMOTE_USER_TERMINATED_CONNECTION);
+}
+
+void Link::Encrypt() {
+  acl_connection_->SetConnectionEncryption(hci::Enable::ENABLED);
+}
+
+void Link::Authenticate() {
+  acl_connection_->AuthenticationRequested();
+}
+
+bool Link::IsAuthenticated() const {
+  return encryption_enabled_ != hci::EncryptionEnabled::OFF;
+}
+
+void Link::ReadRemoteVersionInformation() {
+  acl_connection_->ReadRemoteVersionInformation();
+}
+
+void Link::ReadRemoteSupportedFeatures() {
+  acl_connection_->ReadRemoteSupportedFeatures();
+}
+
+void Link::ReadRemoteExtendedFeatures() {
+  acl_connection_->ReadRemoteExtendedFeatures();
+}
+
+void Link::ReadClockOffset() {
+  acl_connection_->ReadClockOffset();
+}
+
+std::shared_ptr<FixedChannelImpl> Link::AllocateFixedChannel(Cid cid, SecurityPolicy security_policy) {
+  auto channel = fixed_channel_allocator_.AllocateChannel(cid, security_policy);
+  data_pipeline_manager_.AttachChannel(cid, channel, l2cap::internal::DataPipelineManager::ChannelMode::BASIC);
+  return channel;
+}
+
+bool Link::IsFixedChannelAllocated(Cid cid) {
+  return fixed_channel_allocator_.IsChannelAllocated(cid);
+}
+
+Cid Link::ReserveDynamicChannel() {
+  return dynamic_channel_allocator_.ReserveChannel();
+}
+
+void Link::SendConnectionRequest(Psm psm, Cid local_cid) {
+  signalling_manager_.SendConnectionRequest(psm, local_cid);
+}
+
+void Link::SendConnectionRequest(Psm psm, Cid local_cid,
+                                 PendingDynamicChannelConnection pending_dynamic_channel_connection) {
+  local_cid_to_pending_dynamic_channel_connection_map_[local_cid] = std::move(pending_dynamic_channel_connection);
+  signalling_manager_.SendConnectionRequest(psm, local_cid);
+}
+
+void Link::OnOutgoingConnectionRequestFail(Cid local_cid) {
+  if (local_cid_to_pending_dynamic_channel_connection_map_.find(local_cid) !=
+      local_cid_to_pending_dynamic_channel_connection_map_.end()) {
+    DynamicChannelManager::ConnectionResult result{
+        .connection_result_code = DynamicChannelManager::ConnectionResultCode::FAIL_HCI_ERROR,
+        .hci_error = hci::ErrorCode::CONNECTION_TIMEOUT,
+        .l2cap_connection_response_result = ConnectionResponseResult::SUCCESS,
+    };
+    NotifyChannelFail(local_cid, result);
+  }
+  dynamic_channel_allocator_.FreeChannel(local_cid);
+}
+
+void Link::SendDisconnectionRequest(Cid local_cid, Cid remote_cid) {
+  signalling_manager_.SendDisconnectionRequest(local_cid, remote_cid);
+}
+
+void Link::SendInformationRequest(InformationRequestInfoType type) {
+  signalling_manager_.SendInformationRequest(type);
+}
+
+std::shared_ptr<l2cap::internal::DynamicChannelImpl> Link::AllocateDynamicChannel(Psm psm, Cid remote_cid,
+                                                                                  SecurityPolicy security_policy) {
+  auto channel = dynamic_channel_allocator_.AllocateChannel(psm, remote_cid, security_policy);
+  if (channel != nullptr) {
+    data_pipeline_manager_.AttachChannel(channel->GetCid(), channel,
+                                         l2cap::internal::DataPipelineManager::ChannelMode::BASIC);
+    RefreshRefCount();
+  }
+  channel->local_initiated_ = false;
+  return channel;
+}
+
+std::shared_ptr<l2cap::internal::DynamicChannelImpl> Link::AllocateReservedDynamicChannel(
+    Cid reserved_cid, Psm psm, Cid remote_cid, SecurityPolicy security_policy) {
+  auto channel = dynamic_channel_allocator_.AllocateReservedChannel(reserved_cid, psm, remote_cid, security_policy);
+  if (channel != nullptr) {
+    data_pipeline_manager_.AttachChannel(channel->GetCid(), channel,
+                                         l2cap::internal::DataPipelineManager::ChannelMode::BASIC);
+    RefreshRefCount();
+  }
+  channel->local_initiated_ = true;
+  return channel;
+}
+
+classic::DynamicChannelConfigurationOption Link::GetConfigurationForInitialConfiguration(Cid cid) {
+  ASSERT(local_cid_to_pending_dynamic_channel_connection_map_.find(cid) !=
+         local_cid_to_pending_dynamic_channel_connection_map_.end());
+  return local_cid_to_pending_dynamic_channel_connection_map_[cid].configuration_;
+}
+
+void Link::FreeDynamicChannel(Cid cid) {
+  if (dynamic_channel_allocator_.FindChannelByCid(cid) == nullptr) {
+    return;
+  }
+  data_pipeline_manager_.DetachChannel(cid);
+  dynamic_channel_allocator_.FreeChannel(cid);
+  RefreshRefCount();
+}
+
+void Link::RefreshRefCount() {
+  int ref_count = 0;
+  ref_count += fixed_channel_allocator_.GetRefCount();
+  ref_count += dynamic_channel_allocator_.NumberOfChannels();
+  ASSERT_LOG(ref_count >= 0, "ref_count %d is less than 0", ref_count);
+  if (ref_count > 0) {
+    link_idle_disconnect_alarm_.Cancel();
+  } else {
+    link_idle_disconnect_alarm_.Schedule(common::BindOnce(&Link::Disconnect, common::Unretained(this)),
+                                         parameter_provider_->GetClassicLinkIdleDisconnectTimeout());
+  }
+}
+
+void Link::NotifyChannelCreation(Cid cid, std::unique_ptr<DynamicChannel> user_channel) {
+  ASSERT(local_cid_to_pending_dynamic_channel_connection_map_.find(cid) !=
+         local_cid_to_pending_dynamic_channel_connection_map_.end());
+  auto& pending_dynamic_channel_connection = local_cid_to_pending_dynamic_channel_connection_map_[cid];
+  pending_dynamic_channel_connection.handler_->Post(
+      common::BindOnce(std::move(pending_dynamic_channel_connection.on_open_callback_), std::move(user_channel)));
+  local_cid_to_pending_dynamic_channel_connection_map_.erase(cid);
+}
+
+void Link::NotifyChannelFail(Cid cid, DynamicChannelManager::ConnectionResult result) {
+  ASSERT(local_cid_to_pending_dynamic_channel_connection_map_.find(cid) !=
+         local_cid_to_pending_dynamic_channel_connection_map_.end());
+  auto& pending_dynamic_channel_connection = local_cid_to_pending_dynamic_channel_connection_map_[cid];
+  pending_dynamic_channel_connection.handler_->Post(
+      common::BindOnce(std::move(pending_dynamic_channel_connection.on_fail_callback_), result));
+  local_cid_to_pending_dynamic_channel_connection_map_.erase(cid);
+}
+
+void Link::SetRemoteConnectionlessMtu(Mtu mtu) {
+  remote_connectionless_mtu_ = mtu;
+}
+
+Mtu Link::GetRemoteConnectionlessMtu() const {
+  return remote_connectionless_mtu_;
+}
+
+void Link::SetRemoteSupportsErtm(bool supported) {
+  remote_supports_ertm_ = supported;
+}
+
+bool Link::GetRemoteSupportsErtm() const {
+  return remote_supports_ertm_;
+}
+
+void Link::SetRemoteSupportsFcs(bool supported) {
+  remote_supports_fcs_ = supported;
+}
+
+bool Link::GetRemoteSupportsFcs() const {
+  return remote_supports_fcs_;
+}
+
+void Link::AddChannelPendingingAuthentication(PendingAuthenticateDynamicChannelConnection pending_channel) {
+  pending_channel_list_.push_back(std::move(pending_channel));
+}
+
+void Link::OnConnectionPacketTypeChanged(uint16_t packet_type) {
+  LOG_DEBUG("UNIMPLEMENTED %s packet_type:%x", __func__, packet_type);
+}
+
+void Link::OnAuthenticationComplete() {
+  if (!pending_channel_list_.empty()) {
+    acl_connection_->SetConnectionEncryption(hci::Enable::ENABLED);
+  }
+}
+
+void Link::OnEncryptionChange(hci::EncryptionEnabled enabled) {
+  encryption_enabled_ = enabled;
+  if (encryption_enabled_ == hci::EncryptionEnabled::OFF) {
+    LOG_DEBUG("Encryption has changed to disabled");
+    return;
+  }
+  LOG_DEBUG("Encryption has changed to enabled .. restarting channels:%zd", pending_channel_list_.size());
+
+  for (auto& channel : pending_channel_list_) {
+    local_cid_to_pending_dynamic_channel_connection_map_[channel.cid_] =
+        std::move(channel.pending_dynamic_channel_connection_);
+    signalling_manager_.SendConnectionRequest(channel.psm_, channel.cid_);
+  }
+  pending_channel_list_.clear();
+}
+
+void Link::OnChangeConnectionLinkKeyComplete() {
+  LOG_DEBUG("UNIMPLEMENTED %s", __func__);
+}
+
+void Link::OnReadClockOffsetComplete(uint16_t clock_offset) {
+  LOG_DEBUG("UNIMPLEMENTED %s clock_offset:%d", __func__, clock_offset);
+}
+
+void Link::OnModeChange(hci::Mode current_mode, uint16_t interval) {
+  LOG_DEBUG("UNIMPLEMENTED %s mode:%s interval:%d", __func__, hci::ModeText(current_mode).c_str(), interval);
+}
+
+void Link::OnQosSetupComplete(hci::ServiceType service_type, uint32_t token_rate, uint32_t peak_bandwidth,
+                              uint32_t latency, uint32_t delay_variation) {
+  LOG_DEBUG("UNIMPLEMENTED %s service_type:%s token_rate:%d peak_bandwidth:%d latency:%d delay_varitation:%d", __func__,
+            hci::ServiceTypeText(service_type).c_str(), token_rate, peak_bandwidth, latency, delay_variation);
+}
+void Link::OnFlowSpecificationComplete(hci::FlowDirection flow_direction, hci::ServiceType service_type,
+                                       uint32_t token_rate, uint32_t token_bucket_size, uint32_t peak_bandwidth,
+                                       uint32_t access_latency) {
+  LOG_DEBUG(
+      "UNIMPLEMENTED %s flow_direction:%s service_type:%s token_rate:%d token_bucket_size:%d peak_bandwidth:%d "
+      "access_latency:%d",
+      __func__, hci::FlowDirectionText(flow_direction).c_str(), hci::ServiceTypeText(service_type).c_str(), token_rate,
+      token_bucket_size, peak_bandwidth, access_latency);
+}
+void Link::OnFlushOccurred() {
+  LOG_DEBUG("UNIMPLEMENTED %s", __func__);
+}
+void Link::OnRoleDiscoveryComplete(hci::Role current_role) {
+  LOG_DEBUG("UNIMPLEMENTED %s current_role:%s", __func__, hci::RoleText(current_role).c_str());
+}
+void Link::OnReadLinkPolicySettingsComplete(uint16_t link_policy_settings) {
+  LOG_DEBUG("UNIMPLEMENTED %s link_policy_settings:0x%x", __func__, link_policy_settings);
+}
+void Link::OnReadAutomaticFlushTimeoutComplete(uint16_t flush_timeout) {
+  LOG_DEBUG("UNIMPLEMENTED %s flush_timeout:%d", __func__, flush_timeout);
+}
+void Link::OnReadTransmitPowerLevelComplete(uint8_t transmit_power_level) {
+  LOG_DEBUG("UNIMPLEMENTED %s transmit_power_level:%d", __func__, transmit_power_level);
+}
+void Link::OnReadLinkSupervisionTimeoutComplete(uint16_t link_supervision_timeout) {
+  LOG_DEBUG("UNIMPLEMENTED %s link_supervision_timeout:%d", __func__, link_supervision_timeout);
+}
+void Link::OnReadFailedContactCounterComplete(uint16_t failed_contact_counter) {
+  LOG_DEBUG("UNIMPLEMENTED %sfailed_contact_counter:%hu", __func__, failed_contact_counter);
+}
+void Link::OnReadLinkQualityComplete(uint8_t link_quality) {
+  LOG_DEBUG("UNIMPLEMENTED %s link_quality:%hhu", __func__, link_quality);
+}
+void Link::OnReadAfhChannelMapComplete(hci::AfhMode afh_mode, std::array<uint8_t, 10> afh_channel_map) {
+  LOG_DEBUG("UNIMPLEMENTED %s afh_mode:%s", __func__, hci::AfhModeText(afh_mode).c_str());
+}
+void Link::OnReadRssiComplete(uint8_t rssi) {
+  LOG_DEBUG("UNIMPLEMENTED %s rssi:%hhd", __func__, rssi);
+}
+void Link::OnReadClockComplete(uint32_t clock, uint16_t accuracy) {
+  LOG_DEBUG("UNIMPLEMENTED %s clock:%u accuracy:%hu", __func__, clock, accuracy);
+}
+
+}  // namespace internal
+}  // namespace classic
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/classic/internal/link.h b/gd/l2cap/classic/internal/link.h
new file mode 100644
index 0000000..fe40b52
--- /dev/null
+++ b/gd/l2cap/classic/internal/link.h
@@ -0,0 +1,187 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <memory>
+#include <unordered_map>
+
+#include "hci/acl_manager.h"
+#include "l2cap/classic/dynamic_channel_configuration_option.h"
+#include "l2cap/classic/internal/dynamic_channel_service_manager_impl.h"
+#include "l2cap/classic/internal/fixed_channel_impl.h"
+#include "l2cap/classic/internal/fixed_channel_service_manager_impl.h"
+#include "l2cap/internal/data_pipeline_manager.h"
+#include "l2cap/internal/dynamic_channel_allocator.h"
+#include "l2cap/internal/dynamic_channel_impl.h"
+#include "l2cap/internal/fixed_channel_allocator.h"
+#include "l2cap/internal/ilink.h"
+#include "l2cap/internal/parameter_provider.h"
+#include "os/alarm.h"
+#include "os/handler.h"
+#include "signalling_manager.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace classic {
+namespace internal {
+
+class Link : public l2cap::internal::ILink, public hci::ConnectionManagementCallbacks {
+ public:
+  Link(os::Handler* l2cap_handler, std::unique_ptr<hci::AclConnection> acl_connection,
+       l2cap::internal::ParameterProvider* parameter_provider,
+       DynamicChannelServiceManagerImpl* dynamic_service_manager,
+       FixedChannelServiceManagerImpl* fixed_service_manager);
+  ~Link();
+
+  hci::AddressWithType GetDevice() override {
+    return {acl_connection_->GetAddress(), acl_connection_->GetAddressType()};
+  }
+
+  struct PendingDynamicChannelConnection {
+    os::Handler* handler_;
+    DynamicChannelManager::OnConnectionOpenCallback on_open_callback_;
+    DynamicChannelManager::OnConnectionFailureCallback on_fail_callback_;
+    classic::DynamicChannelConfigurationOption configuration_;
+  };
+
+  struct PendingAuthenticateDynamicChannelConnection {
+    Psm psm_;
+    Cid cid_;
+    PendingDynamicChannelConnection pending_dynamic_channel_connection_;
+  };
+
+  // ACL methods
+
+  virtual void OnAclDisconnected(hci::ErrorCode status);
+
+  virtual void Disconnect();
+
+  virtual void Encrypt();
+
+  virtual void Authenticate();
+
+  virtual bool IsAuthenticated() const;
+
+  virtual void ReadRemoteVersionInformation();
+
+  virtual void ReadRemoteSupportedFeatures();
+
+  virtual void ReadRemoteExtendedFeatures();
+
+  virtual void ReadClockOffset();
+
+  // FixedChannel methods
+
+  std::shared_ptr<FixedChannelImpl> AllocateFixedChannel(Cid cid, SecurityPolicy security_policy);
+
+  virtual bool IsFixedChannelAllocated(Cid cid);
+
+  // DynamicChannel methods
+
+  virtual Cid ReserveDynamicChannel();
+
+  virtual void SendConnectionRequest(Psm psm, Cid local_cid);
+  virtual void SendConnectionRequest(Psm psm, Cid local_cid,
+                                     PendingDynamicChannelConnection pending_dynamic_channel_connection);
+
+  // Invoked by signalling manager to indicate an outgoing connection request failed and link shall free resources
+  virtual void OnOutgoingConnectionRequestFail(Cid local_cid);
+
+  virtual void SendInformationRequest(InformationRequestInfoType type);
+
+  virtual void SendDisconnectionRequest(Cid local_cid, Cid remote_cid) override;
+
+  virtual std::shared_ptr<l2cap::internal::DynamicChannelImpl> AllocateDynamicChannel(Psm psm, Cid remote_cid,
+                                                                                      SecurityPolicy security_policy);
+
+  virtual std::shared_ptr<l2cap::internal::DynamicChannelImpl> AllocateReservedDynamicChannel(
+      Cid reserved_cid, Psm psm, Cid remote_cid, SecurityPolicy security_policy);
+
+  virtual classic::DynamicChannelConfigurationOption GetConfigurationForInitialConfiguration(Cid cid);
+
+  virtual void FreeDynamicChannel(Cid cid);
+
+  // Check how many channels are acquired or in use, if zero, start tear down timer, if non-zero, cancel tear down timer
+  virtual void RefreshRefCount();
+
+  virtual void NotifyChannelCreation(Cid cid, std::unique_ptr<DynamicChannel> channel);
+  virtual void NotifyChannelFail(Cid cid, DynamicChannelManager::ConnectionResult result);
+
+  // Information received from signaling channel
+  virtual void SetRemoteConnectionlessMtu(Mtu mtu);
+  virtual Mtu GetRemoteConnectionlessMtu() const;
+  virtual void SetRemoteSupportsErtm(bool supported);
+  virtual bool GetRemoteSupportsErtm() const;
+  virtual void SetRemoteSupportsFcs(bool supported);
+  virtual bool GetRemoteSupportsFcs() const;
+
+  virtual std::string ToString() {
+    return GetDevice().ToString();
+  }
+
+  void SendLeCredit(Cid local_cid, uint16_t credit) override {}
+
+  void AddChannelPendingingAuthentication(PendingAuthenticateDynamicChannelConnection pending_channel);
+
+  // ConnectionManagementCallbacks
+  virtual void OnConnectionPacketTypeChanged(uint16_t packet_type) override;
+  virtual void OnAuthenticationComplete() override;
+  virtual void OnEncryptionChange(hci::EncryptionEnabled enabled) override;
+  virtual void OnChangeConnectionLinkKeyComplete() override;
+  virtual void OnReadClockOffsetComplete(uint16_t clock_offset) override;
+  virtual void OnModeChange(hci::Mode current_mode, uint16_t interval) override;
+  virtual void OnQosSetupComplete(hci::ServiceType service_type, uint32_t token_rate, uint32_t peak_bandwidth,
+                                  uint32_t latency, uint32_t delay_variation) override;
+  virtual void OnFlowSpecificationComplete(hci::FlowDirection flow_direction, hci::ServiceType service_type,
+                                           uint32_t token_rate, uint32_t token_bucket_size, uint32_t peak_bandwidth,
+                                           uint32_t access_latency) override;
+  virtual void OnFlushOccurred() override;
+  virtual void OnRoleDiscoveryComplete(hci::Role current_role) override;
+  virtual void OnReadLinkPolicySettingsComplete(uint16_t link_policy_settings) override;
+  virtual void OnReadAutomaticFlushTimeoutComplete(uint16_t flush_timeout) override;
+  virtual void OnReadTransmitPowerLevelComplete(uint8_t transmit_power_level) override;
+  virtual void OnReadLinkSupervisionTimeoutComplete(uint16_t link_supervision_timeout) override;
+  virtual void OnReadFailedContactCounterComplete(uint16_t failed_contact_counter) override;
+  virtual void OnReadLinkQualityComplete(uint8_t link_quality) override;
+  virtual void OnReadAfhChannelMapComplete(hci::AfhMode afh_mode, std::array<uint8_t, 10> afh_channel_map) override;
+  virtual void OnReadRssiComplete(uint8_t rssi) override;
+  virtual void OnReadClockComplete(uint32_t clock, uint16_t accuracy) override;
+
+ private:
+  os::Handler* l2cap_handler_;
+  l2cap::internal::FixedChannelAllocator<FixedChannelImpl, Link> fixed_channel_allocator_{this, l2cap_handler_};
+  l2cap::internal::DynamicChannelAllocator dynamic_channel_allocator_{this, l2cap_handler_};
+  std::unique_ptr<hci::AclConnection> acl_connection_;
+  l2cap::internal::DataPipelineManager data_pipeline_manager_;
+  l2cap::internal::ParameterProvider* parameter_provider_;
+  DynamicChannelServiceManagerImpl* dynamic_service_manager_;
+  FixedChannelServiceManagerImpl* fixed_service_manager_;
+  ClassicSignallingManager signalling_manager_;
+  std::unordered_map<Cid, PendingDynamicChannelConnection> local_cid_to_pending_dynamic_channel_connection_map_;
+  os::Alarm link_idle_disconnect_alarm_{l2cap_handler_};
+  Mtu remote_connectionless_mtu_ = kMinimumClassicMtu;
+  bool remote_supports_ertm_ = false;
+  bool remote_supports_fcs_ = false;
+  hci::EncryptionEnabled encryption_enabled_ = hci::EncryptionEnabled::OFF;
+  std::list<Link::PendingAuthenticateDynamicChannelConnection> pending_channel_list_;
+  DISALLOW_COPY_AND_ASSIGN(Link);
+};
+
+}  // namespace internal
+}  // namespace classic
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/classic/internal/link_manager.cc b/gd/l2cap/classic/internal/link_manager.cc
new file mode 100644
index 0000000..d71e35a
--- /dev/null
+++ b/gd/l2cap/classic/internal/link_manager.cc
@@ -0,0 +1,212 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <memory>
+#include <unordered_map>
+
+#include "hci/acl_manager.h"
+#include "hci/address.h"
+#include "l2cap/classic/internal/link.h"
+#include "l2cap/internal/scheduler_fifo.h"
+#include "os/log.h"
+
+#include "l2cap/classic/internal/link_manager.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace classic {
+namespace internal {
+
+void LinkManager::ConnectFixedChannelServices(hci::Address device,
+                                              PendingFixedChannelConnection pending_fixed_channel_connection) {
+  // Check if there is any service registered
+  auto fixed_channel_services = fixed_channel_service_manager_->GetRegisteredServices();
+  if (fixed_channel_services.empty()) {
+    // If so, return error
+    pending_fixed_channel_connection.handler_->Post(common::BindOnce(
+        std::move(pending_fixed_channel_connection.on_fail_callback_),
+        FixedChannelManager::ConnectionResult{
+            .connection_result_code = FixedChannelManager::ConnectionResultCode::FAIL_NO_SERVICE_REGISTERED}));
+    return;
+  }
+  // Otherwise, check if device has an ACL connection
+  auto* link = GetLink(device);
+  if (link != nullptr) {
+    // If device already have an ACL connection
+    // Check if all registered services have an allocated channel and allocate one if not already allocated
+    int num_new_channels = 0;
+    for (auto& fixed_channel_service : fixed_channel_services) {
+      if (link->IsFixedChannelAllocated(fixed_channel_service.first)) {
+        // This channel is already allocated for this link, do not allocated twice
+        continue;
+      }
+      if (fixed_channel_service.first == kClassicPairingTriggerCid) {
+        this->TriggerPairing(link);
+      }
+      // Allocate channel for newly registered fixed channels
+      auto fixed_channel_impl = link->AllocateFixedChannel(fixed_channel_service.first, SecurityPolicy());
+      fixed_channel_service.second->NotifyChannelCreation(
+          std::make_unique<FixedChannel>(fixed_channel_impl, l2cap_handler_));
+      num_new_channels++;
+    }
+    // Declare connection failure if no new channels are created
+    if (num_new_channels == 0) {
+      pending_fixed_channel_connection.handler_->Post(common::BindOnce(
+          std::move(pending_fixed_channel_connection.on_fail_callback_),
+          FixedChannelManager::ConnectionResult{
+              .connection_result_code = FixedChannelManager::ConnectionResultCode::FAIL_ALL_SERVICES_HAVE_CHANNEL}));
+    }
+    // No need to create ACL connection, return without saving any pending connections
+    return;
+  }
+  // If not, create new ACL connection
+  // Add request to pending link list first
+  auto pending_link = pending_links_.find(device);
+  if (pending_link == pending_links_.end()) {
+    // Create pending link if not exist
+    pending_links_.try_emplace(device);
+    pending_link = pending_links_.find(device);
+  }
+  pending_link->second.pending_fixed_channel_connections_.push_back(std::move(pending_fixed_channel_connection));
+  // Then create new ACL connection
+  acl_manager_->CreateConnection(device);
+}
+
+void LinkManager::ConnectDynamicChannelServices(
+    hci::Address device, Link::PendingDynamicChannelConnection pending_dynamic_channel_connection, Psm psm) {
+  auto* link = GetLink(device);
+  if (link == nullptr) {
+    acl_manager_->CreateConnection(device);
+    if (pending_dynamic_channels_.find(device) != pending_dynamic_channels_.end()) {
+      pending_dynamic_channels_[device].push_back(psm);
+      pending_dynamic_channels_callbacks_[device].push_back(std::move(pending_dynamic_channel_connection));
+    } else {
+      pending_dynamic_channels_[device] = {psm};
+      pending_dynamic_channels_callbacks_[device].push_back(std::move(pending_dynamic_channel_connection));
+    }
+    return;
+  }
+  if (dynamic_channel_service_manager_->GetService(psm)->GetSecurityPolicy().RequiresAuthentication() &&
+      !link->IsAuthenticated()) {
+    link->AddChannelPendingingAuthentication(
+        {psm, link->ReserveDynamicChannel(), std::move(pending_dynamic_channel_connection)});
+    link->Authenticate();
+    return;
+  }
+  link->SendConnectionRequest(psm, link->ReserveDynamicChannel(), std::move(pending_dynamic_channel_connection));
+}
+
+Link* LinkManager::GetLink(const hci::Address device) {
+  if (links_.find(device) == links_.end()) {
+    return nullptr;
+  }
+  return &links_.find(device)->second;
+}
+
+void LinkManager::TriggerPairing(Link* link) {
+  if (!link->IsAuthenticated()) {
+    link->Authenticate();
+  }
+  link->ReadRemoteVersionInformation();
+  link->ReadRemoteSupportedFeatures();
+  link->ReadRemoteExtendedFeatures();
+  link->ReadClockOffset();
+}
+
+void LinkManager::OnConnectSuccess(std::unique_ptr<hci::AclConnection> acl_connection) {
+  // Same link should not be connected twice
+  hci::Address device = acl_connection->GetAddress();
+  ASSERT_LOG(GetLink(device) == nullptr, "%s is connected twice without disconnection",
+             acl_connection->GetAddress().ToString().c_str());
+  // Register ACL disconnection callback in LinkManager so that we can clean up link resource properly
+  acl_connection->RegisterDisconnectCallback(
+      common::BindOnce(&LinkManager::OnDisconnect, common::Unretained(this), device), l2cap_handler_);
+  links_.try_emplace(device, l2cap_handler_, std::move(acl_connection), parameter_provider_,
+                     dynamic_channel_service_manager_, fixed_channel_service_manager_);
+  auto* link = GetLink(device);
+  ASSERT(link != nullptr);
+  link->SendInformationRequest(InformationRequestInfoType::EXTENDED_FEATURES_SUPPORTED);
+  link->SendInformationRequest(InformationRequestInfoType::FIXED_CHANNELS_SUPPORTED);
+
+  // Allocate and distribute channels for all registered fixed channel services
+  auto fixed_channel_services = fixed_channel_service_manager_->GetRegisteredServices();
+  for (auto& fixed_channel_service : fixed_channel_services) {
+    auto fixed_channel_impl = link->AllocateFixedChannel(fixed_channel_service.first, SecurityPolicy());
+    fixed_channel_service.second->NotifyChannelCreation(
+        std::make_unique<FixedChannel>(fixed_channel_impl, l2cap_handler_));
+    if (fixed_channel_service.first == kClassicPairingTriggerCid) {
+      this->TriggerPairing(link);
+    }
+  }
+  if (pending_dynamic_channels_.find(device) != pending_dynamic_channels_.end()) {
+    for (Psm psm : pending_dynamic_channels_[device]) {
+      auto& callbacks = pending_dynamic_channels_callbacks_[device].front();
+      link->SendConnectionRequest(psm, link->ReserveDynamicChannel(), std::move(callbacks));
+      pending_dynamic_channels_callbacks_[device].pop_front();
+    }
+    pending_dynamic_channels_.erase(device);
+    pending_dynamic_channels_callbacks_.erase(device);
+  }
+  // Remove device from pending links list, if any
+  auto pending_link = pending_links_.find(device);
+  if (pending_link == pending_links_.end()) {
+    // This an incoming connection, exit
+    return;
+  }
+  // This is an outgoing connection, remove entry in pending link list
+  pending_links_.erase(pending_link);
+}
+
+void LinkManager::OnConnectFail(hci::Address device, hci::ErrorCode reason) {
+  // Notify all pending links for this device
+  auto pending_link = pending_links_.find(device);
+  if (pending_link == pending_links_.end()) {
+    // There is no pending link, exit
+    LOG_DEBUG("Connection to %s failed without a pending link; reason: %s", device.ToString().c_str(),
+              hci::ErrorCodeText(reason).c_str());
+    if (pending_dynamic_channels_callbacks_.find(device) != pending_dynamic_channels_callbacks_.end()) {
+      for (Link::PendingDynamicChannelConnection& callbacks : pending_dynamic_channels_callbacks_[device]) {
+        callbacks.handler_->Post(common::BindOnce(std::move(callbacks.on_fail_callback_),
+                                                  DynamicChannelManager::ConnectionResult{
+                                                      .hci_error = hci::ErrorCode::CONNECTION_TIMEOUT,
+                                                  }));
+      }
+      pending_dynamic_channels_.erase(device);
+      pending_dynamic_channels_callbacks_.erase(device);
+    }
+    return;
+  }
+  for (auto& pending_fixed_channel_connection : pending_link->second.pending_fixed_channel_connections_) {
+    pending_fixed_channel_connection.handler_->Post(common::BindOnce(
+        std::move(pending_fixed_channel_connection.on_fail_callback_),
+        FixedChannelManager::ConnectionResult{
+            .connection_result_code = FixedChannelManager::ConnectionResultCode::FAIL_HCI_ERROR, .hci_error = reason}));
+  }
+  // Remove entry in pending link list
+  pending_links_.erase(pending_link);
+}
+
+void LinkManager::OnDisconnect(hci::Address device, hci::ErrorCode status) {
+  auto* link = GetLink(device);
+  ASSERT_LOG(link != nullptr, "Device %s is disconnected with reason 0x%x, but not in local database",
+             device.ToString().c_str(), static_cast<uint8_t>(status));
+  link->OnAclDisconnected(status);
+  links_.erase(device);
+}
+
+}  // namespace internal
+}  // namespace classic
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/classic/internal/link_manager.h b/gd/l2cap/classic/internal/link_manager.h
new file mode 100644
index 0000000..661533b
--- /dev/null
+++ b/gd/l2cap/classic/internal/link_manager.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <memory>
+#include <unordered_map>
+
+#include "hci/acl_manager.h"
+#include "hci/address.h"
+#include "l2cap/classic/dynamic_channel_manager.h"
+#include "l2cap/classic/fixed_channel_manager.h"
+#include "l2cap/classic/internal/dynamic_channel_service_manager_impl.h"
+#include "l2cap/classic/internal/fixed_channel_service_manager_impl.h"
+#include "l2cap/classic/internal/link.h"
+#include "l2cap/internal/parameter_provider.h"
+#include "l2cap/internal/scheduler.h"
+#include "os/handler.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace classic {
+namespace internal {
+
+class LinkManager : public hci::ConnectionCallbacks {
+ public:
+  LinkManager(os::Handler* l2cap_handler, hci::AclManager* acl_manager,
+              FixedChannelServiceManagerImpl* fixed_channel_service_manager,
+              DynamicChannelServiceManagerImpl* dynamic_channel_service_manager,
+              l2cap::internal::ParameterProvider* parameter_provider)
+      : l2cap_handler_(l2cap_handler), acl_manager_(acl_manager),
+        fixed_channel_service_manager_(fixed_channel_service_manager),
+        dynamic_channel_service_manager_(dynamic_channel_service_manager), parameter_provider_(parameter_provider) {
+    acl_manager_->RegisterCallbacks(this, l2cap_handler_);
+  }
+
+  struct PendingFixedChannelConnection {
+    os::Handler* handler_;
+    FixedChannelManager::OnConnectionFailureCallback on_fail_callback_;
+  };
+
+  struct PendingLink {
+    std::vector<PendingFixedChannelConnection> pending_fixed_channel_connections_;
+  };
+
+  // ACL methods
+
+  Link* GetLink(hci::Address device);
+  void OnConnectSuccess(std::unique_ptr<hci::AclConnection> acl_connection) override;
+  void OnConnectFail(hci::Address device, hci::ErrorCode reason) override;
+  void OnDisconnect(hci::Address device, hci::ErrorCode status);
+
+  // FixedChannelManager methods
+
+  void ConnectFixedChannelServices(hci::Address device, PendingFixedChannelConnection pending_fixed_channel_connection);
+
+  // DynamicChannelManager methods
+
+  void ConnectDynamicChannelServices(hci::Address device,
+                                     Link::PendingDynamicChannelConnection pending_dynamic_channel_connection, Psm psm);
+
+ private:
+  void TriggerPairing(Link* link);
+
+  // Dependencies
+  os::Handler* l2cap_handler_;
+  hci::AclManager* acl_manager_;
+  FixedChannelServiceManagerImpl* fixed_channel_service_manager_;
+  DynamicChannelServiceManagerImpl* dynamic_channel_service_manager_;
+  l2cap::internal::ParameterProvider* parameter_provider_;
+
+  // Internal states
+  std::unordered_map<hci::Address, PendingLink> pending_links_;
+  std::unordered_map<hci::Address, Link> links_;
+  std::unordered_map<hci::Address, std::list<Psm>> pending_dynamic_channels_;
+  std::unordered_map<hci::Address, std::list<Link::PendingDynamicChannelConnection>>
+      pending_dynamic_channels_callbacks_;
+  DISALLOW_COPY_AND_ASSIGN(LinkManager);
+};
+
+}  // namespace internal
+}  // namespace classic
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/classic/internal/link_manager_test.cc b/gd/l2cap/classic/internal/link_manager_test.cc
new file mode 100644
index 0000000..c385756
--- /dev/null
+++ b/gd/l2cap/classic/internal/link_manager_test.cc
@@ -0,0 +1,522 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "l2cap/classic/internal/link_manager.h"
+
+#include <future>
+#include <thread>
+
+#include "common/bind.h"
+#include "common/testing/bind_test_util.h"
+#include "dynamic_channel_service_manager_impl_mock.h"
+#include "hci/acl_manager_mock.h"
+#include "hci/address.h"
+#include "l2cap/cid.h"
+#include "l2cap/classic/fixed_channel_manager.h"
+#include "l2cap/classic/internal/fixed_channel_service_impl_mock.h"
+#include "l2cap/classic/internal/fixed_channel_service_manager_impl_mock.h"
+#include "l2cap/internal/parameter_provider_mock.h"
+#include "os/handler.h"
+#include "os/thread.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace bluetooth {
+namespace l2cap {
+namespace classic {
+namespace internal {
+namespace {
+
+using hci::testing::MockAclConnection;
+using hci::testing::MockAclManager;
+using l2cap::internal::testing::MockParameterProvider;
+using ::testing::_;  // Matcher to any value
+using ::testing::ByMove;
+using ::testing::DoAll;
+using testing::MockDynamicChannelServiceManagerImpl;
+using testing::MockFixedChannelServiceImpl;
+using testing::MockFixedChannelServiceManagerImpl;
+using ::testing::Return;
+using ::testing::SaveArg;
+
+constexpr static auto kTestIdleDisconnectTimeoutLong = std::chrono::milliseconds(1000);
+constexpr static auto kTestIdleDisconnectTimeoutShort = std::chrono::milliseconds(1000);
+
+class L2capClassicLinkManagerTest : public ::testing::Test {
+ public:
+  static void SyncHandler(os::Handler* handler) {
+    std::promise<void> promise;
+    auto future = promise.get_future();
+    handler->Post(common::BindOnce(&std::promise<void>::set_value, common::Unretained(&promise)));
+    future.wait_for(std::chrono::milliseconds(3));
+  }
+
+ protected:
+  void SetUp() override {
+    thread_ = new os::Thread("test_thread", os::Thread::Priority::NORMAL);
+    l2cap_handler_ = new os::Handler(thread_);
+    mock_parameter_provider_ = new MockParameterProvider;
+    EXPECT_CALL(*mock_parameter_provider_, GetClassicLinkIdleDisconnectTimeout)
+        .WillRepeatedly(Return(kTestIdleDisconnectTimeoutLong));
+  }
+
+  void TearDown() override {
+    delete mock_parameter_provider_;
+    l2cap_handler_->Clear();
+    delete l2cap_handler_;
+    delete thread_;
+  }
+
+  os::Thread* thread_ = nullptr;
+  os::Handler* l2cap_handler_ = nullptr;
+  MockParameterProvider* mock_parameter_provider_ = nullptr;
+};
+
+TEST_F(L2capClassicLinkManagerTest, connect_fixed_channel_service_without_acl) {
+  MockFixedChannelServiceManagerImpl mock_classic_fixed_channel_service_manager;
+  MockDynamicChannelServiceManagerImpl mock_classic_dynamic_channel_service_manager;
+  MockAclManager mock_acl_manager;
+  hci::Address device{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}};
+  auto user_handler = std::make_unique<os::Handler>(thread_);
+
+  // Step 1: Verify callback registration with HCI
+  hci::ConnectionCallbacks* hci_connection_callbacks = nullptr;
+  os::Handler* hci_callback_handler = nullptr;
+  EXPECT_CALL(mock_acl_manager, RegisterCallbacks(_, _))
+      .WillOnce(DoAll(SaveArg<0>(&hci_connection_callbacks), SaveArg<1>(&hci_callback_handler)));
+  LinkManager classic_link_manager(l2cap_handler_, &mock_acl_manager, &mock_classic_fixed_channel_service_manager,
+                                   &mock_classic_dynamic_channel_service_manager, mock_parameter_provider_);
+  EXPECT_EQ(hci_connection_callbacks, &classic_link_manager);
+  EXPECT_EQ(hci_callback_handler, l2cap_handler_);
+
+  // Register fake services
+  MockFixedChannelServiceImpl mock_service_1, mock_service_2;
+  std::vector<std::pair<Cid, FixedChannelServiceImpl*>> results;
+  results.emplace_back(kSmpBrCid, &mock_service_1);
+  results.emplace_back(kConnectionlessCid, &mock_service_2);
+  EXPECT_CALL(mock_classic_fixed_channel_service_manager, GetRegisteredServices()).WillRepeatedly(Return(results));
+
+  // Step 2: Connect to fixed channel without ACL connection should trigger ACL connection process
+  EXPECT_CALL(mock_acl_manager, CreateConnection(device)).Times(1);
+  LinkManager::PendingFixedChannelConnection pending_fixed_channel_connection{
+      .handler_ = user_handler.get(),
+      .on_fail_callback_ = common::BindOnce([](FixedChannelManager::ConnectionResult result) { FAIL(); })};
+  classic_link_manager.ConnectFixedChannelServices(device, std::move(pending_fixed_channel_connection));
+
+  // Step 3: ACL connection success event should trigger channel creation for all registered services
+
+  std::unique_ptr<MockAclConnection> acl_connection = std::make_unique<MockAclConnection>();
+  EXPECT_CALL(*acl_connection, GetAddress()).WillRepeatedly(Return(device));
+  EXPECT_CALL(*acl_connection, GetAddressType()).WillRepeatedly(Return(hci::AddressType::PUBLIC_DEVICE_ADDRESS));
+  EXPECT_CALL(*acl_connection, RegisterCallbacks(_, l2cap_handler_)).Times(1);
+  EXPECT_CALL(*acl_connection, RegisterDisconnectCallback(_, l2cap_handler_)).Times(1);
+  EXPECT_CALL(*acl_connection, UnregisterCallbacks(_)).Times(1);
+  std::unique_ptr<FixedChannel> channel_1, channel_2;
+  std::promise<void> promise_1, promise_2;
+  auto future_1 = promise_1.get_future();
+  auto future_2 = promise_2.get_future();
+  EXPECT_CALL(mock_service_1, NotifyChannelCreation(_))
+      .WillOnce([&channel_1, &promise_1](std::unique_ptr<FixedChannel> channel) {
+        channel_1 = std::move(channel);
+        promise_1.set_value();
+      });
+  EXPECT_CALL(mock_service_2, NotifyChannelCreation(_))
+      .WillOnce([&channel_2, &promise_2](std::unique_ptr<FixedChannel> channel) {
+        channel_2 = std::move(channel);
+        promise_2.set_value();
+      });
+  hci_callback_handler->Post(common::BindOnce(&hci::ConnectionCallbacks::OnConnectSuccess,
+                                              common::Unretained(hci_connection_callbacks), std::move(acl_connection)));
+  SyncHandler(hci_callback_handler);
+  auto future_1_status = future_1.wait_for(kTestIdleDisconnectTimeoutShort);
+  EXPECT_EQ(future_1_status, std::future_status::ready);
+  EXPECT_NE(channel_1, nullptr);
+  auto future_2_status = future_2.wait_for(kTestIdleDisconnectTimeoutShort);
+  EXPECT_EQ(future_2_status, std::future_status::ready);
+  EXPECT_NE(channel_2, nullptr);
+
+  // Step 4: Calling ConnectServices() to the same device will no trigger another connection attempt
+  FixedChannelManager::ConnectionResult my_result;
+  LinkManager::PendingFixedChannelConnection pending_fixed_channel_connection_2{
+      .handler_ = user_handler.get(),
+      .on_fail_callback_ = common::testing::BindLambdaForTesting(
+          [&my_result](FixedChannelManager::ConnectionResult result) { my_result = result; })};
+  classic_link_manager.ConnectFixedChannelServices(device, std::move(pending_fixed_channel_connection_2));
+  SyncHandler(user_handler.get());
+  EXPECT_EQ(my_result.connection_result_code,
+            FixedChannelManager::ConnectionResultCode::FAIL_ALL_SERVICES_HAVE_CHANNEL);
+
+  // Step 5: Register new service will cause new channels to be created during ConnectServices()
+  MockFixedChannelServiceImpl mock_service_3;
+  results.emplace_back(kSmpBrCid + 1, &mock_service_3);
+  EXPECT_CALL(mock_classic_fixed_channel_service_manager, GetRegisteredServices()).WillRepeatedly(Return(results));
+  LinkManager::PendingFixedChannelConnection pending_fixed_channel_connection_3{
+      .handler_ = user_handler.get(),
+      .on_fail_callback_ = common::BindOnce([](FixedChannelManager::ConnectionResult result) { FAIL(); })};
+  std::unique_ptr<FixedChannel> channel_3;
+  EXPECT_CALL(mock_service_3, NotifyChannelCreation(_)).WillOnce([&channel_3](std::unique_ptr<FixedChannel> channel) {
+    channel_3 = std::move(channel);
+  });
+  classic_link_manager.ConnectFixedChannelServices(device, std::move(pending_fixed_channel_connection_3));
+  EXPECT_NE(channel_3, nullptr);
+
+  user_handler->Clear();
+
+  classic_link_manager.OnDisconnect(device, hci::ErrorCode::SUCCESS);
+}
+
+TEST_F(L2capClassicLinkManagerTest, connect_fixed_channel_service_without_acl_with_no_service) {
+  MockFixedChannelServiceManagerImpl mock_classic_fixed_channel_service_manager;
+  MockAclManager mock_acl_manager;
+  hci::Address device{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}};
+  auto user_handler = std::make_unique<os::Handler>(thread_);
+
+  // Step 1: Verify callback registration with HCI
+  hci::ConnectionCallbacks* hci_connection_callbacks = nullptr;
+  os::Handler* hci_callback_handler = nullptr;
+  EXPECT_CALL(mock_acl_manager, RegisterCallbacks(_, _))
+      .WillOnce(DoAll(SaveArg<0>(&hci_connection_callbacks), SaveArg<1>(&hci_callback_handler)));
+  LinkManager classic_link_manager(l2cap_handler_, &mock_acl_manager, &mock_classic_fixed_channel_service_manager,
+                                   nullptr, mock_parameter_provider_);
+  EXPECT_EQ(hci_connection_callbacks, &classic_link_manager);
+  EXPECT_EQ(hci_callback_handler, l2cap_handler_);
+
+  // Make sure no service is registered
+  std::vector<std::pair<Cid, FixedChannelServiceImpl*>> results;
+  EXPECT_CALL(mock_classic_fixed_channel_service_manager, GetRegisteredServices()).WillRepeatedly(Return(results));
+
+  // Step 2: Connect to fixed channel without any service registered will result in failure
+  EXPECT_CALL(mock_acl_manager, CreateConnection(device)).Times(0);
+  FixedChannelManager::ConnectionResult my_result;
+  LinkManager::PendingFixedChannelConnection pending_fixed_channel_connection{
+      .handler_ = user_handler.get(),
+      .on_fail_callback_ = common::testing::BindLambdaForTesting(
+          [&my_result](FixedChannelManager::ConnectionResult result) { my_result = result; })};
+  classic_link_manager.ConnectFixedChannelServices(device, std::move(pending_fixed_channel_connection));
+  SyncHandler(user_handler.get());
+  EXPECT_EQ(my_result.connection_result_code, FixedChannelManager::ConnectionResultCode::FAIL_NO_SERVICE_REGISTERED);
+
+  user_handler->Clear();
+}
+
+TEST_F(L2capClassicLinkManagerTest, connect_fixed_channel_service_without_acl_with_hci_failure) {
+  MockFixedChannelServiceManagerImpl mock_classic_fixed_channel_service_manager;
+  MockAclManager mock_acl_manager;
+  hci::Address device{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}};
+  auto user_handler = std::make_unique<os::Handler>(thread_);
+
+  // Step 1: Verify callback registration with HCI
+  hci::ConnectionCallbacks* hci_connection_callbacks = nullptr;
+  os::Handler* hci_callback_handler = nullptr;
+  EXPECT_CALL(mock_acl_manager, RegisterCallbacks(_, _))
+      .WillOnce(DoAll(SaveArg<0>(&hci_connection_callbacks), SaveArg<1>(&hci_callback_handler)));
+  LinkManager classic_link_manager(l2cap_handler_, &mock_acl_manager, &mock_classic_fixed_channel_service_manager,
+                                   nullptr, mock_parameter_provider_);
+  EXPECT_EQ(hci_connection_callbacks, &classic_link_manager);
+  EXPECT_EQ(hci_callback_handler, l2cap_handler_);
+
+  // Register fake services
+  MockFixedChannelServiceImpl mock_service_1;
+  std::vector<std::pair<Cid, FixedChannelServiceImpl*>> results;
+  results.emplace_back(kSmpBrCid, &mock_service_1);
+  EXPECT_CALL(mock_classic_fixed_channel_service_manager, GetRegisteredServices()).WillRepeatedly(Return(results));
+
+  // Step 2: Connect to fixed channel without ACL connection should trigger ACL connection process
+  EXPECT_CALL(mock_acl_manager, CreateConnection(device)).Times(1);
+  FixedChannelManager::ConnectionResult my_result;
+  LinkManager::PendingFixedChannelConnection pending_fixed_channel_connection{
+      .handler_ = user_handler.get(),
+      .on_fail_callback_ = common::testing::BindLambdaForTesting(
+          [&my_result](FixedChannelManager::ConnectionResult result) { my_result = result; })};
+  classic_link_manager.ConnectFixedChannelServices(device, std::move(pending_fixed_channel_connection));
+
+  // Step 3: ACL connection failure event should trigger connection failure callback
+  EXPECT_CALL(mock_service_1, NotifyChannelCreation(_)).Times(0);
+  hci_callback_handler->Post(common::BindOnce(&hci::ConnectionCallbacks::OnConnectFail,
+                                              common::Unretained(hci_connection_callbacks), device,
+                                              hci::ErrorCode::PAGE_TIMEOUT));
+  SyncHandler(hci_callback_handler);
+  SyncHandler(user_handler.get());
+  EXPECT_EQ(my_result.connection_result_code, FixedChannelManager::ConnectionResultCode::FAIL_HCI_ERROR);
+  EXPECT_EQ(my_result.hci_error, hci::ErrorCode::PAGE_TIMEOUT);
+
+  user_handler->Clear();
+}
+
+TEST_F(L2capClassicLinkManagerTest, not_acquiring_channels_should_disconnect_acl_after_timeout) {
+  EXPECT_CALL(*mock_parameter_provider_, GetClassicLinkIdleDisconnectTimeout)
+      .WillRepeatedly(Return(kTestIdleDisconnectTimeoutShort));
+  MockFixedChannelServiceManagerImpl mock_classic_fixed_channel_service_manager;
+  MockAclManager mock_acl_manager;
+  hci::Address device{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}};
+  auto user_handler = std::make_unique<os::Handler>(thread_);
+
+  // Step 1: Verify callback registration with HCI
+  hci::ConnectionCallbacks* hci_connection_callbacks = nullptr;
+  os::Handler* hci_callback_handler = nullptr;
+  EXPECT_CALL(mock_acl_manager, RegisterCallbacks(_, _))
+      .WillOnce(DoAll(SaveArg<0>(&hci_connection_callbacks), SaveArg<1>(&hci_callback_handler)));
+  LinkManager classic_link_manager(l2cap_handler_, &mock_acl_manager, &mock_classic_fixed_channel_service_manager,
+                                   nullptr, mock_parameter_provider_);
+  EXPECT_EQ(hci_connection_callbacks, &classic_link_manager);
+  EXPECT_EQ(hci_callback_handler, l2cap_handler_);
+
+  // Register fake services
+  MockFixedChannelServiceImpl mock_service_1, mock_service_2;
+  std::vector<std::pair<Cid, FixedChannelServiceImpl*>> results;
+  results.emplace_back(kSmpBrCid, &mock_service_1);
+  results.emplace_back(kConnectionlessCid, &mock_service_2);
+  EXPECT_CALL(mock_classic_fixed_channel_service_manager, GetRegisteredServices()).WillRepeatedly(Return(results));
+
+  // Step 2: Connect to fixed channel without ACL connection should trigger ACL connection process
+  EXPECT_CALL(mock_acl_manager, CreateConnection(device)).Times(1);
+  LinkManager::PendingFixedChannelConnection pending_fixed_channel_connection{
+      .handler_ = user_handler.get(),
+      .on_fail_callback_ = common::BindOnce([](FixedChannelManager::ConnectionResult result) { FAIL(); })};
+  classic_link_manager.ConnectFixedChannelServices(device, std::move(pending_fixed_channel_connection));
+
+  // Step 3: ACL connection success event should trigger channel creation for all registered services
+  auto* raw_acl_connection = new MockAclConnection();
+  std::unique_ptr<MockAclConnection> acl_connection(raw_acl_connection);
+  EXPECT_CALL(*acl_connection, GetAddress()).WillRepeatedly(Return(device));
+  EXPECT_CALL(*acl_connection, GetAddressType()).WillRepeatedly(Return(hci::AddressType::PUBLIC_DEVICE_ADDRESS));
+  EXPECT_CALL(*acl_connection, RegisterCallbacks(_, l2cap_handler_)).Times(1);
+  EXPECT_CALL(*acl_connection, RegisterDisconnectCallback(_, l2cap_handler_)).Times(1);
+  EXPECT_CALL(*acl_connection, UnregisterCallbacks(_)).Times(1);
+  std::unique_ptr<FixedChannel> channel_1, channel_2;
+  std::promise<void> promise_1, promise_2;
+  auto future_1 = promise_1.get_future();
+  auto future_2 = promise_2.get_future();
+  EXPECT_CALL(mock_service_1, NotifyChannelCreation(_))
+      .WillOnce([&channel_1, &promise_1](std::unique_ptr<FixedChannel> channel) {
+        channel_1 = std::move(channel);
+        promise_1.set_value();
+      });
+  EXPECT_CALL(mock_service_2, NotifyChannelCreation(_))
+      .WillOnce([&channel_2, &promise_2](std::unique_ptr<FixedChannel> channel) {
+        channel_2 = std::move(channel);
+        promise_2.set_value();
+      });
+  hci_callback_handler->Post(common::BindOnce(&hci::ConnectionCallbacks::OnConnectSuccess,
+                                              common::Unretained(hci_connection_callbacks), std::move(acl_connection)));
+  SyncHandler(hci_callback_handler);
+  auto future_1_status = future_1.wait_for(kTestIdleDisconnectTimeoutShort);
+  EXPECT_EQ(future_1_status, std::future_status::ready);
+  EXPECT_NE(channel_1, nullptr);
+  auto future_2_status = future_2.wait_for(kTestIdleDisconnectTimeoutShort);
+  EXPECT_EQ(future_2_status, std::future_status::ready);
+  EXPECT_NE(channel_2, nullptr);
+  hci::ErrorCode status_1 = hci::ErrorCode::SUCCESS;
+  channel_1->RegisterOnCloseCallback(
+      user_handler.get(), common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { status_1 = status; }));
+  hci::ErrorCode status_2 = hci::ErrorCode::SUCCESS;
+  channel_2->RegisterOnCloseCallback(
+      user_handler.get(), common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { status_2 = status; }));
+
+  // Step 4: Leave channel IDLE long enough, they will disconnect
+  EXPECT_CALL(*raw_acl_connection, Disconnect(hci::DisconnectReason::REMOTE_USER_TERMINATED_CONNECTION)).Times(1);
+  std::this_thread::sleep_for(kTestIdleDisconnectTimeoutShort * 1.2);
+
+  // Step 5: Link disconnect will trigger all callbacks
+  classic_link_manager.OnDisconnect(device, hci::ErrorCode::CONNECTION_TERMINATED_BY_LOCAL_HOST);
+  SyncHandler(user_handler.get());
+  EXPECT_EQ(hci::ErrorCode::CONNECTION_TERMINATED_BY_LOCAL_HOST, status_1);
+  EXPECT_EQ(hci::ErrorCode::CONNECTION_TERMINATED_BY_LOCAL_HOST, status_2);
+
+  user_handler->Clear();
+}
+
+TEST_F(L2capClassicLinkManagerTest, acquiring_channels_should_not_disconnect_acl_after_timeout) {
+  EXPECT_CALL(*mock_parameter_provider_, GetClassicLinkIdleDisconnectTimeout)
+      .WillRepeatedly(Return(kTestIdleDisconnectTimeoutShort));
+  MockFixedChannelServiceManagerImpl mock_classic_fixed_channel_service_manager;
+  MockAclManager mock_acl_manager;
+  hci::Address device{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}};
+  auto user_handler = std::make_unique<os::Handler>(thread_);
+
+  // Step 1: Verify callback registration with HCI
+  hci::ConnectionCallbacks* hci_connection_callbacks = nullptr;
+  os::Handler* hci_callback_handler = nullptr;
+  EXPECT_CALL(mock_acl_manager, RegisterCallbacks(_, _))
+      .WillOnce(DoAll(SaveArg<0>(&hci_connection_callbacks), SaveArg<1>(&hci_callback_handler)));
+  LinkManager classic_link_manager(l2cap_handler_, &mock_acl_manager, &mock_classic_fixed_channel_service_manager,
+                                   nullptr, mock_parameter_provider_);
+  EXPECT_EQ(hci_connection_callbacks, &classic_link_manager);
+  EXPECT_EQ(hci_callback_handler, l2cap_handler_);
+
+  // Register fake services
+  MockFixedChannelServiceImpl mock_service_1, mock_service_2;
+  std::vector<std::pair<Cid, FixedChannelServiceImpl*>> results;
+  results.emplace_back(kSmpBrCid, &mock_service_1);
+  results.emplace_back(kConnectionlessCid, &mock_service_2);
+  EXPECT_CALL(mock_classic_fixed_channel_service_manager, GetRegisteredServices()).WillRepeatedly(Return(results));
+
+  // Step 2: Connect to fixed channel without ACL connection should trigger ACL connection process
+  EXPECT_CALL(mock_acl_manager, CreateConnection(device)).Times(1);
+  LinkManager::PendingFixedChannelConnection pending_fixed_channel_connection{
+      .handler_ = user_handler.get(),
+      .on_fail_callback_ = common::BindOnce([](FixedChannelManager::ConnectionResult result) { FAIL(); })};
+  classic_link_manager.ConnectFixedChannelServices(device, std::move(pending_fixed_channel_connection));
+
+  // Step 3: ACL connection success event should trigger channel creation for all registered services
+  auto* raw_acl_connection = new MockAclConnection();
+  std::unique_ptr<MockAclConnection> acl_connection(raw_acl_connection);
+  EXPECT_CALL(*acl_connection, GetAddress()).WillRepeatedly(Return(device));
+  EXPECT_CALL(*acl_connection, GetAddressType()).WillRepeatedly(Return(hci::AddressType::PUBLIC_DEVICE_ADDRESS));
+  EXPECT_CALL(*acl_connection, RegisterCallbacks(_, l2cap_handler_)).Times(1);
+  EXPECT_CALL(*acl_connection, RegisterDisconnectCallback(_, l2cap_handler_)).Times(1);
+  EXPECT_CALL(*acl_connection, UnregisterCallbacks(_)).Times(1);
+  std::unique_ptr<FixedChannel> channel_1, channel_2;
+  std::promise<void> promise_1, promise_2;
+  auto future_1 = promise_1.get_future();
+  auto future_2 = promise_2.get_future();
+  EXPECT_CALL(mock_service_1, NotifyChannelCreation(_))
+      .WillOnce([&channel_1, &promise_1](std::unique_ptr<FixedChannel> channel) {
+        channel_1 = std::move(channel);
+        promise_1.set_value();
+      });
+  EXPECT_CALL(mock_service_2, NotifyChannelCreation(_))
+      .WillOnce([&channel_2, &promise_2](std::unique_ptr<FixedChannel> channel) {
+        channel_2 = std::move(channel);
+        promise_2.set_value();
+      });
+  hci_callback_handler->Post(common::BindOnce(&hci::ConnectionCallbacks::OnConnectSuccess,
+                                              common::Unretained(hci_connection_callbacks), std::move(acl_connection)));
+  SyncHandler(hci_callback_handler);
+  auto future_1_status = future_1.wait_for(kTestIdleDisconnectTimeoutShort);
+  EXPECT_EQ(future_1_status, std::future_status::ready);
+  EXPECT_NE(channel_1, nullptr);
+  auto future_2_status = future_2.wait_for(kTestIdleDisconnectTimeoutShort);
+  EXPECT_EQ(future_2_status, std::future_status::ready);
+  EXPECT_NE(channel_2, nullptr);
+  hci::ErrorCode status_1 = hci::ErrorCode::SUCCESS;
+  channel_1->RegisterOnCloseCallback(
+      user_handler.get(), common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { status_1 = status; }));
+  hci::ErrorCode status_2 = hci::ErrorCode::SUCCESS;
+  channel_2->RegisterOnCloseCallback(
+      user_handler.get(), common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { status_2 = status; }));
+
+  channel_1->Acquire();
+
+  // Step 4: Leave channel IDLE, it won't disconnect to due acquired channel 1
+  EXPECT_CALL(*raw_acl_connection, Disconnect(hci::DisconnectReason::REMOTE_USER_TERMINATED_CONNECTION)).Times(0);
+  std::this_thread::sleep_for(kTestIdleDisconnectTimeoutShort * 2);
+
+  // Step 5: Link disconnect will trigger all callbacks
+  classic_link_manager.OnDisconnect(device, hci::ErrorCode::CONNECTION_TERMINATED_BY_LOCAL_HOST);
+  SyncHandler(user_handler.get());
+  EXPECT_EQ(hci::ErrorCode::CONNECTION_TERMINATED_BY_LOCAL_HOST, status_1);
+  EXPECT_EQ(hci::ErrorCode::CONNECTION_TERMINATED_BY_LOCAL_HOST, status_2);
+
+  user_handler->Clear();
+}
+
+TEST_F(L2capClassicLinkManagerTest, acquiring_and_releasing_channels_should_eventually_disconnect_acl) {
+  EXPECT_CALL(*mock_parameter_provider_, GetClassicLinkIdleDisconnectTimeout)
+      .WillRepeatedly(Return(kTestIdleDisconnectTimeoutShort));
+  MockFixedChannelServiceManagerImpl mock_classic_fixed_channel_service_manager;
+  MockAclManager mock_acl_manager;
+  hci::Address device{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}};
+  auto user_handler = std::make_unique<os::Handler>(thread_);
+
+  // Step 1: Verify callback registration with HCI
+  hci::ConnectionCallbacks* hci_connection_callbacks = nullptr;
+  os::Handler* hci_callback_handler = nullptr;
+  EXPECT_CALL(mock_acl_manager, RegisterCallbacks(_, _))
+      .WillOnce(DoAll(SaveArg<0>(&hci_connection_callbacks), SaveArg<1>(&hci_callback_handler)));
+  LinkManager classic_link_manager(l2cap_handler_, &mock_acl_manager, &mock_classic_fixed_channel_service_manager,
+                                   nullptr, mock_parameter_provider_);
+  EXPECT_EQ(hci_connection_callbacks, &classic_link_manager);
+  EXPECT_EQ(hci_callback_handler, l2cap_handler_);
+
+  // Register fake services
+  MockFixedChannelServiceImpl mock_service_1, mock_service_2;
+  std::vector<std::pair<Cid, FixedChannelServiceImpl*>> results;
+  results.emplace_back(kSmpBrCid, &mock_service_1);
+  results.emplace_back(kConnectionlessCid, &mock_service_2);
+  EXPECT_CALL(mock_classic_fixed_channel_service_manager, GetRegisteredServices()).WillRepeatedly(Return(results));
+
+  // Step 2: Connect to fixed channel without ACL connection should trigger ACL connection process
+  EXPECT_CALL(mock_acl_manager, CreateConnection(device)).Times(1);
+  LinkManager::PendingFixedChannelConnection pending_fixed_channel_connection{
+      .handler_ = user_handler.get(),
+      .on_fail_callback_ = common::BindOnce([](FixedChannelManager::ConnectionResult result) { FAIL(); })};
+  classic_link_manager.ConnectFixedChannelServices(device, std::move(pending_fixed_channel_connection));
+
+  // Step 3: ACL connection success event should trigger channel creation for all registered services
+  auto* raw_acl_connection = new MockAclConnection();
+  std::unique_ptr<MockAclConnection> acl_connection(raw_acl_connection);
+  EXPECT_CALL(*acl_connection, GetAddress()).WillRepeatedly(Return(device));
+  EXPECT_CALL(*acl_connection, GetAddressType()).WillRepeatedly(Return(hci::AddressType::PUBLIC_DEVICE_ADDRESS));
+  EXPECT_CALL(*acl_connection, RegisterCallbacks(_, l2cap_handler_)).Times(1);
+  EXPECT_CALL(*acl_connection, RegisterDisconnectCallback(_, l2cap_handler_)).Times(1);
+  EXPECT_CALL(*acl_connection, UnregisterCallbacks(_)).Times(1);
+  std::unique_ptr<FixedChannel> channel_1, channel_2;
+  std::promise<void> promise_1, promise_2;
+  auto future_1 = promise_1.get_future();
+  auto future_2 = promise_2.get_future();
+  EXPECT_CALL(mock_service_1, NotifyChannelCreation(_))
+      .WillOnce([&channel_1, &promise_1](std::unique_ptr<FixedChannel> channel) {
+        channel_1 = std::move(channel);
+        promise_1.set_value();
+      });
+  EXPECT_CALL(mock_service_2, NotifyChannelCreation(_))
+      .WillOnce([&channel_2, &promise_2](std::unique_ptr<FixedChannel> channel) {
+        channel_2 = std::move(channel);
+        promise_2.set_value();
+      });
+  hci_callback_handler->Post(common::BindOnce(&hci::ConnectionCallbacks::OnConnectSuccess,
+                                              common::Unretained(hci_connection_callbacks), std::move(acl_connection)));
+  SyncHandler(hci_callback_handler);
+  auto future_1_status = future_1.wait_for(kTestIdleDisconnectTimeoutShort);
+  EXPECT_EQ(future_1_status, std::future_status::ready);
+  EXPECT_NE(channel_1, nullptr);
+  auto future_2_status = future_2.wait_for(kTestIdleDisconnectTimeoutShort);
+  EXPECT_EQ(future_2_status, std::future_status::ready);
+  EXPECT_NE(channel_2, nullptr);
+  hci::ErrorCode status_1 = hci::ErrorCode::SUCCESS;
+  channel_1->RegisterOnCloseCallback(
+      user_handler.get(), common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { status_1 = status; }));
+  hci::ErrorCode status_2 = hci::ErrorCode::SUCCESS;
+  channel_2->RegisterOnCloseCallback(
+      user_handler.get(), common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { status_2 = status; }));
+
+  channel_1->Acquire();
+
+  // Step 4: Leave channel IDLE, it won't disconnect to due acquired channel 1
+  EXPECT_CALL(*raw_acl_connection, Disconnect(hci::DisconnectReason::REMOTE_USER_TERMINATED_CONNECTION)).Times(0);
+  std::this_thread::sleep_for(kTestIdleDisconnectTimeoutShort * 2);
+
+  // Step 5: Leave channel IDLE long enough, they will disconnect
+  channel_1->Release();
+  EXPECT_CALL(*raw_acl_connection, Disconnect(hci::DisconnectReason::REMOTE_USER_TERMINATED_CONNECTION)).Times(1);
+  std::this_thread::sleep_for(kTestIdleDisconnectTimeoutShort * 1.2);
+
+  // Step 6: Link disconnect will trigger all callbacks
+  classic_link_manager.OnDisconnect(device, hci::ErrorCode::CONNECTION_TERMINATED_BY_LOCAL_HOST);
+  SyncHandler(user_handler.get());
+  EXPECT_EQ(hci::ErrorCode::CONNECTION_TERMINATED_BY_LOCAL_HOST, status_1);
+  EXPECT_EQ(hci::ErrorCode::CONNECTION_TERMINATED_BY_LOCAL_HOST, status_2);
+
+  user_handler->Clear();
+}
+
+}  // namespace
+}  // namespace internal
+}  // namespace classic
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/classic/internal/link_mock.h b/gd/l2cap/classic/internal/link_mock.h
new file mode 100644
index 0000000..4eb336e
--- /dev/null
+++ b/gd/l2cap/classic/internal/link_mock.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include "hci/acl_manager_mock.h"
+#include "hci/address.h"
+#include "l2cap/classic/internal/link.h"
+#include "l2cap/internal/scheduler_mock.h"
+
+#include <gmock/gmock.h>
+
+// Unit test interfaces
+namespace bluetooth {
+namespace l2cap {
+namespace classic {
+namespace internal {
+namespace testing {
+
+using hci::testing::MockAclConnection;
+
+class MockLink : public Link {
+ public:
+  explicit MockLink(os::Handler* handler, l2cap::internal::ParameterProvider* parameter_provider)
+      : Link(handler, std::make_unique<MockAclConnection>(), parameter_provider, nullptr, nullptr){};
+  explicit MockLink(os::Handler* handler, l2cap::internal::ParameterProvider* parameter_provider,
+                    std::unique_ptr<hci::AclConnection> acl_connection)
+      : Link(handler, std::move(acl_connection), parameter_provider, nullptr, nullptr){};
+  MOCK_METHOD(hci::AddressWithType, GetDevice, (), (override));
+  MOCK_METHOD(void, OnAclDisconnected, (hci::ErrorCode status), (override));
+  MOCK_METHOD(void, Disconnect, (), (override));
+  MOCK_METHOD(std::shared_ptr<l2cap::internal::DynamicChannelImpl>, AllocateDynamicChannel,
+              (Psm psm, Cid cid, SecurityPolicy security_policy), (override));
+  MOCK_METHOD(bool, IsFixedChannelAllocated, (Cid cid), (override));
+  MOCK_METHOD(void, RefreshRefCount, (), (override));
+};
+
+}  // namespace testing
+}  // namespace internal
+}  // namespace classic
+}  // namespace l2cap
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/l2cap/classic/internal/link_test.cc b/gd/l2cap/classic/internal/link_test.cc
new file mode 100644
index 0000000..67064f3
--- /dev/null
+++ b/gd/l2cap/classic/internal/link_test.cc
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "l2cap/classic/internal/link.h"
+
+#include "hci/acl_manager_mock.h"
+#include "hci/address.h"
+#include "l2cap/classic/internal/dynamic_channel_service_manager_impl_mock.h"
+#include "l2cap/classic/internal/fixed_channel_service_manager_impl_mock.h"
+#include "l2cap/internal/parameter_provider_mock.h"
+
+#include <gmock/gmock-nice-strict.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+using ::testing::NiceMock;
+
+namespace bluetooth {
+namespace l2cap {
+namespace classic {
+namespace internal {
+namespace {
+
+constexpr Psm kPsm = 123;
+constexpr Cid kCid = 456;
+
+using classic::internal::testing::MockDynamicChannelServiceManagerImpl;
+using hci::testing::MockAclConnection;
+using l2cap::internal::testing::MockParameterProvider;
+using testing::MockFixedChannelServiceManagerImpl;
+
+class L2capClassicLinkTest : public ::testing::Test {
+ public:
+  void OnOpen(std::unique_ptr<DynamicChannel> channel) {
+    on_open_promise_.set_value();
+  }
+
+  void OnFail(DynamicChannelManager::ConnectionResult result) {
+    on_fail_promise_.set_value();
+  }
+
+  void OnDequeueCallbackForTest() {
+    std::unique_ptr<BasePacketBuilder> data = raw_acl_connection_->acl_queue_.GetDownEnd()->TryDequeue();
+    if (data != nullptr) {
+      dequeue_promise_.set_value();
+    }
+  }
+
+  void EnqueueCallbackForTest() {
+    raw_acl_connection_->acl_queue_.GetDownEnd()->RegisterDequeue(
+        handler_, common::Bind(&L2capClassicLinkTest::OnDequeueCallbackForTest, common::Unretained(this)));
+  }
+
+  void DequeueCallback() {
+    raw_acl_connection_->acl_queue_.GetDownEnd()->UnregisterDequeue();
+  }
+
+ protected:
+  void SetUp() override {
+    thread_ = new os::Thread("test_thread", os::Thread::Priority::NORMAL);
+    handler_ = new os::Handler(thread_);
+    signalling_handler_ = new os::Handler(thread_);
+
+    raw_acl_connection_ = new NiceMock<MockAclConnection>();
+    link_ = new Link(signalling_handler_, std::unique_ptr<MockAclConnection>(raw_acl_connection_),
+                     &mock_parameter_provider_, &mock_classic_dynamic_channel_service_manager_,
+                     &mock_classic_fixed_channel_service_manager_);
+  }
+
+  void TearDown() override {
+    delete link_;
+
+    signalling_handler_->Clear();
+    delete signalling_handler_;
+
+    handler_->Clear();
+    delete handler_;
+
+    delete thread_;
+  }
+
+  os::Thread* thread_ = nullptr;
+  os::Handler* handler_ = nullptr;
+  os::Handler* signalling_handler_ = nullptr;
+
+  MockAclConnection* raw_acl_connection_ = nullptr;
+  std::unique_ptr<MockAclConnection> acl_connection_;
+
+  NiceMock<MockParameterProvider> mock_parameter_provider_;
+  MockFixedChannelServiceManagerImpl mock_classic_fixed_channel_service_manager_;
+  MockDynamicChannelServiceManagerImpl mock_classic_dynamic_channel_service_manager_;
+
+  std::promise<void> on_open_promise_;
+  std::promise<void> on_fail_promise_;
+  std::promise<void> dequeue_promise_;
+
+  Link* link_;
+};
+
+TEST_F(L2capClassicLinkTest, pending_channels_get_notified_on_acl_disconnect) {
+  EnqueueCallbackForTest();
+
+  Link::PendingDynamicChannelConnection pending_dynamic_channel_connection{
+      .handler_ = handler_,
+      .on_open_callback_ = common::Bind(&L2capClassicLinkTest::OnOpen, common::Unretained(this)),
+      .on_fail_callback_ = common::Bind(&L2capClassicLinkTest::OnFail, common::Unretained(this)),
+      .configuration_ = DynamicChannelConfigurationOption(),
+  };
+  auto future = on_fail_promise_.get_future();
+
+  link_->SendConnectionRequest(kPsm, kCid, std::move(pending_dynamic_channel_connection));
+  link_->OnAclDisconnected(hci::ErrorCode::UNKNOWN_HCI_COMMAND);
+  future.wait();
+
+  auto dequeue_future = dequeue_promise_.get_future();
+  dequeue_future.wait();
+  DequeueCallback();
+}
+
+}  // namespace
+}  // namespace internal
+}  // namespace classic
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/classic/internal/signalling_manager.cc b/gd/l2cap/classic/internal/signalling_manager.cc
new file mode 100644
index 0000000..30553ca
--- /dev/null
+++ b/gd/l2cap/classic/internal/signalling_manager.cc
@@ -0,0 +1,743 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "l2cap/classic/internal/signalling_manager.h"
+
+#include <chrono>
+
+#include "common/bind.h"
+#include "l2cap/classic/internal/channel_configuration_state.h"
+#include "l2cap/classic/internal/link.h"
+#include "l2cap/internal/data_pipeline_manager.h"
+#include "l2cap/l2cap_packets.h"
+#include "os/log.h"
+#include "packet/raw_builder.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace classic {
+namespace internal {
+static constexpr auto kTimeout = std::chrono::seconds(3);
+
+ClassicSignallingManager::ClassicSignallingManager(os::Handler* handler, Link* link,
+                                                   l2cap::internal::DataPipelineManager* data_pipeline_manager,
+                                                   DynamicChannelServiceManagerImpl* dynamic_service_manager,
+                                                   l2cap::internal::DynamicChannelAllocator* channel_allocator,
+                                                   FixedChannelServiceManagerImpl* fixed_service_manager)
+    : handler_(handler), link_(link), data_pipeline_manager_(data_pipeline_manager),
+      dynamic_service_manager_(dynamic_service_manager), channel_allocator_(channel_allocator),
+      fixed_service_manager_(fixed_service_manager), alarm_(handler) {
+  ASSERT(handler_ != nullptr);
+  ASSERT(link_ != nullptr);
+  signalling_channel_ = link_->AllocateFixedChannel(kClassicSignallingCid, {});
+  signalling_channel_->GetQueueUpEnd()->RegisterDequeue(
+      handler_, common::Bind(&ClassicSignallingManager::on_incoming_packet, common::Unretained(this)));
+  enqueue_buffer_ =
+      std::make_unique<os::EnqueueBuffer<packet::BasePacketBuilder>>(signalling_channel_->GetQueueUpEnd());
+}
+
+ClassicSignallingManager::~ClassicSignallingManager() {
+  enqueue_buffer_.reset();
+  signalling_channel_->GetQueueUpEnd()->UnregisterDequeue();
+  signalling_channel_ = nullptr;
+}
+
+void ClassicSignallingManager::OnCommandReject(CommandRejectView command_reject_view) {
+  if (command_just_sent_.signal_id_ != command_reject_view.GetIdentifier() ||
+      command_just_sent_.command_code_ != command_reject_view.GetCode()) {
+    LOG_WARN("Unexpected command reject: no pending request");
+    return;
+  }
+  alarm_.Cancel();
+  handle_send_next_command();
+
+  LOG_INFO("Command rejected");
+}
+
+void ClassicSignallingManager::SendConnectionRequest(Psm psm, Cid local_cid) {
+  PendingCommand pending_command = {next_signal_id_, CommandCode::CONNECTION_REQUEST, psm, local_cid, {}, {}, {}};
+  next_signal_id_++;
+  pending_commands_.push(std::move(pending_command));
+  if (command_just_sent_.signal_id_ == kInvalidSignalId) {
+    handle_send_next_command();
+  }
+}
+
+void ClassicSignallingManager::SendConfigurationRequest(Cid remote_cid,
+                                                        std::vector<std::unique_ptr<ConfigurationOption>> config) {
+  PendingCommand pending_command = {next_signal_id_,  CommandCode::CONFIGURATION_REQUEST, {}, {}, remote_cid, {},
+                                    std::move(config)};
+  next_signal_id_++;
+  pending_commands_.push(std::move(pending_command));
+  if (command_just_sent_.signal_id_ == kInvalidSignalId) {
+    handle_send_next_command();
+  }
+}
+
+void ClassicSignallingManager::SendDisconnectionRequest(Cid local_cid, Cid remote_cid) {
+  PendingCommand pending_command = {
+      next_signal_id_, CommandCode::DISCONNECTION_REQUEST, {}, local_cid, remote_cid, {}, {}};
+  next_signal_id_++;
+  pending_commands_.push(std::move(pending_command));
+  channel_configuration_.erase(local_cid);
+  if (command_just_sent_.signal_id_ == kInvalidSignalId) {
+    handle_send_next_command();
+  }
+}
+
+void ClassicSignallingManager::SendInformationRequest(InformationRequestInfoType type) {
+  PendingCommand pending_command = {next_signal_id_, CommandCode::INFORMATION_REQUEST, {}, {}, {}, type, {}};
+  next_signal_id_++;
+  pending_commands_.push(std::move(pending_command));
+  if (command_just_sent_.signal_id_ == kInvalidSignalId) {
+    handle_send_next_command();
+  }
+}
+
+void ClassicSignallingManager::SendEchoRequest(std::unique_ptr<packet::RawBuilder> payload) {
+  LOG_WARN("Not supported");
+}
+
+void ClassicSignallingManager::CancelAlarm() {
+  alarm_.Cancel();
+}
+
+void ClassicSignallingManager::OnConnectionRequest(SignalId signal_id, Psm psm, Cid remote_cid) {
+  if (!IsPsmValid(psm)) {
+    LOG_WARN("Invalid psm received from remote psm:%d remote_cid:%d", psm, remote_cid);
+    send_connection_response(signal_id, remote_cid, kInvalidCid, ConnectionResponseResult::PSM_NOT_SUPPORTED,
+                             ConnectionResponseStatus::NO_FURTHER_INFORMATION_AVAILABLE);
+    return;
+  }
+
+  if (remote_cid == kInvalidCid) {
+    LOG_WARN("Invalid remote cid received from remote psm:%d remote_cid:%d", psm, remote_cid);
+    send_connection_response(signal_id, remote_cid, kInvalidCid, ConnectionResponseResult::INVALID_CID,
+                             ConnectionResponseStatus::NO_FURTHER_INFORMATION_AVAILABLE);
+    return;
+  }
+  if (channel_allocator_->IsPsmUsed(psm)) {
+    LOG_WARN("Psm already exists");
+    send_connection_response(signal_id, remote_cid, kInvalidCid, ConnectionResponseResult::PSM_NOT_SUPPORTED,
+                             ConnectionResponseStatus::NO_FURTHER_INFORMATION_AVAILABLE);
+    return;
+  }
+
+  if (!dynamic_service_manager_->IsServiceRegistered(psm)) {
+    LOG_INFO("Service for this psm (%d) is not registered", psm);
+    send_connection_response(signal_id, remote_cid, kInvalidCid, ConnectionResponseResult::PSM_NOT_SUPPORTED,
+                             ConnectionResponseStatus::NO_FURTHER_INFORMATION_AVAILABLE);
+    return;
+  }
+
+  auto new_channel = link_->AllocateDynamicChannel(psm, remote_cid, {});
+  if (new_channel == nullptr) {
+    LOG_WARN("Can't allocate dynamic channel");
+    return;
+  }
+  send_connection_response(signal_id, remote_cid, new_channel->GetCid(), ConnectionResponseResult::SUCCESS,
+                           ConnectionResponseStatus::NO_FURTHER_INFORMATION_AVAILABLE);
+  auto& configuration_state = channel_configuration_[new_channel->GetCid()];
+  auto* service = dynamic_service_manager_->GetService(psm);
+  auto initial_config = service->GetConfigOption();
+
+  auto mtu_configuration = std::make_unique<MtuConfigurationOption>();
+  mtu_configuration->mtu_ = initial_config.incoming_mtu;
+  configuration_state.incoming_mtu_ = initial_config.incoming_mtu;
+
+  auto fcs_option = std::make_unique<FrameCheckSequenceOption>();
+  fcs_option->fcs_type_ = FcsType::NO_FCS;
+  if (link_->GetRemoteSupportsFcs()) {
+    fcs_option->fcs_type_ = FcsType::DEFAULT;
+    configuration_state.fcs_type_ = FcsType::DEFAULT;
+  }
+
+  auto retransmission_flow_control_configuration = std::make_unique<RetransmissionAndFlowControlConfigurationOption>();
+  switch (initial_config.channel_mode) {
+    case DynamicChannelConfigurationOption::RetransmissionAndFlowControlMode::L2CAP_BASIC:
+      retransmission_flow_control_configuration->mode_ = RetransmissionAndFlowControlModeOption::L2CAP_BASIC;
+      configuration_state.retransmission_and_flow_control_mode_ = RetransmissionAndFlowControlModeOption::L2CAP_BASIC;
+      break;
+    case DynamicChannelConfigurationOption::RetransmissionAndFlowControlMode::ENHANCED_RETRANSMISSION:
+      retransmission_flow_control_configuration->mode_ =
+          RetransmissionAndFlowControlModeOption::ENHANCED_RETRANSMISSION;
+      configuration_state.retransmission_and_flow_control_mode_ =
+          RetransmissionAndFlowControlModeOption::ENHANCED_RETRANSMISSION;
+      // TODO: Decide where to put initial values
+      retransmission_flow_control_configuration->tx_window_size_ = 10;
+      retransmission_flow_control_configuration->max_transmit_ = 20;
+      retransmission_flow_control_configuration->retransmission_time_out_ = 2000;
+      retransmission_flow_control_configuration->monitor_time_out_ = 12000;
+      retransmission_flow_control_configuration->maximum_pdu_size_ = 1010;
+      break;
+  }
+  configuration_state.local_retransmission_and_flow_control_ = *retransmission_flow_control_configuration;
+
+  std::vector<std::unique_ptr<ConfigurationOption>> config;
+  config.emplace_back(std::move(mtu_configuration));
+  if (initial_config.channel_mode != DynamicChannelConfigurationOption::RetransmissionAndFlowControlMode::L2CAP_BASIC) {
+    config.emplace_back(std::move(retransmission_flow_control_configuration));
+    config.emplace_back(std::move(fcs_option));
+  }
+  SendConfigurationRequest(remote_cid, std::move(config));
+}
+
+void ClassicSignallingManager::OnConnectionResponse(SignalId signal_id, Cid remote_cid, Cid cid,
+                                                    ConnectionResponseResult result, ConnectionResponseStatus status) {
+  if (command_just_sent_.signal_id_ != signal_id ||
+      command_just_sent_.command_code_ != CommandCode::CONNECTION_REQUEST) {
+    LOG_WARN("Unexpected response: no pending request. Expected signal id %d type %s, got %d",
+             command_just_sent_.signal_id_.Value(), CommandCodeText(command_just_sent_.command_code_).data(),
+             signal_id.Value());
+    return;
+  }
+  if (command_just_sent_.source_cid_ != cid) {
+    LOG_WARN("SCID doesn't match: expected %d, received %d", command_just_sent_.source_cid_, cid);
+    handle_send_next_command();
+    return;
+  }
+  if (result == ConnectionResponseResult::PENDING) {
+    alarm_.Schedule(common::BindOnce(&ClassicSignallingManager::on_command_timeout, common::Unretained(this)),
+                    kTimeout);
+    return;
+  }
+
+  command_just_sent_.signal_id_ = kInvalidSignalId;
+  alarm_.Cancel();
+  if (result != ConnectionResponseResult::SUCCESS) {
+    link_->OnOutgoingConnectionRequestFail(cid);
+    handle_send_next_command();
+    return;
+  }
+  Psm pending_psm = command_just_sent_.psm_;
+  auto new_channel = link_->AllocateReservedDynamicChannel(cid, pending_psm, remote_cid, {});
+  if (new_channel == nullptr) {
+    LOG_WARN("Can't allocate dynamic channel");
+    link_->OnOutgoingConnectionRequestFail(cid);
+    handle_send_next_command();
+    return;
+  }
+
+  auto& configuration_state = channel_configuration_[new_channel->GetCid()];
+  auto initial_config = link_->GetConfigurationForInitialConfiguration(new_channel->GetCid());
+
+  auto mtu_configuration = std::make_unique<MtuConfigurationOption>();
+  mtu_configuration->mtu_ = initial_config.incoming_mtu;
+  configuration_state.incoming_mtu_ = initial_config.incoming_mtu;
+
+  auto fcs_option = std::make_unique<FrameCheckSequenceOption>();
+  fcs_option->fcs_type_ = FcsType::DEFAULT;
+  if (!link_->GetRemoteSupportsFcs()) {
+    fcs_option->fcs_type_ = FcsType::NO_FCS;
+    configuration_state.fcs_type_ = FcsType::NO_FCS;
+  }
+
+  auto retransmission_flow_control_configuration = std::make_unique<RetransmissionAndFlowControlConfigurationOption>();
+  switch (initial_config.channel_mode) {
+    case DynamicChannelConfigurationOption::RetransmissionAndFlowControlMode::L2CAP_BASIC:
+      retransmission_flow_control_configuration->mode_ = RetransmissionAndFlowControlModeOption::L2CAP_BASIC;
+      configuration_state.retransmission_and_flow_control_mode_ = RetransmissionAndFlowControlModeOption::L2CAP_BASIC;
+      break;
+    case DynamicChannelConfigurationOption::RetransmissionAndFlowControlMode::ENHANCED_RETRANSMISSION:
+      retransmission_flow_control_configuration->mode_ =
+          RetransmissionAndFlowControlModeOption::ENHANCED_RETRANSMISSION;
+      configuration_state.retransmission_and_flow_control_mode_ =
+          RetransmissionAndFlowControlModeOption::ENHANCED_RETRANSMISSION;
+      // TODO: Decide where to put initial values
+      retransmission_flow_control_configuration->tx_window_size_ = 10;
+      retransmission_flow_control_configuration->max_transmit_ = 20;
+      retransmission_flow_control_configuration->retransmission_time_out_ = 2000;
+      retransmission_flow_control_configuration->monitor_time_out_ = 12000;
+      retransmission_flow_control_configuration->maximum_pdu_size_ = 1010;
+      break;
+  }
+  configuration_state.local_retransmission_and_flow_control_ = *retransmission_flow_control_configuration;
+
+  std::vector<std::unique_ptr<ConfigurationOption>> config;
+  config.emplace_back(std::move(mtu_configuration));
+  if (initial_config.channel_mode != DynamicChannelConfigurationOption::RetransmissionAndFlowControlMode::L2CAP_BASIC) {
+    config.emplace_back(std::move(retransmission_flow_control_configuration));
+    config.emplace_back(std::move(fcs_option));
+  }
+  SendConfigurationRequest(remote_cid, std::move(config));
+}
+
+void ClassicSignallingManager::OnConfigurationRequest(SignalId signal_id, Cid cid, Continuation is_continuation,
+                                                      std::vector<std::unique_ptr<ConfigurationOption>> options) {
+  auto channel = channel_allocator_->FindChannelByCid(cid);
+  if (channel == nullptr) {
+    LOG_WARN("Configuration request for an unknown channel");
+    return;
+  }
+
+  auto& configuration_state = channel_configuration_[cid];
+  std::vector<std::unique_ptr<ConfigurationOption>> rsp_options;
+
+  for (auto& option : options) {
+    switch (option->type_) {
+      case ConfigurationOptionType::MTU: {
+        configuration_state.outgoing_mtu_ = MtuConfigurationOption::Specialize(option.get())->mtu_;
+        // TODO: If less than minimum (required by spec), reject
+        break;
+      }
+      case ConfigurationOptionType::FLUSH_TIMEOUT: {
+        // TODO: Handle this configuration option
+        break;
+      }
+      case ConfigurationOptionType::RETRANSMISSION_AND_FLOW_CONTROL: {
+        auto* config = RetransmissionAndFlowControlConfigurationOption::Specialize(option.get());
+        if (config->retransmission_time_out_ == 0) {
+          config->retransmission_time_out_ = 2000;
+        }
+        if (config->monitor_time_out_ == 0) {
+          config->monitor_time_out_ = 12000;
+        }
+        configuration_state.remote_retransmission_and_flow_control_ = *config;
+        rsp_options.emplace_back(std::make_unique<RetransmissionAndFlowControlConfigurationOption>(*config));
+        break;
+      }
+      case ConfigurationOptionType::FRAME_CHECK_SEQUENCE: {
+        configuration_state.fcs_type_ = FrameCheckSequenceOption::Specialize(option.get())->fcs_type_;
+        break;
+      }
+      default:
+        LOG_WARN("Received some unsupported configuration option: %d", static_cast<int>(option->type_));
+        auto response =
+            ConfigurationResponseBuilder::Create(signal_id.Value(), channel->GetRemoteCid(), is_continuation,
+                                                 ConfigurationResponseResult::UNKNOWN_OPTIONS, {});
+        enqueue_buffer_->Enqueue(std::move(response), handler_);
+        return;
+    }
+  }
+
+  if (configuration_state.state_ == ChannelConfigurationState::State::WAIT_CONFIG_REQ) {
+    std::unique_ptr<DynamicChannel> user_channel = std::make_unique<DynamicChannel>(channel, handler_);
+    if (channel->local_initiated_) {
+      link_->NotifyChannelCreation(cid, std::move(user_channel));
+    } else {
+      dynamic_service_manager_->GetService(channel->GetPsm())->NotifyChannelCreation(std::move(user_channel));
+    }
+    configuration_state.state_ = ChannelConfigurationState::State::CONFIGURED;
+    data_pipeline_manager_->UpdateClassicConfiguration(cid, configuration_state);
+  } else if (configuration_state.state_ == ChannelConfigurationState::State::WAIT_CONFIG_REQ_RSP) {
+    configuration_state.state_ = ChannelConfigurationState::State::WAIT_CONFIG_RSP;
+  }
+
+  auto response = ConfigurationResponseBuilder::Create(signal_id.Value(), channel->GetRemoteCid(), is_continuation,
+                                                       ConfigurationResponseResult::SUCCESS, std::move(rsp_options));
+  enqueue_buffer_->Enqueue(std::move(response), handler_);
+}
+
+void ClassicSignallingManager::OnConfigurationResponse(SignalId signal_id, Cid cid, Continuation is_continuation,
+                                                       ConfigurationResponseResult result,
+                                                       std::vector<std::unique_ptr<ConfigurationOption>> options) {
+  if (command_just_sent_.signal_id_ != signal_id ||
+      command_just_sent_.command_code_ != CommandCode::CONFIGURATION_REQUEST) {
+    LOG_WARN("Unexpected response: no pending request. Expected signal id %d type %s, got %d",
+             command_just_sent_.signal_id_.Value(), CommandCodeText(command_just_sent_.command_code_).data(),
+             signal_id.Value());
+    return;
+  }
+
+  auto channel = channel_allocator_->FindChannelByCid(cid);
+  if (channel == nullptr) {
+    LOG_WARN("Configuration request for an unknown channel");
+    handle_send_next_command();
+    return;
+  }
+
+  if (result == ConfigurationResponseResult::PENDING) {
+    alarm_.Schedule(common::BindOnce(&ClassicSignallingManager::on_command_timeout, common::Unretained(this)),
+                    kTimeout);
+    return;
+  }
+
+  if (result != ConfigurationResponseResult::SUCCESS) {
+    LOG_WARN("Configuration response not SUCCESS");
+    handle_send_next_command();
+    return;
+  }
+
+  auto& configuration_state = channel_configuration_[channel->GetCid()];
+
+  for (auto& option : options) {
+    switch (option->type_) {
+      case ConfigurationOptionType::MTU: {
+        auto config = MtuConfigurationOption::Specialize(option.get());
+        configuration_state.incoming_mtu_ = config->mtu_;
+        break;
+      }
+      case ConfigurationOptionType::FLUSH_TIMEOUT: {
+        // TODO: Handle this configuration option
+        break;
+      }
+      case ConfigurationOptionType::RETRANSMISSION_AND_FLOW_CONTROL: {
+        auto config = RetransmissionAndFlowControlConfigurationOption::Specialize(option.get());
+        configuration_state.retransmission_and_flow_control_mode_ = config->mode_;
+        configuration_state.local_retransmission_and_flow_control_ = *config;
+        break;
+      }
+      case ConfigurationOptionType::FRAME_CHECK_SEQUENCE: {
+        configuration_state.fcs_type_ = FrameCheckSequenceOption::Specialize(option.get())->fcs_type_;
+        break;
+      }
+      default:
+        LOG_WARN("Received some unsupported configuration option: %d", static_cast<int>(option->type_));
+        return;
+    }
+  }
+
+  if (configuration_state.state_ == ChannelConfigurationState::State::WAIT_CONFIG_RSP) {
+    std::unique_ptr<DynamicChannel> user_channel = std::make_unique<DynamicChannel>(channel, handler_);
+    if (channel->local_initiated_) {
+      link_->NotifyChannelCreation(cid, std::move(user_channel));
+    } else {
+      dynamic_service_manager_->GetService(channel->GetPsm())->NotifyChannelCreation(std::move(user_channel));
+    }
+    configuration_state.state_ = ChannelConfigurationState::State::CONFIGURED;
+    data_pipeline_manager_->UpdateClassicConfiguration(cid, configuration_state);
+  } else if (configuration_state.state_ == ChannelConfigurationState::State::WAIT_CONFIG_REQ_RSP) {
+    configuration_state.state_ = ChannelConfigurationState::State::WAIT_CONFIG_REQ;
+  }
+
+  alarm_.Cancel();
+  handle_send_next_command();
+}
+
+void ClassicSignallingManager::OnDisconnectionRequest(SignalId signal_id, Cid cid, Cid remote_cid) {
+  // TODO: check cid match
+  auto channel = channel_allocator_->FindChannelByCid(cid);
+  if (channel == nullptr) {
+    LOG_WARN("Disconnect request for an unknown channel");
+    return;
+  }
+  channel_configuration_.erase(cid);
+  auto builder = DisconnectionResponseBuilder::Create(signal_id.Value(), cid, remote_cid);
+  enqueue_buffer_->Enqueue(std::move(builder), handler_);
+  channel->OnClosed(hci::ErrorCode::SUCCESS);
+  link_->FreeDynamicChannel(cid);
+}
+
+void ClassicSignallingManager::OnDisconnectionResponse(SignalId signal_id, Cid remote_cid, Cid cid) {
+  if (command_just_sent_.signal_id_ != signal_id ||
+      command_just_sent_.command_code_ != CommandCode::DISCONNECTION_REQUEST) {
+    LOG_WARN("Unexpected response: no pending request. Expected signal id %d type %s, got %d",
+             command_just_sent_.signal_id_.Value(), CommandCodeText(command_just_sent_.command_code_).data(),
+             signal_id.Value());
+    return;
+  }
+
+  alarm_.Cancel();
+
+  auto channel = channel_allocator_->FindChannelByCid(cid);
+  if (channel == nullptr) {
+    LOG_WARN("Disconnect response for an unknown channel");
+    handle_send_next_command();
+    return;
+  }
+
+  channel->OnClosed(hci::ErrorCode::SUCCESS);
+  link_->FreeDynamicChannel(cid);
+  handle_send_next_command();
+}
+
+void ClassicSignallingManager::OnEchoRequest(SignalId signal_id, const PacketView<kLittleEndian>& packet) {
+  std::vector<uint8_t> packet_vector{packet.begin(), packet.end()};
+  auto raw_builder = std::make_unique<packet::RawBuilder>();
+  raw_builder->AddOctets(packet_vector);
+  auto builder = EchoResponseBuilder::Create(signal_id.Value(), std::move(raw_builder));
+  enqueue_buffer_->Enqueue(std::move(builder), handler_);
+}
+
+void ClassicSignallingManager::OnEchoResponse(SignalId signal_id, const PacketView<kLittleEndian>& packet) {
+  if (command_just_sent_.signal_id_ != signal_id || command_just_sent_.command_code_ != CommandCode::ECHO_REQUEST) {
+    LOG_WARN("Unexpected response: no pending request. Expected signal id %d type %s, got %d",
+             command_just_sent_.signal_id_.Value(), CommandCodeText(command_just_sent_.command_code_).data(),
+             signal_id.Value());
+    return;
+  }
+  LOG_INFO("Echo response received");
+  alarm_.Cancel();
+  handle_send_next_command();
+}
+
+void ClassicSignallingManager::OnInformationRequest(SignalId signal_id, InformationRequestInfoType type) {
+  switch (type) {
+    case InformationRequestInfoType::CONNECTIONLESS_MTU: {
+      auto response = InformationResponseConnectionlessMtuBuilder::Create(
+          signal_id.Value(), InformationRequestResult::SUCCESS, kDefaultClassicMtu);
+      enqueue_buffer_->Enqueue(std::move(response), handler_);
+      break;
+    }
+    case InformationRequestInfoType::EXTENDED_FEATURES_SUPPORTED: {
+      // TODO: implement this response
+      auto response = InformationResponseExtendedFeaturesBuilder::Create(
+          signal_id.Value(), InformationRequestResult::SUCCESS, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0);
+      enqueue_buffer_->Enqueue(std::move(response), handler_);
+      break;
+    }
+    case InformationRequestInfoType::FIXED_CHANNELS_SUPPORTED: {
+      auto response = InformationResponseFixedChannelsBuilder::Create(
+          signal_id.Value(), InformationRequestResult::SUCCESS, fixed_service_manager_->GetSupportedFixedChannelMask());
+      enqueue_buffer_->Enqueue(std::move(response), handler_);
+      break;
+    }
+  }
+}
+
+void ClassicSignallingManager::OnInformationResponse(SignalId signal_id, const InformationResponseView& response) {
+  if (command_just_sent_.signal_id_ != signal_id ||
+      command_just_sent_.command_code_ != CommandCode::INFORMATION_REQUEST) {
+    LOG_WARN("Unexpected response: no pending request. Expected signal id %d type %s, got %d",
+             command_just_sent_.signal_id_.Value(), CommandCodeText(command_just_sent_.command_code_).data(),
+             signal_id.Value());
+    return;
+  }
+
+  auto type = response.GetInfoType();
+  switch (type) {
+    case InformationRequestInfoType::CONNECTIONLESS_MTU: {
+      auto view = InformationResponseConnectionlessMtuView::Create(response);
+      if (!view.IsValid()) {
+        LOG_WARN("Invalid InformationResponseConnectionlessMtu received");
+        return;
+      }
+      link_->SetRemoteConnectionlessMtu(view.GetConnectionlessMtu());
+      break;
+    }
+    case InformationRequestInfoType::EXTENDED_FEATURES_SUPPORTED: {
+      auto view = InformationResponseExtendedFeaturesView::Create(response);
+      if (!view.IsValid()) {
+        LOG_WARN("Invalid InformationResponseExtendedFeatures received");
+        return;
+      }
+      link_->SetRemoteSupportsErtm((view.GetEnhancedRetransmissionMode()));
+      link_->SetRemoteSupportsFcs(view.GetFcsOption());
+      // We don't care about other parameters
+      break;
+    }
+    case InformationRequestInfoType::FIXED_CHANNELS_SUPPORTED: {
+      auto view = InformationResponseFixedChannelsView::Create(response);
+      if (!view.IsValid()) {
+        LOG_WARN("Invalid InformationResponseFixedChannel received");
+        return;
+      }
+      // We don't use fixed channels (connectionless or BR/EDR security) for now so we don't care
+      break;
+    }
+  }
+
+  alarm_.Cancel();
+  handle_send_next_command();
+}
+
+void ClassicSignallingManager::on_incoming_packet() {
+  auto packet = signalling_channel_->GetQueueUpEnd()->TryDequeue();
+  ControlView control_packet_view = ControlView::Create(*packet);
+  if (!control_packet_view.IsValid()) {
+    LOG_WARN("Invalid signalling packet received");
+    return;
+  }
+  auto code = control_packet_view.GetCode();
+  switch (code) {
+    case CommandCode::COMMAND_REJECT: {
+      CommandRejectView command_reject_view = CommandRejectView::Create(control_packet_view);
+      if (!command_reject_view.IsValid()) {
+        return;
+      }
+      OnCommandReject(command_reject_view);
+      return;
+    }
+    case CommandCode::CONNECTION_REQUEST: {
+      ConnectionRequestView connection_request_view = ConnectionRequestView::Create(control_packet_view);
+      if (!connection_request_view.IsValid()) {
+        return;
+      }
+      OnConnectionRequest(control_packet_view.GetIdentifier(), connection_request_view.GetPsm(),
+                          connection_request_view.GetSourceCid());
+      return;
+    }
+    case CommandCode::CONNECTION_RESPONSE: {
+      ConnectionResponseView connection_response_view = ConnectionResponseView::Create(control_packet_view);
+      if (!connection_response_view.IsValid()) {
+        return;
+      }
+      OnConnectionResponse(connection_response_view.GetIdentifier(), connection_response_view.GetDestinationCid(),
+                           connection_response_view.GetSourceCid(), connection_response_view.GetResult(),
+                           connection_response_view.GetStatus());
+      return;
+    }
+    case CommandCode::CONFIGURATION_REQUEST: {
+      ConfigurationRequestView configuration_request_view = ConfigurationRequestView::Create(control_packet_view);
+      if (!configuration_request_view.IsValid()) {
+        return;
+      }
+      OnConfigurationRequest(configuration_request_view.GetIdentifier(), configuration_request_view.GetDestinationCid(),
+                             configuration_request_view.GetContinuation(), configuration_request_view.GetConfig());
+      return;
+    }
+    case CommandCode::CONFIGURATION_RESPONSE: {
+      ConfigurationResponseView configuration_response_view = ConfigurationResponseView::Create(control_packet_view);
+      if (!configuration_response_view.IsValid()) {
+        return;
+      }
+      OnConfigurationResponse(configuration_response_view.GetIdentifier(), configuration_response_view.GetSourceCid(),
+                              configuration_response_view.GetContinuation(), configuration_response_view.GetResult(),
+                              configuration_response_view.GetConfig());
+      return;
+    }
+    case CommandCode::DISCONNECTION_REQUEST: {
+      DisconnectionRequestView disconnection_request_view = DisconnectionRequestView::Create(control_packet_view);
+      if (!disconnection_request_view.IsValid()) {
+        return;
+      }
+      OnDisconnectionRequest(disconnection_request_view.GetIdentifier(), disconnection_request_view.GetDestinationCid(),
+                             disconnection_request_view.GetSourceCid());
+      return;
+    }
+    case CommandCode::DISCONNECTION_RESPONSE: {
+      DisconnectionResponseView disconnection_response_view = DisconnectionResponseView::Create(control_packet_view);
+      if (!disconnection_response_view.IsValid()) {
+        return;
+      }
+      OnDisconnectionResponse(disconnection_response_view.GetIdentifier(),
+                              disconnection_response_view.GetDestinationCid(),
+                              disconnection_response_view.GetSourceCid());
+      return;
+    }
+    case CommandCode::ECHO_REQUEST: {
+      EchoRequestView echo_request_view = EchoRequestView::Create(control_packet_view);
+      if (!echo_request_view.IsValid()) {
+        return;
+      }
+      OnEchoRequest(echo_request_view.GetIdentifier(), echo_request_view.GetPayload());
+      return;
+    }
+    case CommandCode::ECHO_RESPONSE: {
+      EchoResponseView echo_response_view = EchoResponseView::Create(control_packet_view);
+      if (!echo_response_view.IsValid()) {
+        return;
+      }
+      OnEchoResponse(echo_response_view.GetIdentifier(), echo_response_view.GetPayload());
+      return;
+    }
+    case CommandCode::INFORMATION_REQUEST: {
+      InformationRequestView information_request_view = InformationRequestView::Create(control_packet_view);
+      if (!information_request_view.IsValid()) {
+        return;
+      }
+      OnInformationRequest(information_request_view.GetIdentifier(), information_request_view.GetInfoType());
+      return;
+    }
+    case CommandCode::INFORMATION_RESPONSE: {
+      InformationResponseView information_response_view = InformationResponseView::Create(control_packet_view);
+      if (!information_response_view.IsValid()) {
+        return;
+      }
+      OnInformationResponse(information_response_view.GetIdentifier(), information_response_view);
+      return;
+    }
+    default:
+      LOG_WARN("Unhandled event 0x%x", static_cast<int>(code));
+      auto builder = CommandRejectNotUnderstoodBuilder::Create(control_packet_view.GetIdentifier());
+      enqueue_buffer_->Enqueue(std::move(builder), handler_);
+      return;
+  }
+}
+
+void ClassicSignallingManager::send_connection_response(SignalId signal_id, Cid remote_cid, Cid local_cid,
+                                                        ConnectionResponseResult result,
+                                                        ConnectionResponseStatus status) {
+  auto builder = ConnectionResponseBuilder::Create(signal_id.Value(), local_cid, remote_cid, result, status);
+  enqueue_buffer_->Enqueue(std::move(builder), handler_);
+}
+
+void ClassicSignallingManager::on_command_timeout() {
+  LOG_WARN("Response time out");
+  if (command_just_sent_.signal_id_ == kInvalidSignalId) {
+    LOG_ERROR("No pending command");
+    return;
+  }
+
+  switch (command_just_sent_.command_code_) {
+    case CommandCode::CONNECTION_REQUEST: {
+      link_->OnOutgoingConnectionRequestFail(command_just_sent_.source_cid_);
+      break;
+    }
+    case CommandCode::CONFIGURATION_REQUEST: {
+      auto channel = channel_allocator_->FindChannelByRemoteCid(command_just_sent_.destination_cid_);
+      SendDisconnectionRequest(channel->GetCid(), channel->GetRemoteCid());
+      break;
+    }
+    default:
+      break;
+  }
+  handle_send_next_command();
+}
+
+void ClassicSignallingManager::handle_send_next_command() {
+  command_just_sent_.signal_id_ = kInvalidSignalId;
+  if (pending_commands_.empty()) {
+    return;
+  }
+  command_just_sent_ = std::move(pending_commands_.front());
+  pending_commands_.pop();
+
+  auto signal_id = command_just_sent_.signal_id_;
+  auto psm = command_just_sent_.psm_;
+  auto source_cid = command_just_sent_.source_cid_;
+  auto destination_cid = command_just_sent_.destination_cid_;
+  auto info_type = command_just_sent_.info_type_;
+  auto config = std::move(command_just_sent_.config_);
+  switch (command_just_sent_.command_code_) {
+    case CommandCode::CONNECTION_REQUEST: {
+      auto builder = ConnectionRequestBuilder::Create(signal_id.Value(), psm, source_cid);
+      enqueue_buffer_->Enqueue(std::move(builder), handler_);
+      alarm_.Schedule(common::BindOnce(&ClassicSignallingManager::on_command_timeout, common::Unretained(this)),
+                      kTimeout);
+      break;
+    }
+    case CommandCode::CONFIGURATION_REQUEST: {
+      auto builder =
+          ConfigurationRequestBuilder::Create(signal_id.Value(), destination_cid, Continuation::END, std::move(config));
+      enqueue_buffer_->Enqueue(std::move(builder), handler_);
+      alarm_.Schedule(common::BindOnce(&ClassicSignallingManager::on_command_timeout, common::Unretained(this)),
+                      kTimeout);
+      break;
+    }
+    case CommandCode::DISCONNECTION_REQUEST: {
+      auto builder = DisconnectionRequestBuilder::Create(signal_id.Value(), destination_cid, source_cid);
+      enqueue_buffer_->Enqueue(std::move(builder), handler_);
+      alarm_.Schedule(common::BindOnce(&ClassicSignallingManager::on_command_timeout, common::Unretained(this)),
+                      kTimeout);
+      break;
+    }
+    case CommandCode::INFORMATION_REQUEST: {
+      auto builder = InformationRequestBuilder::Create(signal_id.Value(), info_type);
+      enqueue_buffer_->Enqueue(std::move(builder), handler_);
+      alarm_.Schedule(common::BindOnce(&ClassicSignallingManager::on_command_timeout, common::Unretained(this)),
+                      kTimeout);
+      break;
+    }
+    default:
+      LOG_WARN("Unsupported command code 0x%x", static_cast<int>(command_just_sent_.command_code_));
+  }
+}
+
+}  // namespace internal
+}  // namespace classic
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/classic/internal/signalling_manager.h b/gd/l2cap/classic/internal/signalling_manager.h
new file mode 100644
index 0000000..2a7b3c5
--- /dev/null
+++ b/gd/l2cap/classic/internal/signalling_manager.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <queue>
+#include <vector>
+
+#include "l2cap/cid.h"
+#include "l2cap/classic/internal/channel_configuration_state.h"
+#include "l2cap/classic/internal/dynamic_channel_service_manager_impl.h"
+#include "l2cap/classic/internal/fixed_channel_impl.h"
+#include "l2cap/classic/internal/fixed_channel_service_manager_impl.h"
+#include "l2cap/internal/data_pipeline_manager.h"
+#include "l2cap/internal/dynamic_channel_allocator.h"
+#include "l2cap/l2cap_packets.h"
+#include "l2cap/psm.h"
+#include "l2cap/signal_id.h"
+#include "os/alarm.h"
+#include "os/handler.h"
+#include "os/queue.h"
+#include "packet/raw_builder.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace classic {
+namespace internal {
+
+struct PendingCommand {
+  SignalId signal_id_ = kInvalidSignalId;
+  CommandCode command_code_;
+  Psm psm_;
+  Cid source_cid_;
+  Cid destination_cid_;
+  InformationRequestInfoType info_type_;
+  std::vector<std::unique_ptr<ConfigurationOption>> config_;
+};
+
+class Link;
+
+class ClassicSignallingManager {
+ public:
+  ClassicSignallingManager(os::Handler* handler, Link* link,
+                           l2cap::internal::DataPipelineManager* data_pipeline_manager,
+                           classic::internal::DynamicChannelServiceManagerImpl* dynamic_service_manager,
+                           l2cap::internal::DynamicChannelAllocator* channel_allocator,
+                           classic::internal::FixedChannelServiceManagerImpl* fixed_service_manager);
+
+  virtual ~ClassicSignallingManager();
+
+  void OnCommandReject(CommandRejectView command_reject_view);
+
+  void SendConnectionRequest(Psm psm, Cid local_cid);
+
+  void SendConfigurationRequest(Cid remote_cid, std::vector<std::unique_ptr<ConfigurationOption>> config);
+
+  void SendDisconnectionRequest(Cid local_cid, Cid remote_cid);
+
+  void SendInformationRequest(InformationRequestInfoType type);
+
+  void SendEchoRequest(std::unique_ptr<packet::RawBuilder> payload);
+
+  void CancelAlarm();
+
+  void OnConnectionRequest(SignalId signal_id, Psm psm, Cid remote_cid);
+
+  void OnConnectionResponse(SignalId signal_id, Cid remote_cid, Cid cid, ConnectionResponseResult result,
+                            ConnectionResponseStatus status);
+
+  void OnDisconnectionRequest(SignalId signal_id, Cid cid, Cid remote_cid);
+
+  void OnDisconnectionResponse(SignalId signal_id, Cid cid, Cid remote_cid);
+
+  void OnConfigurationRequest(SignalId signal_id, Cid cid, Continuation is_continuation,
+                              std::vector<std::unique_ptr<ConfigurationOption>>);
+
+  void OnConfigurationResponse(SignalId signal_id, Cid cid, Continuation is_continuation,
+                               ConfigurationResponseResult result, std::vector<std::unique_ptr<ConfigurationOption>>);
+
+  void OnEchoRequest(SignalId signal_id, const PacketView<kLittleEndian>& packet);
+
+  void OnEchoResponse(SignalId signal_id, const PacketView<kLittleEndian>& packet);
+
+  void OnInformationRequest(SignalId signal_id, InformationRequestInfoType type);
+
+  void OnInformationResponse(SignalId signal_id, const InformationResponseView& response);
+
+ private:
+  void on_incoming_packet();
+  void send_connection_response(SignalId signal_id, Cid remote_cid, Cid local_cid, ConnectionResponseResult result,
+                                ConnectionResponseStatus status);
+  void on_command_timeout();
+  void handle_send_next_command();
+
+  os::Handler* handler_;
+  Link* link_;
+  l2cap::internal::DataPipelineManager* data_pipeline_manager_;
+  std::shared_ptr<classic::internal::FixedChannelImpl> signalling_channel_;
+  DynamicChannelServiceManagerImpl* dynamic_service_manager_;
+  l2cap::internal::DynamicChannelAllocator* channel_allocator_;
+  FixedChannelServiceManagerImpl* fixed_service_manager_;
+  std::unique_ptr<os::EnqueueBuffer<packet::BasePacketBuilder>> enqueue_buffer_;
+  std::queue<PendingCommand> pending_commands_;
+  PendingCommand command_just_sent_;
+  os::Alarm alarm_;
+  SignalId next_signal_id_ = kInitialSignalId;
+  std::unordered_map<Cid, ChannelConfigurationState> channel_configuration_;
+};
+
+}  // namespace internal
+}  // namespace classic
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/classic/internal/signalling_manager_test.cc b/gd/l2cap/classic/internal/signalling_manager_test.cc
new file mode 100644
index 0000000..a89b445
--- /dev/null
+++ b/gd/l2cap/classic/internal/signalling_manager_test.cc
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "l2cap/classic/internal/signalling_manager.h"
+
+#include "l2cap/classic/internal/dynamic_channel_service_manager_impl_mock.h"
+#include "l2cap/classic/internal/fixed_channel_service_manager_impl_mock.h"
+#include "l2cap/classic/internal/link_mock.h"
+#include "l2cap/internal/parameter_provider_mock.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+using ::testing::_;
+using ::testing::Return;
+
+namespace bluetooth {
+namespace l2cap {
+namespace classic {
+namespace internal {
+namespace {
+
+class L2capClassicSignallingManagerTest : public ::testing::Test {
+ public:
+  static void SyncHandler(os::Handler* handler) {
+    std::promise<void> promise;
+    auto future = promise.get_future();
+    handler->Post(common::BindOnce(&std::promise<void>::set_value, common::Unretained(&promise)));
+    future.wait_for(std::chrono::milliseconds(3));
+  }
+
+ protected:
+  void SetUp() override {
+    thread_ = new os::Thread("test_thread", os::Thread::Priority::NORMAL);
+    l2cap_handler_ = new os::Handler(thread_);
+  }
+
+  void TearDown() override {
+    l2cap_handler_->Clear();
+    delete l2cap_handler_;
+    delete thread_;
+  }
+
+  os::Thread* thread_ = nullptr;
+  os::Handler* l2cap_handler_ = nullptr;
+};
+
+TEST_F(L2capClassicSignallingManagerTest, precondition) {}
+
+}  // namespace
+}  // namespace internal
+}  // namespace classic
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/classic/l2cap_classic_module.cc b/gd/l2cap/classic/l2cap_classic_module.cc
new file mode 100644
index 0000000..ca5a0d5
--- /dev/null
+++ b/gd/l2cap/classic/l2cap_classic_module.cc
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_TAG "l2cap2"
+
+#include <memory>
+
+#include "common/bidi_queue.h"
+#include "hci/acl_manager.h"
+#include "hci/address.h"
+#include "hci/hci_layer.h"
+#include "hci/hci_packets.h"
+#include "l2cap/classic/internal/dynamic_channel_service_manager_impl.h"
+#include "l2cap/classic/internal/fixed_channel_service_manager_impl.h"
+#include "l2cap/classic/internal/link_manager.h"
+#include "l2cap/internal/parameter_provider.h"
+#include "module.h"
+#include "os/handler.h"
+#include "os/log.h"
+
+#include "l2cap/classic/l2cap_classic_module.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace classic {
+
+const ModuleFactory L2capClassicModule::Factory = ModuleFactory([]() { return new L2capClassicModule(); });
+
+struct L2capClassicModule::impl {
+  impl(os::Handler* l2cap_handler, hci::AclManager* acl_manager)
+      : l2cap_handler_(l2cap_handler), acl_manager_(acl_manager) {}
+  os::Handler* l2cap_handler_;
+  hci::AclManager* acl_manager_;
+  l2cap::internal::ParameterProvider parameter_provider_;
+  internal::FixedChannelServiceManagerImpl fixed_channel_service_manager_impl_{l2cap_handler_};
+  internal::DynamicChannelServiceManagerImpl dynamic_channel_service_manager_impl_{l2cap_handler_};
+  internal::LinkManager link_manager_{l2cap_handler_, acl_manager_, &fixed_channel_service_manager_impl_,
+                                      &dynamic_channel_service_manager_impl_, &parameter_provider_};
+};
+
+L2capClassicModule::L2capClassicModule() {}
+
+L2capClassicModule::~L2capClassicModule() {}
+
+void L2capClassicModule::ListDependencies(ModuleList* list) {
+  list->add<hci::AclManager>();
+}
+
+void L2capClassicModule::Start() {
+  pimpl_ = std::make_unique<impl>(GetHandler(), GetDependency<hci::AclManager>());
+}
+
+void L2capClassicModule::Stop() {
+  pimpl_.reset();
+}
+
+std::string L2capClassicModule::ToString() const {
+  return "L2cap Classic Module";
+}
+
+std::unique_ptr<FixedChannelManager> L2capClassicModule::GetFixedChannelManager() {
+  return std::unique_ptr<FixedChannelManager>(new FixedChannelManager(&pimpl_->fixed_channel_service_manager_impl_,
+                                                                      &pimpl_->link_manager_, pimpl_->l2cap_handler_));
+}
+
+std::unique_ptr<DynamicChannelManager> L2capClassicModule::GetDynamicChannelManager() {
+  return std::unique_ptr<DynamicChannelManager>(new DynamicChannelManager(
+      &pimpl_->dynamic_channel_service_manager_impl_, &pimpl_->link_manager_, pimpl_->l2cap_handler_));
+}
+
+}  // namespace classic
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/classic/l2cap_classic_module.h b/gd/l2cap/classic/l2cap_classic_module.h
new file mode 100644
index 0000000..388cae2
--- /dev/null
+++ b/gd/l2cap/classic/l2cap_classic_module.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <memory>
+
+#include "l2cap/classic/dynamic_channel_manager.h"
+#include "l2cap/classic/fixed_channel_manager.h"
+#include "module.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace classic {
+
+class L2capClassicModule : public bluetooth::Module {
+ public:
+  L2capClassicModule();
+  virtual ~L2capClassicModule();
+
+  /**
+   * Get the api to the classic fixed channel l2cap module
+   */
+  virtual std::unique_ptr<FixedChannelManager> GetFixedChannelManager();
+
+  /**
+   * Get the api to the classic dynamic channel l2cap module
+   */
+  virtual std::unique_ptr<DynamicChannelManager> GetDynamicChannelManager();
+
+  static const ModuleFactory Factory;
+
+ protected:
+  void ListDependencies(ModuleList* list) override;
+
+  void Start() override;
+
+  void Stop() override;
+
+  std::string ToString() const override;
+
+ private:
+  struct impl;
+  std::unique_ptr<impl> pimpl_;
+  DISALLOW_COPY_AND_ASSIGN(L2capClassicModule);
+};
+
+}  // namespace classic
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/classic/l2cap_classic_module_mock.h b/gd/l2cap/classic/l2cap_classic_module_mock.h
new file mode 100644
index 0000000..1252854
--- /dev/null
+++ b/gd/l2cap/classic/l2cap_classic_module_mock.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include "l2cap/classic/l2cap_classic_module.h"
+
+#include <gmock/gmock.h>
+
+// Unit test interfaces
+namespace bluetooth {
+namespace l2cap {
+namespace classic {
+namespace testing {
+
+class MockL2capClassicModule : public L2capClassicModule {
+ public:
+  MOCK_METHOD(std::unique_ptr<FixedChannelManager>, GetFixedChannelManager, (), (override));
+  MOCK_METHOD(std::unique_ptr<DynamicChannelManager>, GetDynamicChannelManager, (), (override));
+};
+
+}  // namespace testing
+}  // namespace classic
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/dynamic_channel.cc b/gd/l2cap/dynamic_channel.cc
new file mode 100644
index 0000000..f8b64e6
--- /dev/null
+++ b/gd/l2cap/dynamic_channel.cc
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "l2cap/dynamic_channel.h"
+#include "common/bind.h"
+#include "l2cap/internal/dynamic_channel_impl.h"
+
+namespace bluetooth {
+namespace l2cap {
+
+hci::Address DynamicChannel::GetDevice() const {
+  return impl_->GetDevice();
+}
+
+void DynamicChannel::RegisterOnCloseCallback(os::Handler* user_handler,
+                                             DynamicChannel::OnCloseCallback on_close_callback) {
+  l2cap_handler_->Post(common::BindOnce(&l2cap::internal::DynamicChannelImpl::RegisterOnCloseCallback, impl_,
+                                        user_handler, std::move(on_close_callback)));
+}
+
+void DynamicChannel::Close() {
+  l2cap_handler_->Post(common::BindOnce(&l2cap::internal::DynamicChannelImpl::Close, impl_));
+}
+
+common::BidiQueueEnd<packet::BasePacketBuilder, packet::PacketView<packet::kLittleEndian>>*
+DynamicChannel::GetQueueUpEnd() const {
+  return impl_->GetQueueUpEnd();
+}
+}  // namespace l2cap
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/l2cap/dynamic_channel.h b/gd/l2cap/dynamic_channel.h
new file mode 100644
index 0000000..b7496f8
--- /dev/null
+++ b/gd/l2cap/dynamic_channel.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include "common/bidi_queue.h"
+#include "common/callback.h"
+#include "hci/acl_manager.h"
+#include "os/handler.h"
+#include "packet/base_packet_builder.h"
+#include "packet/packet_view.h"
+
+namespace bluetooth {
+namespace l2cap {
+
+namespace internal {
+class DynamicChannelImpl;
+}  // namespace internal
+
+/**
+ * L2CAP Dynamic channel object. User needs to call Close() when user no longer wants to use it. Otherwise the link
+ * won't be disconnected.
+ */
+class DynamicChannel {
+ public:
+  // Should only be constructed by modules that have access to LinkManager
+  DynamicChannel(std::shared_ptr<l2cap::internal::DynamicChannelImpl> impl, os::Handler* l2cap_handler)
+      : impl_(std::move(impl)), l2cap_handler_(l2cap_handler) {
+    ASSERT(impl_ != nullptr);
+    ASSERT(l2cap_handler_ != nullptr);
+  }
+
+  hci::Address GetDevice() const;
+
+  /**
+   * Register close callback. If close callback is registered, when a channel is closed, the channel's resource will
+   * only be freed after on_close callback is invoked. Otherwise, if no on_close callback is registered, the channel's
+   * resource will be freed immediately after closing.
+   *
+   * @param user_handler The handler used to invoke the callback on
+   * @param on_close_callback The callback invoked upon channel closing.
+   */
+  using OnCloseCallback = common::OnceCallback<void(hci::ErrorCode)>;
+  void RegisterOnCloseCallback(os::Handler* user_handler, OnCloseCallback on_close_callback);
+
+  /**
+   * Indicate that this Dynamic Channel should be closed. OnCloseCallback will be invoked when channel close is done.
+   * L2cay layer may terminate this ACL connection to free the resource after channel is closed.
+   */
+  void Close();
+
+  /**
+   * This method will retrieve the data channel queue to send and receive packets.
+   *
+   * {@see BidiQueueEnd}
+   *
+   * @return The upper end of a bi-directional queue.
+   */
+  common::BidiQueueEnd<packet::BasePacketBuilder, packet::PacketView<packet::kLittleEndian>>* GetQueueUpEnd() const;
+
+ private:
+  std::shared_ptr<l2cap::internal::DynamicChannelImpl> impl_;
+  os::Handler* l2cap_handler_;
+};
+
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/fcs.cc b/gd/l2cap/fcs.cc
new file mode 100644
index 0000000..3ad78cc
--- /dev/null
+++ b/gd/l2cap/fcs.cc
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "l2cap/fcs.h"
+
+namespace {
+// Table for optimizing the CRC calculation, which is a bitwise operation.
+static const uint16_t crctab[256] = {
+    0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241, 0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1,
+    0xc481, 0x0440, 0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40, 0x0a00, 0xcac1, 0xcb81, 0x0b40,
+    0xc901, 0x09c0, 0x0880, 0xc841, 0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40, 0x1e00, 0xdec1,
+    0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41, 0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641,
+    0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040, 0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1,
+    0xf281, 0x3240, 0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441, 0x3c00, 0xfcc1, 0xfd81, 0x3d40,
+    0xff01, 0x3fc0, 0x3e80, 0xfe41, 0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840, 0x2800, 0xe8c1,
+    0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41, 0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40,
+    0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640, 0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0,
+    0x2080, 0xe041, 0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240, 0x6600, 0xa6c1, 0xa781, 0x6740,
+    0xa501, 0x65c0, 0x6480, 0xa441, 0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41, 0xaa01, 0x6ac0,
+    0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840, 0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41,
+    0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40, 0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1,
+    0xb681, 0x7640, 0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041, 0x5000, 0x90c1, 0x9181, 0x5140,
+    0x9301, 0x53c0, 0x5280, 0x9241, 0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440, 0x9c01, 0x5cc0,
+    0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40, 0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841,
+    0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40, 0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0,
+    0x4c80, 0x8c41, 0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641, 0x8201, 0x42c0, 0x4380, 0x8341,
+    0x4100, 0x81c1, 0x8081, 0x4040,
+};
+}  // namespace
+
+namespace bluetooth {
+namespace l2cap {
+
+void Fcs::Initialize() {
+  crc = 0;
+}
+
+void Fcs::AddByte(uint8_t byte) {
+  crc = ((crc >> 8) & 0x00ff) ^ crctab[(crc & 0x00ff) ^ byte];
+}
+
+uint16_t Fcs::GetChecksum() const {
+  return crc;
+}
+
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/fcs.h b/gd/l2cap/fcs.h
new file mode 100644
index 0000000..127fa9d
--- /dev/null
+++ b/gd/l2cap/fcs.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+
+namespace bluetooth {
+namespace l2cap {
+
+// Frame Check Sequence from the L2CAP spec.
+class Fcs {
+ public:
+  void Initialize();
+
+  void AddByte(uint8_t byte);
+
+  uint16_t GetChecksum() const;
+
+ private:
+  uint16_t crc;
+};
+
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/internal/basic_mode_channel_data_controller.cc b/gd/l2cap/internal/basic_mode_channel_data_controller.cc
new file mode 100644
index 0000000..0badf0e
--- /dev/null
+++ b/gd/l2cap/internal/basic_mode_channel_data_controller.cc
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "l2cap/internal/basic_mode_channel_data_controller.h"
+
+#include "l2cap/l2cap_packets.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+
+BasicModeDataController::BasicModeDataController(Cid cid, Cid remote_cid, UpperQueueDownEnd* channel_queue_end,
+                                                 os::Handler* handler, Scheduler* scheduler)
+    : cid_(cid), remote_cid_(remote_cid), enqueue_buffer_(channel_queue_end), handler_(handler), scheduler_(scheduler) {
+}
+
+void BasicModeDataController::OnSdu(std::unique_ptr<packet::BasePacketBuilder> sdu) {
+  auto l2cap_information = BasicFrameBuilder::Create(remote_cid_, std::move(sdu));
+  pdu_queue_.emplace(std::move(l2cap_information));
+  scheduler_->OnPacketsReady(cid_, 1);
+}
+
+void BasicModeDataController::OnPdu(packet::PacketView<true> pdu) {
+  auto basic_frame_view = BasicFrameView::Create(pdu);
+  if (!basic_frame_view.IsValid()) {
+    LOG_WARN("Received invalid frame");
+    return;
+  }
+  enqueue_buffer_.Enqueue(std::make_unique<PacketView<kLittleEndian>>(basic_frame_view.GetPayload()), handler_);
+}
+
+std::unique_ptr<packet::BasePacketBuilder> BasicModeDataController::GetNextPacket() {
+  auto next = std::move(pdu_queue_.front());
+  pdu_queue_.pop();
+  return next;
+}
+
+}  // namespace internal
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/internal/basic_mode_channel_data_controller.h b/gd/l2cap/internal/basic_mode_channel_data_controller.h
new file mode 100644
index 0000000..8e40402
--- /dev/null
+++ b/gd/l2cap/internal/basic_mode_channel_data_controller.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <memory>
+#include <unordered_map>
+#include <utility>
+
+#include "common/bidi_queue.h"
+#include "l2cap/cid.h"
+#include "l2cap/internal/channel_impl.h"
+#include "l2cap/internal/data_controller.h"
+#include "l2cap/internal/scheduler.h"
+#include "l2cap/l2cap_packets.h"
+#include "l2cap/mtu.h"
+#include "os/handler.h"
+#include "os/queue.h"
+#include "packet/base_packet_builder.h"
+#include "packet/packet_view.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+
+class BasicModeDataController : public DataController {
+ public:
+  using UpperEnqueue = packet::PacketView<packet::kLittleEndian>;
+  using UpperDequeue = packet::BasePacketBuilder;
+  using UpperQueueDownEnd = common::BidiQueueEnd<UpperEnqueue, UpperDequeue>;
+  BasicModeDataController(Cid cid, Cid remote_cid, UpperQueueDownEnd* channel_queue_end, os::Handler* handler,
+                          Scheduler* scheduler);
+
+  void OnSdu(std::unique_ptr<packet::BasePacketBuilder> sdu) override;
+
+  void OnPdu(packet::PacketView<true> pdu) override;
+
+  std::unique_ptr<packet::BasePacketBuilder> GetNextPacket() override;
+
+  void EnableFcs(bool enabled) override {}
+  void SetRetransmissionAndFlowControlOptions(const RetransmissionAndFlowControlConfigurationOption& option) override {}
+
+ private:
+  Cid cid_;
+  Cid remote_cid_;
+  os::EnqueueBuffer<UpperEnqueue> enqueue_buffer_;
+  os::Handler* handler_;
+  std::queue<std::unique_ptr<packet::BasePacketBuilder>> pdu_queue_;
+  Scheduler* scheduler_;
+};
+
+}  // namespace internal
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/internal/basic_mode_channel_data_controller_test.cc b/gd/l2cap/internal/basic_mode_channel_data_controller_test.cc
new file mode 100644
index 0000000..c4cc56d
--- /dev/null
+++ b/gd/l2cap/internal/basic_mode_channel_data_controller_test.cc
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "l2cap/internal/basic_mode_channel_data_controller.h"
+
+#include <gtest/gtest.h>
+
+#include "l2cap/internal/scheduler_mock.h"
+#include "packet/raw_builder.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+namespace {
+
+std::unique_ptr<packet::BasePacketBuilder> CreateSdu(std::vector<uint8_t> payload) {
+  auto raw_builder = std::make_unique<packet::RawBuilder>();
+  raw_builder->AddOctets(payload);
+  return raw_builder;
+}
+
+PacketView<kLittleEndian> GetPacketView(std::unique_ptr<packet::BasePacketBuilder> packet) {
+  auto bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter i(*bytes);
+  bytes->reserve(packet->size());
+  packet->Serialize(i);
+  return packet::PacketView<packet::kLittleEndian>(bytes);
+}
+
+void sync_handler(os::Handler* handler) {
+  std::promise<void> promise;
+  auto future = promise.get_future();
+  handler->Post(common::BindOnce(&std::promise<void>::set_value, common::Unretained(&promise)));
+  auto status = future.wait_for(std::chrono::milliseconds(300));
+  EXPECT_EQ(status, std::future_status::ready);
+}
+
+class BasicModeDataControllerTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    thread_ = new os::Thread("test_thread", os::Thread::Priority::NORMAL);
+    user_handler_ = new os::Handler(thread_);
+    queue_handler_ = new os::Handler(thread_);
+  }
+
+  void TearDown() override {
+    queue_handler_->Clear();
+    user_handler_->Clear();
+    delete queue_handler_;
+    delete user_handler_;
+    delete thread_;
+  }
+
+  os::Thread* thread_ = nullptr;
+  os::Handler* user_handler_ = nullptr;
+  os::Handler* queue_handler_ = nullptr;
+};
+
+TEST_F(BasicModeDataControllerTest, transmit) {
+  common::BidiQueue<Scheduler::UpperEnqueue, Scheduler::UpperDequeue> channel_queue{10};
+  testing::MockScheduler scheduler;
+  BasicModeDataController controller{1, 1, channel_queue.GetDownEnd(), queue_handler_, &scheduler};
+  EXPECT_CALL(scheduler, OnPacketsReady(1, 1));
+  controller.OnSdu(CreateSdu({'a', 'b', 'c', 'd'}));
+  auto next_packet = controller.GetNextPacket();
+  EXPECT_NE(next_packet, nullptr);
+  auto view = GetPacketView(std::move(next_packet));
+  auto pdu_view = BasicFrameView::Create(view);
+  EXPECT_TRUE(pdu_view.IsValid());
+  auto payload = pdu_view.GetPayload();
+  std::string data = std::string(payload.begin(), payload.end());
+  EXPECT_EQ(data, "abcd");
+}
+
+TEST_F(BasicModeDataControllerTest, receive) {
+  common::BidiQueue<Scheduler::UpperEnqueue, Scheduler::UpperDequeue> channel_queue{10};
+  testing::MockScheduler scheduler;
+  BasicModeDataController controller{1, 1, channel_queue.GetDownEnd(), queue_handler_, &scheduler};
+  auto base_view = GetPacketView(BasicFrameBuilder::Create(1, CreateSdu({'a', 'b', 'c', 'd'})));
+  controller.OnPdu(base_view);
+  sync_handler(queue_handler_);
+  auto packet_view = channel_queue.GetUpEnd()->TryDequeue();
+  EXPECT_NE(packet_view, nullptr);
+  std::string data = std::string(packet_view->begin(), packet_view->end());
+  EXPECT_EQ(data, "abcd");
+}
+
+}  // namespace
+}  // namespace internal
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/internal/channel_impl.h b/gd/l2cap/internal/channel_impl.h
new file mode 100644
index 0000000..427a15f
--- /dev/null
+++ b/gd/l2cap/internal/channel_impl.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "common/bidi_queue.h"
+#include "l2cap/cid.h"
+#include "l2cap/l2cap_packets.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+class Sender;
+
+/**
+ * Common interface for internal channel implementation
+ */
+class ChannelImpl {
+ public:
+  virtual ~ChannelImpl() = default;
+
+  /**
+   * Return the queue end for upper layer (L2CAP user)
+   */
+  virtual common::BidiQueueEnd<packet::BasePacketBuilder, packet::PacketView<packet::kLittleEndian>>*
+  GetQueueUpEnd() = 0;
+
+  /**
+   * Return the queue end for lower layer (sender and receiver)
+   */
+  virtual common::BidiQueueEnd<packet::PacketView<packet::kLittleEndian>, packet::BasePacketBuilder>*
+  GetQueueDownEnd() = 0;
+
+  virtual Cid GetCid() const = 0;
+
+  virtual Cid GetRemoteCid() const = 0;
+};
+
+}  // namespace internal
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/internal/channel_impl_mock.h b/gd/l2cap/internal/channel_impl_mock.h
new file mode 100644
index 0000000..4430487
--- /dev/null
+++ b/gd/l2cap/internal/channel_impl_mock.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "l2cap/internal/channel_impl.h"
+
+#include <gmock/gmock.h>
+
+// Unit test interfaces
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+namespace testing {
+
+class MockChannelImpl : public ChannelImpl {
+ public:
+  MOCK_METHOD((common::BidiQueueEnd<packet::BasePacketBuilder, packet::PacketView<packet::kLittleEndian>>*),
+              GetQueueUpEnd, (), (override));
+  MOCK_METHOD((common::BidiQueueEnd<packet::PacketView<packet::kLittleEndian>, packet::BasePacketBuilder>*),
+              GetQueueDownEnd, (), (override));
+  MOCK_METHOD(Cid, GetCid, (), (const, override));
+  MOCK_METHOD(Cid, GetRemoteCid, (), (const, override));
+};
+
+}  // namespace testing
+}  // namespace internal
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/internal/data_controller.h b/gd/l2cap/internal/data_controller.h
new file mode 100644
index 0000000..bc7c2e5
--- /dev/null
+++ b/gd/l2cap/internal/data_controller.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include "l2cap/l2cap_packets.h"
+#include "packet/base_packet_builder.h"
+#include "packet/packet_view.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+
+class DataController {
+ public:
+  virtual ~DataController() = default;
+
+  // SDU -> PDUs and notify Scheduler
+  virtual void OnSdu(std::unique_ptr<packet::BasePacketBuilder> sdu) = 0;
+
+  // PDUs -> SDU and enqueue to channel queue end
+  virtual void OnPdu(packet::PacketView<true> pdu) = 0;
+
+  // Used by Scheduler to get next PDU
+  virtual std::unique_ptr<packet::BasePacketBuilder> GetNextPacket() = 0;
+
+  // Set FCS mode. This only applies to some modes (ERTM).
+  virtual void EnableFcs(bool enabled) = 0;
+
+  // Set retransmission and flow control. Ignore the mode option because each DataController only handles one mode.
+  // This only applies to some modes (ERTM).
+  virtual void SetRetransmissionAndFlowControlOptions(
+      const RetransmissionAndFlowControlConfigurationOption& option) = 0;
+};
+
+}  // namespace internal
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/internal/data_controller_mock.h b/gd/l2cap/internal/data_controller_mock.h
new file mode 100644
index 0000000..d071c68
--- /dev/null
+++ b/gd/l2cap/internal/data_controller_mock.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "l2cap/internal/data_controller.h"
+
+#include <gmock/gmock.h>
+
+// Unit test interfaces
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+namespace testing {
+
+class MockDataController : public DataController {
+ public:
+  MOCK_METHOD(void, OnSdu, (std::unique_ptr<packet::BasePacketBuilder>), (override));
+  MOCK_METHOD(void, OnPdu, (packet::PacketView<true>), (override));
+  MOCK_METHOD(std::unique_ptr<packet::BasePacketBuilder>, GetNextPacket, (), (override));
+  MOCK_METHOD(void, EnableFcs, (bool), (override));
+  MOCK_METHOD(void, SetRetransmissionAndFlowControlOptions, (const RetransmissionAndFlowControlConfigurationOption&),
+              (override));
+};
+
+}  // namespace testing
+}  // namespace internal
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/internal/data_pipeline_manager.cc b/gd/l2cap/internal/data_pipeline_manager.cc
new file mode 100644
index 0000000..8fd40fb
--- /dev/null
+++ b/gd/l2cap/internal/data_pipeline_manager.cc
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <unordered_map>
+
+#include "l2cap/cid.h"
+#include "l2cap/internal/channel_impl.h"
+#include "l2cap/internal/data_controller.h"
+#include "l2cap/internal/data_pipeline_manager.h"
+#include "l2cap/internal/sender.h"
+#include "os/log.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+
+void DataPipelineManager::AttachChannel(Cid cid, std::shared_ptr<ChannelImpl> channel, ChannelMode mode) {
+  ASSERT(sender_map_.find(cid) == sender_map_.end());
+  sender_map_.emplace(std::piecewise_construct, std::forward_as_tuple(cid),
+                      std::forward_as_tuple(handler_, link_, scheduler_.get(), channel, mode));
+}
+
+void DataPipelineManager::DetachChannel(Cid cid) {
+  ASSERT(sender_map_.find(cid) != sender_map_.end());
+  sender_map_.erase(cid);
+}
+
+DataController* DataPipelineManager::GetDataController(Cid cid) {
+  ASSERT(sender_map_.find(cid) != sender_map_.end());
+  return sender_map_.find(cid)->second.GetDataController();
+}
+
+void DataPipelineManager::OnPacketSent(Cid cid) {
+  ASSERT(sender_map_.find(cid) != sender_map_.end());
+  sender_map_.find(cid)->second.OnPacketSent();
+}
+
+void DataPipelineManager::UpdateClassicConfiguration(Cid cid, classic::internal::ChannelConfigurationState config) {
+  ASSERT(sender_map_.find(cid) != sender_map_.end());
+  sender_map_.find(cid)->second.UpdateClassicConfiguration(config);
+}
+
+}  // namespace internal
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/internal/data_pipeline_manager.h b/gd/l2cap/internal/data_pipeline_manager.h
new file mode 100644
index 0000000..0424de9
--- /dev/null
+++ b/gd/l2cap/internal/data_pipeline_manager.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+#include <unordered_map>
+
+#include "common/bidi_queue.h"
+#include "common/bind.h"
+#include "data_controller.h"
+#include "l2cap/cid.h"
+#include "l2cap/classic/internal/channel_configuration_state.h"
+#include "l2cap/internal/channel_impl.h"
+#include "l2cap/internal/receiver.h"
+#include "l2cap/internal/scheduler.h"
+#include "l2cap/internal/scheduler_fifo.h"
+#include "l2cap/l2cap_packets.h"
+#include "l2cap/mtu.h"
+#include "os/handler.h"
+#include "os/log.h"
+#include "os/queue.h"
+#include "packet/base_packet_builder.h"
+#include "packet/packet_view.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+class ILink;
+
+/**
+ * Manages data pipeline from channel queue end to link queue end, per link.
+ * Contains a Scheduler and Receiver per link.
+ * Contains a Sender and its corresponding DataController per attached channel.
+ */
+class DataPipelineManager {
+ public:
+  using UpperEnqueue = packet::PacketView<packet::kLittleEndian>;
+  using UpperDequeue = packet::BasePacketBuilder;
+  using UpperQueueDownEnd = common::BidiQueueEnd<UpperEnqueue, UpperDequeue>;
+  using LowerEnqueue = UpperDequeue;
+  using LowerDequeue = UpperEnqueue;
+  using LowerQueueUpEnd = common::BidiQueueEnd<LowerEnqueue, LowerDequeue>;
+
+  DataPipelineManager(os::Handler* handler, ILink* link, LowerQueueUpEnd* link_queue_up_end)
+      : handler_(handler), link_(link), scheduler_(std::make_unique<Fifo>(this, link_queue_up_end, handler)),
+        receiver_(link_queue_up_end, handler, this) {}
+
+  using ChannelMode = Sender::ChannelMode;
+
+  virtual void AttachChannel(Cid cid, std::shared_ptr<ChannelImpl> channel, ChannelMode mode);
+  virtual void DetachChannel(Cid cid);
+  virtual DataController* GetDataController(Cid cid);
+  virtual void OnPacketSent(Cid cid);
+  virtual void UpdateClassicConfiguration(Cid cid, classic::internal::ChannelConfigurationState config);
+  virtual ~DataPipelineManager() = default;
+
+ private:
+  os::Handler* handler_;
+  ILink* link_;
+  std::unique_ptr<Scheduler> scheduler_;
+  Receiver receiver_;
+  std::unordered_map<Cid, Sender> sender_map_;
+};
+}  // namespace internal
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/internal/data_pipeline_manager_mock.h b/gd/l2cap/internal/data_pipeline_manager_mock.h
new file mode 100644
index 0000000..01fb926
--- /dev/null
+++ b/gd/l2cap/internal/data_pipeline_manager_mock.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "l2cap/internal/data_pipeline_manager.h"
+
+#include "l2cap/internal/channel_impl.h"
+#include "l2cap/internal/data_controller.h"
+
+#include <gmock/gmock.h>
+
+// Unit test interfaces
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+namespace testing {
+
+class MockDataPipelineManager : public DataPipelineManager {
+ public:
+  MockDataPipelineManager(os::Handler* handler, LowerQueueUpEnd* link_queue_up_end)
+      : DataPipelineManager(handler, nullptr, link_queue_up_end) {}
+  MOCK_METHOD(void, AttachChannel, (Cid, std::shared_ptr<ChannelImpl>, ChannelMode), (override));
+  MOCK_METHOD(void, DetachChannel, (Cid), (override));
+  MOCK_METHOD(DataController*, GetDataController, (Cid), (override));
+  MOCK_METHOD(void, OnPacketSent, (Cid), (override));
+};
+
+}  // namespace testing
+}  // namespace internal
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/internal/dynamic_channel_allocator.cc b/gd/l2cap/internal/dynamic_channel_allocator.cc
new file mode 100644
index 0000000..1b34471
--- /dev/null
+++ b/gd/l2cap/internal/dynamic_channel_allocator.cc
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <unordered_map>
+
+#include "l2cap/cid.h"
+#include "l2cap/classic/internal/link.h"
+#include "l2cap/internal/dynamic_channel_allocator.h"
+#include "l2cap/internal/dynamic_channel_impl.h"
+#include "l2cap/security_policy.h"
+#include "os/log.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+
+std::shared_ptr<DynamicChannelImpl> DynamicChannelAllocator::AllocateChannel(Psm psm, Cid remote_cid,
+                                                                             SecurityPolicy security_policy) {
+  ASSERT_LOG(IsPsmValid(psm), "Psm 0x%x is invalid", psm);
+
+  if (used_remote_cid_.find(remote_cid) != used_remote_cid_.end()) {
+    LOG_INFO("Remote cid 0x%x is used", remote_cid);
+    return nullptr;
+  }
+  Cid cid = kFirstDynamicChannel;
+  for (; cid <= kLastDynamicChannel; cid++) {
+    if (used_cid_.find(cid) == used_cid_.end()) break;
+  }
+  if (cid > kLastDynamicChannel) {
+    LOG_WARN("All cid are used");
+    return nullptr;
+  }
+  auto elem =
+      channels_.try_emplace(cid, std::make_shared<DynamicChannelImpl>(psm, cid, remote_cid, link_, l2cap_handler_));
+  ASSERT_LOG(elem.second, "Failed to create channel for psm 0x%x device %s", psm,
+             link_->GetDevice().ToString().c_str());
+  ASSERT(elem.first->second != nullptr);
+  used_remote_cid_.insert(remote_cid);
+  used_cid_.insert(cid);
+  return elem.first->second;
+}
+
+std::shared_ptr<DynamicChannelImpl> DynamicChannelAllocator::AllocateReservedChannel(Cid reserved_cid, Psm psm,
+                                                                                     Cid remote_cid,
+                                                                                     SecurityPolicy security_policy) {
+  ASSERT_LOG(IsPsmValid(psm), "Psm 0x%x is invalid", psm);
+
+  if (used_remote_cid_.find(remote_cid) != used_remote_cid_.end()) {
+    LOG_INFO("Remote cid 0x%x is used", remote_cid);
+    return nullptr;
+  }
+  auto elem = channels_.try_emplace(
+      reserved_cid, std::make_shared<DynamicChannelImpl>(psm, reserved_cid, remote_cid, link_, l2cap_handler_));
+  ASSERT_LOG(elem.second, "Failed to create channel for psm 0x%x device %s", psm,
+             link_->GetDevice().ToString().c_str());
+  ASSERT(elem.first->second != nullptr);
+  used_remote_cid_.insert(remote_cid);
+  return elem.first->second;
+}
+
+Cid DynamicChannelAllocator::ReserveChannel() {
+  Cid cid = kFirstDynamicChannel;
+  for (; cid <= kLastDynamicChannel; cid++) {
+    if (used_cid_.find(cid) == used_cid_.end()) break;
+  }
+  if (cid > kLastDynamicChannel) {
+    LOG_WARN("All cid are used");
+    return kInvalidCid;
+  }
+  used_cid_.insert(cid);
+  return cid;
+}
+
+void DynamicChannelAllocator::FreeChannel(Cid cid) {
+  used_cid_.erase(cid);
+  auto channel = FindChannelByCid(cid);
+  if (channel == nullptr) {
+    LOG_INFO("Channel is not in use: psm %d, device %s", cid, link_->GetDevice().ToString().c_str());
+    return;
+  }
+  used_remote_cid_.erase(channel->GetRemoteCid());
+  channels_.erase(cid);
+}
+
+bool DynamicChannelAllocator::IsPsmUsed(Psm psm) const {
+  for (const auto& channel : channels_) {
+    if (channel.second->GetPsm() == psm) {
+      return true;
+    }
+  }
+  return false;
+}
+
+std::shared_ptr<DynamicChannelImpl> DynamicChannelAllocator::FindChannelByCid(Cid cid) {
+  if (channels_.find(cid) == channels_.end()) {
+    LOG_WARN("Can't find cid %d", cid);
+    return nullptr;
+  }
+  return channels_.find(cid)->second;
+}
+
+std::shared_ptr<DynamicChannelImpl> DynamicChannelAllocator::FindChannelByRemoteCid(Cid remote_cid) {
+  for (auto& channel : channels_) {
+    if (channel.second->GetRemoteCid() == remote_cid) {
+      return channel.second;
+    }
+  }
+  return nullptr;
+}
+
+size_t DynamicChannelAllocator::NumberOfChannels() const {
+  return channels_.size();
+}
+
+void DynamicChannelAllocator::OnAclDisconnected(hci::ErrorCode reason) {
+  for (auto& elem : channels_) {
+    elem.second->OnClosed(reason);
+  }
+}
+
+}  // namespace internal
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/internal/dynamic_channel_allocator.h b/gd/l2cap/internal/dynamic_channel_allocator.h
new file mode 100644
index 0000000..09a3588
--- /dev/null
+++ b/gd/l2cap/internal/dynamic_channel_allocator.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <unordered_map>
+#include <unordered_set>
+
+#include "hci/acl_manager.h"
+#include "l2cap/cid.h"
+#include "l2cap/internal/ilink.h"
+#include "l2cap/psm.h"
+#include "l2cap/security_policy.h"
+#include "os/handler.h"
+#include "os/log.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+
+class DynamicChannelImpl;
+
+// Helper class for keeping channels in a Link. It allocates and frees Channel object, and supports querying whether a
+// channel is in use
+class DynamicChannelAllocator {
+ public:
+  DynamicChannelAllocator(l2cap::internal::ILink* link, os::Handler* l2cap_handler)
+      : link_(link), l2cap_handler_(l2cap_handler) {
+    ASSERT(link_ != nullptr);
+    ASSERT(l2cap_handler_ != nullptr);
+  }
+
+  // Allocates a channel. If psm is used, OR the remote cid already exists, return nullptr.
+  // NOTE: The returned DynamicChannelImpl object is still owned by the channel allocator, NOT the client.
+  std::shared_ptr<DynamicChannelImpl> AllocateChannel(Psm psm, Cid remote_cid, SecurityPolicy security_policy);
+
+  std::shared_ptr<DynamicChannelImpl> AllocateReservedChannel(Cid reserved_cid, Psm psm, Cid remote_cid,
+                                                              SecurityPolicy security_policy);
+
+  // Gives an unused Cid to be used for opening a channel. If a channel is used, call AllocateReservedChannel. If no
+  // longer needed, use FreeChannel.
+  Cid ReserveChannel();
+
+  // Frees a channel. If psm doesn't exist, it will crash
+  void FreeChannel(Cid cid);
+
+  bool IsPsmUsed(Psm psm) const;
+
+  std::shared_ptr<DynamicChannelImpl> FindChannelByCid(Cid cid);
+  std::shared_ptr<DynamicChannelImpl> FindChannelByRemoteCid(Cid cid);
+
+  // Returns number of open, but not reserved channels
+  size_t NumberOfChannels() const;
+
+  void OnAclDisconnected(hci::ErrorCode hci_status);
+
+ private:
+  l2cap::internal::ILink* link_;
+  os::Handler* l2cap_handler_;
+  std::unordered_set<Cid> used_cid_;
+  std::unordered_map<Cid, std::shared_ptr<DynamicChannelImpl>> channels_;
+  std::unordered_set<Cid> used_remote_cid_;
+};
+
+}  // namespace internal
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/internal/dynamic_channel_allocator_fuzz_test.cc b/gd/l2cap/internal/dynamic_channel_allocator_fuzz_test.cc
new file mode 100644
index 0000000..c3821dc
--- /dev/null
+++ b/gd/l2cap/internal/dynamic_channel_allocator_fuzz_test.cc
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "l2cap/classic/internal/link_mock.h"
+#include "l2cap/internal/dynamic_channel_allocator.h"
+#include "l2cap/internal/parameter_provider_mock.h"
+
+#include <gmock/gmock.h>
+
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+
+using classic::internal::testing::MockLink;
+using hci::testing::MockAclConnection;
+using l2cap::internal::testing::MockParameterProvider;
+using l2cap::internal::testing::MockScheduler;
+using ::testing::NiceMock;
+using ::testing::Return;
+
+const hci::AddressWithType device{{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}}, hci::AddressType::PUBLIC_IDENTITY_ADDRESS};
+
+class L2capClassicDynamicChannelAllocatorFuzzTest {
+ public:
+  void RunTests(const uint8_t* data, size_t size) {
+    SetUp();
+    TestPrecondition(data, size);
+    TearDown();
+  }
+
+ private:
+  void SetUp() {
+    thread_ = new os::Thread("test_thread", os::Thread::Priority::NORMAL);
+    handler_ = new os::Handler(thread_);
+    mock_parameter_provider_ = new NiceMock<MockParameterProvider>();
+    mock_classic_link_ =
+        new NiceMock<MockLink>(handler_, mock_parameter_provider_, std::make_unique<NiceMock<MockAclConnection>>());
+    EXPECT_CALL(*mock_classic_link_, GetDevice()).WillRepeatedly(Return(device));
+    channel_allocator_ = std::make_unique<DynamicChannelAllocator>(mock_classic_link_, handler_);
+  }
+
+  void TearDown() {
+    channel_allocator_.reset();
+    delete mock_classic_link_;
+    delete mock_parameter_provider_;
+    handler_->Clear();
+    delete handler_;
+    delete thread_;
+  }
+
+  void TestPrecondition(const uint8_t* data, size_t size) {
+    if (size != 2) {
+      return;
+    }
+    Psm psm = *reinterpret_cast<const Psm*>(data);
+    EXPECT_FALSE(channel_allocator_->IsPsmUsed(psm));
+  }
+
+  os::Thread* thread_{nullptr};
+  os::Handler* handler_{nullptr};
+  NiceMock<MockParameterProvider>* mock_parameter_provider_{nullptr};
+  NiceMock<MockLink>* mock_classic_link_{nullptr};
+  std::unique_ptr<DynamicChannelAllocator> channel_allocator_;
+};
+
+}  // namespace internal
+}  // namespace l2cap
+}  // namespace bluetooth
+
+void RunL2capClassicDynamicChannelAllocatorFuzzTest(const uint8_t* data, size_t size) {
+  bluetooth::l2cap::internal::L2capClassicDynamicChannelAllocatorFuzzTest test;
+  test.RunTests(data, size);
+}
\ No newline at end of file
diff --git a/gd/l2cap/internal/dynamic_channel_allocator_test.cc b/gd/l2cap/internal/dynamic_channel_allocator_test.cc
new file mode 100644
index 0000000..4fc0160
--- /dev/null
+++ b/gd/l2cap/internal/dynamic_channel_allocator_test.cc
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "l2cap/internal/dynamic_channel_allocator.h"
+#include "l2cap/classic/internal/link_mock.h"
+#include "l2cap/internal/parameter_provider_mock.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+
+using classic::internal::testing::MockLink;
+using l2cap::internal::testing::MockParameterProvider;
+using ::testing::Return;
+
+const hci::AddressWithType device{{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}}, hci::AddressType::PUBLIC_IDENTITY_ADDRESS};
+
+class L2capClassicDynamicChannelAllocatorTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    thread_ = new os::Thread("test_thread", os::Thread::Priority::NORMAL);
+    handler_ = new os::Handler(thread_);
+    mock_parameter_provider_ = new MockParameterProvider();
+    mock_classic_link_ = new MockLink(handler_, mock_parameter_provider_);
+    EXPECT_CALL(*mock_classic_link_, GetDevice()).WillRepeatedly(Return(device));
+    channel_allocator_ = std::make_unique<DynamicChannelAllocator>(mock_classic_link_, handler_);
+  }
+
+  void TearDown() override {
+    channel_allocator_.reset();
+    delete mock_classic_link_;
+    delete mock_parameter_provider_;
+    handler_->Clear();
+    delete handler_;
+    delete thread_;
+  }
+
+  os::Thread* thread_{nullptr};
+  os::Handler* handler_{nullptr};
+  MockParameterProvider* mock_parameter_provider_{nullptr};
+  MockLink* mock_classic_link_{nullptr};
+  std::unique_ptr<DynamicChannelAllocator> channel_allocator_;
+};
+
+TEST_F(L2capClassicDynamicChannelAllocatorTest, precondition) {
+  Psm psm = 0x03;
+  EXPECT_FALSE(channel_allocator_->IsPsmUsed(psm));
+}
+
+TEST_F(L2capClassicDynamicChannelAllocatorTest, allocate_and_free_channel) {
+  Psm psm = 0x03;
+  Cid remote_cid = kFirstDynamicChannel;
+  auto channel = channel_allocator_->AllocateChannel(psm, remote_cid, {});
+  Cid local_cid = channel->GetCid();
+  EXPECT_TRUE(channel_allocator_->IsPsmUsed(psm));
+  EXPECT_EQ(channel, channel_allocator_->FindChannelByCid(local_cid));
+  ASSERT_NO_FATAL_FAILURE(channel_allocator_->FreeChannel(local_cid));
+  EXPECT_FALSE(channel_allocator_->IsPsmUsed(psm));
+}
+
+TEST_F(L2capClassicDynamicChannelAllocatorTest, reserve_channel) {
+  Psm psm = 0x03;
+  Cid remote_cid = kFirstDynamicChannel;
+  Cid reserved = channel_allocator_->ReserveChannel();
+  auto channel = channel_allocator_->AllocateReservedChannel(reserved, psm, remote_cid, {});
+  Cid local_cid = channel->GetCid();
+  EXPECT_EQ(local_cid, reserved);
+  EXPECT_TRUE(channel_allocator_->IsPsmUsed(psm));
+  EXPECT_EQ(channel, channel_allocator_->FindChannelByCid(local_cid));
+  ASSERT_NO_FATAL_FAILURE(channel_allocator_->FreeChannel(local_cid));
+  EXPECT_FALSE(channel_allocator_->IsPsmUsed(psm));
+}
+
+}  // namespace internal
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/internal/dynamic_channel_impl.cc b/gd/l2cap/internal/dynamic_channel_impl.cc
new file mode 100644
index 0000000..947a32f
--- /dev/null
+++ b/gd/l2cap/internal/dynamic_channel_impl.cc
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <unordered_map>
+
+#include "l2cap/cid.h"
+#include "l2cap/classic/internal/link.h"
+#include "l2cap/internal/dynamic_channel_impl.h"
+#include "l2cap/internal/sender.h"
+#include "l2cap/psm.h"
+#include "l2cap/security_policy.h"
+#include "os/handler.h"
+#include "os/log.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+
+DynamicChannelImpl::DynamicChannelImpl(Psm psm, Cid cid, Cid remote_cid, l2cap::internal::ILink* link,
+                                       os::Handler* l2cap_handler)
+    : psm_(psm), cid_(cid), remote_cid_(remote_cid), link_(link), l2cap_handler_(l2cap_handler),
+      device_(link->GetDevice()) {
+  ASSERT(IsPsmValid(psm_));
+  ASSERT(cid_ > 0);
+  ASSERT(remote_cid_ > 0);
+  ASSERT(link_ != nullptr);
+  ASSERT(l2cap_handler_ != nullptr);
+}
+
+hci::Address DynamicChannelImpl::GetDevice() const {
+  return device_.GetAddress();
+}
+
+void DynamicChannelImpl::RegisterOnCloseCallback(os::Handler* user_handler,
+                                                 DynamicChannel::OnCloseCallback on_close_callback) {
+  ASSERT_LOG(user_handler_ == nullptr, "OnCloseCallback can only be registered once");
+  // If channel is already closed, call the callback immediately without saving it
+  if (closed_) {
+    user_handler->Post(common::BindOnce(std::move(on_close_callback), close_reason_));
+    return;
+  }
+  user_handler_ = user_handler;
+  on_close_callback_ = std::move(on_close_callback);
+}
+
+void DynamicChannelImpl::Close() {
+  link_->SendDisconnectionRequest(cid_, remote_cid_);
+}
+
+void DynamicChannelImpl::OnClosed(hci::ErrorCode status) {
+  ASSERT_LOG(!closed_, "Device %s Cid 0x%x closed twice, old status 0x%x, new status 0x%x", device_.ToString().c_str(),
+             cid_, static_cast<int>(close_reason_), static_cast<int>(status));
+  closed_ = true;
+  close_reason_ = status;
+  link_ = nullptr;
+  l2cap_handler_ = nullptr;
+  if (user_handler_ == nullptr) {
+    return;
+  }
+  // On close callback can only be called once
+  user_handler_->Post(common::BindOnce(std::move(on_close_callback_), status));
+  user_handler_ = nullptr;
+  on_close_callback_.Reset();
+}
+
+std::string DynamicChannelImpl::ToString() {
+  std::ostringstream ss;
+  ss << "Device " << device_ << "Psm 0x" << std::hex << psm_ << " Cid 0x" << std::hex << cid_;
+  return ss.str();
+}
+
+}  // namespace internal
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/internal/dynamic_channel_impl.h b/gd/l2cap/internal/dynamic_channel_impl.h
new file mode 100644
index 0000000..856e21d
--- /dev/null
+++ b/gd/l2cap/internal/dynamic_channel_impl.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "common/bidi_queue.h"
+#include "hci/address.h"
+#include "l2cap/cid.h"
+#include "l2cap/dynamic_channel.h"
+#include "l2cap/internal/channel_impl.h"
+#include "l2cap/internal/ilink.h"
+#include "l2cap/l2cap_packets.h"
+#include "l2cap/mtu.h"
+#include "l2cap/psm.h"
+#include "os/handler.h"
+#include "os/log.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+
+class DynamicChannelImpl : public l2cap::internal::ChannelImpl {
+ public:
+  DynamicChannelImpl(Psm psm, Cid cid, Cid remote_cid, l2cap::internal::ILink* link, os::Handler* l2cap_handler);
+
+  virtual ~DynamicChannelImpl() = default;
+
+  hci::Address GetDevice() const;
+
+  virtual void RegisterOnCloseCallback(os::Handler* user_handler, DynamicChannel::OnCloseCallback on_close_callback);
+
+  virtual void Close();
+  virtual void OnClosed(hci::ErrorCode status);
+  virtual std::string ToString();
+
+  common::BidiQueueEnd<packet::BasePacketBuilder, packet::PacketView<packet::kLittleEndian>>* GetQueueUpEnd() {
+    return channel_queue_.GetUpEnd();
+  }
+
+  common::BidiQueueEnd<packet::PacketView<packet::kLittleEndian>, packet::BasePacketBuilder>* GetQueueDownEnd() {
+    return channel_queue_.GetDownEnd();
+  }
+
+  virtual Cid GetCid() const {
+    return cid_;
+  }
+
+  virtual Cid GetRemoteCid() const {
+    return remote_cid_;
+  }
+
+  virtual Psm GetPsm() const {
+    return psm_;
+  }
+
+  // TODO(cmanton) Do something a little bit better than this
+  bool local_initiated_{false};
+
+ private:
+  const Psm psm_;
+  const Cid cid_;
+  const Cid remote_cid_;
+  l2cap::internal::ILink* link_;
+  os::Handler* l2cap_handler_;
+  const hci::AddressWithType device_;
+
+  // User supported states
+  os::Handler* user_handler_ = nullptr;
+  DynamicChannel::OnCloseCallback on_close_callback_{};
+
+  // Internal states
+  bool closed_ = false;
+  hci::ErrorCode close_reason_ = hci::ErrorCode::SUCCESS;
+  static constexpr size_t kChannelQueueSize = 10;
+  common::BidiQueue<packet::PacketView<packet::kLittleEndian>, packet::BasePacketBuilder> channel_queue_{
+      kChannelQueueSize};
+
+  DISALLOW_COPY_AND_ASSIGN(DynamicChannelImpl);
+};
+
+}  // namespace internal
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/internal/dynamic_channel_impl_test.cc b/gd/l2cap/internal/dynamic_channel_impl_test.cc
new file mode 100644
index 0000000..677b504
--- /dev/null
+++ b/gd/l2cap/internal/dynamic_channel_impl_test.cc
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "l2cap/internal/dynamic_channel_impl.h"
+
+#include "common/testing/bind_test_util.h"
+#include "l2cap/cid.h"
+#include "l2cap/classic/internal/link_mock.h"
+#include "l2cap/internal/parameter_provider_mock.h"
+#include "os/handler.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+
+using classic::internal::testing::MockLink;
+using l2cap::internal::testing::MockParameterProvider;
+using ::testing::Return;
+
+class L2capClassicDynamicChannelImplTest : public ::testing::Test {
+ public:
+  static void SyncHandler(os::Handler* handler) {
+    std::promise<void> promise;
+    auto future = promise.get_future();
+    handler->Post(common::BindOnce(&std::promise<void>::set_value, common::Unretained(&promise)));
+    future.wait_for(std::chrono::milliseconds(3));
+  }
+
+ protected:
+  void SetUp() override {
+    thread_ = new os::Thread("test_thread", os::Thread::Priority::NORMAL);
+    l2cap_handler_ = new os::Handler(thread_);
+  }
+
+  void TearDown() override {
+    l2cap_handler_->Clear();
+    delete l2cap_handler_;
+    delete thread_;
+  }
+
+  os::Thread* thread_ = nullptr;
+  os::Handler* l2cap_handler_ = nullptr;
+};
+
+TEST_F(L2capClassicDynamicChannelImplTest, get_device) {
+  MockParameterProvider mock_parameter_provider;
+  MockLink mock_classic_link(l2cap_handler_, &mock_parameter_provider);
+  const hci::AddressWithType device{{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}}, hci::AddressType::PUBLIC_IDENTITY_ADDRESS};
+  EXPECT_CALL(mock_classic_link, GetDevice()).WillRepeatedly(Return(device));
+  DynamicChannelImpl dynamic_channel_impl(0x01, kFirstDynamicChannel, kFirstDynamicChannel, &mock_classic_link,
+                                          l2cap_handler_);
+  EXPECT_EQ(device.GetAddress(), dynamic_channel_impl.GetDevice());
+}
+
+TEST_F(L2capClassicDynamicChannelImplTest, close_triggers_callback) {
+  MockParameterProvider mock_parameter_provider;
+  MockLink mock_classic_link(l2cap_handler_, &mock_parameter_provider);
+  const hci::AddressWithType device{{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}}, hci::AddressType::PUBLIC_IDENTITY_ADDRESS};
+  EXPECT_CALL(mock_classic_link, GetDevice()).WillRepeatedly(Return(device));
+  DynamicChannelImpl dynamic_channel_impl(0x01, kFirstDynamicChannel, kFirstDynamicChannel, &mock_classic_link,
+                                          l2cap_handler_);
+
+  // Register on close callback
+  auto user_handler = std::make_unique<os::Handler>(thread_);
+  hci::ErrorCode my_status = hci::ErrorCode::SUCCESS;
+  dynamic_channel_impl.RegisterOnCloseCallback(
+      user_handler.get(), common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { my_status = status; }));
+
+  // Channel closure should trigger such callback
+  dynamic_channel_impl.OnClosed(hci::ErrorCode::REMOTE_USER_TERMINATED_CONNECTION);
+  SyncHandler(user_handler.get());
+  EXPECT_EQ(hci::ErrorCode::REMOTE_USER_TERMINATED_CONNECTION, my_status);
+
+  user_handler->Clear();
+}
+
+TEST_F(L2capClassicDynamicChannelImplTest, register_callback_after_close_should_call_immediately) {
+  MockParameterProvider mock_parameter_provider;
+  MockLink mock_classic_link(l2cap_handler_, &mock_parameter_provider);
+  const hci::AddressWithType device{{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}}, hci::AddressType::PUBLIC_IDENTITY_ADDRESS};
+  EXPECT_CALL(mock_classic_link, GetDevice()).WillRepeatedly(Return(device));
+  DynamicChannelImpl dynamic_channel_impl(0x01, kFirstDynamicChannel, kFirstDynamicChannel, &mock_classic_link,
+                                          l2cap_handler_);
+
+  // Channel closure should do nothing
+  dynamic_channel_impl.OnClosed(hci::ErrorCode::REMOTE_USER_TERMINATED_CONNECTION);
+
+  // Register on close callback should trigger callback immediately
+  auto user_handler = std::make_unique<os::Handler>(thread_);
+  hci::ErrorCode my_status = hci::ErrorCode::SUCCESS;
+  dynamic_channel_impl.RegisterOnCloseCallback(
+      user_handler.get(), common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { my_status = status; }));
+  SyncHandler(user_handler.get());
+  EXPECT_EQ(hci::ErrorCode::REMOTE_USER_TERMINATED_CONNECTION, my_status);
+
+  user_handler->Clear();
+}
+
+TEST_F(L2capClassicDynamicChannelImplTest, close_twice_should_fail) {
+  MockParameterProvider mock_parameter_provider;
+  MockLink mock_classic_link(l2cap_handler_, &mock_parameter_provider);
+  const hci::AddressWithType device{{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}}, hci::AddressType::PUBLIC_IDENTITY_ADDRESS};
+  EXPECT_CALL(mock_classic_link, GetDevice()).WillRepeatedly(Return(device));
+  DynamicChannelImpl dynamic_channel_impl(0x01, kFirstDynamicChannel, kFirstDynamicChannel, &mock_classic_link,
+                                          l2cap_handler_);
+
+  // Register on close callback
+  auto user_handler = std::make_unique<os::Handler>(thread_);
+  hci::ErrorCode my_status = hci::ErrorCode::SUCCESS;
+  dynamic_channel_impl.RegisterOnCloseCallback(
+      user_handler.get(), common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { my_status = status; }));
+
+  // Channel closure should trigger such callback
+  dynamic_channel_impl.OnClosed(hci::ErrorCode::REMOTE_USER_TERMINATED_CONNECTION);
+  SyncHandler(user_handler.get());
+  EXPECT_EQ(hci::ErrorCode::REMOTE_USER_TERMINATED_CONNECTION, my_status);
+
+  // 2nd OnClose() callback should fail
+  EXPECT_DEATH(dynamic_channel_impl.OnClosed(hci::ErrorCode::PAGE_TIMEOUT), ".*OnClosed.*");
+
+  user_handler->Clear();
+}
+
+TEST_F(L2capClassicDynamicChannelImplTest, multiple_registeration_should_fail) {
+  MockParameterProvider mock_parameter_provider;
+  MockLink mock_classic_link(l2cap_handler_, &mock_parameter_provider);
+  const hci::AddressWithType device{{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}}, hci::AddressType::PUBLIC_IDENTITY_ADDRESS};
+  EXPECT_CALL(mock_classic_link, GetDevice()).WillRepeatedly(Return(device));
+  DynamicChannelImpl dynamic_channel_impl(0x01, kFirstDynamicChannel, kFirstDynamicChannel, &mock_classic_link,
+                                          l2cap_handler_);
+
+  // Register on close callback
+  auto user_handler = std::make_unique<os::Handler>(thread_);
+  hci::ErrorCode my_status = hci::ErrorCode::SUCCESS;
+  dynamic_channel_impl.RegisterOnCloseCallback(
+      user_handler.get(), common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { my_status = status; }));
+
+  EXPECT_DEATH(dynamic_channel_impl.RegisterOnCloseCallback(user_handler.get(),
+                                                            common::BindOnce([](hci::ErrorCode status) { FAIL(); })),
+               ".*RegisterOnCloseCallback.*");
+
+  user_handler->Clear();
+}
+
+}  // namespace internal
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller.cc b/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller.cc
new file mode 100644
index 0000000..e57f708
--- /dev/null
+++ b/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller.cc
@@ -0,0 +1,1034 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "l2cap/internal/enhanced_retransmission_mode_channel_data_controller.h"
+
+#include <map>
+#include <queue>
+#include <vector>
+
+#include "common/bind.h"
+#include "l2cap/internal/ilink.h"
+#include "os/alarm.h"
+#include "packet/fragmenting_inserter.h"
+#include "packet/raw_builder.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+ErtmController::ErtmController(ILink* link, Cid cid, Cid remote_cid, UpperQueueDownEnd* channel_queue_end,
+                               os::Handler* handler, Scheduler* scheduler)
+    : link_(link), cid_(cid), remote_cid_(remote_cid), enqueue_buffer_(channel_queue_end), handler_(handler),
+      scheduler_(scheduler), pimpl_(std::make_unique<impl>(this, handler)) {}
+
+ErtmController::~ErtmController() = default;
+
+struct ErtmController::impl {
+  impl(ErtmController* controller, os::Handler* handler)
+      : controller_(controller), handler_(handler), retrans_timer_(handler), monitor_timer_(handler) {}
+
+  ErtmController* controller_;
+  os::Handler* handler_;
+
+  // We don't support extended window
+  static constexpr uint8_t kMaxTxWin = 64;
+
+  // We don't support sending SREJ
+  static constexpr bool kSendSrej = false;
+
+  // States (@see 8.6.5.2): Transmitter state and receiver state
+
+  enum class TxState {
+    XMIT,
+    WAIT_F,
+  };
+  TxState tx_state_ = TxState::XMIT;
+
+  enum class RxState {
+    RECV,
+    REJ_SENT,
+    SREJ_SENT,
+  };
+  RxState rx_state_ = RxState::RECV;
+
+  // Variables and Timers (@see 8.6.5.3)
+
+  uint8_t tx_seq_ = 0;
+  uint8_t next_tx_seq_ = 0;
+  uint8_t expected_ack_seq_ = 0;
+  uint8_t req_seq_ = 0;
+  uint8_t expected_tx_seq_ = 0;
+  uint8_t buffer_seq_ = 0;
+
+  bool remote_busy_ = false;
+  bool local_busy_ = false;
+  int unacked_frames_ = 0;
+  // TODO: Instead of having a map, we may consider about a better data structure
+  // Map from TxSeq to (SAR, SDU size for START packet, information payload)
+  std::map<uint8_t, std::tuple<SegmentationAndReassembly, uint16_t, std::shared_ptr<packet::RawBuilder>>> unacked_list_;
+  // Stores (SAR, SDU size for START packet, information payload)
+  std::queue<std::tuple<SegmentationAndReassembly, uint16_t, std::unique_ptr<packet::RawBuilder>>> pending_frames_;
+  int retry_count_ = 0;
+  std::map<uint8_t /* tx_seq, */, int /* count */> retry_i_frames_;
+  bool rnr_sent_ = false;
+  bool rej_actioned_ = false;
+  bool srej_actioned_ = false;
+  uint16_t srej_save_req_seq_ = 0;
+  bool send_rej_ = false;
+  int buffer_seq_srej_ = 0;
+  int frames_sent_ = 0;
+  os::Alarm retrans_timer_;
+  os::Alarm monitor_timer_;
+
+  // Events (@see 8.6.5.4)
+
+  void data_request(SegmentationAndReassembly sar, std::unique_ptr<packet::RawBuilder> pdu, uint16_t sdu_size = 0) {
+    // Note: sdu_size only applies to START packet
+    if (tx_state_ == TxState::XMIT && !remote_busy() && rem_window_not_full()) {
+      send_data(sar, sdu_size, std::move(pdu));
+    } else if (tx_state_ == TxState::XMIT && (remote_busy() || rem_window_full())) {
+      pend_data(sar, sdu_size, std::move(pdu));
+    } else if (tx_state_ == TxState::WAIT_F) {
+      pend_data(sar, sdu_size, std::move(pdu));
+    }
+  }
+
+  void local_busy_detected() {
+    local_busy_ = true;
+  }
+
+  void local_busy_clear() {
+    if (tx_state_ == TxState::XMIT && rnr_sent()) {
+      local_busy_ = false;
+      rnr_sent_ = false;
+      send_rr(Poll::POLL);
+      retry_count_ = 1;
+      stop_retrans_timer();
+      start_monitor_timer();
+    } else if (tx_state_ == TxState::XMIT) {
+      local_busy_ = false;
+      rnr_sent_ = false;
+    }
+  }
+
+  void recv_req_seq_and_f_bit(uint8_t req_seq, Final f) {
+    if (tx_state_ == TxState::XMIT) {
+      process_req_seq(req_seq);
+    } else if (f == Final::POLL_RESPONSE) {
+      process_req_seq(req_seq);
+      stop_monitor_timer();
+      if (unacked_frames_ > 0) {
+        start_retrans_timer();
+      }
+      tx_state_ = TxState::XMIT;
+    } else {
+      process_req_seq(req_seq);
+    }
+  }
+
+  void recv_f_bit(Final f) {
+    if (tx_state_ == TxState::WAIT_F && f == Final::POLL_RESPONSE) {
+      stop_monitor_timer();
+      if (unacked_frames_ > 0) {
+        start_retrans_timer();
+      }
+      tx_state_ = TxState::XMIT;
+    }
+  }
+
+  void retrans_timer_expires() {
+    if (tx_state_ == TxState::XMIT) {
+      send_rr_or_rnr(Poll::POLL);
+      // send rr or rnr(p=1)
+      retry_count_ = 1;
+      start_monitor_timer();
+      tx_state_ = TxState::WAIT_F;
+    }
+  }
+
+  void monitor_timer_expires() {
+    if (tx_state_ == TxState::WAIT_F && retry_count_less_than_max_transmit()) {
+      retry_count_++;
+      send_rr_or_rnr(Poll::POLL);
+      start_monitor_timer();
+    } else if (tx_state_ == TxState::WAIT_F) {
+      CloseChannel();
+    }
+  }
+
+  void recv_i_frame(Final f, uint8_t tx_seq, uint8_t req_seq, SegmentationAndReassembly sar, uint16_t sdu_size,
+                    const packet::PacketView<true>& payload) {
+    if (rx_state_ == RxState::RECV) {
+      if (f == Final::NOT_SET && with_expected_tx_seq(tx_seq) && with_valid_req_seq(req_seq) && with_valid_f_bit(f) &&
+          !local_busy()) {
+        increment_expected_tx_seq();
+        pass_to_tx(req_seq, f);
+        data_indication(sar, sdu_size, payload);
+        send_ack(Final::NOT_SET);
+      } else if (f == Final::POLL_RESPONSE && with_expected_tx_seq(tx_seq) && with_valid_req_seq(req_seq) &&
+                 with_valid_f_bit(f) && !local_busy()) {
+        increment_expected_tx_seq();
+        pass_to_tx(req_seq, f);
+        data_indication(sar, sdu_size, payload);
+        if (!rej_actioned_) {
+          retransmit_i_frames(req_seq);
+          send_pending_i_frames();
+        } else {
+          rej_actioned_ = false;
+        }
+        send_ack(Final::NOT_SET);
+      } else if (with_duplicate_tx_seq(tx_seq) && with_valid_req_seq(req_seq) && with_valid_f_bit(f) && !local_busy()) {
+        pass_to_tx(req_seq, f);
+      } else if (with_unexpected_tx_seq(tx_seq) && with_valid_req_seq(req_seq) && with_valid_f_bit(f) &&
+                 !local_busy()) {
+        if constexpr (kSendSrej) {
+          // We don't support sending SREJ
+        } else {
+          pass_to_tx(req_seq, f);
+          send_rej();
+          rx_state_ = RxState::REJ_SENT;
+        }
+      } else if (with_expected_tx_seq(tx_seq) && with_valid_req_seq(req_seq) && with_valid_f_bit(f) && local_busy()) {
+        pass_to_tx(req_seq, f);
+        store_or_ignore();
+      } else if (with_valid_req_seq(req_seq) && not_with_expected_tx_seq(tx_seq) && with_valid_f_bit(f) &&
+                 local_busy()) {
+        pass_to_tx(req_seq, f);
+      } else if ((with_invalid_tx_seq(tx_seq) && controller_->local_tx_window_ > kMaxTxWin / 2) ||
+                 with_invalid_req_seq(req_seq)) {
+        CloseChannel();
+      } else if (with_invalid_tx_seq(tx_seq) && controller_->local_tx_window_ <= kMaxTxWin / 2) {
+        // We decided to ignore
+      }
+    } else if (rx_state_ == RxState::REJ_SENT) {
+      if (f == Final::NOT_SET && with_expected_tx_seq(tx_seq) && with_valid_req_seq(req_seq) && with_valid_f_bit(f)) {
+        increment_expected_tx_seq();
+        pass_to_tx(req_seq, f);
+        data_indication(sar, sdu_size, payload);
+        send_ack(Final::NOT_SET);
+        rx_state_ = RxState::RECV;
+      } else if (f == Final::POLL_RESPONSE && with_expected_tx_seq(tx_seq) && with_valid_req_seq(req_seq) &&
+                 with_valid_f_bit(f)) {
+        increment_expected_tx_seq();
+        pass_to_tx(req_seq, f);
+        data_indication(sar, sdu_size, payload);
+        if (!rej_actioned_) {
+          retransmit_i_frames(req_seq);
+          send_pending_i_frames();
+        } else {
+          rej_actioned_ = false;
+        }
+        send_ack(Final::NOT_SET);
+        rx_state_ = RxState::RECV;
+      } else if (with_unexpected_tx_seq(tx_seq) && with_valid_req_seq(req_seq) && with_valid_f_bit(f)) {
+        pass_to_tx(req_seq, f);
+      }
+    } else if (rx_state_ == RxState::SREJ_SENT) {
+      // SREJ NOT SUPPORTED
+    }
+  }
+
+  void recv_rr(uint8_t req_seq, Poll p = Poll::NOT_SET, Final f = Final::NOT_SET) {
+    if (rx_state_ == RxState::RECV) {
+      if (p == Poll::NOT_SET && f == Final::NOT_SET && with_valid_req_seq_rr(req_seq) && with_valid_f_bit(f)) {
+        pass_to_tx(req_seq, f);
+        if (remote_busy() && unacked_frames_ > 0) {
+          start_retrans_timer();
+        }
+        remote_busy_ = false;
+        send_pending_i_frames();
+      } else if (f == Final::POLL_RESPONSE && with_valid_req_seq(req_seq) && with_valid_f_bit(f)) {
+        remote_busy_ = false;
+        pass_to_tx(req_seq, f);
+        if (!rej_actioned_) {
+          retransmit_i_frames(req_seq, p);
+        } else {
+          rej_actioned_ = false;
+        }
+        send_pending_i_frames();
+      } else if (p == Poll::POLL && with_valid_req_seq(req_seq) && with_valid_f_bit(f)) {
+        pass_to_tx(req_seq, f);
+        send_i_or_rr_or_rnr(Final::POLL_RESPONSE);
+      } else if (with_invalid_req_seq_rr(req_seq)) {
+        CloseChannel();
+      }
+    } else if (rx_state_ == RxState::REJ_SENT) {
+      if (f == Final::POLL_RESPONSE && with_valid_req_seq(req_seq) && with_valid_f_bit(f)) {
+        remote_busy_ = false;
+        pass_to_tx(req_seq, f);
+        if (!rej_actioned_) {
+          retransmit_i_frames(req_seq, p);
+        } else {
+          rej_actioned_ = false;
+        }
+        send_pending_i_frames();
+      } else if (p == Poll::NOT_SET && f == Final::NOT_SET && with_valid_req_seq_rr(req_seq) && with_valid_f_bit(f)) {
+        pass_to_tx(req_seq, f);
+        if (remote_busy() and unacked_frames_ > 0) {
+          start_retrans_timer();
+        }
+        remote_busy_ = false;
+        send_ack(Final::NOT_SET);
+      } else if (p == Poll::POLL && with_valid_req_seq(req_seq) && with_valid_f_bit(f)) {
+        pass_to_tx(req_seq, f);
+        if (remote_busy() and unacked_frames_ > 0) {
+          start_retrans_timer();
+        }
+        remote_busy_ = false;
+        send_rr(Final::POLL_RESPONSE);
+      } else if (with_invalid_req_seq_rr(req_seq)) {
+        CloseChannel();
+      }
+    } else if (rx_state_ == RxState::SREJ_SENT) {
+      // SREJ NOT SUPPORTED
+    }
+  }
+
+  void recv_rej(uint8_t req_seq, Poll p = Poll::NOT_SET, Final f = Final::NOT_SET) {
+    if (rx_state_ == RxState::RECV) {
+      if (f == Final::NOT_SET && with_valid_req_seq_retrans(req_seq) &&
+          retry_i_frames_less_than_max_transmit(req_seq) && with_valid_f_bit(f)) {
+        remote_busy_ = false;
+        pass_to_tx(req_seq, f);
+        retransmit_i_frames(req_seq, p);
+        send_pending_i_frames();
+        if (p_bit_outstanding()) {
+          rej_actioned_ = true;
+        }
+      } else if (f == Final::POLL_RESPONSE && with_valid_req_seq_retrans(req_seq) &&
+                 retry_i_frames_less_than_max_transmit(req_seq) && with_valid_f_bit(f)) {
+        remote_busy_ = false;
+        pass_to_tx(req_seq, f);
+        if (!rej_actioned_) {
+          retransmit_i_frames(req_seq, p);
+        } else {
+          rej_actioned_ = false;
+        }
+        send_pending_i_frames();
+      } else if (with_valid_req_seq_retrans(req_seq) && !retry_i_frames_less_than_max_transmit(req_seq)) {
+        CloseChannel();
+      } else if (with_invalid_req_seq_retrans(req_seq)) {
+        CloseChannel();
+      }
+    } else if (rx_state_ == RxState::REJ_SENT) {
+      if (f == Final::NOT_SET && with_valid_req_seq_retrans(req_seq) &&
+          retry_i_frames_less_than_max_transmit(req_seq) && with_valid_f_bit(f)) {
+        remote_busy_ = false;
+        pass_to_tx(req_seq, f);
+        retransmit_i_frames(req_seq, p);
+        send_pending_i_frames();
+        if (p_bit_outstanding()) {
+          rej_actioned_ = true;
+        }
+      } else if (f == Final::POLL_RESPONSE && with_valid_req_seq_retrans(req_seq) &&
+                 retry_i_frames_less_than_max_transmit(req_seq) && with_valid_f_bit(f)) {
+        remote_busy_ = false;
+        pass_to_tx(req_seq, f);
+        if (!rej_actioned_) {
+          retransmit_i_frames(req_seq, p);
+        } else {
+          rej_actioned_ = false;
+        }
+        send_pending_i_frames();
+      } else if (with_valid_req_seq_retrans(req_seq) && !retry_i_frames_less_than_max_transmit(req_seq)) {
+        CloseChannel();
+      } else if (with_invalid_req_seq_retrans(req_seq)) {
+        CloseChannel();
+      }
+    } else if (rx_state_ == RxState::SREJ_SENT) {
+      // SREJ NOT SUPPORTED
+    }
+  }
+
+  void recv_rnr(uint8_t req_seq, Poll p = Poll::NOT_SET, Final f = Final::NOT_SET) {
+    if (rx_state_ == RxState::RECV) {
+      if (p == Poll::NOT_SET && with_valid_req_seq(req_seq) && with_valid_f_bit(f)) {
+        remote_busy_ = true;
+        pass_to_tx(req_seq, f);
+        stop_retrans_timer();
+      } else if (p == Poll::POLL && with_valid_req_seq(req_seq) && with_valid_f_bit(f)) {
+        remote_busy_ = true;
+        pass_to_tx(req_seq, f);
+        stop_retrans_timer();
+        send_rr_or_rnr(Poll::NOT_SET, Final::POLL_RESPONSE);
+      } else if (with_invalid_req_seq_retrans(req_seq)) {
+        CloseChannel();
+      }
+    } else if (rx_state_ == RxState::REJ_SENT) {
+      if (p == Poll::NOT_SET && with_valid_req_seq(req_seq) && with_valid_f_bit(f)) {
+        remote_busy_ = true;
+        pass_to_tx(req_seq, f);
+        send_rr(Final::POLL_RESPONSE);
+      } else if (p == Poll::POLL && with_valid_req_seq(req_seq) && with_valid_f_bit(f)) {
+        remote_busy_ = true;
+        pass_to_tx(req_seq, f);
+        send_rr(Final::NOT_SET);
+      } else if (with_invalid_req_seq_retrans(req_seq)) {
+        CloseChannel();
+      }
+    } else if (rx_state_ == RxState::SREJ_SENT) {
+      // SREJ NOT SUPPORTED
+    }
+  }
+
+  void recv_srej(uint8_t req_seq, Poll p = Poll::NOT_SET, Final f = Final::NOT_SET) {
+    if (rx_state_ == RxState::RECV) {
+      if (p == Poll::NOT_SET && f == Final::NOT_SET && with_valid_req_seq_retrans(req_seq) &&
+          retry_i_frames_less_than_max_transmit(req_seq) && with_valid_f_bit(f)) {
+        remote_busy_ = false;
+        pass_to_tx_f_bit(f);
+        retransmit_requested_i_frame(req_seq, p);
+        if (p_bit_outstanding()) {
+          srej_actioned_ = true;
+          srej_save_req_seq_ = req_seq;
+        }
+      } else if (f == Final::POLL_RESPONSE && with_valid_req_seq_retrans(req_seq) &&
+                 retry_i_frames_less_than_max_transmit(req_seq) && with_valid_f_bit(f)) {
+        remote_busy_ = false;
+        pass_to_tx_f_bit(f);
+        if (srej_actioned_ && srej_save_req_seq_ == req_seq) {
+          srej_actioned_ = false;
+        } else {
+          retransmit_requested_i_frame(req_seq, p);
+        }
+      } else if (p == Poll::POLL && with_valid_req_seq_retrans(req_seq) &&
+                 retry_i_frames_less_than_max_transmit(req_seq) && with_valid_f_bit(f)) {
+        remote_busy_ = false;
+        pass_to_tx(req_seq, f);
+        retransmit_requested_i_frame(req_seq, p);
+        if (p_bit_outstanding()) {
+          srej_actioned_ = true;
+          srej_save_req_seq_ = req_seq;
+        }
+      } else if (with_valid_req_seq_retrans(req_seq) && !retry_i_frames_less_than_max_transmit(req_seq)) {
+        CloseChannel();
+      } else if (with_invalid_req_seq_retrans(req_seq)) {
+        CloseChannel();
+      }
+    } else if (rx_state_ == RxState::REJ_SENT) {
+      if (p == Poll::NOT_SET && f == Final::NOT_SET && with_valid_req_seq_retrans(req_seq) &&
+          retry_i_frames_less_than_max_transmit(req_seq) && with_valid_f_bit(f)) {
+        remote_busy_ = false;
+        pass_to_tx_f_bit(f);
+        retransmit_requested_i_frame(req_seq, p);
+        if (p_bit_outstanding()) {
+          srej_actioned_ = true;
+          srej_save_req_seq_ = req_seq;
+        }
+      } else if (f == Final::POLL_RESPONSE && with_valid_req_seq_retrans(req_seq) &&
+                 retry_i_frames_less_than_max_transmit(req_seq) && with_valid_f_bit(f)) {
+        remote_busy_ = false;
+        pass_to_tx_f_bit(f);
+        if (srej_actioned_ && srej_save_req_seq_ == req_seq) {
+          srej_actioned_ = false;
+        } else {
+          retransmit_requested_i_frame(req_seq, p);
+        }
+      } else if (p == Poll::POLL && with_valid_req_seq_retrans(req_seq) &&
+                 retry_i_frames_less_than_max_transmit(req_seq) && with_valid_f_bit(f)) {
+        remote_busy_ = false;
+        pass_to_tx(req_seq, f);
+        retransmit_requested_i_frame(req_seq, p);
+        send_pending_i_frames();
+        if (p_bit_outstanding()) {
+          srej_actioned_ = true;
+          srej_save_req_seq_ = req_seq;
+        }
+      } else if (with_valid_req_seq_retrans(req_seq) && !retry_i_frames_less_than_max_transmit(req_seq)) {
+        CloseChannel();
+      } else if (with_invalid_req_seq_retrans(req_seq)) {
+        CloseChannel();
+      }
+    } else if (rx_state_ == RxState::SREJ_SENT) {
+      // SREJ NOT SUPPORTED
+    }
+  }
+
+  // Conditions (@see 8.6.5.5)
+  bool remote_busy() {
+    return remote_busy_;
+  }
+
+  bool local_busy() {
+    return local_busy_;
+  }
+
+  bool rem_window_not_full() {
+    return unacked_frames_ < controller_->remote_tx_window_;
+  }
+
+  bool rem_window_full() {
+    return unacked_frames_ == controller_->remote_tx_window_;
+  }
+
+  bool rnr_sent() {
+    return rnr_sent_;
+  }
+
+  bool retry_i_frames_less_than_max_transmit(uint8_t req_seq) {
+    return retry_i_frames_[req_seq] < controller_->local_max_transmit_;
+  }
+
+  bool retry_count_less_than_max_transmit() {
+    return retry_count_ < controller_->local_max_transmit_;
+  }
+
+  bool with_expected_tx_seq(uint8_t tx_seq) {
+    return tx_seq == expected_tx_seq_;
+  }
+
+  bool with_valid_req_seq(uint8_t req_seq) {
+    return expected_ack_seq_ <= req_seq && req_seq <= next_tx_seq_;
+  }
+
+  bool with_valid_req_seq_retrans(uint8_t req_seq) {
+    return expected_ack_seq_ <= req_seq && req_seq <= next_tx_seq_;
+  }
+
+  bool with_valid_f_bit(Final f) {
+    return f == Final::NOT_SET ^ tx_state_ == TxState::WAIT_F;
+  }
+
+  bool with_unexpected_tx_seq(uint8_t tx_seq) {
+    return tx_seq > expected_tx_seq_ && tx_seq <= expected_tx_seq_ + controller_->local_tx_window_;
+  }
+
+  bool with_duplicate_tx_seq(uint8_t tx_seq) {
+    return tx_seq < expected_tx_seq_ && tx_seq >= expected_tx_seq_ - controller_->local_tx_window_;
+  }
+
+  bool with_invalid_tx_seq(uint8_t tx_seq) {
+    return tx_seq < expected_tx_seq_ - controller_->local_tx_window_ ||
+           tx_seq > expected_tx_seq_ + controller_->local_tx_window_;
+  }
+
+  bool with_invalid_req_seq(uint8_t req_seq) {
+    return req_seq < expected_ack_seq_ || req_seq > next_tx_seq_;
+  }
+
+  bool with_invalid_req_seq_retrans(uint8_t req_seq) {
+    return req_seq < expected_ack_seq_ || req_seq >= next_tx_seq_;
+  }
+
+  bool not_with_expected_tx_seq(uint8_t tx_seq) {
+    return !with_invalid_tx_seq(tx_seq) && !with_expected_tx_seq(tx_seq);
+  }
+
+  bool with_valid_req_seq_rr(uint8_t req_seq) {
+    return expected_ack_seq_ < req_seq && req_seq <= next_tx_seq_;
+  }
+
+  bool with_invalid_req_seq_rr(uint8_t req_seq) {
+    return req_seq <= expected_ack_seq_ || req_seq > next_tx_seq_;
+  }
+
+  bool with_expected_tx_seq_srej() {
+    // We don't support sending SREJ
+    return false;
+  }
+
+  bool send_req_is_true() {
+    // We don't support sending SREJ
+    return false;
+  }
+
+  bool srej_list_is_one() {
+    // We don't support sending SREJ
+    return false;
+  }
+
+  bool with_unexpected_tx_seq_srej() {
+    // We don't support sending SREJ
+    return false;
+  }
+
+  bool with_duplicate_tx_seq_srej() {
+    // We don't support sending SREJ
+    return false;
+  }
+
+  // Actions (@see 8.6.5.6)
+
+  void _send_i_frame(SegmentationAndReassembly sar, std::unique_ptr<CopyablePacketBuilder> segment, uint8_t req_seq,
+                     uint8_t tx_seq, uint16_t sdu_size = 0, Final f = Final::NOT_SET) {
+    std::unique_ptr<packet::BasePacketBuilder> builder;
+    if (sar == SegmentationAndReassembly::START) {
+      if (controller_->fcs_enabled_) {
+        builder = EnhancedInformationStartFrameWithFcsBuilder::Create(controller_->remote_cid_, tx_seq, f, req_seq,
+                                                                      sdu_size, std::move(segment));
+      } else {
+        builder = EnhancedInformationStartFrameBuilder::Create(controller_->remote_cid_, tx_seq, f, req_seq, sdu_size,
+                                                               std::move(segment));
+      }
+    } else {
+      if (controller_->fcs_enabled_) {
+        builder = EnhancedInformationFrameWithFcsBuilder::Create(controller_->remote_cid_, tx_seq, f, req_seq, sar,
+                                                                 std::move(segment));
+      } else {
+        builder = EnhancedInformationFrameBuilder::Create(controller_->remote_cid_, tx_seq, f, req_seq, sar,
+                                                          std::move(segment));
+      }
+    }
+    controller_->send_pdu(std::move(builder));
+  }
+
+  void send_data(SegmentationAndReassembly sar, uint16_t sdu_size, std::unique_ptr<packet::RawBuilder> segment,
+                 Final f = Final::NOT_SET) {
+    std::shared_ptr<packet::RawBuilder> shared_segment(segment.release());
+    unacked_list_.emplace(std::piecewise_construct, std::forward_as_tuple(next_tx_seq_),
+                          std::forward_as_tuple(sar, sdu_size, shared_segment));
+
+    std::unique_ptr<CopyablePacketBuilder> copyable_packet_builder =
+        std::make_unique<CopyablePacketBuilder>(std::get<2>(unacked_list_.find(next_tx_seq_)->second));
+    _send_i_frame(sar, std::move(copyable_packet_builder), buffer_seq_, next_tx_seq_, sdu_size, f);
+    unacked_frames_++;
+    frames_sent_++;
+    retry_i_frames_[next_tx_seq_] = 1;
+    next_tx_seq_ = (next_tx_seq_ + 1) % kMaxTxWin;
+    start_retrans_timer();
+  }
+
+  void pend_data(SegmentationAndReassembly sar, uint16_t sdu_size, std::unique_ptr<packet::RawBuilder> data) {
+    pending_frames_.emplace(std::make_tuple(sar, sdu_size, std::move(data)));
+  }
+
+  void process_req_seq(uint8_t req_seq) {
+    for (int i = expected_ack_seq_; i < req_seq; i++) {
+      unacked_list_.erase(i);
+      retry_i_frames_[i] = 0;
+    }
+    unacked_frames_ -= ((req_seq - expected_ack_seq_) + kMaxTxWin) % kMaxTxWin;
+    if (unacked_frames_ == 0) {
+      stop_retrans_timer();
+    }
+  }
+
+  void _send_s_frame(SupervisoryFunction s, uint8_t req_seq, Poll p, Final f) {
+    std::unique_ptr<packet::BasePacketBuilder> builder;
+    if (controller_->fcs_enabled_) {
+      builder = EnhancedSupervisoryFrameWithFcsBuilder::Create(controller_->remote_cid_, s, p, f, req_seq);
+    } else {
+      builder = EnhancedSupervisoryFrameBuilder::Create(controller_->remote_cid_, s, p, f, req_seq);
+    }
+    controller_->send_pdu(std::move(builder));
+  }
+
+  void send_rr(Poll p) {
+    _send_s_frame(SupervisoryFunction::RECEIVER_READY, expected_tx_seq_, p, Final::NOT_SET);
+  }
+
+  void send_rr(Final f) {
+    _send_s_frame(SupervisoryFunction::RECEIVER_READY, expected_tx_seq_, Poll::NOT_SET, f);
+  }
+
+  void send_rnr(Poll p) {
+    _send_s_frame(SupervisoryFunction::RECEIVER_NOT_READY, expected_tx_seq_, p, Final::NOT_SET);
+  }
+
+  void send_rnr(Final f) {
+    _send_s_frame(SupervisoryFunction::RECEIVER_NOT_READY, expected_tx_seq_, Poll::NOT_SET, f);
+  }
+
+  void send_rej(Poll p = Poll::NOT_SET, Final f = Final::NOT_SET) {
+    _send_s_frame(SupervisoryFunction::REJECT, expected_tx_seq_, p, f);
+  }
+
+  void send_rr_or_rnr(Poll p = Poll::NOT_SET, Final f = Final::NOT_SET) {
+    if (local_busy()) {
+      _send_s_frame(SupervisoryFunction::RECEIVER_NOT_READY, buffer_seq_, p, f);
+    } else {
+      _send_s_frame(SupervisoryFunction::RECEIVER_READY, buffer_seq_, p, f);
+    }
+  }
+
+  void send_i_or_rr_or_rnr(Final f = Final::POLL_RESPONSE) {
+    auto frames_sent = 0;
+    if (local_busy()) {
+      send_rnr(Final::POLL_RESPONSE);
+    }
+    if (remote_busy() && unacked_frames_ > 0) {
+      start_retrans_timer();
+    }
+    remote_busy_ = false;
+    send_pending_i_frames(f);  // TODO: Only first has f = 1, other f = 0. Also increase frames_sent
+    if (!local_busy() && frames_sent == 0) {
+      send_rr(Final::POLL_RESPONSE);
+    }
+  }
+
+  void send_srej() {
+    // Sending SREJ is not supported
+  }
+
+  void start_retrans_timer() {
+    retrans_timer_.Schedule(common::BindOnce(&impl::retrans_timer_expires, common::Unretained(this)),
+                            std::chrono::milliseconds(controller_->local_retransmit_timeout_ms_));
+  }
+
+  void start_monitor_timer() {
+    monitor_timer_.Schedule(common::BindOnce(&impl::monitor_timer_expires, common::Unretained(this)),
+                            std::chrono::milliseconds(controller_->local_monitor_timeout_ms_));
+  }
+
+  void pass_to_tx(uint8_t req_seq, Final f) {
+    recv_req_seq_and_f_bit(req_seq, f);
+  }
+
+  void pass_to_tx_f_bit(Final f) {
+    recv_f_bit(f);
+  }
+
+  void data_indication(SegmentationAndReassembly sar, uint16_t sdu_size, const packet::PacketView<true>& segment) {
+    controller_->stage_for_reassembly(sar, sdu_size, segment);
+    buffer_seq_ = (buffer_seq_ + 1) % kMaxTxWin;
+  }
+
+  void increment_expected_tx_seq() {
+    expected_tx_seq_ = (expected_tx_seq_ + 1) % kMaxTxWin;
+  }
+
+  void stop_retrans_timer() {
+    retrans_timer_.Cancel();
+  }
+
+  void stop_monitor_timer() {
+    monitor_timer_.Cancel();
+  }
+
+  void send_ack(Final f = Final::NOT_SET) {
+    if (local_busy()) {
+      send_rnr(f);
+    } else if (!remote_busy() && !pending_frames_.empty() && rem_window_not_full()) {
+      send_pending_i_frames(f);
+    } else {
+      send_rr(f);
+    }
+  }
+
+  void init_srej() {
+    // We don't support sending SREJ
+  }
+
+  void save_i_frame_srej() {
+    // We don't support sending SREJ
+  }
+
+  void store_or_ignore() {
+    // We choose to ignore. We don't support local busy so far.
+  }
+
+  bool p_bit_outstanding() {
+    return tx_state_ == TxState::WAIT_F;
+  }
+
+  void retransmit_i_frames(uint8_t req_seq, Poll p = Poll::NOT_SET) {
+    uint8_t i = req_seq;
+    Final f = (p == Poll::NOT_SET ? Final::NOT_SET : Final::POLL_RESPONSE);
+    while (unacked_list_.find(i) != unacked_list_.end()) {
+      if (retry_i_frames_[i] == controller_->local_max_transmit_) {
+        CloseChannel();
+        return;
+      }
+      std::unique_ptr<CopyablePacketBuilder> copyable_packet_builder =
+          std::make_unique<CopyablePacketBuilder>(std::get<2>(unacked_list_.find(i)->second));
+      _send_i_frame(std::get<0>(unacked_list_.find(i)->second), std::move(copyable_packet_builder), buffer_seq_, i,
+                    std::get<1>(unacked_list_.find(i)->second), f);
+      retry_i_frames_[i]++;
+      frames_sent_++;
+      f = Final::NOT_SET;
+      i++;
+    }
+    if (i != req_seq) {
+      start_retrans_timer();
+    }
+  }
+
+  void retransmit_requested_i_frame(uint8_t req_seq, Poll p) {
+    Final f = p == Poll::POLL ? Final::POLL_RESPONSE : Final::NOT_SET;
+    if (unacked_list_.find(req_seq) == unacked_list_.end()) {
+      LOG_ERROR("Received invalid SREJ");
+      return;
+    }
+    std::unique_ptr<CopyablePacketBuilder> copyable_packet_builder =
+        std::make_unique<CopyablePacketBuilder>(std::get<2>(unacked_list_.find(req_seq)->second));
+    _send_i_frame(std::get<0>(unacked_list_.find(req_seq)->second), std::move(copyable_packet_builder), buffer_seq_,
+                  req_seq, std::get<1>(unacked_list_.find(req_seq)->second), f);
+    retry_i_frames_[req_seq]++;
+    start_retrans_timer();
+  }
+
+  void send_pending_i_frames(Final f = Final::NOT_SET) {
+    if (p_bit_outstanding()) {
+      return;
+    }
+    while (rem_window_not_full() && !pending_frames_.empty()) {
+      auto& frame = pending_frames_.front();
+      send_data(std::get<0>(frame), std::get<1>(frame), std::move(std::get<2>(frame)), f);
+      pending_frames_.pop();
+      f = Final::NOT_SET;
+    }
+  }
+
+  void CloseChannel() {
+    controller_->close_channel();
+  }
+
+  void pop_srej_list() {
+    // We don't support sending SREJ
+  }
+
+  void data_indication_srej() {
+    // We don't support sending SREJ
+  }
+};
+
+// Segmentation is handled here
+void ErtmController::OnSdu(std::unique_ptr<packet::BasePacketBuilder> sdu) {
+  auto sdu_size = sdu->size();
+  std::vector<std::unique_ptr<packet::RawBuilder>> segments;
+  packet::FragmentingInserter fragmenting_inserter(size_each_packet_, std::back_insert_iterator(segments));
+  sdu->Serialize(fragmenting_inserter);
+  fragmenting_inserter.finalize();
+  if (segments.size() == 1) {
+    pimpl_->data_request(SegmentationAndReassembly::UNSEGMENTED, std::move(segments[0]));
+    return;
+  }
+  pimpl_->data_request(SegmentationAndReassembly::START, std::move(segments[0]), sdu_size);
+  for (auto i = 1; i < segments.size() - 1; i++) {
+    pimpl_->data_request(SegmentationAndReassembly::CONTINUATION, std::move(segments[i]));
+  }
+  pimpl_->data_request(SegmentationAndReassembly::END, std::move(segments.back()));
+}
+
+void ErtmController::OnPdu(packet::PacketView<true> pdu) {
+  if (fcs_enabled_) {
+    on_pdu_fcs(pdu);
+  } else {
+    on_pdu_no_fcs(pdu);
+  }
+}
+
+void ErtmController::on_pdu_no_fcs(const packet::PacketView<true>& pdu) {
+  auto basic_frame_view = BasicFrameView::Create(pdu);
+  if (!basic_frame_view.IsValid()) {
+    return;
+  }
+  auto standard_frame_view = StandardFrameView::Create(basic_frame_view);
+  if (!standard_frame_view.IsValid()) {
+    LOG_WARN("Received invalid frame");
+    return;
+  }
+  auto type = standard_frame_view.GetFrameType();
+  if (type == FrameType::I_FRAME) {
+    auto i_frame_view = EnhancedInformationFrameView::Create(standard_frame_view);
+    if (!i_frame_view.IsValid()) {
+      LOG_WARN("Received invalid frame");
+      return;
+    }
+    Final f = i_frame_view.GetF();
+    uint8_t tx_seq = i_frame_view.GetTxSeq();
+    uint8_t req_seq = i_frame_view.GetReqSeq();
+    auto sar = i_frame_view.GetSar();
+    if (sar == SegmentationAndReassembly::START) {
+      auto i_frame_start_view = EnhancedInformationStartFrameView::Create(i_frame_view);
+      if (!i_frame_start_view.IsValid()) {
+        LOG_WARN("Received invalid I-Frame START");
+        return;
+      }
+      pimpl_->recv_i_frame(f, tx_seq, req_seq, sar, i_frame_start_view.GetL2capSduLength(),
+                           i_frame_start_view.GetPayload());
+    } else {
+      pimpl_->recv_i_frame(f, tx_seq, req_seq, sar, 0, i_frame_view.GetPayload());
+    }
+  } else if (type == FrameType::S_FRAME) {
+    auto s_frame_view = EnhancedSupervisoryFrameView::Create(standard_frame_view);
+    if (!s_frame_view.IsValid()) {
+      LOG_WARN("Received invalid frame");
+      return;
+    }
+    auto req_seq = s_frame_view.GetReqSeq();
+    auto f = s_frame_view.GetF();
+    auto p = s_frame_view.GetP();
+    switch (s_frame_view.GetS()) {
+      case SupervisoryFunction::RECEIVER_READY:
+        pimpl_->recv_rr(req_seq, p, f);
+        break;
+      case SupervisoryFunction::RECEIVER_NOT_READY:
+        pimpl_->recv_rnr(req_seq, p, f);
+        break;
+      case SupervisoryFunction::REJECT:
+        pimpl_->recv_rej(req_seq, p, f);
+        break;
+      case SupervisoryFunction::SELECT_REJECT:
+        pimpl_->recv_srej(req_seq, p, f);
+        break;
+    }
+  } else {
+    LOG_WARN("Received invalid frame");
+  }
+}
+
+void ErtmController::on_pdu_fcs(const packet::PacketView<true>& pdu) {
+  auto basic_frame_view = BasicFrameWithFcsView::Create(pdu);
+  if (!basic_frame_view.IsValid()) {
+    return;
+  }
+  auto standard_frame_view = StandardFrameWithFcsView::Create(basic_frame_view);
+  if (!standard_frame_view.IsValid()) {
+    LOG_WARN("Received invalid frame");
+    return;
+  }
+  auto type = standard_frame_view.GetFrameType();
+  if (type == FrameType::I_FRAME) {
+    auto i_frame_view = EnhancedInformationFrameWithFcsView::Create(standard_frame_view);
+    if (!i_frame_view.IsValid()) {
+      LOG_WARN("Received invalid frame");
+      return;
+    }
+    Final f = i_frame_view.GetF();
+    uint8_t tx_seq = i_frame_view.GetTxSeq();
+    uint8_t req_seq = i_frame_view.GetReqSeq();
+    auto sar = i_frame_view.GetSar();
+    if (sar == SegmentationAndReassembly::START) {
+      auto i_frame_start_view = EnhancedInformationStartFrameWithFcsView::Create(i_frame_view);
+      if (!i_frame_start_view.IsValid()) {
+        LOG_WARN("Received invalid I-Frame START");
+        return;
+      }
+      pimpl_->recv_i_frame(f, tx_seq, req_seq, sar, i_frame_start_view.GetL2capSduLength(),
+                           i_frame_start_view.GetPayload());
+    } else {
+      pimpl_->recv_i_frame(f, tx_seq, req_seq, sar, 0, i_frame_view.GetPayload());
+    }
+  } else if (type == FrameType::S_FRAME) {
+    auto s_frame_view = EnhancedSupervisoryFrameWithFcsView::Create(standard_frame_view);
+    if (!s_frame_view.IsValid()) {
+      LOG_WARN("Received invalid frame");
+      return;
+    }
+    auto req_seq = s_frame_view.GetReqSeq();
+    auto f = s_frame_view.GetF();
+    auto p = s_frame_view.GetP();
+    switch (s_frame_view.GetS()) {
+      case SupervisoryFunction::RECEIVER_READY:
+        pimpl_->recv_rr(req_seq, p, f);
+        break;
+      case SupervisoryFunction::RECEIVER_NOT_READY:
+        pimpl_->recv_rnr(req_seq, p, f);
+        break;
+      case SupervisoryFunction::REJECT:
+        pimpl_->recv_rej(req_seq, p, f);
+        break;
+      case SupervisoryFunction::SELECT_REJECT:
+        pimpl_->recv_srej(req_seq, p, f);
+        break;
+    }
+  } else {
+    LOG_WARN("Received invalid frame");
+  }
+}
+
+std::unique_ptr<packet::BasePacketBuilder> ErtmController::GetNextPacket() {
+  auto next = std::move(pdu_queue_.front());
+  pdu_queue_.pop();
+  return next;
+}
+
+void ErtmController::stage_for_reassembly(SegmentationAndReassembly sar, uint16_t sdu_size,
+                                          const packet::PacketView<kLittleEndian>& payload) {
+  switch (sar) {
+    case SegmentationAndReassembly::UNSEGMENTED:
+      if (sar_state_ != SegmentationAndReassembly::END) {
+        LOG_WARN("Received invalid SAR");
+        close_channel();
+        return;
+      }
+      // TODO: Enforce MTU
+      enqueue_buffer_.Enqueue(std::make_unique<packet::PacketView<kLittleEndian>>(payload), handler_);
+      break;
+    case SegmentationAndReassembly::START:
+      if (sar_state_ != SegmentationAndReassembly::END) {
+        LOG_WARN("Received invalid SAR");
+        close_channel();
+        return;
+      }
+      // TODO: Enforce MTU
+      sar_state_ = SegmentationAndReassembly::START;
+      reassembly_stage_ = payload;
+      remaining_sdu_continuation_packet_size_ = sdu_size - payload.size();
+      break;
+    case SegmentationAndReassembly::CONTINUATION:
+      if (sar_state_ == SegmentationAndReassembly::END) {
+        LOG_WARN("Received invalid SAR");
+        close_channel();
+        return;
+      }
+      reassembly_stage_.AppendPacketView(payload);
+      remaining_sdu_continuation_packet_size_ -= payload.size();
+      break;
+    case SegmentationAndReassembly::END:
+      if (sar_state_ == SegmentationAndReassembly::END) {
+        LOG_WARN("Received invalid SAR");
+        close_channel();
+        return;
+      }
+      sar_state_ = SegmentationAndReassembly::END;
+      remaining_sdu_continuation_packet_size_ -= payload.size();
+      if (remaining_sdu_continuation_packet_size_ != 0) {
+        LOG_WARN("Received invalid END I-Frame");
+        reassembly_stage_ = PacketViewForReassembly(std::make_shared<std::vector<uint8_t>>());
+        remaining_sdu_continuation_packet_size_ = 0;
+        close_channel();
+        return;
+      }
+      reassembly_stage_.AppendPacketView(payload);
+      enqueue_buffer_.Enqueue(std::make_unique<packet::PacketView<kLittleEndian>>(reassembly_stage_), handler_);
+      break;
+  }
+}
+
+void ErtmController::EnableFcs(bool enabled) {
+  fcs_enabled_ = enabled;
+}
+
+void ErtmController::send_pdu(std::unique_ptr<packet::BasePacketBuilder> pdu) {
+  pdu_queue_.emplace(std::move(pdu));
+  scheduler_->OnPacketsReady(cid_, 1);
+}
+
+void ErtmController::SetRetransmissionAndFlowControlOptions(
+    const RetransmissionAndFlowControlConfigurationOption& option) {
+  remote_tx_window_ = option.tx_window_size_;
+  local_max_transmit_ = option.max_transmit_;
+  local_retransmit_timeout_ms_ = option.retransmission_time_out_;
+  local_monitor_timeout_ms_ = option.monitor_time_out_;
+}
+
+void ErtmController::close_channel() {
+  link_->SendDisconnectionRequest(cid_, remote_cid_);
+}
+
+size_t ErtmController::CopyablePacketBuilder::size() const {
+  return builder_->size();
+}
+
+void ErtmController::CopyablePacketBuilder::Serialize(BitInserter& it) const {
+  builder_->Serialize(it);
+}
+
+}  // namespace internal
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller.h b/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller.h
new file mode 100644
index 0000000..184d6c1
--- /dev/null
+++ b/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <memory>
+#include <unordered_map>
+#include <utility>
+
+#include "common/bidi_queue.h"
+#include "l2cap/cid.h"
+#include "l2cap/internal/channel_impl.h"
+#include "l2cap/internal/data_controller.h"
+#include "l2cap/internal/scheduler.h"
+#include "l2cap/l2cap_packets.h"
+#include "l2cap/mtu.h"
+#include "os/handler.h"
+#include "os/queue.h"
+#include "packet/base_packet_builder.h"
+#include "packet/packet_view.h"
+#include "packet/raw_builder.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+class ILink;
+
+class ErtmController : public DataController {
+ public:
+  using UpperEnqueue = packet::PacketView<packet::kLittleEndian>;
+  using UpperDequeue = packet::BasePacketBuilder;
+  using UpperQueueDownEnd = common::BidiQueueEnd<UpperEnqueue, UpperDequeue>;
+  ErtmController(ILink* link, Cid cid, Cid remote_cid, UpperQueueDownEnd* channel_queue_end, os::Handler* handler,
+                 Scheduler* scheduler);
+  ~ErtmController();
+  // Segmentation is handled here
+  void OnSdu(std::unique_ptr<packet::BasePacketBuilder> sdu) override;
+  void OnPdu(packet::PacketView<true> pdu) override;
+  std::unique_ptr<packet::BasePacketBuilder> GetNextPacket() override;
+  void EnableFcs(bool enabled) override;
+  void SetRetransmissionAndFlowControlOptions(const RetransmissionAndFlowControlConfigurationOption& option) override;
+
+ private:
+  ILink* link_;
+  Cid cid_;
+  Cid remote_cid_;
+  os::EnqueueBuffer<UpperEnqueue> enqueue_buffer_;
+  os::Handler* handler_;
+  std::queue<std::unique_ptr<packet::BasePacketBuilder>> pdu_queue_;
+  Scheduler* scheduler_;
+
+  // Configuration options
+  bool fcs_enabled_ = false;
+  uint16_t local_tx_window_ = 10;
+  uint16_t local_max_transmit_ = 20;
+  uint16_t local_retransmit_timeout_ms_ = 2000;
+  uint16_t local_monitor_timeout_ms_ = 12000;
+
+  uint16_t remote_tx_window_ = 10;
+  uint16_t remote_mps_ = 1010;
+
+  uint16_t size_each_packet_ =
+      (remote_mps_ - 4 /* basic L2CAP header */ - 2 /* SDU length */ - 2 /* Extended control */ - 2 /* FCS */);
+
+  class PacketViewForReassembly : public packet::PacketView<kLittleEndian> {
+   public:
+    PacketViewForReassembly(const PacketView& packetView) : PacketView(packetView) {}
+    void AppendPacketView(packet::PacketView<kLittleEndian> to_append) {
+      Append(to_append);
+    }
+  };
+
+  class CopyablePacketBuilder : public packet::BasePacketBuilder {
+   public:
+    CopyablePacketBuilder(std::shared_ptr<packet::RawBuilder> builder) : builder_(std::move(builder)) {}
+
+    void Serialize(BitInserter& it) const override;
+
+    size_t size() const override;
+
+   private:
+    std::shared_ptr<packet::RawBuilder> builder_;
+  };
+
+  PacketViewForReassembly reassembly_stage_{std::make_shared<std::vector<uint8_t>>()};
+  SegmentationAndReassembly sar_state_ = SegmentationAndReassembly::END;
+  uint16_t remaining_sdu_continuation_packet_size_ = 0;
+
+  void stage_for_reassembly(SegmentationAndReassembly sar, uint16_t sdu_size,
+                            const packet::PacketView<kLittleEndian>& payload);
+  void send_pdu(std::unique_ptr<packet::BasePacketBuilder> pdu);
+
+  void close_channel();
+
+  void on_pdu_no_fcs(const packet::PacketView<true>& pdu);
+  void on_pdu_fcs(const packet::PacketView<true>& pdu);
+
+  struct impl;
+  std::unique_ptr<impl> pimpl_;
+};
+
+}  // namespace internal
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller_test.cc b/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller_test.cc
new file mode 100644
index 0000000..f813898
--- /dev/null
+++ b/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller_test.cc
@@ -0,0 +1,210 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "l2cap/internal/enhanced_retransmission_mode_channel_data_controller.h"
+
+#include <gtest/gtest.h>
+
+#include "l2cap/internal/ilink_mock.h"
+#include "l2cap/internal/scheduler_mock.h"
+#include "l2cap/l2cap_packets.h"
+#include "packet/raw_builder.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+namespace {
+
+std::unique_ptr<packet::BasePacketBuilder> CreateSdu(std::vector<uint8_t> payload) {
+  auto raw_builder = std::make_unique<packet::RawBuilder>();
+  raw_builder->AddOctets(payload);
+  return raw_builder;
+}
+
+PacketView<kLittleEndian> GetPacketView(std::unique_ptr<packet::BasePacketBuilder> packet) {
+  auto bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter i(*bytes);
+  bytes->reserve(packet->size());
+  packet->Serialize(i);
+  return packet::PacketView<packet::kLittleEndian>(bytes);
+}
+
+void sync_handler(os::Handler* handler) {
+  std::promise<void> promise;
+  auto future = promise.get_future();
+  handler->Post(common::BindOnce(&std::promise<void>::set_value, common::Unretained(&promise)));
+  auto status = future.wait_for(std::chrono::milliseconds(300));
+  EXPECT_EQ(status, std::future_status::ready);
+}
+
+class ErtmDataControllerTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    thread_ = new os::Thread("test_thread", os::Thread::Priority::NORMAL);
+    user_handler_ = new os::Handler(thread_);
+    queue_handler_ = new os::Handler(thread_);
+  }
+
+  void TearDown() override {
+    queue_handler_->Clear();
+    user_handler_->Clear();
+    delete queue_handler_;
+    delete user_handler_;
+    delete thread_;
+  }
+
+  os::Thread* thread_ = nullptr;
+  os::Handler* user_handler_ = nullptr;
+  os::Handler* queue_handler_ = nullptr;
+};
+
+TEST_F(ErtmDataControllerTest, transmit_no_fcs) {
+  common::BidiQueue<Scheduler::UpperEnqueue, Scheduler::UpperDequeue> channel_queue{10};
+  testing::MockScheduler scheduler;
+  testing::MockILink link;
+  ErtmController controller{&link, 1, 1, channel_queue.GetDownEnd(), queue_handler_, &scheduler};
+  EXPECT_CALL(scheduler, OnPacketsReady(1, 1));
+  controller.OnSdu(CreateSdu({'a', 'b', 'c', 'd'}));
+  auto next_packet = controller.GetNextPacket();
+  EXPECT_NE(next_packet, nullptr);
+  auto view = GetPacketView(std::move(next_packet));
+  auto pdu_view = BasicFrameView::Create(view);
+  EXPECT_TRUE(pdu_view.IsValid());
+  auto standard_view = StandardFrameView::Create(pdu_view);
+  EXPECT_TRUE(standard_view.IsValid());
+  auto i_frame_view = EnhancedInformationFrameView::Create(standard_view);
+  EXPECT_TRUE(i_frame_view.IsValid());
+  auto payload = i_frame_view.GetPayload();
+  std::string data = std::string(payload.begin(), payload.end());
+  EXPECT_EQ(data, "abcd");
+  EXPECT_EQ(i_frame_view.GetTxSeq(), 0);
+  EXPECT_EQ(i_frame_view.GetReqSeq(), 0);
+}
+
+TEST_F(ErtmDataControllerTest, receive_no_fcs) {
+  common::BidiQueue<Scheduler::UpperEnqueue, Scheduler::UpperDequeue> channel_queue{10};
+  testing::MockScheduler scheduler;
+  testing::MockILink link;
+  ErtmController controller{&link, 1, 1, channel_queue.GetDownEnd(), queue_handler_, &scheduler};
+  auto segment = CreateSdu({'a', 'b', 'c', 'd'});
+  auto builder = EnhancedInformationFrameBuilder::Create(1, 0, Final::NOT_SET, 0,
+                                                         SegmentationAndReassembly::UNSEGMENTED, std::move(segment));
+  auto base_view = GetPacketView(std::move(builder));
+  controller.OnPdu(base_view);
+  sync_handler(queue_handler_);
+  auto payload = channel_queue.GetUpEnd()->TryDequeue();
+  EXPECT_NE(payload, nullptr);
+  std::string data = std::string(payload->begin(), payload->end());
+  EXPECT_EQ(data, "abcd");
+}
+
+TEST_F(ErtmDataControllerTest, reassemble_valid_sdu) {
+  common::BidiQueue<Scheduler::UpperEnqueue, Scheduler::UpperDequeue> channel_queue{10};
+  testing::MockScheduler scheduler;
+  testing::MockILink link;
+  ErtmController controller{&link, 1, 1, channel_queue.GetDownEnd(), queue_handler_, &scheduler};
+  auto segment1 = CreateSdu({'a'});
+  auto segment2 = CreateSdu({'b', 'c'});
+  auto segment3 = CreateSdu({'d', 'e', 'f'});
+  auto builder1 = EnhancedInformationStartFrameBuilder::Create(1, 0, Final::NOT_SET, 0, 6, std::move(segment1));
+  auto base_view = GetPacketView(std::move(builder1));
+  controller.OnPdu(base_view);
+  auto builder2 = EnhancedInformationFrameBuilder::Create(1, 1, Final::NOT_SET, 0,
+                                                          SegmentationAndReassembly::CONTINUATION, std::move(segment2));
+  base_view = GetPacketView(std::move(builder2));
+  controller.OnPdu(base_view);
+  auto builder3 = EnhancedInformationFrameBuilder::Create(1, 2, Final::NOT_SET, 0, SegmentationAndReassembly::END,
+                                                          std::move(segment3));
+  base_view = GetPacketView(std::move(builder3));
+  controller.OnPdu(base_view);
+  sync_handler(queue_handler_);
+  auto payload = channel_queue.GetUpEnd()->TryDequeue();
+  EXPECT_NE(payload, nullptr);
+  std::string data = std::string(payload->begin(), payload->end());
+  EXPECT_EQ(data, "abcdef");
+}
+
+TEST_F(ErtmDataControllerTest, reassemble_invalid_sdu_size_in_start_frame_will_disconnect) {
+  common::BidiQueue<Scheduler::UpperEnqueue, Scheduler::UpperDequeue> channel_queue{10};
+  testing::MockScheduler scheduler;
+  testing::MockILink link;
+  ErtmController controller{&link, 1, 1, channel_queue.GetDownEnd(), queue_handler_, &scheduler};
+  auto segment1 = CreateSdu({'a'});
+  auto segment2 = CreateSdu({'b', 'c'});
+  auto segment3 = CreateSdu({'d', 'e', 'f'});
+  auto builder1 = EnhancedInformationStartFrameBuilder::Create(1, 0, Final::NOT_SET, 0, 10, std::move(segment1));
+  auto base_view = GetPacketView(std::move(builder1));
+  controller.OnPdu(base_view);
+  auto builder2 = EnhancedInformationFrameBuilder::Create(1, 1, Final::NOT_SET, 0,
+                                                          SegmentationAndReassembly::CONTINUATION, std::move(segment2));
+  base_view = GetPacketView(std::move(builder2));
+  controller.OnPdu(base_view);
+  auto builder3 = EnhancedInformationFrameBuilder::Create(1, 2, Final::NOT_SET, 0, SegmentationAndReassembly::END,
+                                                          std::move(segment3));
+  base_view = GetPacketView(std::move(builder3));
+  EXPECT_CALL(link, SendDisconnectionRequest(1, 1));
+  controller.OnPdu(base_view);
+  sync_handler(queue_handler_);
+  auto payload = channel_queue.GetUpEnd()->TryDequeue();
+  EXPECT_EQ(payload, nullptr);
+}
+
+TEST_F(ErtmDataControllerTest, transmit_with_fcs) {
+  common::BidiQueue<Scheduler::UpperEnqueue, Scheduler::UpperDequeue> channel_queue{10};
+  testing::MockScheduler scheduler;
+  testing::MockILink link;
+  ErtmController controller{&link, 1, 1, channel_queue.GetDownEnd(), queue_handler_, &scheduler};
+  controller.EnableFcs(true);
+  EXPECT_CALL(scheduler, OnPacketsReady(1, 1));
+  controller.OnSdu(CreateSdu({'a', 'b', 'c', 'd'}));
+  auto next_packet = controller.GetNextPacket();
+  EXPECT_NE(next_packet, nullptr);
+  auto view = GetPacketView(std::move(next_packet));
+  auto pdu_view = BasicFrameWithFcsView::Create(view);
+  EXPECT_TRUE(pdu_view.IsValid());
+  auto standard_view = StandardFrameWithFcsView::Create(pdu_view);
+  EXPECT_TRUE(standard_view.IsValid());
+  auto i_frame_view = EnhancedInformationFrameWithFcsView::Create(standard_view);
+  EXPECT_TRUE(i_frame_view.IsValid());
+  auto payload = i_frame_view.GetPayload();
+  std::string data = std::string(payload.begin(), payload.end());
+  EXPECT_EQ(data, "abcd");
+  EXPECT_EQ(i_frame_view.GetTxSeq(), 0);
+  EXPECT_EQ(i_frame_view.GetReqSeq(), 0);
+}
+
+TEST_F(ErtmDataControllerTest, receive_packet_with_fcs) {
+  common::BidiQueue<Scheduler::UpperEnqueue, Scheduler::UpperDequeue> channel_queue{10};
+  testing::MockScheduler scheduler;
+  testing::MockILink link;
+  ErtmController controller{&link, 1, 1, channel_queue.GetDownEnd(), queue_handler_, &scheduler};
+  controller.EnableFcs(true);
+  auto segment = CreateSdu({'a', 'b', 'c', 'd'});
+  auto builder = EnhancedInformationFrameWithFcsBuilder::Create(
+      1, 0, Final::NOT_SET, 0, SegmentationAndReassembly::UNSEGMENTED, std::move(segment));
+  auto base_view = GetPacketView(std::move(builder));
+  controller.OnPdu(base_view);
+  sync_handler(queue_handler_);
+  auto payload = channel_queue.GetUpEnd()->TryDequeue();
+  EXPECT_NE(payload, nullptr);
+  std::string data = std::string(payload->begin(), payload->end());
+  EXPECT_EQ(data, "abcd");
+}
+
+}  // namespace
+}  // namespace internal
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/internal/fixed_channel_allocator.h b/gd/l2cap/internal/fixed_channel_allocator.h
new file mode 100644
index 0000000..b821cb1
--- /dev/null
+++ b/gd/l2cap/internal/fixed_channel_allocator.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <type_traits>
+#include <unordered_map>
+
+#include "hci/hci_packets.h"
+#include "l2cap/cid.h"
+#include "l2cap/security_policy.h"
+#include "os/handler.h"
+#include "os/log.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+
+// Helper class for keeping channels in a Link. It allocates and frees Channel object, and supports querying whether a
+// channel is in use
+template <typename FixedChannelImplType, typename LinkType>
+class FixedChannelAllocator {
+ public:
+  FixedChannelAllocator(LinkType* link, os::Handler* l2cap_handler) : link_(link), l2cap_handler_(l2cap_handler) {
+    ASSERT(link_ != nullptr);
+    ASSERT(l2cap_handler_ != nullptr);
+  }
+
+  virtual ~FixedChannelAllocator() = default;
+
+  // Allocates a channel. If cid is used, return nullptr. NOTE: The returned BaseFixedChannelImpl object is still
+  // owned by the channel allocator, NOT the client.
+  virtual std::shared_ptr<FixedChannelImplType> AllocateChannel(Cid cid, SecurityPolicy security_policy) {
+    ASSERT_LOG(!IsChannelAllocated((cid)), "Cid 0x%x for link %s is already in use", cid, link_->ToString().c_str());
+    ASSERT_LOG(cid >= kFirstFixedChannel && cid <= kLastFixedChannel, "Cid %d out of bound", cid);
+    auto elem = channels_.try_emplace(cid, std::make_shared<FixedChannelImplType>(cid, link_, l2cap_handler_));
+    ASSERT_LOG(elem.second, "Failed to create channel for cid 0x%x link %s", cid, link_->ToString().c_str());
+    ASSERT(elem.first->second != nullptr);
+    return elem.first->second;
+  }
+
+  // Frees a channel. If cid doesn't exist, it will crash
+  virtual void FreeChannel(Cid cid) {
+    ASSERT_LOG(IsChannelAllocated(cid), "Channel is not in use: cid %d, link %s", cid, link_->ToString().c_str());
+    channels_.erase(cid);
+  }
+
+  virtual bool IsChannelAllocated(Cid cid) const {
+    return channels_.find(cid) != channels_.end();
+  }
+
+  virtual std::shared_ptr<FixedChannelImplType> FindChannel(Cid cid) {
+    ASSERT_LOG(IsChannelAllocated(cid), "Channel is not in use: cid %d, link %s", cid, link_->ToString().c_str());
+    return channels_.find(cid)->second;
+  }
+
+  virtual size_t NumberOfChannels() const {
+    return channels_.size();
+  }
+
+  virtual void OnAclDisconnected(hci::ErrorCode hci_status) {
+    for (auto& elem : channels_) {
+      elem.second->OnClosed(hci_status);
+    }
+  }
+
+  virtual int GetRefCount() {
+    int ref_count = 0;
+    for (auto& elem : channels_) {
+      if (elem.second->IsAcquired()) {
+        ref_count++;
+      }
+    }
+    return ref_count;
+  }
+
+ private:
+  LinkType* link_;
+  os::Handler* l2cap_handler_;
+  std::unordered_map<Cid, std::shared_ptr<FixedChannelImplType>> channels_;
+};
+
+}  // namespace internal
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/internal/fixed_channel_allocator_test.cc b/gd/l2cap/internal/fixed_channel_allocator_test.cc
new file mode 100644
index 0000000..38eac13
--- /dev/null
+++ b/gd/l2cap/internal/fixed_channel_allocator_test.cc
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "l2cap/internal/fixed_channel_allocator.h"
+#include "l2cap/classic/internal/fixed_channel_impl_mock.h"
+#include "l2cap/classic/internal/link_mock.h"
+#include "l2cap/internal/parameter_provider_mock.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+
+using l2cap::classic::internal::testing::MockFixedChannelImpl;
+using l2cap::classic::internal::testing::MockLink;
+using testing::MockParameterProvider;
+using ::testing::Return;
+
+const hci::AddressWithType device{{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}}, hci::AddressType::PUBLIC_IDENTITY_ADDRESS};
+
+class L2capFixedChannelAllocatorTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    thread_ = new os::Thread("test_thread", os::Thread::Priority::NORMAL);
+    handler_ = new os::Handler(thread_);
+    mock_parameter_provider_ = new MockParameterProvider();
+    mock_classic_link_ = new MockLink(handler_, mock_parameter_provider_);
+    EXPECT_CALL(*mock_classic_link_, GetDevice()).WillRepeatedly(Return(device));
+    // Use classic as a place holder
+    channel_allocator_ =
+        std::make_unique<FixedChannelAllocator<MockFixedChannelImpl, MockLink>>(mock_classic_link_, handler_);
+  }
+
+  void TearDown() override {
+    channel_allocator_.reset();
+    delete mock_classic_link_;
+    delete mock_parameter_provider_;
+    handler_->Clear();
+    delete handler_;
+    delete thread_;
+  }
+
+  os::Thread* thread_{nullptr};
+  os::Handler* handler_{nullptr};
+  MockParameterProvider* mock_parameter_provider_{nullptr};
+  MockLink* mock_classic_link_{nullptr};
+  std::unique_ptr<FixedChannelAllocator<MockFixedChannelImpl, MockLink>> channel_allocator_;
+};
+
+TEST_F(L2capFixedChannelAllocatorTest, precondition) {
+  Cid cid = kFirstFixedChannel;
+  EXPECT_FALSE(channel_allocator_->IsChannelAllocated(cid));
+}
+
+TEST_F(L2capFixedChannelAllocatorTest, allocate_and_free_channel) {
+  Cid cid = kFirstFixedChannel;
+  auto channel = channel_allocator_->AllocateChannel(cid, {});
+  EXPECT_TRUE(channel_allocator_->IsChannelAllocated(cid));
+  EXPECT_EQ(channel, channel_allocator_->FindChannel(cid));
+  ASSERT_NO_FATAL_FAILURE(channel_allocator_->FreeChannel(cid));
+  EXPECT_FALSE(channel_allocator_->IsChannelAllocated(cid));
+}
+
+}  // namespace internal
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/internal/ilink.h b/gd/l2cap/internal/ilink.h
new file mode 100644
index 0000000..abeb2ea
--- /dev/null
+++ b/gd/l2cap/internal/ilink.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "hci/address_with_type.h"
+#include "l2cap/cid.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+
+/**
+ * Common interface for link (Classic ACL and LE)
+ */
+class ILink {
+ public:
+  virtual ~ILink() = default;
+  virtual void SendDisconnectionRequest(Cid local_cid, Cid remote_cid) = 0;
+  virtual hci::AddressWithType GetDevice() = 0;
+
+  // To be used by LE credit based channel data controller over LE link
+  virtual void SendLeCredit(Cid local_cid, uint16_t credit) = 0;
+};
+}  // namespace internal
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/internal/ilink_mock.h b/gd/l2cap/internal/ilink_mock.h
new file mode 100644
index 0000000..18fc626
--- /dev/null
+++ b/gd/l2cap/internal/ilink_mock.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "l2cap/internal/ilink.h"
+
+#include <gmock/gmock.h>
+
+// Unit test interfaces
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+namespace testing {
+
+class MockILink : public ILink {
+ public:
+  MOCK_METHOD(hci::AddressWithType, GetDevice, (), (override));
+  MOCK_METHOD(void, SendDisconnectionRequest, (Cid, Cid), (override));
+  MOCK_METHOD(void, SendLeCredit, (Cid, uint16_t), (override));
+};
+
+}  // namespace testing
+}  // namespace internal
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/internal/le_credit_based_channel_data_controller.cc b/gd/l2cap/internal/le_credit_based_channel_data_controller.cc
new file mode 100644
index 0000000..943cb17
--- /dev/null
+++ b/gd/l2cap/internal/le_credit_based_channel_data_controller.cc
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "l2cap/internal/le_credit_based_channel_data_controller.h"
+
+#include "l2cap/l2cap_packets.h"
+#include "l2cap/le/internal/link.h"
+#include "packet/fragmenting_inserter.h"
+#include "packet/raw_builder.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+
+LeCreditBasedDataController::LeCreditBasedDataController(ILink* link, Cid cid, Cid remote_cid,
+                                                         UpperQueueDownEnd* channel_queue_end, os::Handler* handler,
+                                                         Scheduler* scheduler)
+    : cid_(cid), remote_cid_(remote_cid), enqueue_buffer_(channel_queue_end), handler_(handler), scheduler_(scheduler),
+      link_(link) {}
+
+void LeCreditBasedDataController::OnSdu(std::unique_ptr<packet::BasePacketBuilder> sdu) {
+  auto sdu_size = sdu->size();
+  if (sdu_size == 0) {
+    LOG_WARN("Received empty SDU");
+    return;
+  }
+  if (sdu_size > mtu_) {
+    LOG_WARN("Received sdu_size %d > mtu %d", static_cast<int>(sdu_size), mtu_);
+  }
+  std::vector<std::unique_ptr<packet::RawBuilder>> segments;
+  // TODO: We don't need to waste 2 bytes for continuation segment.
+  packet::FragmentingInserter fragmenting_inserter(mps_ - 2, std::back_insert_iterator(segments));
+  sdu->Serialize(fragmenting_inserter);
+  fragmenting_inserter.finalize();
+  std::unique_ptr<BasicFrameBuilder> builder;
+  builder = FirstLeInformationFrameBuilder::Create(remote_cid_, sdu_size, std::move(segments[0]));
+  pdu_queue_.emplace(std::move(builder));
+  for (auto i = 1; i < segments.size(); i++) {
+    builder = BasicFrameBuilder::Create(remote_cid_, std::move(segments[i]));
+    pdu_queue_.emplace(std::move(builder));
+  }
+  if (credits_ >= segments.size()) {
+    scheduler_->OnPacketsReady(cid_, segments.size());
+    credits_ -= segments.size();
+  } else if (credits_ > 0) {
+    scheduler_->OnPacketsReady(cid_, credits_);
+    pending_frames_count_ += (segments.size() - credits_);
+    credits_ = 0;
+  } else {
+    pending_frames_count_ += segments.size();
+  }
+}
+
+void LeCreditBasedDataController::OnPdu(packet::PacketView<true> pdu) {
+  auto basic_frame_view = BasicFrameView::Create(pdu);
+  if (!basic_frame_view.IsValid()) {
+    LOG_WARN("Received invalid frame");
+    return;
+  }
+  if (basic_frame_view.size() > mps_) {
+    LOG_WARN("Received frame size %d > mps %d, dropping the packet", static_cast<int>(basic_frame_view.size()), mps_);
+    return;
+  }
+  if (remaining_sdu_continuation_packet_size_ == 0) {
+    auto start_frame_view = FirstLeInformationFrameView::Create(basic_frame_view);
+    if (!start_frame_view.IsValid()) {
+      LOG_WARN("Received invalid frame");
+      return;
+    }
+    auto payload = start_frame_view.GetPayload();
+    auto sdu_size = start_frame_view.GetL2capSduLength();
+    remaining_sdu_continuation_packet_size_ = sdu_size - payload.size();
+    reassembly_stage_ = payload;
+  } else {
+    auto payload = basic_frame_view.GetPayload();
+    remaining_sdu_continuation_packet_size_ -= payload.size();
+    reassembly_stage_.AppendPacketView(payload);
+  }
+  if (remaining_sdu_continuation_packet_size_ == 0) {
+    enqueue_buffer_.Enqueue(std::make_unique<PacketView<kLittleEndian>>(reassembly_stage_), handler_);
+  } else if (remaining_sdu_continuation_packet_size_ < 0 || reassembly_stage_.size() > mtu_) {
+    LOG_WARN("Received larger SDU size than expected");
+    reassembly_stage_ = PacketViewForReassembly(std::make_shared<std::vector<uint8_t>>());
+    remaining_sdu_continuation_packet_size_ = 0;
+    link_->SendDisconnectionRequest(cid_, remote_cid_);
+  }
+  // TODO: Improve the logic by sending credit only after user dequeued the SDU
+  link_->SendLeCredit(cid_, 1);
+}
+
+std::unique_ptr<packet::BasePacketBuilder> LeCreditBasedDataController::GetNextPacket() {
+  auto next = std::move(pdu_queue_.front());
+  pdu_queue_.pop();
+  return next;
+}
+
+void LeCreditBasedDataController::SetMtu(Mtu mtu) {
+  mtu_ = mtu;
+}
+
+void LeCreditBasedDataController::SetMps(uint16_t mps) {
+  mps_ = mps;
+}
+
+void LeCreditBasedDataController::OnCredit(uint16_t credits) {
+  int total_credits = credits_ + credits;
+  if (total_credits > 0xffff) {
+    link_->SendDisconnectionRequest(cid_, remote_cid_);
+  }
+  credits_ = total_credits;
+  if (pending_frames_count_ > 0 && credits_ >= pending_frames_count_) {
+    scheduler_->OnPacketsReady(cid_, pending_frames_count_);
+    credits_ -= pending_frames_count_;
+  } else if (pending_frames_count_ > 0) {
+    scheduler_->OnPacketsReady(cid_, credits_);
+    pending_frames_count_ -= credits_;
+    credits_ = 0;
+  }
+}
+
+}  // namespace internal
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/internal/le_credit_based_channel_data_controller.h b/gd/l2cap/internal/le_credit_based_channel_data_controller.h
new file mode 100644
index 0000000..bbd4c06
--- /dev/null
+++ b/gd/l2cap/internal/le_credit_based_channel_data_controller.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <memory>
+#include <unordered_map>
+#include <utility>
+
+#include "common/bidi_queue.h"
+#include "l2cap/cid.h"
+#include "l2cap/internal/channel_impl.h"
+#include "l2cap/internal/data_controller.h"
+#include "l2cap/internal/ilink.h"
+#include "l2cap/internal/scheduler.h"
+#include "l2cap/l2cap_packets.h"
+#include "l2cap/mtu.h"
+#include "os/handler.h"
+#include "os/queue.h"
+#include "packet/base_packet_builder.h"
+#include "packet/packet_view.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+
+class LeCreditBasedDataController : public DataController {
+ public:
+  using UpperEnqueue = packet::PacketView<packet::kLittleEndian>;
+  using UpperDequeue = packet::BasePacketBuilder;
+  using UpperQueueDownEnd = common::BidiQueueEnd<UpperEnqueue, UpperDequeue>;
+  LeCreditBasedDataController(ILink* link, Cid cid, Cid remote_cid, UpperQueueDownEnd* channel_queue_end,
+                              os::Handler* handler, Scheduler* scheduler);
+
+  void OnSdu(std::unique_ptr<packet::BasePacketBuilder> sdu) override;
+  void OnPdu(packet::PacketView<true> pdu) override;
+  std::unique_ptr<packet::BasePacketBuilder> GetNextPacket() override;
+
+  void EnableFcs(bool enabled) override {}
+  void SetRetransmissionAndFlowControlOptions(const RetransmissionAndFlowControlConfigurationOption& option) override {}
+
+  // TODO: Set MTU and MPS from signalling channel
+  void SetMtu(Mtu mtu);
+  void SetMps(uint16_t mps);
+  // TODO: Handle credits
+  void OnCredit(uint16_t credits);
+
+ private:
+  Cid cid_;
+  Cid remote_cid_;
+  os::EnqueueBuffer<UpperEnqueue> enqueue_buffer_;
+  os::Handler* handler_;
+  std::queue<std::unique_ptr<packet::BasePacketBuilder>> pdu_queue_;
+  Scheduler* scheduler_;
+  ILink* link_;
+  Mtu mtu_ = 512;
+  uint16_t mps_ = 251;
+  uint16_t credits_ = 0;
+  uint16_t pending_frames_count_ = 0;
+
+  class PacketViewForReassembly : public packet::PacketView<kLittleEndian> {
+   public:
+    PacketViewForReassembly(const PacketView& packetView) : PacketView(packetView) {}
+    void AppendPacketView(packet::PacketView<kLittleEndian> to_append) {
+      Append(to_append);
+    }
+  };
+  PacketViewForReassembly reassembly_stage_{std::make_shared<std::vector<uint8_t>>()};
+  uint16_t remaining_sdu_continuation_packet_size_ = 0;
+};
+
+}  // namespace internal
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/internal/le_credit_based_channel_data_controller_test.cc b/gd/l2cap/internal/le_credit_based_channel_data_controller_test.cc
new file mode 100644
index 0000000..885826a
--- /dev/null
+++ b/gd/l2cap/internal/le_credit_based_channel_data_controller_test.cc
@@ -0,0 +1,187 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "l2cap/internal/le_credit_based_channel_data_controller.h"
+
+#include <gtest/gtest.h>
+
+#include "l2cap/internal/ilink_mock.h"
+#include "l2cap/internal/scheduler_mock.h"
+#include "l2cap/l2cap_packets.h"
+#include "packet/raw_builder.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+namespace {
+
+std::unique_ptr<packet::BasePacketBuilder> CreateSdu(std::vector<uint8_t> payload) {
+  auto raw_builder = std::make_unique<packet::RawBuilder>();
+  raw_builder->AddOctets(payload);
+  return raw_builder;
+}
+
+PacketView<kLittleEndian> GetPacketView(std::unique_ptr<packet::BasePacketBuilder> packet) {
+  auto bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter i(*bytes);
+  bytes->reserve(packet->size());
+  packet->Serialize(i);
+  return packet::PacketView<packet::kLittleEndian>(bytes);
+}
+
+void sync_handler(os::Handler* handler) {
+  std::promise<void> promise;
+  auto future = promise.get_future();
+  handler->Post(common::BindOnce(&std::promise<void>::set_value, common::Unretained(&promise)));
+  auto status = future.wait_for(std::chrono::milliseconds(300));
+  EXPECT_EQ(status, std::future_status::ready);
+}
+
+class LeCreditBasedDataControllerTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    thread_ = new os::Thread("test_thread", os::Thread::Priority::NORMAL);
+    user_handler_ = new os::Handler(thread_);
+    queue_handler_ = new os::Handler(thread_);
+  }
+
+  void TearDown() override {
+    queue_handler_->Clear();
+    user_handler_->Clear();
+    delete queue_handler_;
+    delete user_handler_;
+    delete thread_;
+  }
+
+  os::Thread* thread_ = nullptr;
+  os::Handler* user_handler_ = nullptr;
+  os::Handler* queue_handler_ = nullptr;
+};
+
+TEST_F(LeCreditBasedDataControllerTest, transmit_unsegmented) {
+  common::BidiQueue<Scheduler::UpperEnqueue, Scheduler::UpperDequeue> channel_queue{10};
+  testing::MockScheduler scheduler;
+  testing::MockILink link;
+  LeCreditBasedDataController controller{&link, 0x41, 0x41, channel_queue.GetDownEnd(), queue_handler_, &scheduler};
+  controller.OnCredit(10);
+  EXPECT_CALL(scheduler, OnPacketsReady(0x41, 1));
+  controller.OnSdu(CreateSdu({'a', 'b', 'c', 'd'}));
+  auto next_packet = controller.GetNextPacket();
+  EXPECT_NE(next_packet, nullptr);
+  auto view = GetPacketView(std::move(next_packet));
+  auto pdu_view = BasicFrameView::Create(view);
+  EXPECT_TRUE(pdu_view.IsValid());
+  auto first_le_info_view = FirstLeInformationFrameView::Create(pdu_view);
+  EXPECT_TRUE(first_le_info_view.IsValid());
+  auto payload = first_le_info_view.GetPayload();
+  std::string data = std::string(payload.begin(), payload.end());
+  EXPECT_EQ(data, "abcd");
+}
+
+TEST_F(LeCreditBasedDataControllerTest, transmit_segmented) {
+  common::BidiQueue<Scheduler::UpperEnqueue, Scheduler::UpperDequeue> channel_queue{10};
+  testing::MockScheduler scheduler;
+  testing::MockILink link;
+  LeCreditBasedDataController controller{&link, 0x41, 0x41, channel_queue.GetDownEnd(), queue_handler_, &scheduler};
+  controller.OnCredit(10);
+  controller.SetMps(4);
+  EXPECT_CALL(scheduler, OnPacketsReady(0x41, 2));
+  // Should be divided into 'ab', and 'cd'
+  controller.OnSdu(CreateSdu({'a', 'b', 'c', 'd'}));
+  auto next_packet = controller.GetNextPacket();
+  EXPECT_NE(next_packet, nullptr);
+  auto view = GetPacketView(std::move(next_packet));
+  auto pdu_view = BasicFrameView::Create(view);
+  EXPECT_TRUE(pdu_view.IsValid());
+  auto first_le_info_view = FirstLeInformationFrameView::Create(pdu_view);
+  EXPECT_TRUE(first_le_info_view.IsValid());
+  auto payload = first_le_info_view.GetPayload();
+  std::string data = std::string(payload.begin(), payload.end());
+  EXPECT_EQ(data, "ab");
+  EXPECT_EQ(first_le_info_view.GetL2capSduLength(), 4);
+
+  next_packet = controller.GetNextPacket();
+  EXPECT_NE(next_packet, nullptr);
+  view = GetPacketView(std::move(next_packet));
+  pdu_view = BasicFrameView::Create(view);
+  EXPECT_TRUE(pdu_view.IsValid());
+  payload = pdu_view.GetPayload();
+  data = std::string(payload.begin(), payload.end());
+  EXPECT_EQ(data, "cd");
+}
+
+TEST_F(LeCreditBasedDataControllerTest, receive_unsegmented) {
+  common::BidiQueue<Scheduler::UpperEnqueue, Scheduler::UpperDequeue> channel_queue{10};
+  testing::MockScheduler scheduler;
+  testing::MockILink link;
+  LeCreditBasedDataController controller{&link, 0x41, 0x41, channel_queue.GetDownEnd(), queue_handler_, &scheduler};
+  controller.OnCredit(10);
+  auto segment = CreateSdu({'a', 'b', 'c', 'd'});
+  auto builder = FirstLeInformationFrameBuilder::Create(0x41, 4, std::move(segment));
+  auto base_view = GetPacketView(std::move(builder));
+  controller.OnPdu(base_view);
+  sync_handler(queue_handler_);
+  auto payload = channel_queue.GetUpEnd()->TryDequeue();
+  EXPECT_NE(payload, nullptr);
+  std::string data = std::string(payload->begin(), payload->end());
+  EXPECT_EQ(data, "abcd");
+}
+
+TEST_F(LeCreditBasedDataControllerTest, receive_segmented) {
+  common::BidiQueue<Scheduler::UpperEnqueue, Scheduler::UpperDequeue> channel_queue{10};
+  testing::MockScheduler scheduler;
+  testing::MockILink link;
+  LeCreditBasedDataController controller{&link, 0x41, 0x41, channel_queue.GetDownEnd(), queue_handler_, &scheduler};
+  controller.OnCredit(10);
+  auto segment1 = CreateSdu({'a', 'b', 'c', 'd'});
+  auto builder1 = FirstLeInformationFrameBuilder::Create(0x41, 7, std::move(segment1));
+  auto base_view = GetPacketView(std::move(builder1));
+  controller.OnPdu(base_view);
+  auto segment2 = CreateSdu({'e', 'f', 'g'});
+  auto builder2 = BasicFrameBuilder::Create(0x41, std::move(segment2));
+  base_view = GetPacketView(std::move(builder2));
+  EXPECT_CALL(link, SendLeCredit(0x41, 1));
+  controller.OnPdu(base_view);
+  sync_handler(queue_handler_);
+  auto payload = channel_queue.GetUpEnd()->TryDequeue();
+  EXPECT_NE(payload, nullptr);
+  std::string data = std::string(payload->begin(), payload->end());
+  EXPECT_EQ(data, "abcdefg");
+}
+
+TEST_F(LeCreditBasedDataControllerTest, receive_segmented_with_wrong_sdu_length) {
+  common::BidiQueue<Scheduler::UpperEnqueue, Scheduler::UpperDequeue> channel_queue{10};
+  testing::MockScheduler scheduler;
+  testing::MockILink link;
+  LeCreditBasedDataController controller{&link, 0x41, 0x41, channel_queue.GetDownEnd(), queue_handler_, &scheduler};
+  controller.OnCredit(10);
+  auto segment1 = CreateSdu({'a', 'b', 'c', 'd'});
+  auto builder1 = FirstLeInformationFrameBuilder::Create(0x41, 5, std::move(segment1));
+  auto base_view = GetPacketView(std::move(builder1));
+  controller.OnPdu(base_view);
+  auto segment2 = CreateSdu({'e', 'f', 'g'});
+  auto builder2 = BasicFrameBuilder::Create(0x41, std::move(segment2));
+  base_view = GetPacketView(std::move(builder2));
+  controller.OnPdu(base_view);
+  sync_handler(queue_handler_);
+  auto payload = channel_queue.GetUpEnd()->TryDequeue();
+  EXPECT_EQ(payload, nullptr);
+}
+
+}  // namespace
+}  // namespace internal
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/internal/parameter_provider.h b/gd/l2cap/internal/parameter_provider.h
new file mode 100644
index 0000000..5de6c54
--- /dev/null
+++ b/gd/l2cap/internal/parameter_provider.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <chrono>
+
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+
+/**
+ * A class that provide constant parameters to the L2CAP stack
+ *
+ * All methods are virtual so that they can be override in unit tests
+ */
+class ParameterProvider {
+ public:
+  virtual ~ParameterProvider() = default;
+  virtual std::chrono::milliseconds GetClassicLinkIdleDisconnectTimeout() {
+    return std::chrono::seconds(20);
+  }
+  virtual std::chrono::milliseconds GetLeLinkIdleDisconnectTimeout() {
+    return std::chrono::seconds(20);
+  }
+  virtual uint16_t GetLeMps() {
+    return 251;
+  }
+  virtual uint16_t GetLeInitialCredit() {
+    return 100;
+  }
+};
+
+}  // namespace internal
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/internal/parameter_provider_mock.h b/gd/l2cap/internal/parameter_provider_mock.h
new file mode 100644
index 0000000..823cf8a
--- /dev/null
+++ b/gd/l2cap/internal/parameter_provider_mock.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include "l2cap/internal/parameter_provider.h"
+
+#include <gmock/gmock.h>
+
+// Unit test interfaces
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+namespace testing {
+
+class MockParameterProvider : public ParameterProvider {
+ public:
+  MOCK_METHOD(std::chrono::milliseconds, GetClassicLinkIdleDisconnectTimeout, (), (override));
+  MOCK_METHOD(std::chrono::milliseconds, GetLeLinkIdleDisconnectTimeout, (), (override));
+};
+
+}  // namespace testing
+}  // namespace internal
+}  // namespace l2cap
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/l2cap/internal/receiver.cc b/gd/l2cap/internal/receiver.cc
new file mode 100644
index 0000000..86f43ae
--- /dev/null
+++ b/gd/l2cap/internal/receiver.cc
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "l2cap/internal/receiver.h"
+
+#include "common/bidi_queue.h"
+#include "l2cap/cid.h"
+#include "l2cap/internal/data_pipeline_manager.h"
+#include "l2cap/l2cap_packets.h"
+#include "packet/packet_view.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+Receiver::Receiver(LowerQueueUpEnd* link_queue_up_end, os::Handler* handler,
+                   DataPipelineManager* data_pipeline_manager_)
+    : link_queue_up_end_(link_queue_up_end), handler_(handler), data_pipeline_manager_(data_pipeline_manager_) {
+  ASSERT(link_queue_up_end_ != nullptr && handler_ != nullptr);
+  link_queue_up_end_->RegisterDequeue(handler_,
+                                      common::Bind(&Receiver::link_queue_dequeue_callback, common::Unretained(this)));
+}
+
+Receiver::~Receiver() {
+  link_queue_up_end_->UnregisterDequeue();
+}
+
+void Receiver::link_queue_dequeue_callback() {
+  auto packet = link_queue_up_end_->TryDequeue();
+  auto basic_frame_view = BasicFrameView::Create(*packet);
+  if (!basic_frame_view.IsValid()) {
+    LOG_WARN("Received an invalid basic frame");
+    return;
+  }
+  Cid cid = static_cast<Cid>(basic_frame_view.GetChannelId());
+  auto* data_controller = data_pipeline_manager_->GetDataController(cid);
+  if (data_controller == nullptr) {
+    LOG_WARN("Received a packet with invalid cid: %d", cid);
+    return;
+  }
+  data_controller->OnPdu(*packet);
+}
+
+}  // namespace internal
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/internal/receiver.h b/gd/l2cap/internal/receiver.h
new file mode 100644
index 0000000..f757319
--- /dev/null
+++ b/gd/l2cap/internal/receiver.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <memory>
+#include <unordered_map>
+#include <utility>
+
+#include "common/bidi_queue.h"
+#include "l2cap/cid.h"
+#include "l2cap/internal/channel_impl.h"
+#include "l2cap/internal/scheduler.h"
+#include "l2cap/l2cap_packets.h"
+#include "l2cap/mtu.h"
+#include "os/queue.h"
+#include "packet/base_packet_builder.h"
+#include "packet/packet_view.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+
+class DataPipelineManager;
+
+/**
+ * Handle receiving L2CAP PDUs from link queue and distribute them into into channel data controllers.
+ * Dequeue incoming packets from LinkQueueUpEnd, and enqueue it to ChannelQueueDownEnd. Note: If a channel
+ * cannot dequeue from ChannelQueueDownEnd so that the buffer for incoming packet is full, further incoming packets will
+ * be dropped.
+ * The Reassembler keeps the reference to ChannelImpl objects, because it needs to check channel mode and parameters.
+ * The Reassembler also keeps the reference to Scheduler, to get Segmenter and send signals (Tx, Rx seq) to it.
+ */
+class Receiver {
+ public:
+  using UpperEnqueue = packet::PacketView<packet::kLittleEndian>;
+  using UpperDequeue = packet::BasePacketBuilder;
+  using UpperQueueDownEnd = common::BidiQueueEnd<UpperEnqueue, UpperDequeue>;
+  using LowerEnqueue = UpperDequeue;
+  using LowerDequeue = UpperEnqueue;
+  using LowerQueueUpEnd = common::BidiQueueEnd<LowerEnqueue, LowerDequeue>;
+
+  Receiver(LowerQueueUpEnd* link_queue_up_end, os::Handler* handler, DataPipelineManager* data_pipeline_manager);
+  ~Receiver();
+
+ private:
+  LowerQueueUpEnd* link_queue_up_end_;
+  os::Handler* handler_;
+  DataPipelineManager* data_pipeline_manager_;
+
+  void link_queue_dequeue_callback();
+};
+
+}  // namespace internal
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/internal/receiver_test.cc b/gd/l2cap/internal/receiver_test.cc
new file mode 100644
index 0000000..a401d22
--- /dev/null
+++ b/gd/l2cap/internal/receiver_test.cc
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "l2cap/internal/receiver.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <future>
+
+#include "l2cap/internal/channel_impl_mock.h"
+#include "l2cap/l2cap_packets.h"
+#include "os/handler.h"
+#include "os/queue.h"
+#include "os/thread.h"
+#include "packet/raw_builder.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+namespace {}  // namespace
+}  // namespace internal
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/internal/scheduler.h b/gd/l2cap/internal/scheduler.h
new file mode 100644
index 0000000..416c972
--- /dev/null
+++ b/gd/l2cap/internal/scheduler.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+
+#include "common/bidi_queue.h"
+#include "l2cap/cid.h"
+#include "l2cap/classic/dynamic_channel_configuration_option.h"
+#include "l2cap/internal/channel_impl.h"
+#include "l2cap/internal/data_controller.h"
+#include "l2cap/internal/sender.h"
+#include "l2cap/l2cap_packets.h"
+#include "packet/base_packet_builder.h"
+#include "packet/packet_view.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+
+/**
+ * Handle the scheduling of packets through the l2cap stack.
+ * For each attached channel, dequeue its outgoing packets and enqueue it to the given LinkQueueUpEnd, according to some
+ * policy (cid).
+ *
+ * Note: If a channel cannot dequeue from ChannelQueueDownEnd so that the buffer for incoming packet is full, further
+ * incoming packets will be dropped.
+ */
+class Scheduler {
+ public:
+  using UpperEnqueue = packet::PacketView<packet::kLittleEndian>;
+  using UpperDequeue = packet::BasePacketBuilder;
+  using UpperQueueDownEnd = common::BidiQueueEnd<UpperEnqueue, UpperDequeue>;
+  using LowerEnqueue = UpperDequeue;
+  using LowerDequeue = UpperEnqueue;
+  using LowerQueueUpEnd = common::BidiQueueEnd<LowerEnqueue, LowerDequeue>;
+
+  /**
+   * Callback from the sender to indicate that the scheduler could dequeue number_packets from it
+   */
+  virtual void OnPacketsReady(Cid cid, int number_packets) {}
+
+  virtual ~Scheduler() = default;
+};
+
+}  // namespace internal
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/internal/scheduler_fifo.cc b/gd/l2cap/internal/scheduler_fifo.cc
new file mode 100644
index 0000000..8d2a64a
--- /dev/null
+++ b/gd/l2cap/internal/scheduler_fifo.cc
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "l2cap/internal/scheduler_fifo.h"
+
+#include "dynamic_channel_impl.h"
+#include "l2cap/internal/data_pipeline_manager.h"
+#include "l2cap/l2cap_packets.h"
+#include "os/log.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+
+Fifo::Fifo(DataPipelineManager* data_pipeline_manager, LowerQueueUpEnd* link_queue_up_end, os::Handler* handler)
+    : data_pipeline_manager_(data_pipeline_manager), link_queue_up_end_(link_queue_up_end), handler_(handler) {
+  ASSERT(link_queue_up_end_ != nullptr && handler_ != nullptr);
+}
+
+Fifo::~Fifo() {
+  if (link_queue_enqueue_registered_) {
+    link_queue_up_end_->UnregisterEnqueue();
+  }
+}
+
+void Fifo::OnPacketsReady(Cid cid, int number_packets) {
+  if (number_packets == 0) {
+    return;
+  }
+  next_to_dequeue_and_num_packets.push(std::make_pair(cid, number_packets));
+  try_register_link_queue_enqueue();
+}
+
+std::unique_ptr<Fifo::UpperDequeue> Fifo::link_queue_enqueue_callback() {
+  ASSERT(!next_to_dequeue_and_num_packets.empty());
+  auto& channel_id_and_number_packets = next_to_dequeue_and_num_packets.front();
+  auto channel_id = channel_id_and_number_packets.first;
+  channel_id_and_number_packets.second--;
+  if (channel_id_and_number_packets.second == 0) {
+    next_to_dequeue_and_num_packets.pop();
+  }
+  auto packet = data_pipeline_manager_->GetDataController(channel_id)->GetNextPacket();
+
+  data_pipeline_manager_->OnPacketSent(channel_id);
+  if (next_to_dequeue_and_num_packets.empty()) {
+    link_queue_up_end_->UnregisterEnqueue();
+    link_queue_enqueue_registered_ = false;
+  }
+  return packet;
+}
+
+void Fifo::try_register_link_queue_enqueue() {
+  if (link_queue_enqueue_registered_) {
+    return;
+  }
+  link_queue_up_end_->RegisterEnqueue(handler_,
+                                      common::Bind(&Fifo::link_queue_enqueue_callback, common::Unretained(this)));
+  link_queue_enqueue_registered_ = true;
+}
+
+}  // namespace internal
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/internal/scheduler_fifo.h b/gd/l2cap/internal/scheduler_fifo.h
new file mode 100644
index 0000000..f6fcf7f
--- /dev/null
+++ b/gd/l2cap/internal/scheduler_fifo.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+#include <unordered_map>
+
+#include "common/bidi_queue.h"
+#include "common/bind.h"
+#include "l2cap/cid.h"
+#include "l2cap/internal/channel_impl.h"
+#include "l2cap/internal/scheduler.h"
+#include "l2cap/internal/sender.h"
+#include "os/handler.h"
+#include "os/queue.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+class DataPipelineManager;
+
+class Fifo : public Scheduler {
+ public:
+  Fifo(DataPipelineManager* data_pipeline_manager, LowerQueueUpEnd* link_queue_up_end, os::Handler* handler);
+  ~Fifo() override;
+  void OnPacketsReady(Cid cid, int number_packets) override;
+
+ private:
+  DataPipelineManager* data_pipeline_manager_;
+  LowerQueueUpEnd* link_queue_up_end_;
+  os::Handler* handler_;
+  std::queue<std::pair<Cid, int>> next_to_dequeue_and_num_packets;
+  bool link_queue_enqueue_registered_ = false;
+
+  void try_register_link_queue_enqueue();
+  std::unique_ptr<LowerEnqueue> link_queue_enqueue_callback();
+};
+
+}  // namespace internal
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/internal/scheduler_fifo_test.cc b/gd/l2cap/internal/scheduler_fifo_test.cc
new file mode 100644
index 0000000..cafd3b6
--- /dev/null
+++ b/gd/l2cap/internal/scheduler_fifo_test.cc
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "l2cap/internal/scheduler_fifo.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <future>
+
+#include "l2cap/internal/channel_impl_mock.h"
+#include "l2cap/internal/data_controller_mock.h"
+#include "l2cap/internal/data_pipeline_manager_mock.h"
+#include "os/handler.h"
+#include "os/queue.h"
+#include "os/thread.h"
+#include "packet/raw_builder.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+namespace {
+
+using ::testing::_;
+using ::testing::Return;
+
+std::unique_ptr<packet::BasePacketBuilder> CreateSdu(std::vector<uint8_t> payload) {
+  auto raw_builder = std::make_unique<packet::RawBuilder>();
+  raw_builder->AddOctets(payload);
+  return raw_builder;
+}
+
+PacketView<kLittleEndian> GetPacketView(std::unique_ptr<packet::BasePacketBuilder> packet) {
+  auto bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter i(*bytes);
+  bytes->reserve(packet->size());
+  packet->Serialize(i);
+  return packet::PacketView<packet::kLittleEndian>(bytes);
+}
+
+void sync_handler(os::Handler* handler) {
+  std::promise<void> promise;
+  auto future = promise.get_future();
+  handler->Post(common::BindOnce(&std::promise<void>::set_value, common::Unretained(&promise)));
+  auto status = future.wait_for(std::chrono::milliseconds(300));
+  EXPECT_EQ(status, std::future_status::ready);
+}
+
+class MyDataController : public testing::MockDataController {
+ public:
+  std::unique_ptr<BasePacketBuilder> GetNextPacket() override {
+    return std::move(next_packet);
+  }
+
+  std::unique_ptr<BasePacketBuilder> next_packet;
+};
+
+class L2capSchedulerFifoTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    thread_ = new os::Thread("test_thread", os::Thread::Priority::NORMAL);
+    user_handler_ = new os::Handler(thread_);
+    queue_handler_ = new os::Handler(thread_);
+    mock_data_pipeline_manager_ = new testing::MockDataPipelineManager(queue_handler_, link_queue_.GetUpEnd());
+    fifo_ = new Fifo(mock_data_pipeline_manager_, link_queue_.GetUpEnd(), queue_handler_);
+  }
+
+  void TearDown() override {
+    delete fifo_;
+    delete mock_data_pipeline_manager_;
+    queue_handler_->Clear();
+    user_handler_->Clear();
+    delete queue_handler_;
+    delete user_handler_;
+    delete thread_;
+  }
+
+  os::Thread* thread_ = nullptr;
+  os::Handler* user_handler_ = nullptr;
+  os::Handler* queue_handler_ = nullptr;
+  common::BidiQueue<Scheduler::LowerDequeue, Scheduler::LowerEnqueue> link_queue_{10};
+  testing::MockDataPipelineManager* mock_data_pipeline_manager_ = nullptr;
+  MyDataController data_controller_;
+  Fifo* fifo_ = nullptr;
+};
+
+TEST_F(L2capSchedulerFifoTest, send_packet) {
+  auto frame = BasicFrameBuilder::Create(1, CreateSdu({'a', 'b', 'c'}));
+  data_controller_.next_packet = std::move(frame);
+  EXPECT_CALL(*mock_data_pipeline_manager_, GetDataController(_)).WillOnce(Return(&data_controller_));
+  EXPECT_CALL(*mock_data_pipeline_manager_, OnPacketSent(1));
+  fifo_->OnPacketsReady(1, 1);
+  sync_handler(queue_handler_);
+  sync_handler(user_handler_);
+  auto packet = link_queue_.GetDownEnd()->TryDequeue();
+  auto packet_view = GetPacketView(std::move(packet));
+  auto basic_frame_view = BasicFrameView::Create(packet_view);
+  EXPECT_TRUE(basic_frame_view.IsValid());
+  EXPECT_EQ(basic_frame_view.GetChannelId(), 1);
+  auto payload = basic_frame_view.GetPayload();
+  EXPECT_EQ(std::string(payload.begin(), payload.end()), "abc");
+}
+
+}  // namespace
+}  // namespace internal
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/internal/scheduler_mock.h b/gd/l2cap/internal/scheduler_mock.h
new file mode 100644
index 0000000..c0ac4d7
--- /dev/null
+++ b/gd/l2cap/internal/scheduler_mock.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include "l2cap/internal/channel_impl.h"
+#include "l2cap/internal/scheduler.h"
+
+#include <gmock/gmock.h>
+
+// Unit test interfaces
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+namespace testing {
+
+class MockScheduler : public Scheduler {
+ public:
+  MOCK_METHOD(void, OnPacketsReady, (Cid cid, int number_packet), (override));
+};
+
+}  // namespace testing
+}  // namespace internal
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/internal/sender.cc b/gd/l2cap/internal/sender.cc
new file mode 100644
index 0000000..c031cb5
--- /dev/null
+++ b/gd/l2cap/internal/sender.cc
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <unordered_map>
+
+#include "common/bind.h"
+#include "l2cap/internal/basic_mode_channel_data_controller.h"
+#include "l2cap/internal/enhanced_retransmission_mode_channel_data_controller.h"
+#include "l2cap/internal/le_credit_based_channel_data_controller.h"
+#include "l2cap/internal/scheduler.h"
+#include "l2cap/internal/sender.h"
+#include "os/handler.h"
+#include "os/log.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+
+Sender::Sender(os::Handler* handler, ILink* link, Scheduler* scheduler, std::shared_ptr<ChannelImpl> channel)
+    : handler_(handler), link_(link), queue_end_(channel->GetQueueDownEnd()), scheduler_(scheduler),
+      channel_id_(channel->GetCid()), remote_channel_id_(channel->GetRemoteCid()),
+      data_controller_(std::make_unique<BasicModeDataController>(channel_id_, remote_channel_id_, queue_end_, handler_,
+                                                                 scheduler_)) {
+  try_register_dequeue();
+}
+
+Sender::Sender(os::Handler* handler, ILink* link, Scheduler* scheduler, std::shared_ptr<ChannelImpl> channel,
+               ChannelMode mode)
+    : handler_(handler), link_(link), queue_end_(channel->GetQueueDownEnd()), scheduler_(scheduler),
+      channel_id_(channel->GetCid()), remote_channel_id_(channel->GetRemoteCid()) {
+  if (mode == ChannelMode::BASIC) {
+    data_controller_ =
+        std::make_unique<BasicModeDataController>(channel_id_, remote_channel_id_, queue_end_, handler_, scheduler_);
+  } else if (mode == ChannelMode::ERTM) {
+    data_controller_ =
+        std::make_unique<ErtmController>(link_, channel_id_, remote_channel_id_, queue_end_, handler_, scheduler_);
+  } else if (mode == ChannelMode::LE_CREDIT_BASED) {
+    data_controller_ = std::make_unique<LeCreditBasedDataController>(link_, channel_id_, remote_channel_id_, queue_end_,
+                                                                     handler_, scheduler_);
+  }
+  try_register_dequeue();
+}
+
+Sender::~Sender() {
+  if (is_dequeue_registered_) {
+    queue_end_->UnregisterDequeue();
+  }
+}
+
+void Sender::OnPacketSent() {
+  try_register_dequeue();
+}
+
+std::unique_ptr<Sender::UpperDequeue> Sender::GetNextPacket() {
+  return data_controller_->GetNextPacket();
+}
+
+DataController* Sender::GetDataController() {
+  return data_controller_.get();
+}
+
+void Sender::try_register_dequeue() {
+  if (is_dequeue_registered_) {
+    return;
+  }
+  queue_end_->RegisterDequeue(handler_, common::Bind(&Sender::dequeue_callback, common::Unretained(this)));
+  is_dequeue_registered_ = true;
+}
+
+void Sender::dequeue_callback() {
+  auto packet = queue_end_->TryDequeue();
+  ASSERT(packet != nullptr);
+  data_controller_->OnSdu(std::move(packet));
+  queue_end_->UnregisterDequeue();
+  is_dequeue_registered_ = false;
+}
+
+void Sender::UpdateClassicConfiguration(classic::internal::ChannelConfigurationState config) {
+  auto mode = config.retransmission_and_flow_control_mode_;
+  if (mode == mode_) {
+    return;
+  }
+  if (mode == RetransmissionAndFlowControlModeOption::L2CAP_BASIC) {
+    data_controller_ =
+        std::make_unique<BasicModeDataController>(channel_id_, remote_channel_id_, queue_end_, handler_, scheduler_);
+    return;
+  }
+  if (mode == RetransmissionAndFlowControlModeOption::ENHANCED_RETRANSMISSION) {
+    data_controller_ =
+        std::make_unique<ErtmController>(link_, channel_id_, remote_channel_id_, queue_end_, handler_, scheduler_);
+    RetransmissionAndFlowControlConfigurationOption option = config.local_retransmission_and_flow_control_;
+    option.tx_window_size_ = config.remote_retransmission_and_flow_control_.tx_window_size_;
+    data_controller_->SetRetransmissionAndFlowControlOptions(option);
+    data_controller_->EnableFcs(config.fcs_type_ == FcsType::DEFAULT);
+    return;
+  }
+}
+
+}  // namespace internal
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/internal/sender.h b/gd/l2cap/internal/sender.h
new file mode 100644
index 0000000..540064f
--- /dev/null
+++ b/gd/l2cap/internal/sender.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+#include <unordered_map>
+
+#include "common/bidi_queue.h"
+#include "common/bind.h"
+#include "data_controller.h"
+#include "l2cap/cid.h"
+#include "l2cap/classic/internal/channel_configuration_state.h"
+#include "l2cap/internal/channel_impl.h"
+#include "l2cap/internal/data_controller.h"
+#include "l2cap/l2cap_packets.h"
+#include "l2cap/mtu.h"
+#include "os/handler.h"
+#include "os/queue.h"
+#include "packet/base_packet_builder.h"
+#include "packet/packet_view.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+class Scheduler;
+class ILink;
+
+/**
+ * A middle layer between L2CAP channel and outgoing packet scheduler.
+ * Fetches data (SDU) from an L2CAP channel queue end, handles L2CAP segmentation, and gives data to L2CAP scheduler.
+ */
+class Sender {
+ public:
+  using UpperEnqueue = packet::PacketView<packet::kLittleEndian>;
+  using UpperDequeue = packet::BasePacketBuilder;
+  using UpperQueueDownEnd = common::BidiQueueEnd<UpperEnqueue, UpperDequeue>;
+
+  enum class ChannelMode {
+    BASIC = 0,
+    ERTM = 3,
+    LE_CREDIT_BASED = 10,
+  };
+
+  Sender(os::Handler* handler, ILink* link, Scheduler* scheduler, std::shared_ptr<ChannelImpl> channel);
+  Sender(os::Handler* handler, ILink* link, Scheduler* scheduler, std::shared_ptr<ChannelImpl> channel,
+         ChannelMode mode);
+  ~Sender();
+
+  /**
+   * Callback from scheduler to indicate that scheduler already dequeued a packet from sender's queue.
+   * Segmenter can continue dequeuing from channel queue end.
+   */
+  void OnPacketSent();
+
+  /**
+   * Called by the scheduler to return the next PDU to be sent
+   */
+  std::unique_ptr<UpperDequeue> GetNextPacket();
+
+  void UpdateClassicConfiguration(classic::internal::ChannelConfigurationState config);
+  DataController* GetDataController();
+
+ private:
+  os::Handler* handler_;
+  ILink* link_;
+  UpperQueueDownEnd* queue_end_;
+  Scheduler* scheduler_;
+  const Cid channel_id_;
+  const Cid remote_channel_id_;
+  bool is_dequeue_registered_ = false;
+  RetransmissionAndFlowControlModeOption mode_ = RetransmissionAndFlowControlModeOption::L2CAP_BASIC;
+  std::unique_ptr<DataController> data_controller_;
+
+  void try_register_dequeue();
+  void dequeue_callback();
+};
+}  // namespace internal
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/internal/sender_test.cc b/gd/l2cap/internal/sender_test.cc
new file mode 100644
index 0000000..bd24fd4
--- /dev/null
+++ b/gd/l2cap/internal/sender_test.cc
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "l2cap/internal/sender.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <future>
+
+#include "l2cap/internal/channel_impl_mock.h"
+#include "l2cap/internal/scheduler.h"
+#include "os/handler.h"
+#include "os/queue.h"
+#include "os/thread.h"
+#include "packet/raw_builder.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+namespace {
+
+using ::testing::Return;
+
+std::unique_ptr<packet::BasePacketBuilder> CreateSdu(std::vector<uint8_t> payload) {
+  auto raw_builder = std::make_unique<packet::RawBuilder>();
+  raw_builder->AddOctets(payload);
+  return raw_builder;
+}
+
+PacketView<kLittleEndian> GetPacketView(std::unique_ptr<packet::BasePacketBuilder> packet) {
+  auto bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter i(*bytes);
+  bytes->reserve(packet->size());
+  packet->Serialize(i);
+  return packet::PacketView<packet::kLittleEndian>(bytes);
+}
+
+class FakeScheduler : public Scheduler {
+ public:
+  void OnPacketsReady(Cid cid, int number_packets) override {
+    on_packets_ready_(cid, number_packets);
+  }
+
+  void SetOnPacketsReady(std::function<void(Cid cid, int number_packets)> callback) {
+    on_packets_ready_ = callback;
+  }
+  std::function<void(Cid cid, int number_packets)> on_packets_ready_;
+};
+
+class L2capSenderTest : public ::testing::Test {
+ public:
+  std::unique_ptr<Sender::UpperDequeue> enqueue_callback() {
+    auto packet_one = CreateSdu({'a', 'b', 'c'});
+    channel_queue_.GetUpEnd()->UnregisterEnqueue();
+    return packet_one;
+  }
+
+ protected:
+  void SetUp() override {
+    thread_ = new os::Thread("test_thread", os::Thread::Priority::NORMAL);
+    user_handler_ = new os::Handler(thread_);
+    queue_handler_ = new os::Handler(thread_);
+    mock_channel_ = std::make_shared<testing::MockChannelImpl>();
+    EXPECT_CALL(*mock_channel_, GetQueueDownEnd()).WillRepeatedly(Return(channel_queue_.GetDownEnd()));
+    EXPECT_CALL(*mock_channel_, GetCid()).WillRepeatedly(Return(cid_));
+    EXPECT_CALL(*mock_channel_, GetRemoteCid()).WillRepeatedly(Return(cid_));
+    sender_ = new Sender(queue_handler_, nullptr, &scheduler_, mock_channel_);
+  }
+
+  void TearDown() override {
+    queue_handler_->Clear();
+    user_handler_->Clear();
+    delete sender_;
+    delete queue_handler_;
+    delete user_handler_;
+    delete thread_;
+  }
+
+  os::Thread* thread_ = nullptr;
+  os::Handler* user_handler_ = nullptr;
+  os::Handler* queue_handler_ = nullptr;
+  common::BidiQueue<Sender::UpperEnqueue, Sender::UpperDequeue> channel_queue_{10};
+  std::shared_ptr<testing::MockChannelImpl> mock_channel_;
+  Sender* sender_ = nullptr;
+  Cid cid_ = 0x41;
+  FakeScheduler scheduler_;
+};
+
+TEST_F(L2capSenderTest, send_packet) {
+  std::promise<void> promise;
+  auto future = promise.get_future();
+  scheduler_.SetOnPacketsReady([&promise](Cid cid, int number_packets) { promise.set_value(); });
+  channel_queue_.GetUpEnd()->RegisterEnqueue(
+      queue_handler_, common::Bind(&L2capSenderTest::enqueue_callback, common::Unretained(this)));
+  auto status = future.wait_for(std::chrono::milliseconds(3));
+  EXPECT_EQ(status, std::future_status::ready);
+  auto packet = sender_->GetNextPacket();
+  EXPECT_NE(packet, nullptr);
+  auto packet_view = GetPacketView(std::move(packet));
+  auto basic_frame_view = BasicFrameView::Create(packet_view);
+  EXPECT_TRUE(basic_frame_view.IsValid());
+  EXPECT_EQ(basic_frame_view.GetChannelId(), cid_);
+  auto payload = basic_frame_view.GetPayload();
+  std::string payload_string(payload.begin(), payload.end());
+  EXPECT_EQ(payload_string, "abc");
+}
+
+}  // namespace
+}  // namespace internal
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/l2cap_packet_fuzz_test.cc b/gd/l2cap/l2cap_packet_fuzz_test.cc
new file mode 100644
index 0000000..6fecafc
--- /dev/null
+++ b/gd/l2cap/l2cap_packet_fuzz_test.cc
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define PACKET_FUZZ_TESTING
+#include "l2cap/l2cap_packets.h"
+
+#include <memory>
+
+#include "os/log.h"
+#include "packet/bit_inserter.h"
+#include "packet/raw_builder.h"
+
+using bluetooth::packet::BitInserter;
+using bluetooth::packet::RawBuilder;
+using std::vector;
+
+namespace bluetooth {
+namespace l2cap {
+
+std::vector<void (*)(const uint8_t*, size_t)> l2cap_packet_fuzz_tests;
+
+DEFINE_AND_REGISTER_ExtendedInformationStartFrameReflectionFuzzTest(l2cap_packet_fuzz_tests);
+
+DEFINE_AND_REGISTER_StandardInformationFrameWithFcsReflectionFuzzTest(l2cap_packet_fuzz_tests);
+
+DEFINE_AND_REGISTER_StandardSupervisoryFrameWithFcsReflectionFuzzTest(l2cap_packet_fuzz_tests);
+
+DEFINE_AND_REGISTER_GroupFrameReflectionFuzzTest(l2cap_packet_fuzz_tests);
+
+DEFINE_AND_REGISTER_ConfigurationRequestReflectionFuzzTest(l2cap_packet_fuzz_tests);
+
+}  // namespace l2cap
+}  // namespace bluetooth
+
+void RunL2capPacketFuzzTest(const uint8_t* data, size_t size) {
+  if (data == nullptr) return;
+  for (auto test_function : bluetooth::l2cap::l2cap_packet_fuzz_tests) {
+    test_function(data, size);
+  }
+}
\ No newline at end of file
diff --git a/gd/l2cap/l2cap_packet_test.cc b/gd/l2cap/l2cap_packet_test.cc
new file mode 100644
index 0000000..34645ef
--- /dev/null
+++ b/gd/l2cap/l2cap_packet_test.cc
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define PACKET_TESTING
+#include "l2cap/l2cap_packets.h"
+
+#include <gtest/gtest.h>
+#include <forward_list>
+#include <memory>
+
+#include "os/log.h"
+#include "packet/bit_inserter.h"
+#include "packet/raw_builder.h"
+
+using bluetooth::packet::BitInserter;
+using bluetooth::packet::RawBuilder;
+using std::vector;
+
+namespace bluetooth {
+namespace l2cap {
+
+std::vector<uint8_t> extended_information_start_frame = {
+    0x0B, /* First size byte */
+    0x00, /* Second size byte */
+    0xc1, /* First ChannelId byte */
+    0xc2, /**/
+    0x4A, /* 0x12 ReqSeq, Final, IFrame */
+    0xD0, /* 0x13 ReqSeq */
+    0x89, /* 0x21 TxSeq sar = START */
+    0x8C, /* 0x23 TxSeq  */
+    0x10, /* first length byte */
+    0x11, /**/
+    0x01, /* first payload byte */
+    0x02, 0x03, 0x04, 0x05,
+};
+
+DEFINE_AND_INSTANTIATE_ExtendedInformationStartFrameReflectionTest(extended_information_start_frame);
+
+std::vector<uint8_t> i_frame_with_fcs = {0x0E, 0x00, 0x40, 0x00, 0x02, 0x00, 0x00, 0x01, 0x02,
+                                         0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x38, 0x61};
+DEFINE_AND_INSTANTIATE_StandardInformationFrameWithFcsReflectionTest(i_frame_with_fcs);
+
+std::vector<uint8_t> rr_frame_with_fcs = {0x04, 0x00, 0x40, 0x00, 0x01, 0x01, 0xD4, 0x14};
+DEFINE_AND_INSTANTIATE_StandardSupervisoryFrameWithFcsReflectionTest(rr_frame_with_fcs);
+
+std::vector<uint8_t> g_frame = {0x03, 0x00, 0x02, 0x00, 0x01, 0x02, 0x03};
+DEFINE_AND_INSTANTIATE_GroupFrameReflectionTest(g_frame);
+
+std::vector<uint8_t> config_mtu_request = {0x04, 0x05, 0x08, 0x00, 0x41, 0x00, 0x00, 0x00, 0x01, 0x02, 0xa0, 0x02};
+DEFINE_AND_INSTANTIATE_ConfigurationRequestReflectionTest(config_mtu_request);
+
+DEFINE_ConfigurationRequestReflectionFuzzTest();
+
+TEST(L2capFuzzRegressions, ConfigurationRequestFuzz_5691566077247488) {
+  uint8_t bluetooth_gd_fuzz_test_5691566077247488[] = {
+      0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  };
+  RunConfigurationRequestReflectionFuzzTest(bluetooth_gd_fuzz_test_5691566077247488,
+                                            sizeof(bluetooth_gd_fuzz_test_5691566077247488));
+}
+
+TEST(L2capFuzzRegressions, ConfigurationRequestFuzz_5747922062802944) {
+  uint8_t bluetooth_gd_fuzz_test_5747922062802944[] = {
+      0x04, 0x02, 0x02, 0x7f, 0x3f, 0x7f, 0x3f, 0x7e, 0x7f,
+  };
+  RunConfigurationRequestReflectionFuzzTest(bluetooth_gd_fuzz_test_5747922062802944,
+                                            sizeof(bluetooth_gd_fuzz_test_5747922062802944));
+}
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/l2cap_packets.pdl b/gd/l2cap/l2cap_packets.pdl
new file mode 100644
index 0000000..ae44cb1
--- /dev/null
+++ b/gd/l2cap/l2cap_packets.pdl
@@ -0,0 +1,631 @@
+little_endian_packets
+
+checksum Fcs : 16 "l2cap/"
+
+packet BasicFrame {
+  _size_(_payload_) : 16,
+  channel_id : 16,
+  _payload_,
+}
+
+packet BasicFrameWithFcs {
+  _checksum_start_(fcs),
+  _size_(_payload_) : 16,
+  channel_id : 16,
+  _payload_ : [+2*8], // Include Fcs in the _size_
+  fcs : Fcs,
+}
+
+enum Continuation : 1 {
+  END = 0,
+  CONTINUE = 1,
+}
+
+// ChannelId 2 is connectionless
+packet GroupFrame : BasicFrame (channel_id = 0x02) {
+  psm : 16,
+  _payload_,
+}
+
+enum FrameType : 1 {
+  I_FRAME = 0,
+  S_FRAME = 1,
+}
+
+enum SupervisoryFunction : 2 {
+  RECEIVER_READY = 0,
+  REJECT = 1,
+  RECEIVER_NOT_READY = 2,
+  SELECT_REJECT = 3,
+}
+
+enum RetransmissionDisable : 1 {
+  NORMAL = 0,
+  DISABLE = 1,
+}
+
+enum SegmentationAndReassembly : 2 {
+  UNSEGMENTED = 0,
+  START = 1,
+  END = 2,
+  CONTINUATION = 3,
+}
+
+packet StandardFrame : BasicFrame {
+  frame_type : FrameType,
+  _body_,
+}
+
+packet StandardFrameWithFcs : BasicFrameWithFcs {
+  frame_type : FrameType,
+  _body_,
+}
+
+group StandardSupervisoryControl {
+  _fixed_ = 0 : 1,
+  s : SupervisoryFunction,
+  _reserved_ : 3,
+  r : RetransmissionDisable,
+  req_seq : 6,
+  _reserved_ : 2,
+}
+
+group StandardInformationControl {
+  tx_seq : 6,
+  r : RetransmissionDisable,
+  req_seq : 6,
+  sar : SegmentationAndReassembly,
+}
+
+packet StandardSupervisoryFrame : StandardFrame (frame_type = S_FRAME) {
+  StandardSupervisoryControl,
+}
+
+packet StandardSupervisoryFrameWithFcs : StandardFrameWithFcs (frame_type = S_FRAME) {
+  StandardSupervisoryControl,
+}
+
+packet StandardInformationFrame : StandardFrame (frame_type = I_FRAME) {
+  StandardInformationControl,
+  _payload_,
+}
+
+packet StandardInformationFrameWithFcs : StandardFrameWithFcs (frame_type = I_FRAME) {
+  StandardInformationControl,
+  _payload_,
+}
+
+packet StandardInformationStartFrame : StandardInformationFrame (sar = START) {
+  l2cap_sdu_length : 16,
+  _payload_,
+}
+
+packet StandardInformationStartFrameWithFcs : StandardInformationFrameWithFcs (sar = START) {
+  l2cap_sdu_length : 16,
+  _payload_,
+}
+
+enum Poll : 1 {
+  NOT_SET = 0,
+  POLL = 1,
+}
+
+enum Final : 1 {
+  NOT_SET = 0,
+  POLL_RESPONSE = 1,
+}
+
+group EnhancedSupervisoryControl {
+  _fixed_ = 0 : 1,
+  s : SupervisoryFunction,
+  p : Poll,
+  _reserved_ : 2,
+  f : Final,
+  req_seq : 6,
+  _reserved_ : 2,
+}
+
+group EnhancedInformationControl {
+  tx_seq : 6,
+  f : Final,
+  req_seq : 6,
+  sar : SegmentationAndReassembly,
+}
+
+packet EnhancedSupervisoryFrame : StandardFrame (frame_type = S_FRAME) {
+  EnhancedSupervisoryControl,
+}
+
+packet EnhancedSupervisoryFrameWithFcs : StandardFrameWithFcs (frame_type = S_FRAME) {
+  EnhancedSupervisoryControl,
+}
+
+packet EnhancedInformationFrame : StandardFrame (frame_type = I_FRAME) {
+  EnhancedInformationControl,
+  _payload_,
+}
+
+packet EnhancedInformationFrameWithFcs : StandardFrameWithFcs (frame_type = I_FRAME) {
+  EnhancedInformationControl,
+  _payload_,
+}
+
+packet EnhancedInformationStartFrame : EnhancedInformationFrame (sar = START) {
+  l2cap_sdu_length : 16,
+  _payload_,
+}
+
+packet EnhancedInformationStartFrameWithFcs : EnhancedInformationFrameWithFcs (sar = START) {
+  l2cap_sdu_length : 16,
+  _payload_,
+}
+
+group ExtendedSupervisoryControl {
+  f : Final,
+  req_seq : 14,
+  s : SupervisoryFunction,
+  p : Poll,
+  _reserved_ : 5,
+  _reserved_ : 8,
+}
+
+group ExtendedInformationControl {
+  f : Final,
+  req_seq : 14,
+  sar : SegmentationAndReassembly,
+  tx_seq : 14,
+}
+
+packet ExtendedSupervisoryFrame : StandardFrame (frame_type = S_FRAME) {
+  ExtendedSupervisoryControl,
+}
+
+packet ExtendedSupervisoryFrameWithFcs : StandardFrameWithFcs (frame_type = S_FRAME) {
+  ExtendedSupervisoryControl,
+}
+
+packet ExtendedInformationFrame : StandardFrame (frame_type = I_FRAME) {
+  ExtendedInformationControl,
+  _payload_,
+}
+
+packet ExtendedInformationFrameWithFcs : StandardFrameWithFcs (frame_type = I_FRAME) {
+  ExtendedInformationControl,
+  _payload_,
+}
+
+packet ExtendedInformationStartFrame : ExtendedInformationFrame (sar = START) {
+  l2cap_sdu_length : 16,
+  _payload_,
+}
+
+packet ExtendedInformationStartFrameWithFcs : ExtendedInformationFrameWithFcs (sar = START) {
+  l2cap_sdu_length : 16,
+  _payload_,
+}
+
+packet FirstLeInformationFrame : BasicFrame {
+  l2cap_sdu_length : 16,
+  _payload_,
+}
+
+enum CommandCode : 8 {
+  COMMAND_REJECT = 0x01,
+  CONNECTION_REQUEST = 0x02,
+  CONNECTION_RESPONSE = 0x03,
+  CONFIGURATION_REQUEST = 0x04,
+  CONFIGURATION_RESPONSE = 0x05,
+  DISCONNECTION_REQUEST = 0x06,
+  DISCONNECTION_RESPONSE = 0x07,
+  ECHO_REQUEST = 0x08,
+  ECHO_RESPONSE = 0x09,
+  INFORMATION_REQUEST = 0x0A,
+  INFORMATION_RESPONSE = 0x0B,
+  CREATE_CHANNEL_REQUEST = 0x0C,
+  CREATE_CHANNEL_RESPONSE = 0x0D,
+  MOVE_CHANNEL_REQUEST = 0x0E,
+  MOVE_CHANNEL_RESPONSE = 0x0F,
+  MOVE_CHANNEL_CONFIRMATION_REQUEST = 0x10,
+  MOVE_CHANNEL_CONFIRMATION_RESPONSE = 0x11,
+}
+
+packet ControlFrame : BasicFrame (channel_id = 0x0001) {
+  _payload_,
+}
+
+packet Control {
+  code : CommandCode,
+  identifier : 8,
+  _size_(_payload_) : 16,
+  _payload_,
+}
+
+enum CommandRejectReason : 16 {
+  COMMAND_NOT_UNDERSTOOD = 0x0000,
+  SIGNALING_MTU_EXCEEDED = 0x0001,
+  INVALID_CID_IN_REQUEST = 0x0002,
+}
+
+packet CommandReject : Control (code = COMMAND_REJECT) {
+  reason : CommandRejectReason,
+  _body_,
+}
+
+packet CommandRejectNotUnderstood : CommandReject (reason = COMMAND_NOT_UNDERSTOOD) {
+}
+
+packet CommandRejectMtuExceeded : CommandReject (reason = SIGNALING_MTU_EXCEEDED) {
+  actual_mtu : 16,
+}
+
+packet CommandRejectInvalidCid : CommandReject (reason = INVALID_CID_IN_REQUEST) {
+  local_channel : 16, // Relative to the sender of the CommandReject
+  remote_channel : 16,
+}
+
+packet ConnectionRequest : Control (code = CONNECTION_REQUEST) {
+  psm : 16,
+  source_cid : 16,
+}
+
+enum ConnectionResponseResult : 16 {
+  SUCCESS = 0x0000,
+  PENDING = 0x0001,
+  PSM_NOT_SUPPORTED = 0x0002,
+  SECURITY_BLOCK = 0x0003,
+  NO_RESOURCES_AVAILABLE = 0x0004,
+  INVALID_CID = 0x0006,
+  SOURCE_CID_ALREADY_ALLOCATED = 0x0007,
+}
+
+enum ConnectionResponseStatus : 16 {
+  NO_FURTHER_INFORMATION_AVAILABLE = 0x0000,
+  AUTHENTICATION_PENDING = 0x0001,
+  AUTHORIZATION_PENDING = 0x0002,
+}
+
+packet ConnectionResponse : Control (code = CONNECTION_RESPONSE) {
+  destination_cid : 16,
+  source_cid : 16,
+  result : ConnectionResponseResult,
+  status : ConnectionResponseStatus,
+}
+
+enum ConfigurationOptionType : 7 {
+  MTU = 0x01,
+  FLUSH_TIMEOUT = 0x02,
+  QUALITY_OF_SERVICE = 0x03,
+  RETRANSMISSION_AND_FLOW_CONTROL = 0x04,
+  FRAME_CHECK_SEQUENCE = 0x05,
+  EXTENDED_FLOW_SPECIFICATION = 0x06,
+  EXTENDED_WINDOW_SIZE = 0x07,
+}
+
+enum ConfigurationOptionIsHint : 1 {
+  OPTION_MUST_BE_RECOGNIZED = 0,
+  OPTION_IS_A_HINT = 1,
+}
+
+struct ConfigurationOption {
+  type : ConfigurationOptionType,
+  is_hint : ConfigurationOptionIsHint,
+  length : 8,
+  _body_,
+}
+
+struct MtuConfigurationOption : ConfigurationOption (type = MTU, length = 2) {
+  mtu : 16,
+}
+
+struct FlushTimeoutConfigurationOption : ConfigurationOption (type = FLUSH_TIMEOUT, length = 2) {
+  flush_timeout : 16,
+}
+
+enum QosServiceType : 8 {
+  NO_TRAFFIC = 0x00,
+  BEST_EFFORT = 0x01, // Default
+  GUARANTEED = 0x02,
+}
+
+struct QualityOfServiceConfigurationOption : ConfigurationOption (type = QUALITY_OF_SERVICE, length = 22) {
+  _reserved_ : 8, // Flags
+  service_type : QosServiceType,
+  token_rate : 32,         // 0 = ignore, 0xffffffff = max available
+  token_bucket_size : 32,  // 0 = ignore, 0xffffffff = max available
+  peak_bandwidth : 32,     // Octets/second 0 = ignore
+  latency : 32,            // microseconds 0xffffffff = ignore
+  delay_variation : 32,    // microseconds 0xffffffff = ignore
+}
+
+enum RetransmissionAndFlowControlModeOption : 8 {
+  L2CAP_BASIC = 0x00,
+  RETRANSMISSION = 0x01,
+  FLOW_CONTROL = 0x02,
+  ENHANCED_RETRANSMISSION = 0x03,
+  STREAMING = 0x04,
+}
+
+
+struct RetransmissionAndFlowControlConfigurationOption : ConfigurationOption (type = RETRANSMISSION_AND_FLOW_CONTROL, length = 9) {
+  mode : RetransmissionAndFlowControlModeOption,
+  tx_window_size : 8, // 1-32 for Flow Control and Retransmission, 1-63 for Enhanced
+  max_transmit : 8,
+  retransmission_time_out : 16,
+  monitor_time_out : 16,
+  maximum_pdu_size : 16,
+}
+
+enum FcsType : 8 {
+  NO_FCS = 0,
+  DEFAULT = 1,  // 16-bit FCS
+}
+
+struct FrameCheckSequenceOption : ConfigurationOption (type = FRAME_CHECK_SEQUENCE, length = 1) {
+  fcs_type : FcsType,
+}
+
+
+struct ExtendedFlowSpecificationOption : ConfigurationOption (type = EXTENDED_FLOW_SPECIFICATION, length = 16) {
+  identifier : 8, // Default 0x01, must be 0x01 for Extended Flow-Best-Effort
+  service_type : QosServiceType,
+  maximum_sdu_size : 16, // Octets
+  sdu_interarrival_time : 32, // in microseconds
+  access_latency : 32, // in microseconds, without HCI and stack overheads
+  flush_timeout : 32, // in microseconds 0x0 = no retransmissions 0xFFFFFFFF = never flushed
+}
+
+struct ExtendedWindowSizeOption : ConfigurationOption (type = EXTENDED_WINDOW_SIZE, length = 2) {
+  max_window_size : 16, // 0x0000 = Valid for streaming, 0x0001-0x3FFF Valid for Enhanced Retransmission
+}
+
+packet ConfigurationRequest : Control (code = CONFIGURATION_REQUEST) {
+  destination_cid : 16,
+  continuation : Continuation,
+  _reserved_ : 15,
+  config : ConfigurationOption[],
+}
+
+enum ConfigurationResponseResult : 16 {
+  SUCCESS = 0x0000,
+  UNACCEPTABLE_PARAMETERS = 0x0001,
+  REJECTED = 0x0002,
+  UNKNOWN_OPTIONS = 0x0003,
+  PENDING = 0x0004,
+  FLOW_SPEC_REJECTED = 0x0005,
+}
+
+packet ConfigurationResponse : Control (code = CONFIGURATION_RESPONSE) {
+  source_cid : 16,
+  continuation : Continuation,
+  _reserved_ : 15,
+  result : ConfigurationResponseResult,
+  config : ConfigurationOption[],
+}
+
+packet DisconnectionRequest : Control (code = DISCONNECTION_REQUEST) {
+  destination_cid : 16,
+  source_cid : 16,
+}
+
+packet DisconnectionResponse : Control (code = DISCONNECTION_RESPONSE) {
+  destination_cid : 16,
+  source_cid : 16,
+}
+
+packet EchoRequest : Control (code = ECHO_REQUEST) {
+  _payload_, // Optional and implementation specific
+}
+
+packet EchoResponse : Control (code = ECHO_RESPONSE) {
+  _payload_, // Optional and implementation specific
+}
+
+enum InformationRequestInfoType : 16 {
+  CONNECTIONLESS_MTU = 0x0001,
+  EXTENDED_FEATURES_SUPPORTED = 0x0002,
+  FIXED_CHANNELS_SUPPORTED = 0x0003,
+}
+
+packet InformationRequest : Control (code = INFORMATION_REQUEST) {
+  info_type : InformationRequestInfoType,
+}
+
+enum InformationRequestResult : 16 {
+  SUCCESS = 0x0000,
+  NOT_SUPPORTED = 0x0001,
+}
+
+packet InformationResponse : Control (code = INFORMATION_RESPONSE) {
+  info_type : InformationRequestInfoType,
+  result : InformationRequestResult,
+  _body_,
+}
+
+packet InformationResponseConnectionlessMtu : InformationResponse (info_type = CONNECTIONLESS_MTU) {
+  connectionless_mtu : 16,
+}
+
+packet InformationResponseExtendedFeatures : InformationResponse (info_type = EXTENDED_FEATURES_SUPPORTED) {
+  // ExtendedFeatureMask : 32,
+  flow_control_mode : 1,
+  retransmission_mode : 1,
+  bi_directional_qoS : 1,
+  enhanced_retransmission_mode : 1,
+  streaming_mode : 1,
+  fcs_option : 1,
+  extended_flow_specification_for_br_edr : 1,
+  fixed_channels : 1,
+  extended_window_size : 1,
+  unicast_connectionless_data_reception : 1,
+  _reserved_ : 22,
+}
+
+packet InformationResponseFixedChannels : InformationResponse (info_type = FIXED_CHANNELS_SUPPORTED) {
+  fixed_channels : 64, // bit 0 must be 0, bit 1 must be 1, all others 1 = supported
+}
+
+packet CreateChannelRequest : Control (code = CREATE_CHANNEL_REQUEST) {
+  psm : 16,
+  source_cid : 16,
+  controller_id : 8,
+}
+
+enum CreateChannelResponseResult : 16 {
+  SUCCESS = 0x0000,
+  PENDING = 0x0001,
+  PSM_NOT_SUPPORTED = 0x0002,
+  SECURITY_BLOCK = 0x0003,
+  NO_RESOURCES_AVAILABLE = 0x0004,
+  CONTROLLER_ID_NOT_SUPPORTED = 0x0005,
+  INVALID_CID = 0x0006,
+  SOURCE_CID_ALREADY_ALLOCATED = 0x0007,
+}
+
+enum CreateChannelResponseStatus : 16 {
+  NO_FURTHER_INFORMATION_AVAILABLE = 0x0000,
+  AUTHENTICATION_PENDING = 0x0001,
+  AUTHORIZATION_PENDING = 0x0002,
+}
+
+packet CreateChannelResponse : Control (code = CREATE_CHANNEL_RESPONSE) {
+  destination_cid : 16,
+  source_cid : 16,
+  result : CreateChannelResponseResult,
+  status : CreateChannelResponseStatus,
+}
+
+// AMP Only ?
+packet MoveChannelRequest : Control (code = MOVE_CHANNEL_REQUEST) {
+  initiator_cid : 16,
+  dest_controller_id : 8,
+}
+
+enum MoveChannelResponseResult : 16 {
+  SUCCESS = 0x0000,
+  PENDING = 0x0001,
+  CONTROLLER_ID_NOT_SUPPORTED = 0x0002,
+  NEW_CONTROLLER_ID_IS_SAME = 0x0003,
+  CONFIGURATION_NOT_SUPPORTED = 0x0004,
+  CHANNEL_COLLISION = 0x0005,
+  CHANNEL_NOT_ALLOWED_TO_BE_MOVED = 0x0006,
+}
+
+packet MoveChannelResponse : Control (code = MOVE_CHANNEL_RESPONSE) {
+  initiator_cid : 16,
+  result : MoveChannelResponseResult,
+}
+
+enum MoveChannelConfirmationResult : 16 {
+  SUCCESS = 0x0000,
+  FAILURE = 0x0001,
+}
+
+packet MoveChannelConfirmationRequest : Control (code = MOVE_CHANNEL_CONFIRMATION_REQUEST) {
+  initiator_cid : 16,
+  result : MoveChannelConfirmationResult,
+}
+
+packet MoveChannelConfirmationResponse : Control (code = MOVE_CHANNEL_CONFIRMATION_RESPONSE) {
+  initiator_cid : 16,
+}
+
+enum LeCommandCode : 8 {
+  COMMAND_REJECT = 0x01,
+  DISCONNECTION_REQUEST = 0x06,
+  DISCONNECTION_RESPONSE = 0x07,
+  CONNECTION_PARAMETER_UPDATE_REQUEST = 0x12,
+  CONNECTION_PARAMETER_UPDATE_RESPONSE = 0x13,
+  LE_CREDIT_BASED_CONNECTION_REQUEST = 0x14,
+  LE_CREDIT_BASED_CONNECTION_RESPONSE = 0x15,
+  LE_FLOW_CONTROL_CREDIT = 0x16,
+}
+
+packet LeControlFrame : BasicFrame (channel_id = 0x0005) {
+  _payload_,
+}
+
+packet LeControl {
+  code : LeCommandCode,
+  identifier : 8, // Must be non-zero
+  _size_(_payload_) : 16,
+  _payload_,
+}
+
+
+packet LeCommandReject : LeControl (code = COMMAND_REJECT) {
+  reason : CommandRejectReason,
+  _payload_,
+}
+
+packet LeCommandRejectNotUnderstood : LeCommandReject (reason = COMMAND_NOT_UNDERSTOOD) {
+}
+
+packet LeCommandRejectMtuExceeded : LeCommandReject (reason = SIGNALING_MTU_EXCEEDED) {
+  actual_mtu : 16,
+}
+
+packet LeCommandRejectInvalidCid : LeCommandReject (reason = INVALID_CID_IN_REQUEST) {
+  local_channel : 16, // Relative to the sender of the CommandReject
+  remote_channel : 16,
+}
+
+packet LeDisconnectionRequest : LeControl (code = DISCONNECTION_REQUEST) {
+  destination_cid : 16,
+  source_cid : 16,
+}
+
+packet LeDisconnectionResponse : LeControl (code = DISCONNECTION_RESPONSE) {
+  destination_cid : 16,
+  source_cid : 16,
+}
+
+packet ConnectionParameterUpdateRequest : LeControl (code = CONNECTION_PARAMETER_UPDATE_REQUEST) {
+  interval_min : 16,
+  interval_max : 16,
+  slave_latency : 16,
+  timeout_multiplier : 16,
+}
+
+enum ConnectionParameterUpdateResponseResult : 16 {
+  ACCEPTED = 0,
+  REJECTED = 1,
+}
+
+packet ConnectionParameterUpdateResponse : LeControl (code = CONNECTION_PARAMETER_UPDATE_RESPONSE) {
+  result : ConnectionParameterUpdateResponseResult,
+}
+
+packet LeCreditBasedConnectionRequest : LeControl (code = LE_CREDIT_BASED_CONNECTION_REQUEST) {
+  le_psm : 16, // 0x0001-0x007F Fixed, 0x0080-0x00FF Dynamic
+  source_cid : 16,
+  mtu : 16,
+  mps : 16,
+  initial_credits : 16,
+}
+
+enum LeCreditBasedConnectionResponseResult : 16 {
+  SUCCESS = 0x0000,
+  LE_PSM_NOT_SUPPORTED = 0x0002,
+  NO_RESOURCES_AVAILABLE = 0x0004,
+  INSUFFICIENT_AUTHENTICATION = 0x0005,
+  INSUFFICIENT_AUTHORIZATION = 0x0006,
+  INSUFFICIENT_ENCRYPTION_KEY_SIZE = 0x0007,
+  INSUFFICIENT_ENCRYPTION = 0x0008,
+  INVALID_SOURCE_CID = 0x0009,
+  SOURCE_CID_ALREADY_ALLOCATED = 0x000A,
+  UNACCEPTABLE_PARAMETERS = 0x000B,
+}
+
+packet LeCreditBasedConnectionResponse : LeControl (code = LE_CREDIT_BASED_CONNECTION_RESPONSE) {
+  destination_cid : 16,
+  mtu : 16,
+  mps : 16,
+  initial_credits : 16,
+  result : LeCreditBasedConnectionResponseResult,
+}
+
+packet LeFlowControlCredit : LeControl (code = LE_FLOW_CONTROL_CREDIT) {
+  cid : 16, // Receiver's destination CID
+  credits : 16,
+}
+
diff --git a/gd/l2cap/le/dynamic_channel.h b/gd/l2cap/le/dynamic_channel.h
new file mode 100644
index 0000000..db1d822
--- /dev/null
+++ b/gd/l2cap/le/dynamic_channel.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "l2cap/dynamic_channel.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace le {
+
+using DynamicChannel = l2cap::DynamicChannel;
+
+}  // namespace le
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/le/dynamic_channel_configuration_option.h b/gd/l2cap/le/dynamic_channel_configuration_option.h
new file mode 100644
index 0000000..a70b60d
--- /dev/null
+++ b/gd/l2cap/le/dynamic_channel_configuration_option.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "l2cap/mtu.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace le {
+
+/**
+ * Configuration Option specified by L2CAP Channel user on a dynamic channel. L2CAP module will configure the channel
+ * based on user provided option.
+ */
+struct DynamicChannelConfigurationOption {
+  /**
+   * Maximum SDU size that the L2CAP Channel user is able to receive or send. When the channel is created, the actual
+   * MTU is the minimum of the suggested MTU between two devices.
+   */
+  Mtu mtu = kDefaultClassicMtu;
+};
+
+}  // namespace le
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/le/dynamic_channel_manager.cc b/gd/l2cap/le/dynamic_channel_manager.cc
new file mode 100644
index 0000000..b8303c1
--- /dev/null
+++ b/gd/l2cap/le/dynamic_channel_manager.cc
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "l2cap/le/dynamic_channel_manager.h"
+#include "l2cap/le/internal/dynamic_channel_service_impl.h"
+#include "l2cap/le/internal/dynamic_channel_service_manager_impl.h"
+#include "l2cap/le/internal/link.h"
+#include "l2cap/le/internal/link_manager.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace le {
+
+bool DynamicChannelManager::ConnectChannel(hci::AddressWithType device,
+                                           DynamicChannelConfigurationOption configuration_option, Psm psm,
+                                           OnConnectionOpenCallback on_connection_open,
+                                           OnConnectionFailureCallback on_fail_callback, os::Handler* handler) {
+  internal::Link::PendingDynamicChannelConnection pending_dynamic_channel_connection{
+      .handler_ = handler,
+      .on_open_callback_ = std::move(on_connection_open),
+      .on_fail_callback_ = std::move(on_fail_callback),
+      .configuration_ = configuration_option,
+  };
+  l2cap_layer_handler_->Post(common::BindOnce(&internal::LinkManager::ConnectDynamicChannelServices,
+                                              common::Unretained(link_manager_), device,
+                                              std::move(pending_dynamic_channel_connection), psm));
+
+  return true;
+}
+
+bool DynamicChannelManager::RegisterService(Psm psm, DynamicChannelConfigurationOption configuration_option,
+                                            const SecurityPolicy& security_policy,
+                                            OnRegistrationCompleteCallback on_registration_complete,
+                                            OnConnectionOpenCallback on_connection_open, os::Handler* handler) {
+  internal::DynamicChannelServiceImpl::PendingRegistration pending_registration{
+      .user_handler_ = handler,
+      .on_registration_complete_callback_ = std::move(on_registration_complete),
+      .on_connection_open_callback_ = std::move(on_connection_open),
+      .configuration_ = configuration_option,
+  };
+  l2cap_layer_handler_->Post(common::BindOnce(&internal::DynamicChannelServiceManagerImpl::Register,
+                                              common::Unretained(service_manager_), psm,
+                                              std::move(pending_registration)));
+
+  return true;
+}
+
+}  // namespace le
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/le/dynamic_channel_manager.h b/gd/l2cap/le/dynamic_channel_manager.h
new file mode 100644
index 0000000..b57ec2c
--- /dev/null
+++ b/gd/l2cap/le/dynamic_channel_manager.h
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <string>
+
+#include "hci/acl_manager.h"
+#include "hci/address.h"
+#include "l2cap/l2cap_packets.h"
+#include "l2cap/le/dynamic_channel.h"
+#include "l2cap/le/dynamic_channel_configuration_option.h"
+#include "l2cap/le/dynamic_channel_service.h"
+#include "l2cap/psm.h"
+#include "l2cap/security_policy.h"
+#include "os/handler.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace le {
+
+class L2capLeModule;
+
+namespace internal {
+class LinkManager;
+class DynamicChannelServiceManagerImpl;
+}  // namespace internal
+
+class DynamicChannelManager {
+ public:
+  enum class ConnectionResultCode {
+    SUCCESS = 0,
+    FAIL_NO_SERVICE_REGISTERED = 1,  // No service is registered
+    FAIL_HCI_ERROR = 2,              // See hci_error
+    FAIL_L2CAP_ERROR = 3,            // See l2cap_connection_response_result
+  };
+
+  struct ConnectionResult {
+    ConnectionResultCode connection_result_code = ConnectionResultCode::SUCCESS;
+    hci::ErrorCode hci_error = hci::ErrorCode::SUCCESS;
+    ConnectionResponseResult l2cap_connection_response_result = ConnectionResponseResult::SUCCESS;
+  };
+  /**
+   * OnConnectionFailureCallback(std::string failure_reason);
+   */
+  using OnConnectionFailureCallback = common::OnceCallback<void(ConnectionResult result)>;
+
+  /**
+   * OnConnectionOpenCallback(DynamicChannel channel);
+   */
+  using OnConnectionOpenCallback = common::Callback<void(std::unique_ptr<DynamicChannel>)>;
+
+  enum class RegistrationResult {
+    SUCCESS = 0,
+    FAIL_DUPLICATE_SERVICE = 1,  // Duplicate service registration for the same PSM
+    FAIL_INVALID_SERVICE = 2,    // Invalid PSM
+  };
+
+  /**
+   * OnRegistrationFailureCallback(RegistrationResult result, DynamicChannelService service);
+   */
+  using OnRegistrationCompleteCallback =
+      common::OnceCallback<void(RegistrationResult, std::unique_ptr<DynamicChannelService>)>;
+
+  /**
+   * Connect to a Dynamic channel on a remote device
+   *
+   * - This method is asynchronous
+   * - When false is returned, the connection fails immediately
+   * - When true is returned, method caller should wait for on_fail_callback or on_open_callback
+   * - If an LE connection does not exist, this method will create an LE connection
+   * - If HCI connection failed, on_fail_callback will be triggered with FAIL_HCI_ERROR
+   * - If Dynamic channel on a remote device is already reported as connected via on_open_callback, it won't be
+   *   reported again
+   *
+   * @param device: Remote device to make this connection.
+   * @param psm: Service PSM to connect. PSM is defined in Core spec Vol 3 Part A 4.2.
+   * @param on_open_callback: A callback to indicate success of a connection initiated from a remote device.
+   * @param on_fail_callback: A callback to indicate connection failure along with a status code.
+   * @param handler: The handler context in which to execute the @callback parameters.
+   * @param configuration_option: The configuration options for this channel
+   *
+   * Returns: true if connection was able to be initiated, false otherwise.
+   */
+  bool ConnectChannel(hci::AddressWithType device, DynamicChannelConfigurationOption configuration_option, Psm psm,
+                      OnConnectionOpenCallback on_connection_open, OnConnectionFailureCallback on_fail_callback,
+                      os::Handler* handler);
+
+  /**
+   * Register a service to receive incoming connections bound to a specific channel.
+   *
+   * - This method is asynchronous.
+   * - When false is returned, the registration fails immediately.
+   * - When true is returned, method caller should wait for on_service_registered callback that contains a
+   *   DynamicChannelService object. The registered service can be managed from that object.
+   * - If a PSM is already registered or some other error happens, on_registration_complete will be triggered with a
+   *   non-SUCCESS value
+   * - After a service is registered, a DynamicChannel is delivered through on_open_callback when the remote
+   *   initiates a channel open and channel is opened successfully
+   * - on_open_callback, will only be triggered after on_service_registered callback
+   *
+   * @param security_policy: The security policy used for the connection.
+   * @param psm: Service PSM to register. PSM is defined in Core spec Vol 3 Part A 4.2.
+   * @param on_registration_complete: A callback to indicate the service setup has completed. If the return status is
+   *        not SUCCESS, it means service is not registered due to reasons like PSM already take
+   * @param on_open_callback: A callback to indicate success of a connection initiated from a remote device.
+   * @param handler: The handler context in which to execute the @callback parameter.
+   * @param configuration_option: The configuration options for this channel
+   */
+  bool RegisterService(Psm psm, DynamicChannelConfigurationOption configuration_option,
+                       const SecurityPolicy& security_policy, OnRegistrationCompleteCallback on_registration_complete,
+                       OnConnectionOpenCallback on_connection_open, os::Handler* handler);
+
+  friend class L2capLeModule;
+
+ private:
+  // The constructor is not to be used by user code
+  DynamicChannelManager(internal::DynamicChannelServiceManagerImpl* service_manager,
+                        internal::LinkManager* link_manager, os::Handler* l2cap_layer_handler)
+      : service_manager_(service_manager), link_manager_(link_manager), l2cap_layer_handler_(l2cap_layer_handler) {
+    ASSERT(service_manager_ != nullptr);
+    ASSERT(link_manager_ != nullptr);
+    ASSERT(l2cap_layer_handler_ != nullptr);
+  }
+  internal::DynamicChannelServiceManagerImpl* service_manager_ = nullptr;
+  internal::LinkManager* link_manager_ = nullptr;
+  os::Handler* l2cap_layer_handler_ = nullptr;
+  DISALLOW_COPY_AND_ASSIGN(DynamicChannelManager);
+};
+
+}  // namespace le
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/le/dynamic_channel_service.cc b/gd/l2cap/le/dynamic_channel_service.cc
new file mode 100644
index 0000000..dcdb882
--- /dev/null
+++ b/gd/l2cap/le/dynamic_channel_service.cc
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "l2cap/le/dynamic_channel_service.h"
+
+#include "common/bind.h"
+#include "l2cap/le/internal/dynamic_channel_service_manager_impl.h"
+#include "os/log.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace le {
+
+void DynamicChannelService::Unregister(OnUnregisteredCallback on_unregistered, os::Handler* on_unregistered_handler) {
+  ASSERT_LOG(manager_ != nullptr, "this service is invalid");
+  l2cap_layer_handler_->Post(common::BindOnce(&internal::DynamicChannelServiceManagerImpl::Unregister,
+                                              common::Unretained(manager_), psm_, std::move(on_unregistered),
+                                              on_unregistered_handler));
+}
+
+Psm DynamicChannelService::GetPsm() const {
+  return psm_;
+}
+
+}  // namespace le
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/le/dynamic_channel_service.h b/gd/l2cap/le/dynamic_channel_service.h
new file mode 100644
index 0000000..408a20c
--- /dev/null
+++ b/gd/l2cap/le/dynamic_channel_service.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "common/callback.h"
+#include "l2cap/psm.h"
+#include "os/handler.h"
+#include "os/log.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace le {
+
+namespace internal {
+class DynamicChannelServiceManagerImpl;
+}
+
+class DynamicChannelService {
+ public:
+  DynamicChannelService() = default;
+
+  using OnUnregisteredCallback = common::OnceCallback<void()>;
+
+  /**
+   * Unregister a service from L2CAP module. This operation cannot fail.
+   * All channels opened for this service will be closed.
+   *
+   * @param on_unregistered will be triggered when unregistration is complete
+   */
+  void Unregister(OnUnregisteredCallback on_unregistered, os::Handler* on_unregistered_handler);
+
+  friend internal::DynamicChannelServiceManagerImpl;
+
+  Psm GetPsm() const;
+
+ protected:
+  DynamicChannelService(Psm psm, internal::DynamicChannelServiceManagerImpl* manager, os::Handler* handler)
+      : psm_(psm), manager_(manager), l2cap_layer_handler_(handler) {
+    ASSERT(IsPsmValid(psm));
+    ASSERT(manager_ != nullptr);
+    ASSERT(l2cap_layer_handler_ != nullptr);
+  }
+
+ private:
+  Psm psm_ = kDefaultPsm;
+  internal::DynamicChannelServiceManagerImpl* manager_ = nullptr;
+  os::Handler* l2cap_layer_handler_;
+  DISALLOW_COPY_AND_ASSIGN(DynamicChannelService);
+};
+
+}  // namespace le
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/le/fixed_channel.cc b/gd/l2cap/le/fixed_channel.cc
new file mode 100644
index 0000000..779c185
--- /dev/null
+++ b/gd/l2cap/le/fixed_channel.cc
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "l2cap/le/fixed_channel.h"
+#include "common/bind.h"
+#include "l2cap/le/internal/fixed_channel_impl.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace le {
+
+hci::AddressWithType FixedChannel::GetDevice() const {
+  return impl_->GetDevice();
+}
+
+hci::Role FixedChannel::GetRole() const {
+  return impl_->GetRole();
+}
+
+hci::AclConnection* FixedChannel::GetAclConnection() const {
+  return impl_->GetAclConnection();
+}
+
+void FixedChannel::RegisterOnCloseCallback(os::Handler* user_handler, FixedChannel::OnCloseCallback on_close_callback) {
+  l2cap_handler_->Post(common::BindOnce(&internal::FixedChannelImpl::RegisterOnCloseCallback, impl_, user_handler,
+                                        std::move(on_close_callback)));
+}
+
+void FixedChannel::Acquire() {
+  l2cap_handler_->Post(common::BindOnce(&internal::FixedChannelImpl::Acquire, impl_));
+}
+
+void FixedChannel::Release() {
+  l2cap_handler_->Post(common::BindOnce(&internal::FixedChannelImpl::Release, impl_));
+}
+
+common::BidiQueueEnd<packet::BasePacketBuilder, packet::PacketView<packet::kLittleEndian>>*
+FixedChannel::GetQueueUpEnd() const {
+  return impl_->GetQueueUpEnd();
+}
+}  // namespace le
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/le/fixed_channel.h b/gd/l2cap/le/fixed_channel.h
new file mode 100644
index 0000000..6d3cc42
--- /dev/null
+++ b/gd/l2cap/le/fixed_channel.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include "common/bidi_queue.h"
+#include "common/callback.h"
+#include "hci/acl_manager.h"
+#include "l2cap/cid.h"
+#include "os/handler.h"
+#include "packet/base_packet_builder.h"
+#include "packet/packet_view.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace le {
+
+namespace internal {
+class FixedChannelImpl;
+}  // namespace internal
+
+/**
+ * L2CAP fixed channel object. When a new object is created, it must be
+ * acquired through calling {@link FixedChannel#Acquire()} within X seconds.
+ * Otherwise, {@link FixedChannel#Release()} will be called automatically.
+ *
+ */
+class FixedChannel {
+ public:
+  // Should only be constructed by modules that have access to LinkManager
+  FixedChannel(std::shared_ptr<internal::FixedChannelImpl> impl, os::Handler* l2cap_handler)
+      : impl_(std::move(impl)), l2cap_handler_(l2cap_handler) {
+    ASSERT(impl_ != nullptr);
+    ASSERT(l2cap_handler_ != nullptr);
+  }
+
+  hci::AddressWithType GetDevice() const;
+
+  /**
+   * Return the role we have in the associated link
+   */
+  hci::Role GetRole() const;
+
+  hci::AclConnection* GetAclConnection() const;
+
+  /**
+   * Register close callback. If close callback is registered, when a channel is closed, the channel's resource will
+   * only be freed after on_close callback is invoked. Otherwise, if no on_close callback is registered, the channel's
+   * resource will be freed immediately after closing.
+   *
+   * @param user_handler The handler used to invoke the callback on
+   * @param on_close_callback The callback invoked upon channel closing.
+   */
+  using OnCloseCallback = common::OnceCallback<void(hci::ErrorCode)>;
+  void RegisterOnCloseCallback(os::Handler* user_handler, OnCloseCallback on_close_callback);
+
+  /**
+   * Indicate that this Fixed Channel is being used. This will prevent ACL connection from being disconnected.
+   */
+  void Acquire();
+
+  /**
+   * Indicate that this Fixed Channel is no longer being used. ACL connection will be disconnected after
+   * kLinkIdleDisconnectTimeout if no other DynamicChannel is connected or no other Fixed Channel is  using this
+   * ACL connection. However a module can still receive data on this channel as long as it remains open.
+   */
+  void Release();
+
+  /**
+   * This method will retrieve the data channel queue to send and receive packets.
+   *
+   * {@see BidiQueueEnd}
+   *
+   * @return The upper end of a bi-directional queue.
+   */
+  common::BidiQueueEnd<packet::BasePacketBuilder, packet::PacketView<packet::kLittleEndian>>* GetQueueUpEnd() const;
+
+ private:
+  std::shared_ptr<internal::FixedChannelImpl> impl_;
+  os::Handler* l2cap_handler_;
+};
+
+}  // namespace le
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/le/fixed_channel_manager.cc b/gd/l2cap/le/fixed_channel_manager.cc
new file mode 100644
index 0000000..e36a7b4
--- /dev/null
+++ b/gd/l2cap/le/fixed_channel_manager.cc
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "l2cap/le/fixed_channel_manager.h"
+#include "l2cap/le/internal/fixed_channel_service_impl.h"
+#include "l2cap/le/internal/fixed_channel_service_manager_impl.h"
+#include "l2cap/le/internal/link_manager.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace le {
+
+bool FixedChannelManager::ConnectServices(hci::AddressWithType address_with_type,
+                                          OnConnectionFailureCallback on_fail_callback, os::Handler* handler) {
+  internal::LinkManager::PendingFixedChannelConnection pending_fixed_channel_connection{
+      .handler_ = handler,
+      .on_fail_callback_ = std::move(on_fail_callback),
+  };
+  l2cap_layer_handler_->Post(common::BindOnce(&internal::LinkManager::ConnectFixedChannelServices,
+                                              common::Unretained(link_manager_), address_with_type,
+                                              std::move(pending_fixed_channel_connection)));
+  return true;
+}
+
+bool FixedChannelManager::RegisterService(Cid cid, const SecurityPolicy& security_policy,
+                                          OnRegistrationCompleteCallback on_registration_complete,
+                                          OnConnectionOpenCallback on_connection_open, os::Handler* handler) {
+  internal::FixedChannelServiceImpl::PendingRegistration pending_registration{
+      .user_handler_ = handler,
+      .on_registration_complete_callback_ = std::move(on_registration_complete),
+      .on_connection_open_callback_ = std::move(on_connection_open)};
+  l2cap_layer_handler_->Post(common::BindOnce(&internal::FixedChannelServiceManagerImpl::Register,
+                                              common::Unretained(service_manager_), cid,
+                                              std::move(pending_registration)));
+  return true;
+}
+
+}  // namespace le
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/le/fixed_channel_manager.h b/gd/l2cap/le/fixed_channel_manager.h
new file mode 100644
index 0000000..4583310
--- /dev/null
+++ b/gd/l2cap/le/fixed_channel_manager.h
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <string>
+
+#include "hci/acl_manager.h"
+#include "hci/address_with_type.h"
+#include "l2cap/cid.h"
+#include "l2cap/le/fixed_channel.h"
+#include "l2cap/le/fixed_channel_service.h"
+#include "l2cap/security_policy.h"
+#include "os/handler.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace le {
+
+class L2capLeModule;
+
+namespace internal {
+class LinkManager;
+class FixedChannelServiceManagerImpl;
+}  // namespace internal
+
+class FixedChannelManager {
+ public:
+  enum class ConnectionResultCode {
+    SUCCESS = 0,
+    FAIL_NO_SERVICE_REGISTERED = 1,      // No service is registered
+    FAIL_ALL_SERVICES_HAVE_CHANNEL = 2,  // All registered services already have a channel
+    FAIL_HCI_ERROR = 3,                  // See hci_error
+  };
+
+  struct ConnectionResult {
+    ConnectionResultCode connection_result_code = ConnectionResultCode::SUCCESS;
+    hci::ErrorCode hci_error = hci::ErrorCode::SUCCESS;
+  };
+  /**
+   * OnConnectionFailureCallback(ConnectionResult failure_reason);
+   */
+  using OnConnectionFailureCallback = common::OnceCallback<void(ConnectionResult)>;
+
+  /**
+   * OnConnectionOpenCallback(FixedChannel channel);
+   */
+  using OnConnectionOpenCallback = common::Callback<void(std::unique_ptr<FixedChannel>)>;
+
+  enum class RegistrationResult {
+    SUCCESS = 0,
+    FAIL_DUPLICATE_SERVICE = 1,  // Duplicate service registration for the same CID
+    FAIL_INVALID_SERVICE = 2,    // Invalid CID
+  };
+
+  /**
+   * OnRegistrationFailureCallback(RegistrationResult result, FixedChannelService service);
+   */
+  using OnRegistrationCompleteCallback =
+      common::OnceCallback<void(RegistrationResult, std::unique_ptr<FixedChannelService>)>;
+
+  /**
+   * Connect to ALL fixed channels on a remote device
+   *
+   * - This method is asynchronous
+   * - When false is returned, the connection fails immediately
+   * - When true is returned, method caller should wait for on_fail_callback or on_open_callback registered through
+   *   RegisterService() API.
+   * - If an ACL connection does not exist, this method will create an ACL connection. As a result, on_open_callback
+   *   supplied through RegisterService() will be triggered to provide the actual FixedChannel objects
+   * - If HCI connection failed, on_fail_callback will be triggered with FAIL_HCI_ERROR
+   * - If fixed channel on a remote device is already reported as connected via on_open_callback and has been acquired
+   *   via FixedChannel#Acquire() API, it won't be reported again
+   * - If no service is registered, on_fail_callback will be triggered with FAIL_NO_SERVICE_REGISTERED
+   * - If there is an ACL connection and channels for each service is allocated, on_fail_callback will be triggered with
+   *   FAIL_ALL_SERVICES_HAVE_CHANNEL
+   *
+   * NOTE:
+   * This call will initiate an effort to connect all fixed channel services on a remote device.
+   * Due to the connectionless nature of fixed channels, all fixed channels will be connected together.
+   * If a fixed channel service does not need a particular fixed channel. It should release the received
+   * channel immediately after receiving on_open_callback via FixedChannel#Release()
+   *
+   * A module calling ConnectServices() must have called RegisterService() before.
+   * The callback will come back from on_open_callback in the service that is registered
+   *
+   * @param address_with_type: Remote device with type to make this connection.
+   * @param address_type: Address type of remote device
+   * @param on_fail_callback: A callback to indicate connection failure along with a status code.
+   * @param handler: The handler context in which to execute the @callback parameters.
+   *
+   * Returns: true if connection was able to be initiated, false otherwise.
+   */
+  bool ConnectServices(hci::AddressWithType address_with_type, OnConnectionFailureCallback on_fail_callback,
+                       os::Handler* handler);
+
+  /**
+   * Register a service to receive incoming connections bound to a specific channel.
+   *
+   * - This method is asynchronous.
+   * - When false is returned, the registration fails immediately.
+   * - When true is returned, method caller should wait for on_service_registered callback that contains a
+   *   FixedChannelService object. The registered service can be managed from that object.
+   * - If a CID is already registered or some other error happens, on_registration_complete will be triggered with a
+   *   non-SUCCESS value
+   * - After a service is registered, any classic ACL connection will create a FixedChannel object that is
+   *   delivered through on_open_callback
+   * - on_open_callback, will only be triggered after on_service_registered callback
+   *
+   * @param cid:  cid used to receive incoming connections
+   * @param security_policy: The security policy used for the connection.
+   * @param on_registration_complete: A callback to indicate the service setup has completed. If the return status is
+   *        not SUCCESS, it means service is not registered due to reasons like CID already take
+   * @param on_open_callback: A callback to indicate success of a connection initiated from a remote device.
+   * @param handler: The handler context in which to execute the @callback parameter.
+   */
+  bool RegisterService(Cid cid, const SecurityPolicy& security_policy,
+                       OnRegistrationCompleteCallback on_registration_complete,
+                       OnConnectionOpenCallback on_connection_open, os::Handler* handler);
+
+  friend class L2capLeModule;
+
+ private:
+  // The constructor is not to be used by user code
+  FixedChannelManager(internal::FixedChannelServiceManagerImpl* service_manager, internal::LinkManager* link_manager,
+                      os::Handler* l2cap_layer_handler)
+      : service_manager_(service_manager), link_manager_(link_manager), l2cap_layer_handler_(l2cap_layer_handler) {}
+  internal::FixedChannelServiceManagerImpl* service_manager_ = nullptr;
+  internal::LinkManager* link_manager_ = nullptr;
+  os::Handler* l2cap_layer_handler_ = nullptr;
+  DISALLOW_COPY_AND_ASSIGN(FixedChannelManager);
+};
+
+}  // namespace le
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/le/fixed_channel_service.cc b/gd/l2cap/le/fixed_channel_service.cc
new file mode 100644
index 0000000..888a741
--- /dev/null
+++ b/gd/l2cap/le/fixed_channel_service.cc
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "l2cap/le/fixed_channel_service.h"
+#include "l2cap/le/internal/fixed_channel_service_manager_impl.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace le {
+
+void FixedChannelService::Unregister(OnUnregisteredCallback on_unregistered, os::Handler* on_unregistered_handler) {
+  ASSERT_LOG(manager_ != nullptr, "this service is invalid");
+  l2cap_layer_handler_->Post(common::BindOnce(&internal::FixedChannelServiceManagerImpl::Unregister,
+                                              common::Unretained(manager_), cid_, std::move(on_unregistered),
+                                              on_unregistered_handler));
+}
+
+}  // namespace le
+}  // namespace l2cap
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/l2cap/le/fixed_channel_service.h b/gd/l2cap/le/fixed_channel_service.h
new file mode 100644
index 0000000..0c4556e
--- /dev/null
+++ b/gd/l2cap/le/fixed_channel_service.h
@@ -0,0 +1,59 @@
+
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include "common/callback.h"
+#include "hci/address.h"
+#include "l2cap/cid.h"
+#include "os/handler.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace le {
+
+namespace internal {
+class FixedChannelServiceManagerImpl;
+}
+
+class FixedChannelService {
+ public:
+  FixedChannelService() = default;
+
+  using OnUnregisteredCallback = common::OnceCallback<void()>;
+
+  /**
+   * Unregister a service from L2CAP module. This operation cannot fail.
+   * All channels opened for this service will be invalidated.
+   *
+   * @param on_unregistered will be triggered when unregistration is complete
+   */
+  void Unregister(OnUnregisteredCallback on_unregistered, os::Handler* on_unregistered_handler);
+
+  friend internal::FixedChannelServiceManagerImpl;
+
+ private:
+  FixedChannelService(Cid cid, internal::FixedChannelServiceManagerImpl* manager, os::Handler* handler)
+      : cid_(cid), manager_(manager), l2cap_layer_handler_(handler) {}
+  Cid cid_ = kInvalidCid;
+  internal::FixedChannelServiceManagerImpl* manager_ = nullptr;
+  os::Handler* l2cap_layer_handler_;
+  DISALLOW_COPY_AND_ASSIGN(FixedChannelService);
+};
+
+}  // namespace le
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/le/internal/dynamic_channel_service_impl.h b/gd/l2cap/le/internal/dynamic_channel_service_impl.h
new file mode 100644
index 0000000..101ef62
--- /dev/null
+++ b/gd/l2cap/le/internal/dynamic_channel_service_impl.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "common/bind.h"
+
+#include "l2cap/le/dynamic_channel.h"
+#include "l2cap/le/dynamic_channel_configuration_option.h"
+#include "l2cap/le/dynamic_channel_manager.h"
+#include "l2cap/le/dynamic_channel_service.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace le {
+namespace internal {
+class DynamicChannelServiceImpl {
+ public:
+  virtual ~DynamicChannelServiceImpl() = default;
+
+  struct PendingRegistration {
+    os::Handler* user_handler_ = nullptr;
+    DynamicChannelManager::OnRegistrationCompleteCallback on_registration_complete_callback_;
+    DynamicChannelManager::OnConnectionOpenCallback on_connection_open_callback_;
+    DynamicChannelConfigurationOption configuration_;
+  };
+
+  virtual void NotifyChannelCreation(std::unique_ptr<DynamicChannel> channel) {
+    user_handler_->Post(common::BindOnce(on_connection_open_callback_, std::move(channel)));
+  }
+
+  DynamicChannelConfigurationOption GetConfigOption() const {
+    return config_option_;
+  }
+
+  friend class DynamicChannelServiceManagerImpl;
+
+ protected:
+  // protected access for mocking
+  DynamicChannelServiceImpl(os::Handler* user_handler,
+                            DynamicChannelManager::OnConnectionOpenCallback on_connection_open_callback,
+                            DynamicChannelConfigurationOption config_option)
+      : user_handler_(user_handler), on_connection_open_callback_(std::move(on_connection_open_callback)),
+        config_option_(config_option) {}
+
+ private:
+  os::Handler* user_handler_ = nullptr;
+  DynamicChannelManager::OnConnectionOpenCallback on_connection_open_callback_;
+  DynamicChannelConfigurationOption config_option_;
+};
+
+}  // namespace internal
+}  // namespace le
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/le/internal/dynamic_channel_service_manager_impl.cc b/gd/l2cap/le/internal/dynamic_channel_service_manager_impl.cc
new file mode 100644
index 0000000..ca3381b
--- /dev/null
+++ b/gd/l2cap/le/internal/dynamic_channel_service_manager_impl.cc
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "l2cap/le/internal/dynamic_channel_service_manager_impl.h"
+
+#include "common/bind.h"
+#include "l2cap/le/internal/dynamic_channel_service_impl.h"
+#include "l2cap/psm.h"
+#include "os/log.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace le {
+namespace internal {
+
+void DynamicChannelServiceManagerImpl::Register(Psm psm,
+                                                DynamicChannelServiceImpl::PendingRegistration pending_registration) {
+  if (!IsPsmValid(psm)) {
+    std::unique_ptr<DynamicChannelService> invalid_service(new DynamicChannelService());
+    pending_registration.user_handler_->Post(
+        common::BindOnce(std::move(pending_registration.on_registration_complete_callback_),
+                         DynamicChannelManager::RegistrationResult::FAIL_INVALID_SERVICE, std::move(invalid_service)));
+  } else if (IsServiceRegistered(psm)) {
+    std::unique_ptr<DynamicChannelService> invalid_service(new DynamicChannelService());
+    pending_registration.user_handler_->Post(common::BindOnce(
+        std::move(pending_registration.on_registration_complete_callback_),
+        DynamicChannelManager::RegistrationResult::FAIL_DUPLICATE_SERVICE, std::move(invalid_service)));
+  } else {
+    service_map_.try_emplace(psm,
+                             DynamicChannelServiceImpl(pending_registration.user_handler_,
+                                                       std::move(pending_registration.on_connection_open_callback_),
+                                                       pending_registration.configuration_));
+    std::unique_ptr<DynamicChannelService> user_service(new DynamicChannelService(psm, this, l2cap_layer_handler_));
+    pending_registration.user_handler_->Post(
+        common::BindOnce(std::move(pending_registration.on_registration_complete_callback_),
+                         DynamicChannelManager::RegistrationResult::SUCCESS, std::move(user_service)));
+  }
+}
+
+void DynamicChannelServiceManagerImpl::Unregister(Psm psm, DynamicChannelService::OnUnregisteredCallback callback,
+                                                  os::Handler* handler) {
+  if (IsServiceRegistered(psm)) {
+    service_map_.erase(psm);
+    handler->Post(std::move(callback));
+  } else {
+    LOG_ERROR("service not registered psm:%d", psm);
+  }
+}
+
+bool DynamicChannelServiceManagerImpl::IsServiceRegistered(Psm psm) const {
+  return service_map_.find(psm) != service_map_.end();
+}
+
+DynamicChannelServiceImpl* DynamicChannelServiceManagerImpl::GetService(Psm psm) {
+  ASSERT(IsServiceRegistered(psm));
+  return &service_map_.find(psm)->second;
+}
+
+std::vector<std::pair<Psm, DynamicChannelServiceImpl*>> DynamicChannelServiceManagerImpl::GetRegisteredServices() {
+  std::vector<std::pair<Psm, DynamicChannelServiceImpl*>> results;
+  for (auto& elem : service_map_) {
+    results.emplace_back(elem.first, &elem.second);
+  }
+  return results;
+}
+
+}  // namespace internal
+}  // namespace le
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/le/internal/dynamic_channel_service_manager_impl.h b/gd/l2cap/le/internal/dynamic_channel_service_manager_impl.h
new file mode 100644
index 0000000..5de5f80
--- /dev/null
+++ b/gd/l2cap/le/internal/dynamic_channel_service_manager_impl.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <unordered_map>
+
+#include "l2cap/le/dynamic_channel_service.h"
+#include "l2cap/le/internal/dynamic_channel_service_impl.h"
+#include "l2cap/psm.h"
+#include "os/handler.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace le {
+namespace internal {
+
+class DynamicChannelServiceManagerImpl {
+ public:
+  explicit DynamicChannelServiceManagerImpl(os::Handler* l2cap_layer_handler)
+      : l2cap_layer_handler_(l2cap_layer_handler) {}
+
+  virtual ~DynamicChannelServiceManagerImpl() = default;
+  //
+  // All APIs must be invoked in L2CAP layer handler
+  //
+  virtual void Register(Psm psm, DynamicChannelServiceImpl::PendingRegistration pending_registration);
+  virtual void Unregister(Psm psm, DynamicChannelService::OnUnregisteredCallback callback, os::Handler* handler);
+  virtual bool IsServiceRegistered(Psm psm) const;
+  virtual DynamicChannelServiceImpl* GetService(Psm psm);
+
+  virtual std::vector<std::pair<Psm, DynamicChannelServiceImpl*>> GetRegisteredServices();
+
+ private:
+  os::Handler* l2cap_layer_handler_ = nullptr;
+  std::unordered_map<Psm, DynamicChannelServiceImpl> service_map_;
+};
+}  // namespace internal
+}  // namespace le
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/le/internal/dynamic_channel_service_manager_test.cc b/gd/l2cap/le/internal/dynamic_channel_service_manager_test.cc
new file mode 100644
index 0000000..00d66d1
--- /dev/null
+++ b/gd/l2cap/le/internal/dynamic_channel_service_manager_test.cc
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <future>
+
+#include <gtest/gtest.h>
+
+#include "common/bind.h"
+#include "l2cap/cid.h"
+#include "l2cap/le/dynamic_channel_manager.h"
+#include "l2cap/le/dynamic_channel_service.h"
+#include "l2cap/le/internal/dynamic_channel_service_manager_impl.h"
+#include "os/handler.h"
+#include "os/thread.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace le {
+namespace internal {
+
+class L2capLeDynamicServiceManagerTest : public ::testing::Test {
+ public:
+  ~L2capLeDynamicServiceManagerTest() override = default;
+
+  void OnServiceRegistered(bool expect_success, DynamicChannelManager::RegistrationResult result,
+                           std::unique_ptr<DynamicChannelService> user_service) {
+    EXPECT_EQ(result == DynamicChannelManager::RegistrationResult::SUCCESS, expect_success);
+    service_registered_ = expect_success;
+  }
+
+ protected:
+  void SetUp() override {
+    thread_ = new os::Thread("test_thread", os::Thread::Priority::NORMAL);
+    user_handler_ = new os::Handler(thread_);
+    l2cap_handler_ = new os::Handler(thread_);
+    manager_ = new DynamicChannelServiceManagerImpl{l2cap_handler_};
+  }
+
+  void TearDown() override {
+    delete manager_;
+    l2cap_handler_->Clear();
+    delete l2cap_handler_;
+    user_handler_->Clear();
+    delete user_handler_;
+    delete thread_;
+  }
+
+  void sync_user_handler() {
+    std::promise<void> promise;
+    auto future = promise.get_future();
+    user_handler_->Post(common::BindOnce(&std::promise<void>::set_value, common::Unretained(&promise)));
+    future.wait_for(std::chrono::milliseconds(3));
+  }
+
+  DynamicChannelServiceManagerImpl* manager_ = nullptr;
+  os::Thread* thread_ = nullptr;
+  os::Handler* user_handler_ = nullptr;
+  os::Handler* l2cap_handler_ = nullptr;
+
+  bool service_registered_ = false;
+};
+
+TEST_F(L2capLeDynamicServiceManagerTest, register_and_unregister_le_dynamic_channel) {
+  DynamicChannelServiceImpl::PendingRegistration pending_registration{
+      .user_handler_ = user_handler_,
+      .on_registration_complete_callback_ =
+          common::BindOnce(&L2capLeDynamicServiceManagerTest::OnServiceRegistered, common::Unretained(this), true)};
+  Cid cid = kSmpBrCid;
+  EXPECT_FALSE(manager_->IsServiceRegistered(cid));
+  manager_->Register(cid, std::move(pending_registration));
+  EXPECT_TRUE(manager_->IsServiceRegistered(cid));
+  sync_user_handler();
+  EXPECT_TRUE(service_registered_);
+  manager_->Unregister(cid, common::BindOnce([] {}), user_handler_);
+  EXPECT_FALSE(manager_->IsServiceRegistered(cid));
+}
+
+TEST_F(L2capLeDynamicServiceManagerTest, register_le_dynamic_channel_bad_cid) {
+  DynamicChannelServiceImpl::PendingRegistration pending_registration{
+      .user_handler_ = user_handler_,
+      .on_registration_complete_callback_ =
+          common::BindOnce(&L2capLeDynamicServiceManagerTest::OnServiceRegistered, common::Unretained(this), false)};
+  Cid cid = 0x1000;
+  EXPECT_FALSE(manager_->IsServiceRegistered(cid));
+  manager_->Register(cid, std::move(pending_registration));
+  EXPECT_FALSE(manager_->IsServiceRegistered(cid));
+  sync_user_handler();
+  EXPECT_FALSE(service_registered_);
+}
+
+}  // namespace internal
+}  // namespace le
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/le/internal/fixed_channel_impl.cc b/gd/l2cap/le/internal/fixed_channel_impl.cc
new file mode 100644
index 0000000..af55ba1
--- /dev/null
+++ b/gd/l2cap/le/internal/fixed_channel_impl.cc
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <unordered_map>
+
+#include "l2cap/cid.h"
+#include "l2cap/le/internal/fixed_channel_impl.h"
+#include "l2cap/le/internal/link.h"
+#include "l2cap/security_policy.h"
+#include "os/handler.h"
+#include "os/log.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace le {
+namespace internal {
+
+hci::Role FixedChannelImpl::GetRole() const {
+  return link_->GetRole();
+}
+
+hci::AclConnection* FixedChannelImpl::GetAclConnection() const {
+  return link_->GetAclConnection();
+}
+
+FixedChannelImpl::FixedChannelImpl(Cid cid, Link* link, os::Handler* l2cap_handler)
+    : cid_(cid), device_(link->GetDevice()), link_(link), l2cap_handler_(l2cap_handler) {
+  ASSERT_LOG(cid_ >= kFirstFixedChannel && cid_ <= kLastFixedChannel, "Invalid cid: %d", cid_);
+  ASSERT(link_ != nullptr);
+  ASSERT(l2cap_handler_ != nullptr);
+}
+
+void FixedChannelImpl::RegisterOnCloseCallback(os::Handler* user_handler,
+                                               FixedChannel::OnCloseCallback on_close_callback) {
+  ASSERT_LOG(user_handler_ == nullptr, "OnCloseCallback can only be registered once");
+  // If channel is already closed, call the callback immediately without saving it
+  if (closed_) {
+    user_handler->Post(common::BindOnce(std::move(on_close_callback), close_reason_));
+    return;
+  }
+  user_handler_ = user_handler;
+  on_close_callback_ = std::move(on_close_callback);
+}
+
+void FixedChannelImpl::OnClosed(hci::ErrorCode status) {
+  ASSERT_LOG(!closed_, "Device %s Cid 0x%x closed twice, old status 0x%x, new status 0x%x", device_.ToString().c_str(),
+             cid_, static_cast<int>(close_reason_), static_cast<int>(status));
+  closed_ = true;
+  close_reason_ = status;
+  acquired_ = false;
+  link_ = nullptr;
+  l2cap_handler_ = nullptr;
+  if (user_handler_ == nullptr) {
+    return;
+  }
+  // On close callback can only be called once
+  user_handler_->Post(common::BindOnce(std::move(on_close_callback_), status));
+  user_handler_ = nullptr;
+  on_close_callback_.Reset();
+}
+
+void FixedChannelImpl::Acquire() {
+  ASSERT_LOG(user_handler_ != nullptr, "Must register OnCloseCallback before calling any methods");
+  if (closed_) {
+    LOG_WARN("%s is already closed", ToString().c_str());
+    ASSERT(!acquired_);
+    return;
+  }
+  if (acquired_) {
+    LOG_DEBUG("%s was already acquired", ToString().c_str());
+    return;
+  }
+  acquired_ = true;
+  link_->RefreshRefCount();
+}
+
+void FixedChannelImpl::Release() {
+  ASSERT_LOG(user_handler_ != nullptr, "Must register OnCloseCallback before calling any methods");
+  if (closed_) {
+    LOG_WARN("%s is already closed", ToString().c_str());
+    ASSERT(!acquired_);
+    return;
+  }
+  if (!acquired_) {
+    LOG_DEBUG("%s was already released", ToString().c_str());
+    return;
+  }
+  acquired_ = false;
+  link_->RefreshRefCount();
+}
+
+Cid FixedChannelImpl::GetCid() const {
+  return cid_;
+}
+
+Cid FixedChannelImpl::GetRemoteCid() const {
+  return cid_;
+}
+
+}  // namespace internal
+}  // namespace le
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/le/internal/fixed_channel_impl.h b/gd/l2cap/le/internal/fixed_channel_impl.h
new file mode 100644
index 0000000..cdb4212
--- /dev/null
+++ b/gd/l2cap/le/internal/fixed_channel_impl.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "common/bidi_queue.h"
+#include "l2cap/cid.h"
+#include "l2cap/internal/channel_impl.h"
+#include "l2cap/le/fixed_channel.h"
+#include "os/handler.h"
+#include "os/log.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace le {
+namespace internal {
+
+class Link;
+
+class FixedChannelImpl : public l2cap::internal::ChannelImpl {
+ public:
+  FixedChannelImpl(Cid cid, Link* link, os::Handler* l2cap_handler);
+
+  virtual ~FixedChannelImpl() = default;
+
+  hci::AddressWithType GetDevice() const {
+    return device_;
+  }
+
+  /* Return the role we have in the associated link */
+  virtual hci::Role GetRole() const;
+
+  virtual hci::AclConnection* GetAclConnection() const;
+
+  virtual void RegisterOnCloseCallback(os::Handler* user_handler, FixedChannel::OnCloseCallback on_close_callback);
+
+  virtual void Acquire();
+
+  virtual void Release();
+
+  virtual bool IsAcquired() const {
+    return acquired_;
+  }
+
+  Cid GetCid() const override;
+  Cid GetRemoteCid() const override;
+  virtual void OnClosed(hci::ErrorCode status);
+
+  virtual std::string ToString() {
+    std::ostringstream ss;
+    ss << "Device " << device_ << " Cid 0x" << std::hex << cid_;
+    return ss.str();
+  }
+
+  common::BidiQueueEnd<packet::BasePacketBuilder, packet::PacketView<packet::kLittleEndian>>* GetQueueUpEnd() {
+    return channel_queue_.GetUpEnd();
+  }
+
+  common::BidiQueueEnd<packet::PacketView<packet::kLittleEndian>, packet::BasePacketBuilder>* GetQueueDownEnd() {
+    return channel_queue_.GetDownEnd();
+  }
+
+ private:
+  // Constructor states
+  // For logging purpose only
+  const Cid cid_;
+  // For logging purpose only
+  const hci::AddressWithType device_;
+  // Needed to handle Acquire() and Release()
+  Link* link_;
+  os::Handler* l2cap_handler_;
+
+  // User supported states
+  os::Handler* user_handler_ = nullptr;
+  FixedChannel::OnCloseCallback on_close_callback_{};
+
+  // Internal states
+  bool acquired_ = false;
+  bool closed_ = false;
+  hci::ErrorCode close_reason_ = hci::ErrorCode::SUCCESS;
+  static constexpr size_t kChannelQueueSize = 10;
+  common::BidiQueue<packet::PacketView<packet::kLittleEndian>, packet::BasePacketBuilder> channel_queue_{
+      kChannelQueueSize};
+
+  DISALLOW_COPY_AND_ASSIGN(FixedChannelImpl);
+};
+
+}  // namespace internal
+}  // namespace le
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/le/internal/fixed_channel_impl_mock.h b/gd/l2cap/le/internal/fixed_channel_impl_mock.h
new file mode 100644
index 0000000..5baa7e5
--- /dev/null
+++ b/gd/l2cap/le/internal/fixed_channel_impl_mock.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include "l2cap/le/internal/fixed_channel_impl.h"
+
+#include <gmock/gmock.h>
+
+// Unit test interfaces
+namespace bluetooth {
+namespace l2cap {
+namespace le {
+namespace internal {
+namespace testing {
+
+class MockFixedChannelImpl : public FixedChannelImpl {
+ public:
+  MOCK_METHOD(void, RegisterOnCloseCallback,
+              (os::Handler * user_handler, FixedChannel::OnCloseCallback on_close_callback), (override));
+  MOCK_METHOD(void, Acquire, (), (override));
+  MOCK_METHOD(void, Release, (), (override));
+  MOCK_METHOD(bool, IsAcquired, (), (override, const));
+  MOCK_METHOD(void, OnClosed, (hci::ErrorCode status), (override));
+};
+
+}  // namespace testing
+}  // namespace internal
+}  // namespace le
+}  // namespace l2cap
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/l2cap/le/internal/fixed_channel_impl_test.cc b/gd/l2cap/le/internal/fixed_channel_impl_test.cc
new file mode 100644
index 0000000..d9388f6
--- /dev/null
+++ b/gd/l2cap/le/internal/fixed_channel_impl_test.cc
@@ -0,0 +1,279 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "l2cap/le/internal/fixed_channel_impl.h"
+#include "common/testing/bind_test_util.h"
+#include "hci/address_with_type.h"
+#include "l2cap/cid.h"
+#include "l2cap/internal/parameter_provider_mock.h"
+#include "l2cap/le/internal/link_mock.h"
+#include "os/handler.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace bluetooth {
+namespace l2cap {
+namespace le {
+namespace internal {
+
+using hci::Address;
+using hci::AddressWithType;
+using l2cap::internal::testing::MockParameterProvider;
+using testing::MockLink;
+using ::testing::Return;
+
+class L2capLeFixedChannelImplTest : public ::testing::Test {
+ public:
+  static void SyncHandler(os::Handler* handler) {
+    std::promise<void> promise;
+    auto future = promise.get_future();
+    handler->Post(common::BindOnce(&std::promise<void>::set_value, common::Unretained(&promise)));
+    future.wait_for(std::chrono::milliseconds(3));
+  }
+
+ protected:
+  void SetUp() override {
+    thread_ = new os::Thread("test_thread", os::Thread::Priority::NORMAL);
+    l2cap_handler_ = new os::Handler(thread_);
+  }
+
+  void TearDown() override {
+    l2cap_handler_->Clear();
+    delete l2cap_handler_;
+    delete thread_;
+  }
+
+  os::Thread* thread_ = nullptr;
+  os::Handler* l2cap_handler_ = nullptr;
+};
+
+TEST_F(L2capLeFixedChannelImplTest, get_device) {
+  MockParameterProvider mock_parameter_provider;
+  EXPECT_CALL(mock_parameter_provider, GetLeLinkIdleDisconnectTimeout()).Times(1);
+  testing::MockAclConnection* mock_acl_connection = new testing::MockAclConnection();
+  EXPECT_CALL(*mock_acl_connection, GetAddress()).Times(1);
+  EXPECT_CALL(*mock_acl_connection, GetAddressType()).Times(1);
+  MockLink mock_le_link(l2cap_handler_, &mock_parameter_provider,
+                        std::unique_ptr<testing::MockAclConnection>(mock_acl_connection));
+  AddressWithType device{{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}}, hci::AddressType::PUBLIC_DEVICE_ADDRESS};
+  EXPECT_CALL(mock_le_link, GetDevice()).WillRepeatedly(Return(device));
+  LOG_INFO("------------------");
+  FixedChannelImpl fixed_channel_impl(kSmpBrCid, &mock_le_link, l2cap_handler_);
+  EXPECT_EQ(device, fixed_channel_impl.GetDevice());
+}
+
+TEST_F(L2capLeFixedChannelImplTest, close_triggers_callback) {
+  MockParameterProvider mock_parameter_provider;
+  EXPECT_CALL(mock_parameter_provider, GetLeLinkIdleDisconnectTimeout()).Times(1);
+  testing::MockAclConnection* mock_acl_connection = new testing::MockAclConnection();
+  EXPECT_CALL(*mock_acl_connection, GetAddress()).Times(1);
+  EXPECT_CALL(*mock_acl_connection, GetAddressType()).Times(1);
+  MockLink mock_le_link(l2cap_handler_, &mock_parameter_provider,
+                        std::unique_ptr<testing::MockAclConnection>(mock_acl_connection));
+  AddressWithType device{{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}}, hci::AddressType::PUBLIC_DEVICE_ADDRESS};
+  EXPECT_CALL(mock_le_link, GetDevice()).WillRepeatedly(Return(device));
+  FixedChannelImpl fixed_channel_impl(kSmpBrCid, &mock_le_link, l2cap_handler_);
+
+  // Register on close callback
+  auto user_handler = std::make_unique<os::Handler>(thread_);
+  hci::ErrorCode my_status = hci::ErrorCode::SUCCESS;
+  fixed_channel_impl.RegisterOnCloseCallback(
+      user_handler.get(), common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { my_status = status; }));
+
+  // Channel closure should trigger such callback
+  fixed_channel_impl.OnClosed(hci::ErrorCode::REMOTE_USER_TERMINATED_CONNECTION);
+  SyncHandler(user_handler.get());
+  EXPECT_EQ(hci::ErrorCode::REMOTE_USER_TERMINATED_CONNECTION, my_status);
+
+  user_handler->Clear();
+}
+
+TEST_F(L2capLeFixedChannelImplTest, register_callback_after_close_should_call_immediately) {
+  MockParameterProvider mock_parameter_provider;
+  EXPECT_CALL(mock_parameter_provider, GetLeLinkIdleDisconnectTimeout()).Times(1);
+  testing::MockAclConnection* mock_acl_connection = new testing::MockAclConnection();
+  EXPECT_CALL(*mock_acl_connection, GetAddress()).Times(1);
+  EXPECT_CALL(*mock_acl_connection, GetAddressType()).Times(1);
+  MockLink mock_le_link(l2cap_handler_, &mock_parameter_provider,
+                        std::unique_ptr<testing::MockAclConnection>(mock_acl_connection));
+  AddressWithType device{{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}}, hci::AddressType::PUBLIC_DEVICE_ADDRESS};
+  EXPECT_CALL(mock_le_link, GetDevice()).WillRepeatedly(Return(device));
+  FixedChannelImpl fixed_channel_impl(kSmpBrCid, &mock_le_link, l2cap_handler_);
+
+  // Channel closure should do nothing
+  fixed_channel_impl.OnClosed(hci::ErrorCode::REMOTE_USER_TERMINATED_CONNECTION);
+
+  // Register on close callback should trigger callback immediately
+  auto user_handler = std::make_unique<os::Handler>(thread_);
+  hci::ErrorCode my_status = hci::ErrorCode::SUCCESS;
+  fixed_channel_impl.RegisterOnCloseCallback(
+      user_handler.get(), common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { my_status = status; }));
+  SyncHandler(user_handler.get());
+  EXPECT_EQ(hci::ErrorCode::REMOTE_USER_TERMINATED_CONNECTION, my_status);
+
+  user_handler->Clear();
+}
+
+TEST_F(L2capLeFixedChannelImplTest, close_twice_should_fail) {
+  MockParameterProvider mock_parameter_provider;
+  EXPECT_CALL(mock_parameter_provider, GetLeLinkIdleDisconnectTimeout()).Times(1);
+  testing::MockAclConnection* mock_acl_connection = new testing::MockAclConnection();
+  EXPECT_CALL(*mock_acl_connection, GetAddress()).Times(1);
+  EXPECT_CALL(*mock_acl_connection, GetAddressType()).Times(1);
+  MockLink mock_le_link(l2cap_handler_, &mock_parameter_provider,
+                        std::unique_ptr<testing::MockAclConnection>(mock_acl_connection));
+  AddressWithType device{{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}}, hci::AddressType::PUBLIC_DEVICE_ADDRESS};
+  EXPECT_CALL(mock_le_link, GetDevice()).WillRepeatedly(Return(device));
+  FixedChannelImpl fixed_channel_impl(kSmpBrCid, &mock_le_link, l2cap_handler_);
+
+  // Register on close callback
+  auto user_handler = std::make_unique<os::Handler>(thread_);
+  hci::ErrorCode my_status = hci::ErrorCode::SUCCESS;
+  fixed_channel_impl.RegisterOnCloseCallback(
+      user_handler.get(), common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { my_status = status; }));
+
+  // Channel closure should trigger such callback
+  fixed_channel_impl.OnClosed(hci::ErrorCode::REMOTE_USER_TERMINATED_CONNECTION);
+  SyncHandler(user_handler.get());
+  EXPECT_EQ(hci::ErrorCode::REMOTE_USER_TERMINATED_CONNECTION, my_status);
+
+  // 2nd OnClose() callback should fail
+  EXPECT_DEATH(fixed_channel_impl.OnClosed(hci::ErrorCode::PAGE_TIMEOUT), ".*OnClosed.*");
+
+  user_handler->Clear();
+}
+
+TEST_F(L2capLeFixedChannelImplTest, multiple_registeration_should_fail) {
+  MockParameterProvider mock_parameter_provider;
+  EXPECT_CALL(mock_parameter_provider, GetLeLinkIdleDisconnectTimeout()).Times(1);
+  testing::MockAclConnection* mock_acl_connection = new testing::MockAclConnection();
+  EXPECT_CALL(*mock_acl_connection, GetAddress()).Times(1);
+  EXPECT_CALL(*mock_acl_connection, GetAddressType()).Times(1);
+  MockLink mock_le_link(l2cap_handler_, &mock_parameter_provider,
+                        std::unique_ptr<testing::MockAclConnection>(mock_acl_connection));
+  AddressWithType device{{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}}, hci::AddressType::PUBLIC_DEVICE_ADDRESS};
+  EXPECT_CALL(mock_le_link, GetDevice()).WillRepeatedly(Return(device));
+  FixedChannelImpl fixed_channel_impl(kSmpBrCid, &mock_le_link, l2cap_handler_);
+
+  // Register on close callback
+  auto user_handler = std::make_unique<os::Handler>(thread_);
+  hci::ErrorCode my_status = hci::ErrorCode::SUCCESS;
+  fixed_channel_impl.RegisterOnCloseCallback(
+      user_handler.get(), common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { my_status = status; }));
+
+  EXPECT_DEATH(fixed_channel_impl.RegisterOnCloseCallback(user_handler.get(),
+                                                          common::BindOnce([](hci::ErrorCode status) { FAIL(); })),
+               ".*RegisterOnCloseCallback.*");
+
+  user_handler->Clear();
+}
+
+TEST_F(L2capLeFixedChannelImplTest, call_acquire_before_registeration_should_fail) {
+  MockParameterProvider mock_parameter_provider;
+  EXPECT_CALL(mock_parameter_provider, GetLeLinkIdleDisconnectTimeout()).Times(1);
+  testing::MockAclConnection* mock_acl_connection = new testing::MockAclConnection();
+  EXPECT_CALL(*mock_acl_connection, GetAddress()).Times(1);
+  EXPECT_CALL(*mock_acl_connection, GetAddressType()).Times(1);
+  MockLink mock_le_link(l2cap_handler_, &mock_parameter_provider,
+                        std::unique_ptr<testing::MockAclConnection>(mock_acl_connection));
+  AddressWithType device{{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}}, hci::AddressType::PUBLIC_DEVICE_ADDRESS};
+  EXPECT_CALL(mock_le_link, GetDevice()).WillRepeatedly(Return(device));
+  FixedChannelImpl fixed_channel_impl(kSmpBrCid, &mock_le_link, l2cap_handler_);
+  EXPECT_DEATH(fixed_channel_impl.Acquire(), ".*Acquire.*");
+}
+
+TEST_F(L2capLeFixedChannelImplTest, call_release_before_registeration_should_fail) {
+  MockParameterProvider mock_parameter_provider;
+  EXPECT_CALL(mock_parameter_provider, GetLeLinkIdleDisconnectTimeout()).Times(1);
+  testing::MockAclConnection* mock_acl_connection = new testing::MockAclConnection();
+  EXPECT_CALL(*mock_acl_connection, GetAddress()).Times(1);
+  EXPECT_CALL(*mock_acl_connection, GetAddressType()).Times(1);
+  MockLink mock_le_link(l2cap_handler_, &mock_parameter_provider,
+                        std::unique_ptr<testing::MockAclConnection>(mock_acl_connection));
+  AddressWithType device{{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}}, hci::AddressType::PUBLIC_DEVICE_ADDRESS};
+  EXPECT_CALL(mock_le_link, GetDevice()).WillRepeatedly(Return(device));
+  FixedChannelImpl fixed_channel_impl(kSmpBrCid, &mock_le_link, l2cap_handler_);
+  EXPECT_DEATH(fixed_channel_impl.Release(), ".*Release.*");
+}
+
+TEST_F(L2capLeFixedChannelImplTest, test_acquire_release_channel) {
+  MockParameterProvider mock_parameter_provider;
+  EXPECT_CALL(mock_parameter_provider, GetLeLinkIdleDisconnectTimeout()).Times(1);
+  testing::MockAclConnection* mock_acl_connection = new testing::MockAclConnection();
+  EXPECT_CALL(*mock_acl_connection, GetAddress()).Times(1);
+  EXPECT_CALL(*mock_acl_connection, GetAddressType()).Times(1);
+  MockLink mock_le_link(l2cap_handler_, &mock_parameter_provider,
+                        std::unique_ptr<testing::MockAclConnection>(mock_acl_connection));
+  AddressWithType device{{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}}, hci::AddressType::PUBLIC_DEVICE_ADDRESS};
+  EXPECT_CALL(mock_le_link, GetDevice()).WillRepeatedly(Return(device));
+  FixedChannelImpl fixed_channel_impl(kSmpBrCid, &mock_le_link, l2cap_handler_);
+
+  // Register on close callback
+  auto user_handler = std::make_unique<os::Handler>(thread_);
+  hci::ErrorCode my_status = hci::ErrorCode::SUCCESS;
+  fixed_channel_impl.RegisterOnCloseCallback(
+      user_handler.get(), common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { my_status = status; }));
+
+  // Default should be false
+  EXPECT_FALSE(fixed_channel_impl.IsAcquired());
+
+  // Should be called 2 times after Acquire() and Release()
+  EXPECT_CALL(mock_le_link, RefreshRefCount()).Times(2);
+
+  fixed_channel_impl.Acquire();
+  EXPECT_TRUE(fixed_channel_impl.IsAcquired());
+
+  fixed_channel_impl.Release();
+  EXPECT_FALSE(fixed_channel_impl.IsAcquired());
+
+  user_handler->Clear();
+}
+
+TEST_F(L2capLeFixedChannelImplTest, test_acquire_after_close) {
+  MockParameterProvider mock_parameter_provider;
+  EXPECT_CALL(mock_parameter_provider, GetLeLinkIdleDisconnectTimeout()).Times(1);
+  testing::MockAclConnection* mock_acl_connection = new testing::MockAclConnection();
+  EXPECT_CALL(*mock_acl_connection, GetAddress()).Times(1);
+  EXPECT_CALL(*mock_acl_connection, GetAddressType()).Times(1);
+  MockLink mock_le_link(l2cap_handler_, &mock_parameter_provider,
+                        std::unique_ptr<testing::MockAclConnection>(mock_acl_connection));
+  AddressWithType device{{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}}, hci::AddressType::PUBLIC_DEVICE_ADDRESS};
+  EXPECT_CALL(mock_le_link, GetDevice()).WillRepeatedly(Return(device));
+  FixedChannelImpl fixed_channel_impl(kSmpBrCid, &mock_le_link, l2cap_handler_);
+
+  // Register on close callback
+  auto user_handler = std::make_unique<os::Handler>(thread_);
+  hci::ErrorCode my_status = hci::ErrorCode::SUCCESS;
+  fixed_channel_impl.RegisterOnCloseCallback(
+      user_handler.get(), common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { my_status = status; }));
+
+  // Channel closure should trigger such callback
+  fixed_channel_impl.OnClosed(hci::ErrorCode::REMOTE_USER_TERMINATED_CONNECTION);
+  SyncHandler(user_handler.get());
+  EXPECT_EQ(hci::ErrorCode::REMOTE_USER_TERMINATED_CONNECTION, my_status);
+
+  // Release or Acquire after closing should crash
+  EXPECT_CALL(mock_le_link, RefreshRefCount()).Times(0);
+  EXPECT_FALSE(fixed_channel_impl.IsAcquired());
+  EXPECT_DEATH(fixed_channel_impl.Acquire(), ".*Acquire.*");
+
+  user_handler->Clear();
+}
+
+}  // namespace internal
+}  // namespace le
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/le/internal/fixed_channel_service_impl.h b/gd/l2cap/le/internal/fixed_channel_service_impl.h
new file mode 100644
index 0000000..5eed777
--- /dev/null
+++ b/gd/l2cap/le/internal/fixed_channel_service_impl.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "l2cap/le/fixed_channel.h"
+#include "l2cap/le/fixed_channel_manager.h"
+#include "l2cap/le/fixed_channel_service.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace le {
+namespace internal {
+
+class FixedChannelServiceImpl {
+ public:
+  virtual ~FixedChannelServiceImpl() = default;
+
+  struct PendingRegistration {
+    os::Handler* user_handler_ = nullptr;
+    FixedChannelManager::OnRegistrationCompleteCallback on_registration_complete_callback_;
+    FixedChannelManager::OnConnectionOpenCallback on_connection_open_callback_;
+  };
+
+  virtual void NotifyChannelCreation(std::unique_ptr<FixedChannel> channel) {
+    user_handler_->Post(common::BindOnce(on_connection_open_callback_, std::move(channel)));
+  }
+
+  friend class FixedChannelServiceManagerImpl;
+
+ protected:
+  // protected access for mocking
+  FixedChannelServiceImpl(os::Handler* user_handler,
+                          FixedChannelManager::OnConnectionOpenCallback on_connection_open_callback)
+      : user_handler_(user_handler), on_connection_open_callback_(std::move(on_connection_open_callback)) {}
+
+ private:
+  os::Handler* user_handler_ = nullptr;
+  FixedChannelManager::OnConnectionOpenCallback on_connection_open_callback_;
+};
+
+}  // namespace internal
+}  // namespace le
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/le/internal/fixed_channel_service_impl_mock.h b/gd/l2cap/le/internal/fixed_channel_service_impl_mock.h
new file mode 100644
index 0000000..5a1b65d
--- /dev/null
+++ b/gd/l2cap/le/internal/fixed_channel_service_impl_mock.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include "l2cap/le/internal/fixed_channel_impl.h"
+#include "l2cap/le/internal/fixed_channel_service_manager_impl.h"
+
+#include <gmock/gmock.h>
+
+// Unit test interfaces
+namespace bluetooth {
+namespace l2cap {
+namespace le {
+namespace internal {
+namespace testing {
+
+class MockFixedChannelServiceImpl : public FixedChannelServiceImpl {
+ public:
+  MockFixedChannelServiceImpl() : FixedChannelServiceImpl(nullptr, FixedChannelManager::OnConnectionOpenCallback()) {}
+  MOCK_METHOD(void, NotifyChannelCreation, (std::unique_ptr<FixedChannel> channel), (override));
+};
+
+}  // namespace testing
+}  // namespace internal
+}  // namespace le
+}  // namespace l2cap
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/l2cap/le/internal/fixed_channel_service_manager_impl.cc b/gd/l2cap/le/internal/fixed_channel_service_manager_impl.cc
new file mode 100644
index 0000000..3a2de00
--- /dev/null
+++ b/gd/l2cap/le/internal/fixed_channel_service_manager_impl.cc
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "l2cap/le/internal/fixed_channel_service_manager_impl.h"
+
+#include "common/bind.h"
+#include "l2cap/cid.h"
+#include "l2cap/le/internal/fixed_channel_service_impl.h"
+#include "os/log.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace le {
+namespace internal {
+
+void FixedChannelServiceManagerImpl::Register(Cid cid,
+                                              FixedChannelServiceImpl::PendingRegistration pending_registration) {
+  if (cid < kFirstFixedChannel || cid > kLastFixedChannel || cid == kLeSignallingCid) {
+    std::unique_ptr<FixedChannelService> invalid_service(new FixedChannelService());
+    pending_registration.user_handler_->Post(
+        common::BindOnce(std::move(pending_registration.on_registration_complete_callback_),
+                         FixedChannelManager::RegistrationResult::FAIL_INVALID_SERVICE, std::move(invalid_service)));
+  } else if (IsServiceRegistered(cid)) {
+    std::unique_ptr<FixedChannelService> invalid_service(new FixedChannelService());
+    pending_registration.user_handler_->Post(
+        common::BindOnce(std::move(pending_registration.on_registration_complete_callback_),
+                         FixedChannelManager::RegistrationResult::FAIL_DUPLICATE_SERVICE, std::move(invalid_service)));
+  } else {
+    service_map_.try_emplace(cid,
+                             FixedChannelServiceImpl(pending_registration.user_handler_,
+                                                     std::move(pending_registration.on_connection_open_callback_)));
+    std::unique_ptr<FixedChannelService> user_service(new FixedChannelService(cid, this, l2cap_layer_handler_));
+    pending_registration.user_handler_->Post(
+        common::BindOnce(std::move(pending_registration.on_registration_complete_callback_),
+                         FixedChannelManager::RegistrationResult::SUCCESS, std::move(user_service)));
+  }
+}
+
+void FixedChannelServiceManagerImpl::Unregister(Cid cid, FixedChannelService::OnUnregisteredCallback callback,
+                                                os::Handler* handler) {
+  if (IsServiceRegistered(cid)) {
+    service_map_.erase(cid);
+    handler->Post(std::move(callback));
+  } else {
+    LOG_ERROR("service not registered cid:%d", cid);
+  }
+}
+
+bool FixedChannelServiceManagerImpl::IsServiceRegistered(Cid cid) const {
+  return service_map_.find(cid) != service_map_.end();
+}
+
+FixedChannelServiceImpl* FixedChannelServiceManagerImpl::GetService(Cid cid) {
+  ASSERT(IsServiceRegistered(cid));
+  return &service_map_.find(cid)->second;
+}
+
+std::vector<std::pair<Cid, FixedChannelServiceImpl*>> FixedChannelServiceManagerImpl::GetRegisteredServices() {
+  std::vector<std::pair<Cid, FixedChannelServiceImpl*>> results;
+  for (auto& elem : service_map_) {
+    results.emplace_back(elem.first, &elem.second);
+  }
+  return results;
+}
+
+}  // namespace internal
+}  // namespace le
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/le/internal/fixed_channel_service_manager_impl.h b/gd/l2cap/le/internal/fixed_channel_service_manager_impl.h
new file mode 100644
index 0000000..d57c192
--- /dev/null
+++ b/gd/l2cap/le/internal/fixed_channel_service_manager_impl.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <unordered_map>
+
+#include "l2cap/cid.h"
+#include "l2cap/le/fixed_channel_service.h"
+#include "l2cap/le/internal/fixed_channel_service_impl.h"
+#include "os/handler.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace le {
+namespace internal {
+
+class FixedChannelServiceManagerImpl {
+ public:
+  explicit FixedChannelServiceManagerImpl(os::Handler* l2cap_layer_handler)
+      : l2cap_layer_handler_(l2cap_layer_handler) {}
+  virtual ~FixedChannelServiceManagerImpl() = default;
+
+  // All APIs must be invoked in L2CAP layer handler
+
+  virtual void Register(Cid cid, FixedChannelServiceImpl::PendingRegistration pending_registration);
+  virtual void Unregister(Cid cid, FixedChannelService::OnUnregisteredCallback callback, os::Handler* handler);
+  virtual bool IsServiceRegistered(Cid cid) const;
+  virtual FixedChannelServiceImpl* GetService(Cid cid);
+  virtual std::vector<std::pair<Cid, FixedChannelServiceImpl*>> GetRegisteredServices();
+
+ private:
+  os::Handler* l2cap_layer_handler_ = nullptr;
+  std::unordered_map<Cid, FixedChannelServiceImpl> service_map_;
+};
+}  // namespace internal
+}  // namespace le
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/le/internal/fixed_channel_service_manager_impl_mock.h b/gd/l2cap/le/internal/fixed_channel_service_manager_impl_mock.h
new file mode 100644
index 0000000..068599b
--- /dev/null
+++ b/gd/l2cap/le/internal/fixed_channel_service_manager_impl_mock.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include "l2cap/le/internal/fixed_channel_impl.h"
+#include "l2cap/le/internal/fixed_channel_service_manager_impl.h"
+
+#include <gmock/gmock.h>
+
+// Unit test interfaces
+namespace bluetooth {
+namespace l2cap {
+namespace le {
+namespace internal {
+namespace testing {
+
+class MockFixedChannelServiceManagerImpl : public FixedChannelServiceManagerImpl {
+ public:
+  MockFixedChannelServiceManagerImpl() : FixedChannelServiceManagerImpl(nullptr) {}
+  MOCK_METHOD(void, Register, (Cid cid, FixedChannelServiceImpl::PendingRegistration pending_registration), (override));
+  MOCK_METHOD(void, Unregister, (Cid cid, FixedChannelService::OnUnregisteredCallback callback, os::Handler* handler),
+              (override));
+  MOCK_METHOD(bool, IsServiceRegistered, (Cid cid), (const, override));
+  MOCK_METHOD(FixedChannelServiceImpl*, GetService, (Cid cid), (override));
+  MOCK_METHOD((std::vector<std::pair<Cid, FixedChannelServiceImpl*>>), GetRegisteredServices, (), (override));
+};
+
+}  // namespace testing
+}  // namespace internal
+}  // namespace le
+}  // namespace l2cap
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/l2cap/le/internal/fixed_channel_service_manager_test.cc b/gd/l2cap/le/internal/fixed_channel_service_manager_test.cc
new file mode 100644
index 0000000..030933c
--- /dev/null
+++ b/gd/l2cap/le/internal/fixed_channel_service_manager_test.cc
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "l2cap/le/internal/fixed_channel_service_manager_impl.h"
+
+#include <future>
+
+#include "common/bind.h"
+#include "l2cap/cid.h"
+#include "l2cap/le/fixed_channel_manager.h"
+#include "l2cap/le/fixed_channel_service.h"
+#include "os/handler.h"
+#include "os/thread.h"
+
+#include <gtest/gtest.h>
+
+namespace bluetooth {
+namespace l2cap {
+namespace le {
+namespace internal {
+
+class L2capLeServiceManagerTest : public ::testing::Test {
+ public:
+  ~L2capLeServiceManagerTest() override = default;
+
+  void OnServiceRegistered(bool expect_success, FixedChannelManager::RegistrationResult result,
+                           std::unique_ptr<FixedChannelService> user_service) {
+    EXPECT_EQ(result == FixedChannelManager::RegistrationResult::SUCCESS, expect_success);
+    service_registered_ = expect_success;
+  }
+
+ protected:
+  void SetUp() override {
+    manager_ = new FixedChannelServiceManagerImpl{nullptr};
+    thread_ = new os::Thread("test_thread", os::Thread::Priority::NORMAL);
+    user_handler_ = new os::Handler(thread_);
+  }
+
+  void TearDown() override {
+    user_handler_->Clear();
+    delete user_handler_;
+    delete thread_;
+    delete manager_;
+  }
+
+  void sync_user_handler() {
+    std::promise<void> promise;
+    auto future = promise.get_future();
+    user_handler_->Post(common::BindOnce(&std::promise<void>::set_value, common::Unretained(&promise)));
+    future.wait_for(std::chrono::milliseconds(3));
+  }
+
+  FixedChannelServiceManagerImpl* manager_ = nullptr;
+  os::Thread* thread_ = nullptr;
+  os::Handler* user_handler_ = nullptr;
+
+  bool service_registered_ = false;
+};
+
+TEST_F(L2capLeServiceManagerTest, register_and_unregister_le_fixed_channel) {
+  FixedChannelServiceImpl::PendingRegistration pending_registration{
+      .user_handler_ = user_handler_,
+      .on_registration_complete_callback_ =
+          common::BindOnce(&L2capLeServiceManagerTest::OnServiceRegistered, common::Unretained(this), true)};
+  Cid cid = kSmpBrCid;
+  EXPECT_FALSE(manager_->IsServiceRegistered(cid));
+  manager_->Register(cid, std::move(pending_registration));
+  EXPECT_TRUE(manager_->IsServiceRegistered(cid));
+  sync_user_handler();
+  EXPECT_TRUE(service_registered_);
+  manager_->Unregister(cid, common::BindOnce([] {}), user_handler_);
+  EXPECT_FALSE(manager_->IsServiceRegistered(cid));
+}
+
+TEST_F(L2capLeServiceManagerTest, register_le_fixed_channel_bad_cid) {
+  FixedChannelServiceImpl::PendingRegistration pending_registration{
+      .user_handler_ = user_handler_,
+      .on_registration_complete_callback_ =
+          common::BindOnce(&L2capLeServiceManagerTest::OnServiceRegistered, common::Unretained(this), false)};
+  Cid cid = 0x1000;
+  EXPECT_FALSE(manager_->IsServiceRegistered(cid));
+  manager_->Register(cid, std::move(pending_registration));
+  EXPECT_FALSE(manager_->IsServiceRegistered(cid));
+  sync_user_handler();
+  EXPECT_FALSE(service_registered_);
+}
+
+}  // namespace internal
+}  // namespace le
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/le/internal/link.cc b/gd/l2cap/le/internal/link.cc
new file mode 100644
index 0000000..37305fb
--- /dev/null
+++ b/gd/l2cap/le/internal/link.cc
@@ -0,0 +1,195 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <chrono>
+#include <memory>
+
+#include "hci/acl_manager.h"
+#include "l2cap/internal/dynamic_channel_impl.h"
+#include "l2cap/internal/parameter_provider.h"
+#include "l2cap/le/dynamic_channel_manager.h"
+#include "l2cap/le/internal/fixed_channel_impl.h"
+#include "l2cap/le/internal/link.h"
+#include "os/alarm.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace le {
+namespace internal {
+
+Link::Link(os::Handler* l2cap_handler, std::unique_ptr<hci::AclConnection> acl_connection,
+           l2cap::internal::ParameterProvider* parameter_provider,
+           DynamicChannelServiceManagerImpl* dynamic_service_manager,
+           FixedChannelServiceManagerImpl* fixed_service_manager)
+    : l2cap_handler_(l2cap_handler), acl_connection_(std::move(acl_connection)),
+      data_pipeline_manager_(l2cap_handler, this, acl_connection_->GetAclQueueEnd()),
+      parameter_provider_(parameter_provider), dynamic_service_manager_(dynamic_service_manager),
+      signalling_manager_(l2cap_handler_, this, &data_pipeline_manager_, dynamic_service_manager_,
+                          &dynamic_channel_allocator_) {
+  ASSERT(l2cap_handler_ != nullptr);
+  ASSERT(acl_connection_ != nullptr);
+  ASSERT(parameter_provider_ != nullptr);
+  link_idle_disconnect_alarm_.Schedule(common::BindOnce(&Link::Disconnect, common::Unretained(this)),
+                                       parameter_provider_->GetLeLinkIdleDisconnectTimeout());
+}
+
+void Link::OnAclDisconnected(hci::ErrorCode status) {
+  fixed_channel_allocator_.OnAclDisconnected(status);
+  dynamic_channel_allocator_.OnAclDisconnected(status);
+}
+
+void Link::Disconnect() {
+  acl_connection_->Disconnect(hci::DisconnectReason::REMOTE_USER_TERMINATED_CONNECTION);
+}
+
+void Link::UpdateConnectionParameter(SignalId signal_id, uint16_t conn_interval_min, uint16_t conn_interval_max,
+                                     uint16_t conn_latency, uint16_t supervision_timeout) {
+  acl_connection_->LeConnectionUpdate(
+      conn_interval_min, conn_interval_max, conn_latency, supervision_timeout,
+      common::BindOnce(&Link::on_connection_update_complete, common::Unretained(this), signal_id), l2cap_handler_);
+}
+
+std::shared_ptr<FixedChannelImpl> Link::AllocateFixedChannel(Cid cid, SecurityPolicy security_policy) {
+  auto channel = fixed_channel_allocator_.AllocateChannel(cid, security_policy);
+  data_pipeline_manager_.AttachChannel(cid, channel, l2cap::internal::DataPipelineManager::ChannelMode::BASIC);
+  return channel;
+}
+
+bool Link::IsFixedChannelAllocated(Cid cid) {
+  return fixed_channel_allocator_.IsChannelAllocated(cid);
+}
+
+Cid Link::ReserveDynamicChannel() {
+  return dynamic_channel_allocator_.ReserveChannel();
+}
+
+void Link::SendConnectionRequest(Psm psm, PendingDynamicChannelConnection pending_dynamic_channel_connection) {
+  if (dynamic_channel_allocator_.IsPsmUsed(psm)) {
+    LOG_INFO("Psm %d is already connected", psm);
+    return;
+  }
+  auto reserved_cid = ReserveDynamicChannel();
+  signalling_manager_.SendConnectionRequest(psm, reserved_cid, pending_dynamic_channel_connection.configuration_.mtu);
+}
+
+void Link::SendDisconnectionRequest(Cid local_cid, Cid remote_cid) {
+  auto channel = dynamic_channel_allocator_.FindChannelByCid(local_cid);
+  if (channel == nullptr || channel->GetRemoteCid() != remote_cid) {
+    LOG_ERROR("Invalid cid");
+  }
+  signalling_manager_.SendDisconnectRequest(local_cid, remote_cid);
+}
+
+void Link::OnOutgoingConnectionRequestFail(Cid local_cid) {
+  local_cid_to_pending_dynamic_channel_connection_map_.erase(local_cid);
+  dynamic_channel_allocator_.FreeChannel(local_cid);
+}
+
+std::shared_ptr<l2cap::internal::DynamicChannelImpl> Link::AllocateDynamicChannel(Psm psm, Cid remote_cid,
+                                                                                  SecurityPolicy security_policy) {
+  auto channel = dynamic_channel_allocator_.AllocateChannel(psm, remote_cid, security_policy);
+  if (channel != nullptr) {
+    data_pipeline_manager_.AttachChannel(channel->GetCid(), channel,
+                                         l2cap::internal::DataPipelineManager::ChannelMode::LE_CREDIT_BASED);
+    RefreshRefCount();
+    channel->local_initiated_ = false;
+  }
+  return channel;
+}
+
+std::shared_ptr<l2cap::internal::DynamicChannelImpl> Link::AllocateReservedDynamicChannel(
+    Cid reserved_cid, Psm psm, Cid remote_cid, SecurityPolicy security_policy) {
+  auto channel = dynamic_channel_allocator_.AllocateReservedChannel(reserved_cid, psm, remote_cid, security_policy);
+  if (channel != nullptr) {
+    data_pipeline_manager_.AttachChannel(channel->GetCid(), channel,
+                                         l2cap::internal::DataPipelineManager::ChannelMode::LE_CREDIT_BASED);
+    RefreshRefCount();
+    channel->local_initiated_ = true;
+  }
+  return channel;
+}
+
+DynamicChannelConfigurationOption Link::GetConfigurationForInitialConfiguration(Cid cid) {
+  ASSERT(local_cid_to_pending_dynamic_channel_connection_map_.find(cid) !=
+         local_cid_to_pending_dynamic_channel_connection_map_.end());
+  return local_cid_to_pending_dynamic_channel_connection_map_[cid].configuration_;
+}
+
+void Link::FreeDynamicChannel(Cid cid) {
+  if (dynamic_channel_allocator_.FindChannelByCid(cid) == nullptr) {
+    return;
+  }
+  data_pipeline_manager_.DetachChannel(cid);
+  dynamic_channel_allocator_.FreeChannel(cid);
+  RefreshRefCount();
+}
+
+void Link::RefreshRefCount() {
+  int ref_count = 0;
+  ref_count += fixed_channel_allocator_.GetRefCount();
+  ref_count += dynamic_channel_allocator_.NumberOfChannels();
+  ASSERT_LOG(ref_count >= 0, "ref_count %d is less than 0", ref_count);
+  if (ref_count > 0) {
+    link_idle_disconnect_alarm_.Cancel();
+  } else {
+    link_idle_disconnect_alarm_.Schedule(common::BindOnce(&Link::Disconnect, common::Unretained(this)),
+                                         parameter_provider_->GetLeLinkIdleDisconnectTimeout());
+  }
+}
+
+void Link::NotifyChannelCreation(Cid cid, std::unique_ptr<DynamicChannel> user_channel) {
+  ASSERT(local_cid_to_pending_dynamic_channel_connection_map_.find(cid) !=
+         local_cid_to_pending_dynamic_channel_connection_map_.end());
+  auto& pending_dynamic_channel_connection = local_cid_to_pending_dynamic_channel_connection_map_[cid];
+  pending_dynamic_channel_connection.handler_->Post(
+      common::BindOnce(std::move(pending_dynamic_channel_connection.on_open_callback_), std::move(user_channel)));
+  local_cid_to_pending_dynamic_channel_connection_map_.erase(cid);
+}
+
+void Link::NotifyChannelFail(Cid cid) {
+  ASSERT(local_cid_to_pending_dynamic_channel_connection_map_.find(cid) !=
+         local_cid_to_pending_dynamic_channel_connection_map_.end());
+  auto& pending_dynamic_channel_connection = local_cid_to_pending_dynamic_channel_connection_map_[cid];
+  // TODO(cmanton) Pass proper connection falure result to user
+  DynamicChannelManager::ConnectionResult result;
+  pending_dynamic_channel_connection.handler_->Post(
+      common::BindOnce(std::move(pending_dynamic_channel_connection.on_fail_callback_), result));
+  local_cid_to_pending_dynamic_channel_connection_map_.erase(cid);
+}
+
+uint16_t Link::GetMps() const {
+  return parameter_provider_->GetLeMps();
+}
+
+uint16_t Link::GetInitialCredit() const {
+  return parameter_provider_->GetLeInitialCredit();
+}
+
+void Link::SendLeCredit(Cid local_cid, uint16_t credit) {
+  signalling_manager_.SendCredit(local_cid, credit);
+}
+
+void Link::on_connection_update_complete(SignalId signal_id, hci::ErrorCode error_code) {
+  ConnectionParameterUpdateResponseResult result = (error_code == hci::ErrorCode::SUCCESS)
+                                                       ? ConnectionParameterUpdateResponseResult::ACCEPTED
+                                                       : ConnectionParameterUpdateResponseResult::REJECTED;
+  signalling_manager_.SendConnectionParameterUpdateResponse(SignalId(), result);
+}
+
+}  // namespace internal
+}  // namespace le
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/le/internal/link.h b/gd/l2cap/le/internal/link.h
new file mode 100644
index 0000000..3c90856
--- /dev/null
+++ b/gd/l2cap/le/internal/link.h
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <chrono>
+#include <memory>
+
+#include "hci/acl_manager.h"
+#include "l2cap/internal/data_pipeline_manager.h"
+#include "l2cap/internal/dynamic_channel_allocator.h"
+#include "l2cap/internal/fixed_channel_allocator.h"
+#include "l2cap/internal/ilink.h"
+#include "l2cap/internal/parameter_provider.h"
+#include "l2cap/le/dynamic_channel_manager.h"
+#include "l2cap/le/internal/dynamic_channel_service_manager_impl.h"
+#include "l2cap/le/internal/fixed_channel_impl.h"
+#include "l2cap/le/internal/fixed_channel_service_manager_impl.h"
+#include "l2cap/le/internal/signalling_manager.h"
+#include "os/alarm.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace le {
+namespace internal {
+
+class Link : public l2cap::internal::ILink {
+ public:
+  Link(os::Handler* l2cap_handler, std::unique_ptr<hci::AclConnection> acl_connection,
+       l2cap::internal::ParameterProvider* parameter_provider,
+       DynamicChannelServiceManagerImpl* dynamic_service_manager,
+       FixedChannelServiceManagerImpl* fixed_service_manager);
+
+  ~Link() override = default;
+
+  inline hci::AddressWithType GetDevice() override {
+    return {acl_connection_->GetAddress(), acl_connection_->GetAddressType()};
+  }
+
+  struct PendingDynamicChannelConnection {
+    os::Handler* handler_;
+    DynamicChannelManager::OnConnectionOpenCallback on_open_callback_;
+    DynamicChannelManager::OnConnectionFailureCallback on_fail_callback_;
+    le::DynamicChannelConfigurationOption configuration_;
+  };
+
+  inline virtual hci::Role GetRole() {
+    return acl_connection_->GetRole();
+  }
+
+  inline virtual hci::AclConnection* GetAclConnection() {
+    return acl_connection_.get();
+  }
+
+  // ACL methods
+
+  virtual void OnAclDisconnected(hci::ErrorCode status);
+
+  virtual void Disconnect();
+
+  // Handles connection parameter update request from remote
+  virtual void UpdateConnectionParameter(SignalId signal_id, uint16_t conn_interval_min, uint16_t conn_interval_max,
+                                         uint16_t conn_latency, uint16_t supervision_timeout);
+
+  // FixedChannel methods
+
+  virtual std::shared_ptr<FixedChannelImpl> AllocateFixedChannel(Cid cid, SecurityPolicy security_policy);
+
+  virtual bool IsFixedChannelAllocated(Cid cid);
+
+  // DynamicChannel methods
+
+  virtual Cid ReserveDynamicChannel();
+
+  virtual void SendConnectionRequest(Psm psm, PendingDynamicChannelConnection pending_dynamic_channel_connection);
+
+  void SendDisconnectionRequest(Cid local_cid, Cid remote_cid) override;
+
+  // Invoked by signalling manager to indicate an outgoing connection request failed and link shall free resources
+  virtual void OnOutgoingConnectionRequestFail(Cid local_cid);
+
+  virtual std::shared_ptr<l2cap::internal::DynamicChannelImpl> AllocateDynamicChannel(Psm psm, Cid remote_cid,
+                                                                                      SecurityPolicy security_policy);
+
+  virtual std::shared_ptr<l2cap::internal::DynamicChannelImpl> AllocateReservedDynamicChannel(
+      Cid reserved_cid, Psm psm, Cid remote_cid, SecurityPolicy security_policy);
+
+  virtual DynamicChannelConfigurationOption GetConfigurationForInitialConfiguration(Cid cid);
+
+  virtual void FreeDynamicChannel(Cid cid);
+
+  // Check how many channels are acquired or in use, if zero, start tear down timer, if non-zero, cancel tear down timer
+  virtual void RefreshRefCount();
+
+  void NotifyChannelCreation(Cid cid, std::unique_ptr<DynamicChannel> user_channel);
+  void NotifyChannelFail(Cid cid);
+
+  virtual std::string ToString() {
+    return GetDevice().ToString();
+  }
+
+  virtual uint16_t GetMps() const;
+
+  virtual uint16_t GetInitialCredit() const;
+
+  void SendLeCredit(Cid local_cid, uint16_t credit) override;
+
+ private:
+  os::Handler* l2cap_handler_;
+  l2cap::internal::FixedChannelAllocator<FixedChannelImpl, Link> fixed_channel_allocator_{this, l2cap_handler_};
+  l2cap::internal::DynamicChannelAllocator dynamic_channel_allocator_{this, l2cap_handler_};
+  std::unique_ptr<hci::AclConnection> acl_connection_;
+  l2cap::internal::DataPipelineManager data_pipeline_manager_;
+  l2cap::internal::ParameterProvider* parameter_provider_;
+  DynamicChannelServiceManagerImpl* dynamic_service_manager_;
+  LeSignallingManager signalling_manager_;
+  std::unordered_map<Cid, PendingDynamicChannelConnection> local_cid_to_pending_dynamic_channel_connection_map_;
+  os::Alarm link_idle_disconnect_alarm_{l2cap_handler_};
+  DISALLOW_COPY_AND_ASSIGN(Link);
+
+  void on_connection_update_complete(SignalId signal_id, hci::ErrorCode error_code);
+};
+
+}  // namespace internal
+}  // namespace le
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/le/internal/link_manager.cc b/gd/l2cap/le/internal/link_manager.cc
new file mode 100644
index 0000000..35a3ff2
--- /dev/null
+++ b/gd/l2cap/le/internal/link_manager.cc
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <memory>
+#include <unordered_map>
+
+#include "hci/acl_manager.h"
+#include "hci/address.h"
+#include "l2cap/internal/scheduler_fifo.h"
+#include "l2cap/le/internal/link.h"
+#include "os/handler.h"
+#include "os/log.h"
+
+#include "l2cap/le/internal/link_manager.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace le {
+namespace internal {
+
+void LinkManager::ConnectFixedChannelServices(hci::AddressWithType address_with_type,
+                                              PendingFixedChannelConnection pending_fixed_channel_connection) {
+  // Check if there is any service registered
+  auto fixed_channel_services = fixed_channel_service_manager_->GetRegisteredServices();
+  if (fixed_channel_services.empty()) {
+    // If so, return error
+    pending_fixed_channel_connection.handler_->Post(common::BindOnce(
+        std::move(pending_fixed_channel_connection.on_fail_callback_),
+        FixedChannelManager::ConnectionResult{
+            .connection_result_code = FixedChannelManager::ConnectionResultCode::FAIL_NO_SERVICE_REGISTERED}));
+    return;
+  }
+  // Otherwise, check if device has an ACL connection
+  auto* link = GetLink(address_with_type);
+  if (link != nullptr) {
+    // If device already have an ACL connection
+    // Check if all registered services have an allocated channel and allocate one if not already allocated
+    int num_new_channels = 0;
+    for (auto& fixed_channel_service : fixed_channel_services) {
+      if (link->IsFixedChannelAllocated(fixed_channel_service.first)) {
+        // This channel is already allocated for this link, do not allocated twice
+        continue;
+      }
+      // Allocate channel for newly registered fixed channels
+      auto fixed_channel_impl = link->AllocateFixedChannel(fixed_channel_service.first, SecurityPolicy());
+      fixed_channel_service.second->NotifyChannelCreation(
+          std::make_unique<FixedChannel>(fixed_channel_impl, l2cap_handler_));
+      num_new_channels++;
+    }
+    // Declare connection failure if no new channels are created
+    if (num_new_channels == 0) {
+      pending_fixed_channel_connection.handler_->Post(common::BindOnce(
+          std::move(pending_fixed_channel_connection.on_fail_callback_),
+          FixedChannelManager::ConnectionResult{
+              .connection_result_code = FixedChannelManager::ConnectionResultCode::FAIL_ALL_SERVICES_HAVE_CHANNEL}));
+    }
+    // No need to create ACL connection, return without saving any pending connections
+    return;
+  }
+  // If not, create new ACL connection
+  // Add request to pending link list first
+  auto pending_link = pending_links_.find(address_with_type);
+  if (pending_link == pending_links_.end()) {
+    // Create pending link if not exist
+    pending_links_.try_emplace(address_with_type);
+    pending_link = pending_links_.find(address_with_type);
+  }
+  pending_link->second.pending_fixed_channel_connections_.push_back(std::move(pending_fixed_channel_connection));
+  // Then create new ACL connection
+  acl_manager_->CreateLeConnection(address_with_type);
+}
+
+void LinkManager::ConnectDynamicChannelServices(
+    hci::AddressWithType device, Link::PendingDynamicChannelConnection pending_dynamic_channel_connection, Psm psm) {
+  auto* link = GetLink(device);
+  if (link == nullptr) {
+    acl_manager_->CreateLeConnection(device);
+    pending_dynamic_channels_[device].push_back(std::make_pair(psm, std::move(pending_dynamic_channel_connection)));
+    return;
+  }
+  link->SendConnectionRequest(psm, std::move(pending_dynamic_channel_connection));
+}
+
+Link* LinkManager::GetLink(hci::AddressWithType address_with_type) {
+  if (links_.find(address_with_type) == links_.end()) {
+    return nullptr;
+  }
+  return &links_.find(address_with_type)->second;
+}
+
+void LinkManager::OnLeConnectSuccess(hci::AddressWithType connecting_address_with_type,
+                                     std::unique_ptr<hci::AclConnection> acl_connection) {
+  // Same link should not be connected twice
+  hci::AddressWithType connected_address_with_type(acl_connection->GetAddress(), acl_connection->GetAddressType());
+  ASSERT_LOG(GetLink(connected_address_with_type) == nullptr, "%s is connected twice without disconnection",
+             acl_connection->GetAddress().ToString().c_str());
+  // Register ACL disconnection callback in LinkManager so that we can clean up link resource properly
+  acl_connection->RegisterDisconnectCallback(
+      common::BindOnce(&LinkManager::OnDisconnect, common::Unretained(this), connected_address_with_type),
+      l2cap_handler_);
+  links_.try_emplace(connected_address_with_type, l2cap_handler_, std::move(acl_connection), parameter_provider_,
+                     dynamic_channel_service_manager_, fixed_channel_service_manager_);
+  auto* link = GetLink(connected_address_with_type);
+  // Allocate and distribute channels for all registered fixed channel services
+  auto fixed_channel_services = fixed_channel_service_manager_->GetRegisteredServices();
+  for (auto& fixed_channel_service : fixed_channel_services) {
+    auto fixed_channel_impl = link->AllocateFixedChannel(fixed_channel_service.first, SecurityPolicy());
+    fixed_channel_service.second->NotifyChannelCreation(
+        std::make_unique<FixedChannel>(fixed_channel_impl, l2cap_handler_));
+  }
+  if (pending_dynamic_channels_.find(connected_address_with_type) != pending_dynamic_channels_.end()) {
+    for (auto& psm_callback : pending_dynamic_channels_[connected_address_with_type]) {
+      link->SendConnectionRequest(psm_callback.first, std::move(psm_callback.second));
+    }
+    pending_dynamic_channels_.erase(connected_address_with_type);
+  }
+
+  // Remove device from pending links list, if any
+  auto pending_link = pending_links_.find(connecting_address_with_type);
+  if (pending_link == pending_links_.end()) {
+    // This an incoming connection, exit
+    return;
+  }
+  // This is an outgoing connection, remove entry in pending link list
+  pending_links_.erase(pending_link);
+}
+
+void LinkManager::OnLeConnectFail(hci::AddressWithType address_with_type, hci::ErrorCode reason) {
+  // Notify all pending links for this device
+  auto pending_link = pending_links_.find(address_with_type);
+  if (pending_link == pending_links_.end()) {
+    // There is no pending link, exit
+    LOG_DEBUG("Connection to %s failed without a pending link", address_with_type.ToString().c_str());
+    return;
+  }
+  for (auto& pending_fixed_channel_connection : pending_link->second.pending_fixed_channel_connections_) {
+    pending_fixed_channel_connection.handler_->Post(common::BindOnce(
+        std::move(pending_fixed_channel_connection.on_fail_callback_),
+        FixedChannelManager::ConnectionResult{
+            .connection_result_code = FixedChannelManager::ConnectionResultCode::FAIL_HCI_ERROR, .hci_error = reason}));
+  }
+  // Remove entry in pending link list
+  pending_links_.erase(pending_link);
+}
+
+void LinkManager::OnDisconnect(hci::AddressWithType address_with_type, hci::ErrorCode status) {
+  auto* link = GetLink(address_with_type);
+  ASSERT_LOG(link != nullptr, "Device %s is disconnected with reason 0x%x, but not in local database",
+             address_with_type.ToString().c_str(), static_cast<uint8_t>(status));
+  link->OnAclDisconnected(status);
+  links_.erase(address_with_type);
+}
+
+}  // namespace internal
+}  // namespace le
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/le/internal/link_manager.h b/gd/l2cap/le/internal/link_manager.h
new file mode 100644
index 0000000..9e9c0e8
--- /dev/null
+++ b/gd/l2cap/le/internal/link_manager.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <memory>
+#include <unordered_map>
+#include <utility>
+
+#include "os/handler.h"
+
+#include "hci/acl_manager.h"
+#include "hci/address.h"
+#include "hci/address_with_type.h"
+#include "l2cap/internal/parameter_provider.h"
+#include "l2cap/internal/scheduler.h"
+#include "l2cap/le/fixed_channel_manager.h"
+#include "l2cap/le/internal/fixed_channel_service_manager_impl.h"
+#include "l2cap/le/internal/link.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace le {
+namespace internal {
+
+class LinkManager : public hci::LeConnectionCallbacks {
+ public:
+  LinkManager(os::Handler* l2cap_handler, hci::AclManager* acl_manager, FixedChannelServiceManagerImpl* service_manager,
+              l2cap::internal::ParameterProvider* parameter_provider)
+      : l2cap_handler_(l2cap_handler), acl_manager_(acl_manager), fixed_channel_service_manager_(service_manager),
+        parameter_provider_(parameter_provider) {
+    acl_manager_->RegisterLeCallbacks(this, l2cap_handler_);
+  }
+
+  struct PendingFixedChannelConnection {
+    os::Handler* handler_;
+    FixedChannelManager::OnConnectionFailureCallback on_fail_callback_;
+  };
+
+  struct PendingLink {
+    std::vector<PendingFixedChannelConnection> pending_fixed_channel_connections_;
+  };
+
+  // ACL methods
+
+  Link* GetLink(hci::AddressWithType address_with_type);
+  void OnLeConnectSuccess(hci::AddressWithType connecting_address_with_type,
+                          std::unique_ptr<hci::AclConnection> acl_connection) override;
+  void OnLeConnectFail(hci::AddressWithType address_with_type, hci::ErrorCode reason) override;
+  void OnDisconnect(hci::AddressWithType address_with_type, hci::ErrorCode status);
+
+  // FixedChannelManager methods
+
+  void ConnectFixedChannelServices(hci::AddressWithType address_with_type,
+                                   PendingFixedChannelConnection pending_fixed_channel_connection);
+
+  // DynamicChannelManager methods
+
+  void ConnectDynamicChannelServices(hci::AddressWithType device,
+                                     Link::PendingDynamicChannelConnection pending_dynamic_channel_connection, Psm psm);
+
+ private:
+  // Dependencies
+  os::Handler* l2cap_handler_;
+  hci::AclManager* acl_manager_;
+  FixedChannelServiceManagerImpl* fixed_channel_service_manager_;
+  DynamicChannelServiceManagerImpl* dynamic_channel_service_manager_;
+  l2cap::internal::ParameterProvider* parameter_provider_;
+
+  // Internal states
+  std::unordered_map<hci::AddressWithType, PendingLink> pending_links_;
+  std::unordered_map<hci::AddressWithType, Link> links_;
+  std::unordered_map<hci::AddressWithType, std::list<std::pair<Psm, Link::PendingDynamicChannelConnection>>>
+      pending_dynamic_channels_;
+  DISALLOW_COPY_AND_ASSIGN(LinkManager);
+};
+
+}  // namespace internal
+}  // namespace le
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/le/internal/link_manager_test.cc b/gd/l2cap/le/internal/link_manager_test.cc
new file mode 100644
index 0000000..cbef3f3
--- /dev/null
+++ b/gd/l2cap/le/internal/link_manager_test.cc
@@ -0,0 +1,524 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "l2cap/le/internal/link_manager.h"
+
+#include <future>
+#include <thread>
+
+#include "common/bind.h"
+#include "common/testing/bind_test_util.h"
+#include "hci/acl_manager_mock.h"
+#include "hci/address.h"
+#include "l2cap/cid.h"
+#include "l2cap/internal/parameter_provider_mock.h"
+#include "l2cap/le/fixed_channel_manager.h"
+#include "l2cap/le/internal/fixed_channel_service_impl_mock.h"
+#include "l2cap/le/internal/fixed_channel_service_manager_impl_mock.h"
+#include "os/handler.h"
+#include "os/thread.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace bluetooth {
+namespace l2cap {
+namespace le {
+namespace internal {
+namespace {
+
+using hci::testing::MockAclConnection;
+using hci::testing::MockAclManager;
+using l2cap::internal::testing::MockParameterProvider;
+using ::testing::_;  // Matcher to any value
+using ::testing::ByMove;
+using ::testing::DoAll;
+using testing::MockFixedChannelServiceImpl;
+using testing::MockFixedChannelServiceManagerImpl;
+using ::testing::Return;
+using ::testing::SaveArg;
+
+constexpr static auto kTestIdleDisconnectTimeoutLong = std::chrono::milliseconds(1000);
+constexpr static auto kTestIdleDisconnectTimeoutShort = std::chrono::milliseconds(1000);
+
+class L2capLeLinkManagerTest : public ::testing::Test {
+ public:
+  static void SyncHandler(os::Handler* handler) {
+    std::promise<void> promise;
+    auto future = promise.get_future();
+    handler->Post(common::BindOnce(&std::promise<void>::set_value, common::Unretained(&promise)));
+    future.wait_for(std::chrono::milliseconds(3));
+  }
+
+ protected:
+  void SetUp() override {
+    thread_ = new os::Thread("test_thread", os::Thread::Priority::NORMAL);
+    l2cap_handler_ = new os::Handler(thread_);
+    mock_parameter_provider_ = new MockParameterProvider;
+    EXPECT_CALL(*mock_parameter_provider_, GetLeLinkIdleDisconnectTimeout)
+        .WillRepeatedly(Return(kTestIdleDisconnectTimeoutLong));
+  }
+
+  void TearDown() override {
+    delete mock_parameter_provider_;
+    l2cap_handler_->Clear();
+    delete l2cap_handler_;
+    delete thread_;
+  }
+
+  os::Thread* thread_ = nullptr;
+  os::Handler* l2cap_handler_ = nullptr;
+  MockParameterProvider* mock_parameter_provider_ = nullptr;
+};
+
+TEST_F(L2capLeLinkManagerTest, connect_fixed_channel_service_without_acl) {
+  MockFixedChannelServiceManagerImpl mock_le_fixed_channel_service_manager;
+  MockAclManager mock_acl_manager;
+  hci::AddressWithType address_with_type({{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}},
+                                         hci::AddressType::RANDOM_DEVICE_ADDRESS);
+  auto user_handler = os::Handler(thread_);
+
+  // Step 1: Verify callback registration with HCI
+  hci::LeConnectionCallbacks* hci_le_connection_callbacks = nullptr;
+  os::Handler* hci_callback_handler = nullptr;
+  EXPECT_CALL(mock_acl_manager, RegisterLeCallbacks(_, _))
+      .WillOnce(DoAll(SaveArg<0>(&hci_le_connection_callbacks), SaveArg<1>(&hci_callback_handler)));
+  LinkManager le_link_manager(l2cap_handler_, &mock_acl_manager, &mock_le_fixed_channel_service_manager,
+                              mock_parameter_provider_);
+  EXPECT_EQ(hci_le_connection_callbacks, &le_link_manager);
+  EXPECT_EQ(hci_callback_handler, l2cap_handler_);
+
+  // Register fake services
+  MockFixedChannelServiceImpl mock_service_1, mock_service_2;
+  std::vector<std::pair<Cid, FixedChannelServiceImpl*>> results;
+  results.emplace_back(kSmpBrCid, &mock_service_1);
+  results.emplace_back(kConnectionlessCid, &mock_service_2);
+  EXPECT_CALL(mock_le_fixed_channel_service_manager, GetRegisteredServices()).WillRepeatedly(Return(results));
+
+  // Step 2: Connect to fixed channel without ACL connection should trigger ACL connection process
+  EXPECT_CALL(mock_acl_manager, CreateLeConnection(address_with_type)).Times(1);
+  LinkManager::PendingFixedChannelConnection pending_fixed_channel_connection{
+      .handler_ = &user_handler,
+      .on_fail_callback_ = common::BindOnce([](FixedChannelManager::ConnectionResult result) { FAIL(); })};
+  le_link_manager.ConnectFixedChannelServices(address_with_type, std::move(pending_fixed_channel_connection));
+
+  // Step 3: ACL connection success event should trigger channel creation for all registered services
+
+  std::unique_ptr<MockAclConnection> acl_connection = std::make_unique<MockAclConnection>();
+  EXPECT_CALL(*acl_connection, RegisterDisconnectCallback(_, l2cap_handler_)).Times(1);
+  EXPECT_CALL(*acl_connection, GetAddress()).WillRepeatedly(Return(address_with_type.GetAddress()));
+  EXPECT_CALL(*acl_connection, GetAddressType()).WillRepeatedly(Return(address_with_type.GetAddressType()));
+  std::unique_ptr<FixedChannel> channel_1, channel_2;
+  std::promise<void> promise_1, promise_2;
+  auto future_1 = promise_1.get_future();
+  auto future_2 = promise_2.get_future();
+  EXPECT_CALL(mock_service_1, NotifyChannelCreation(_))
+      .WillOnce([&channel_1, &promise_1](std::unique_ptr<FixedChannel> channel) {
+        channel_1 = std::move(channel);
+        promise_1.set_value();
+      });
+  EXPECT_CALL(mock_service_2, NotifyChannelCreation(_))
+      .WillOnce([&channel_2, &promise_2](std::unique_ptr<FixedChannel> channel) {
+        channel_2 = std::move(channel);
+        promise_2.set_value();
+      });
+  hci_callback_handler->Post(common::BindOnce(&hci::LeConnectionCallbacks::OnLeConnectSuccess,
+                                              common::Unretained(hci_le_connection_callbacks), address_with_type,
+                                              std::move(acl_connection)));
+  SyncHandler(hci_callback_handler);
+  auto future_1_status = future_1.wait_for(kTestIdleDisconnectTimeoutShort);
+  EXPECT_EQ(future_1_status, std::future_status::ready);
+  auto future_2_status = future_2.wait_for(kTestIdleDisconnectTimeoutShort);
+  EXPECT_EQ(future_2_status, std::future_status::ready);
+  EXPECT_NE(channel_1, nullptr);
+  EXPECT_NE(channel_2, nullptr);
+
+  // Step 4: Calling ConnectServices() to the same device will no trigger another connection attempt
+  FixedChannelManager::ConnectionResult my_result;
+  LinkManager::PendingFixedChannelConnection pending_fixed_channel_connection_2{
+      .handler_ = &user_handler,
+      .on_fail_callback_ = common::testing::BindLambdaForTesting(
+          [&my_result](FixedChannelManager::ConnectionResult result) { my_result = result; })};
+  le_link_manager.ConnectFixedChannelServices(address_with_type, std::move(pending_fixed_channel_connection_2));
+  SyncHandler(&user_handler);
+  EXPECT_EQ(my_result.connection_result_code,
+            FixedChannelManager::ConnectionResultCode::FAIL_ALL_SERVICES_HAVE_CHANNEL);
+
+  // Step 5: Register new service will cause new channels to be created during ConnectServices()
+  MockFixedChannelServiceImpl mock_service_3;
+  results.emplace_back(kSmpBrCid + 1, &mock_service_3);
+  EXPECT_CALL(mock_le_fixed_channel_service_manager, GetRegisteredServices()).WillRepeatedly(Return(results));
+  LinkManager::PendingFixedChannelConnection pending_fixed_channel_connection_3{
+      .handler_ = &user_handler,
+      .on_fail_callback_ = common::BindOnce([](FixedChannelManager::ConnectionResult result) { FAIL(); })};
+  std::unique_ptr<FixedChannel> channel_3;
+  std::promise<void> promise_3;
+  auto future_3 = promise_3.get_future();
+  EXPECT_CALL(mock_service_3, NotifyChannelCreation(_))
+      .WillOnce([&channel_3, &promise_3](std::unique_ptr<FixedChannel> channel) {
+        channel_3 = std::move(channel);
+        promise_3.set_value();
+      });
+  le_link_manager.ConnectFixedChannelServices(address_with_type, std::move(pending_fixed_channel_connection_3));
+  auto future_3_status = future_3.wait_for(kTestIdleDisconnectTimeoutShort);
+  EXPECT_EQ(future_3_status, std::future_status::ready);
+  EXPECT_NE(channel_3, nullptr);
+
+  user_handler.Clear();
+
+  le_link_manager.OnDisconnect(address_with_type, hci::ErrorCode::SUCCESS);
+}
+
+TEST_F(L2capLeLinkManagerTest, connect_fixed_channel_service_without_acl_with_no_service) {
+  MockFixedChannelServiceManagerImpl mock_le_fixed_channel_service_manager;
+  MockAclManager mock_acl_manager;
+  hci::AddressWithType address_with_type({{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}},
+                                         hci::AddressType::PUBLIC_DEVICE_ADDRESS);
+  auto user_handler = std::make_unique<os::Handler>(thread_);
+
+  // Step 1: Verify callback registration with HCI
+  hci::LeConnectionCallbacks* hci_le_connection_callbacks = nullptr;
+  os::Handler* hci_callback_handler = nullptr;
+  EXPECT_CALL(mock_acl_manager, RegisterLeCallbacks(_, _))
+      .WillOnce(DoAll(SaveArg<0>(&hci_le_connection_callbacks), SaveArg<1>(&hci_callback_handler)));
+  LinkManager le_link_manager(l2cap_handler_, &mock_acl_manager, &mock_le_fixed_channel_service_manager,
+                              mock_parameter_provider_);
+  EXPECT_EQ(hci_le_connection_callbacks, &le_link_manager);
+  EXPECT_EQ(hci_callback_handler, l2cap_handler_);
+
+  // Make sure no service is registered
+  std::vector<std::pair<Cid, FixedChannelServiceImpl*>> results;
+  EXPECT_CALL(mock_le_fixed_channel_service_manager, GetRegisteredServices()).WillRepeatedly(Return(results));
+
+  // Step 2: Connect to fixed channel without any service registered will result in failure
+  EXPECT_CALL(mock_acl_manager, CreateLeConnection(address_with_type)).Times(0);
+  FixedChannelManager::ConnectionResult my_result;
+  LinkManager::PendingFixedChannelConnection pending_fixed_channel_connection{
+      .handler_ = user_handler.get(),
+      .on_fail_callback_ = common::testing::BindLambdaForTesting(
+          [&my_result](FixedChannelManager::ConnectionResult result) { my_result = result; })};
+  le_link_manager.ConnectFixedChannelServices(address_with_type, std::move(pending_fixed_channel_connection));
+  SyncHandler(user_handler.get());
+  EXPECT_EQ(my_result.connection_result_code, FixedChannelManager::ConnectionResultCode::FAIL_NO_SERVICE_REGISTERED);
+
+  user_handler->Clear();
+}
+
+TEST_F(L2capLeLinkManagerTest, connect_fixed_channel_service_without_acl_with_hci_failure) {
+  MockFixedChannelServiceManagerImpl mock_le_fixed_channel_service_manager;
+  MockAclManager mock_acl_manager;
+  hci::AddressWithType address_with_type({{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}},
+                                         hci::AddressType::RANDOM_DEVICE_ADDRESS);
+  auto user_handler = std::make_unique<os::Handler>(thread_);
+
+  // Step 1: Verify callback registration with HCI
+  hci::LeConnectionCallbacks* hci_le_connection_callbacks = nullptr;
+  os::Handler* hci_callback_handler = nullptr;
+  EXPECT_CALL(mock_acl_manager, RegisterLeCallbacks(_, _))
+      .WillOnce(DoAll(SaveArg<0>(&hci_le_connection_callbacks), SaveArg<1>(&hci_callback_handler)));
+  LinkManager le_link_manager(l2cap_handler_, &mock_acl_manager, &mock_le_fixed_channel_service_manager,
+                              mock_parameter_provider_);
+  EXPECT_EQ(hci_le_connection_callbacks, &le_link_manager);
+  EXPECT_EQ(hci_callback_handler, l2cap_handler_);
+
+  // Register fake services
+  MockFixedChannelServiceImpl mock_service_1;
+  std::vector<std::pair<Cid, FixedChannelServiceImpl*>> results;
+  results.emplace_back(kSmpBrCid, &mock_service_1);
+  EXPECT_CALL(mock_le_fixed_channel_service_manager, GetRegisteredServices()).WillRepeatedly(Return(results));
+
+  // Step 2: Connect to fixed channel without ACL connection should trigger ACL connection process
+  EXPECT_CALL(mock_acl_manager, CreateLeConnection(address_with_type)).Times(1);
+  FixedChannelManager::ConnectionResult my_result;
+  LinkManager::PendingFixedChannelConnection pending_fixed_channel_connection{
+      .handler_ = user_handler.get(),
+      .on_fail_callback_ = common::testing::BindLambdaForTesting(
+          [&my_result](FixedChannelManager::ConnectionResult result) { my_result = result; })};
+  le_link_manager.ConnectFixedChannelServices(address_with_type, std::move(pending_fixed_channel_connection));
+
+  // Step 3: ACL connection failure event should trigger connection failure callback
+  EXPECT_CALL(mock_service_1, NotifyChannelCreation(_)).Times(0);
+  hci_callback_handler->Post(common::BindOnce(&hci::LeConnectionCallbacks::OnLeConnectFail,
+                                              common::Unretained(hci_le_connection_callbacks), address_with_type,
+                                              hci::ErrorCode::PAGE_TIMEOUT));
+  SyncHandler(hci_callback_handler);
+  SyncHandler(user_handler.get());
+  EXPECT_EQ(my_result.connection_result_code, FixedChannelManager::ConnectionResultCode::FAIL_HCI_ERROR);
+  EXPECT_EQ(my_result.hci_error, hci::ErrorCode::PAGE_TIMEOUT);
+
+  user_handler->Clear();
+}
+
+TEST_F(L2capLeLinkManagerTest, not_acquiring_channels_should_disconnect_acl_after_timeout) {
+  EXPECT_CALL(*mock_parameter_provider_, GetLeLinkIdleDisconnectTimeout)
+      .WillRepeatedly(Return(kTestIdleDisconnectTimeoutShort));
+  MockFixedChannelServiceManagerImpl mock_le_fixed_channel_service_manager;
+  MockAclManager mock_acl_manager;
+  hci::AddressWithType address_with_type({{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}},
+                                         hci::AddressType::RANDOM_DEVICE_ADDRESS);
+  auto user_handler = std::make_unique<os::Handler>(thread_);
+
+  // Step 1: Verify callback registration with HCI
+  hci::LeConnectionCallbacks* hci_le_connection_callbacks = nullptr;
+  os::Handler* hci_callback_handler = nullptr;
+  EXPECT_CALL(mock_acl_manager, RegisterLeCallbacks(_, _))
+      .WillOnce(DoAll(SaveArg<0>(&hci_le_connection_callbacks), SaveArg<1>(&hci_callback_handler)));
+  LinkManager le_link_manager(l2cap_handler_, &mock_acl_manager, &mock_le_fixed_channel_service_manager,
+                              mock_parameter_provider_);
+  EXPECT_EQ(hci_le_connection_callbacks, &le_link_manager);
+  EXPECT_EQ(hci_callback_handler, l2cap_handler_);
+
+  // Register fake services
+  MockFixedChannelServiceImpl mock_service_1, mock_service_2;
+  std::vector<std::pair<Cid, FixedChannelServiceImpl*>> results;
+  results.emplace_back(kSmpBrCid, &mock_service_1);
+  results.emplace_back(kConnectionlessCid, &mock_service_2);
+  EXPECT_CALL(mock_le_fixed_channel_service_manager, GetRegisteredServices()).WillRepeatedly(Return(results));
+
+  // Step 2: Connect to fixed channel without ACL connection should trigger ACL connection process
+  EXPECT_CALL(mock_acl_manager, CreateLeConnection(address_with_type)).Times(1);
+  LinkManager::PendingFixedChannelConnection pending_fixed_channel_connection{
+      .handler_ = user_handler.get(),
+      .on_fail_callback_ = common::BindOnce([](FixedChannelManager::ConnectionResult result) { FAIL(); })};
+  le_link_manager.ConnectFixedChannelServices(address_with_type, std::move(pending_fixed_channel_connection));
+
+  // Step 3: ACL connection success event should trigger channel creation for all registered services
+  auto* raw_acl_connection = new MockAclConnection();
+  std::unique_ptr<MockAclConnection> acl_connection(raw_acl_connection);
+  EXPECT_CALL(*acl_connection, GetAddress()).WillRepeatedly(Return(address_with_type.GetAddress()));
+  EXPECT_CALL(*acl_connection, GetAddressType()).WillRepeatedly(Return(address_with_type.GetAddressType()));
+  std::unique_ptr<FixedChannel> channel_1, channel_2;
+  std::promise<void> promise_1, promise_2;
+  auto future_1 = promise_1.get_future();
+  auto future_2 = promise_2.get_future();
+  EXPECT_CALL(mock_service_1, NotifyChannelCreation(_))
+      .WillOnce([&channel_1, &promise_1](std::unique_ptr<FixedChannel> channel) {
+        channel_1 = std::move(channel);
+        promise_1.set_value();
+      });
+  EXPECT_CALL(mock_service_2, NotifyChannelCreation(_))
+      .WillOnce([&channel_2, &promise_2](std::unique_ptr<FixedChannel> channel) {
+        channel_2 = std::move(channel);
+        promise_2.set_value();
+      });
+  hci_callback_handler->Post(common::BindOnce(&hci::LeConnectionCallbacks::OnLeConnectSuccess,
+                                              common::Unretained(hci_le_connection_callbacks), address_with_type,
+                                              std::move(acl_connection)));
+  SyncHandler(hci_callback_handler);
+  auto future_1_status = future_1.wait_for(kTestIdleDisconnectTimeoutShort);
+  EXPECT_EQ(future_1_status, std::future_status::ready);
+  EXPECT_NE(channel_1, nullptr);
+  auto future_2_status = future_2.wait_for(kTestIdleDisconnectTimeoutShort);
+  EXPECT_EQ(future_2_status, std::future_status::ready);
+  EXPECT_NE(channel_2, nullptr);
+  hci::ErrorCode status_1 = hci::ErrorCode::SUCCESS;
+  channel_1->RegisterOnCloseCallback(
+      user_handler.get(), common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { status_1 = status; }));
+  hci::ErrorCode status_2 = hci::ErrorCode::SUCCESS;
+  channel_2->RegisterOnCloseCallback(
+      user_handler.get(), common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { status_2 = status; }));
+
+  // Step 4: ave channel IDLE long enough, they will disconnect
+  EXPECT_CALL(*raw_acl_connection, Disconnect(hci::DisconnectReason::REMOTE_USER_TERMINATED_CONNECTION)).Times(1);
+  std::this_thread::sleep_for(kTestIdleDisconnectTimeoutShort * 1.2);
+
+  // Step 5: Link disconnect will trigger all callbacks
+  le_link_manager.OnDisconnect(address_with_type, hci::ErrorCode::CONNECTION_TERMINATED_BY_LOCAL_HOST);
+  SyncHandler(user_handler.get());
+  EXPECT_EQ(hci::ErrorCode::CONNECTION_TERMINATED_BY_LOCAL_HOST, status_1);
+  EXPECT_EQ(hci::ErrorCode::CONNECTION_TERMINATED_BY_LOCAL_HOST, status_2);
+
+  user_handler->Clear();
+}
+
+TEST_F(L2capLeLinkManagerTest, acquiring_channels_should_not_disconnect_acl_after_timeout) {
+  EXPECT_CALL(*mock_parameter_provider_, GetLeLinkIdleDisconnectTimeout)
+      .WillRepeatedly(Return(kTestIdleDisconnectTimeoutShort));
+  MockFixedChannelServiceManagerImpl mock_le_fixed_channel_service_manager;
+  MockAclManager mock_acl_manager;
+  hci::AddressWithType address_with_type({{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}},
+                                         hci::AddressType::RANDOM_DEVICE_ADDRESS);
+  auto user_handler = std::make_unique<os::Handler>(thread_);
+
+  // Step 1: Verify callback registration with HCI
+  hci::LeConnectionCallbacks* hci_le_connection_callbacks = nullptr;
+  os::Handler* hci_callback_handler = nullptr;
+  EXPECT_CALL(mock_acl_manager, RegisterLeCallbacks(_, _))
+      .WillOnce(DoAll(SaveArg<0>(&hci_le_connection_callbacks), SaveArg<1>(&hci_callback_handler)));
+  LinkManager le_link_manager(l2cap_handler_, &mock_acl_manager, &mock_le_fixed_channel_service_manager,
+                              mock_parameter_provider_);
+  EXPECT_EQ(hci_le_connection_callbacks, &le_link_manager);
+  EXPECT_EQ(hci_callback_handler, l2cap_handler_);
+
+  // Register fake services
+  MockFixedChannelServiceImpl mock_service_1, mock_service_2;
+  std::vector<std::pair<Cid, FixedChannelServiceImpl*>> results;
+  results.emplace_back(kSmpBrCid, &mock_service_1);
+  results.emplace_back(kConnectionlessCid, &mock_service_2);
+  EXPECT_CALL(mock_le_fixed_channel_service_manager, GetRegisteredServices()).WillRepeatedly(Return(results));
+
+  // Step 2: Connect to fixed channel without ACL connection should trigger ACL connection process
+  EXPECT_CALL(mock_acl_manager, CreateLeConnection(address_with_type)).Times(1);
+  LinkManager::PendingFixedChannelConnection pending_fixed_channel_connection{
+      .handler_ = user_handler.get(),
+      .on_fail_callback_ = common::BindOnce([](FixedChannelManager::ConnectionResult result) { FAIL(); })};
+  le_link_manager.ConnectFixedChannelServices(address_with_type, std::move(pending_fixed_channel_connection));
+
+  // Step 3: ACL connection success event should trigger channel creation for all registered services
+  auto* raw_acl_connection = new MockAclConnection();
+  std::unique_ptr<MockAclConnection> acl_connection(raw_acl_connection);
+  EXPECT_CALL(*acl_connection, GetAddress()).WillRepeatedly(Return(address_with_type.GetAddress()));
+  EXPECT_CALL(*acl_connection, GetAddressType()).WillRepeatedly(Return(address_with_type.GetAddressType()));
+  std::unique_ptr<FixedChannel> channel_1, channel_2;
+  std::promise<void> promise_1, promise_2;
+  auto future_1 = promise_1.get_future();
+  auto future_2 = promise_2.get_future();
+  EXPECT_CALL(mock_service_1, NotifyChannelCreation(_))
+      .WillOnce([&channel_1, &promise_1](std::unique_ptr<FixedChannel> channel) {
+        channel_1 = std::move(channel);
+        promise_1.set_value();
+      });
+  EXPECT_CALL(mock_service_2, NotifyChannelCreation(_))
+      .WillOnce([&channel_2, &promise_2](std::unique_ptr<FixedChannel> channel) {
+        channel_2 = std::move(channel);
+        promise_2.set_value();
+      });
+  hci_callback_handler->Post(common::BindOnce(&hci::LeConnectionCallbacks::OnLeConnectSuccess,
+                                              common::Unretained(hci_le_connection_callbacks), address_with_type,
+                                              std::move(acl_connection)));
+  SyncHandler(hci_callback_handler);
+  auto future_1_status = future_1.wait_for(kTestIdleDisconnectTimeoutShort);
+  EXPECT_EQ(future_1_status, std::future_status::ready);
+  EXPECT_NE(channel_1, nullptr);
+  auto future_2_status = future_2.wait_for(kTestIdleDisconnectTimeoutShort);
+  EXPECT_EQ(future_2_status, std::future_status::ready);
+  EXPECT_NE(channel_2, nullptr);
+  hci::ErrorCode status_1 = hci::ErrorCode::SUCCESS;
+  channel_1->RegisterOnCloseCallback(
+      user_handler.get(), common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { status_1 = status; }));
+  hci::ErrorCode status_2 = hci::ErrorCode::SUCCESS;
+  channel_2->RegisterOnCloseCallback(
+      user_handler.get(), common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { status_2 = status; }));
+
+  channel_1->Acquire();
+
+  // Step 4: ave channel IDLE, it won't disconnect to due acquired channel 1
+  EXPECT_CALL(*raw_acl_connection, Disconnect(hci::DisconnectReason::REMOTE_USER_TERMINATED_CONNECTION)).Times(0);
+  std::this_thread::sleep_for(kTestIdleDisconnectTimeoutShort * 2);
+
+  // Step 5: Link disconnect will trigger all callbacks
+  le_link_manager.OnDisconnect(address_with_type, hci::ErrorCode::CONNECTION_TERMINATED_BY_LOCAL_HOST);
+  SyncHandler(user_handler.get());
+  EXPECT_EQ(hci::ErrorCode::CONNECTION_TERMINATED_BY_LOCAL_HOST, status_1);
+  EXPECT_EQ(hci::ErrorCode::CONNECTION_TERMINATED_BY_LOCAL_HOST, status_2);
+
+  user_handler->Clear();
+}
+
+TEST_F(L2capLeLinkManagerTest, acquiring_and_releasing_channels_should_eventually_disconnect_acl) {
+  EXPECT_CALL(*mock_parameter_provider_, GetLeLinkIdleDisconnectTimeout)
+      .WillRepeatedly(Return(kTestIdleDisconnectTimeoutShort));
+  MockFixedChannelServiceManagerImpl mock_le_fixed_channel_service_manager;
+  MockAclManager mock_acl_manager;
+  hci::AddressWithType address_with_type({{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}},
+                                         hci::AddressType::PUBLIC_IDENTITY_ADDRESS);
+  auto user_handler = std::make_unique<os::Handler>(thread_);
+
+  // Step 1: Verify callback registration with HCI
+  hci::LeConnectionCallbacks* hci_le_connection_callbacks = nullptr;
+  os::Handler* hci_callback_handler = nullptr;
+  EXPECT_CALL(mock_acl_manager, RegisterLeCallbacks(_, _))
+      .WillOnce(DoAll(SaveArg<0>(&hci_le_connection_callbacks), SaveArg<1>(&hci_callback_handler)));
+  LinkManager le_link_manager(l2cap_handler_, &mock_acl_manager, &mock_le_fixed_channel_service_manager,
+                              mock_parameter_provider_);
+  EXPECT_EQ(hci_le_connection_callbacks, &le_link_manager);
+  EXPECT_EQ(hci_callback_handler, l2cap_handler_);
+
+  // Register fake services
+  MockFixedChannelServiceImpl mock_service_1, mock_service_2;
+  std::vector<std::pair<Cid, FixedChannelServiceImpl*>> results;
+  results.emplace_back(kSmpBrCid, &mock_service_1);
+  results.emplace_back(kConnectionlessCid, &mock_service_2);
+  EXPECT_CALL(mock_le_fixed_channel_service_manager, GetRegisteredServices()).WillRepeatedly(Return(results));
+
+  // Step 2: Connect to fixed channel without ACL connection should trigger ACL connection process
+  EXPECT_CALL(mock_acl_manager, CreateLeConnection(address_with_type)).Times(1);
+  LinkManager::PendingFixedChannelConnection pending_fixed_channel_connection{
+      .handler_ = user_handler.get(),
+      .on_fail_callback_ = common::BindOnce([](FixedChannelManager::ConnectionResult result) { FAIL(); })};
+  le_link_manager.ConnectFixedChannelServices(address_with_type, std::move(pending_fixed_channel_connection));
+
+  // Step 3: ACL connection success event should trigger channel creation for all registered services
+  auto* raw_acl_connection = new MockAclConnection();
+  std::unique_ptr<MockAclConnection> acl_connection(raw_acl_connection);
+  EXPECT_CALL(*acl_connection, GetAddress()).WillRepeatedly(Return(address_with_type.GetAddress()));
+  EXPECT_CALL(*acl_connection, GetAddressType()).WillRepeatedly(Return(address_with_type.GetAddressType()));
+  std::unique_ptr<FixedChannel> channel_1, channel_2;
+  std::promise<void> promise_1, promise_2;
+  auto future_1 = promise_1.get_future();
+  auto future_2 = promise_2.get_future();
+  EXPECT_CALL(mock_service_1, NotifyChannelCreation(_))
+      .WillOnce([&channel_1, &promise_1](std::unique_ptr<FixedChannel> channel) {
+        channel_1 = std::move(channel);
+        promise_1.set_value();
+      });
+  EXPECT_CALL(mock_service_2, NotifyChannelCreation(_))
+      .WillOnce([&channel_2, &promise_2](std::unique_ptr<FixedChannel> channel) {
+        channel_2 = std::move(channel);
+        promise_2.set_value();
+      });
+  hci_callback_handler->Post(common::BindOnce(&hci::LeConnectionCallbacks::OnLeConnectSuccess,
+                                              common::Unretained(hci_le_connection_callbacks), address_with_type,
+                                              std::move(acl_connection)));
+  SyncHandler(hci_callback_handler);
+  auto future_1_status = future_1.wait_for(kTestIdleDisconnectTimeoutShort);
+  EXPECT_EQ(future_1_status, std::future_status::ready);
+  EXPECT_NE(channel_1, nullptr);
+  auto future_2_status = future_2.wait_for(kTestIdleDisconnectTimeoutShort);
+  EXPECT_EQ(future_2_status, std::future_status::ready);
+  EXPECT_NE(channel_2, nullptr);
+  hci::ErrorCode status_1 = hci::ErrorCode::SUCCESS;
+  channel_1->RegisterOnCloseCallback(
+      user_handler.get(), common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { status_1 = status; }));
+  hci::ErrorCode status_2 = hci::ErrorCode::SUCCESS;
+  channel_2->RegisterOnCloseCallback(
+      user_handler.get(), common::testing::BindLambdaForTesting([&](hci::ErrorCode status) { status_2 = status; }));
+
+  channel_1->Acquire();
+
+  // Step 4: ave channel IDLE, it won't disconnect to due acquired channel 1
+  EXPECT_CALL(*raw_acl_connection, Disconnect(hci::DisconnectReason::REMOTE_USER_TERMINATED_CONNECTION)).Times(0);
+  std::this_thread::sleep_for(kTestIdleDisconnectTimeoutShort * 2);
+
+  // Step 5: ave channel IDLE long enough, they will disconnect
+  channel_1->Release();
+  EXPECT_CALL(*raw_acl_connection, Disconnect(hci::DisconnectReason::REMOTE_USER_TERMINATED_CONNECTION)).Times(1);
+  std::this_thread::sleep_for(kTestIdleDisconnectTimeoutShort * 1.2);
+
+  // Step 6: Link disconnect will trigger all callbacks
+  le_link_manager.OnDisconnect(address_with_type, hci::ErrorCode::CONNECTION_TERMINATED_BY_LOCAL_HOST);
+  SyncHandler(user_handler.get());
+  EXPECT_EQ(hci::ErrorCode::CONNECTION_TERMINATED_BY_LOCAL_HOST, status_1);
+  EXPECT_EQ(hci::ErrorCode::CONNECTION_TERMINATED_BY_LOCAL_HOST, status_2);
+
+  user_handler->Clear();
+}
+
+}  // namespace
+}  // namespace internal
+}  // namespace le
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/le/internal/link_mock.h b/gd/l2cap/le/internal/link_mock.h
new file mode 100644
index 0000000..63bb884
--- /dev/null
+++ b/gd/l2cap/le/internal/link_mock.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include "hci/acl_manager_mock.h"
+#include "hci/address_with_type.h"
+#include "l2cap/internal/scheduler_mock.h"
+#include "l2cap/le/internal/link.h"
+
+#include <gmock/gmock.h>
+
+// Unit test interfaces
+namespace bluetooth {
+namespace l2cap {
+namespace le {
+namespace internal {
+namespace testing {
+
+using hci::testing::MockAclConnection;
+
+class MockLink : public Link {
+ public:
+  explicit MockLink(os::Handler* handler, l2cap::internal::ParameterProvider* parameter_provider,
+                    std::unique_ptr<MockAclConnection> mock_acl_connection)
+      : Link(handler, std::move(mock_acl_connection), parameter_provider, nullptr, nullptr){};
+
+  MOCK_METHOD(hci::AddressWithType, GetDevice, (), (override));
+  MOCK_METHOD(hci::Role, GetRole, (), (override));
+  MOCK_METHOD(void, OnAclDisconnected, (hci::ErrorCode status), (override));
+  MOCK_METHOD(void, Disconnect, (), (override));
+  MOCK_METHOD(std::shared_ptr<FixedChannelImpl>, AllocateFixedChannel, (Cid cid, SecurityPolicy security_policy),
+              (override));
+  MOCK_METHOD(bool, IsFixedChannelAllocated, (Cid cid), (override));
+  MOCK_METHOD(void, RefreshRefCount, (), (override));
+};
+
+}  // namespace testing
+}  // namespace internal
+}  // namespace le
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/le/internal/signalling_manager.cc b/gd/l2cap/le/internal/signalling_manager.cc
new file mode 100644
index 0000000..c9d978c
--- /dev/null
+++ b/gd/l2cap/le/internal/signalling_manager.cc
@@ -0,0 +1,416 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "l2cap/le/internal/signalling_manager.h"
+
+#include <chrono>
+
+#include "common/bind.h"
+#include "l2cap/internal/data_pipeline_manager.h"
+#include "l2cap/internal/dynamic_channel_impl.h"
+#include "l2cap/internal/le_credit_based_channel_data_controller.h"
+#include "l2cap/l2cap_packets.h"
+#include "l2cap/le/internal/link.h"
+#include "os/log.h"
+#include "packet/raw_builder.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace le {
+namespace internal {
+
+static constexpr auto kTimeout = std::chrono::seconds(3);
+
+LeSignallingManager::LeSignallingManager(os::Handler* handler, Link* link,
+                                         l2cap::internal::DataPipelineManager* data_pipeline_manager,
+                                         DynamicChannelServiceManagerImpl* dynamic_service_manager,
+                                         l2cap::internal::DynamicChannelAllocator* channel_allocator)
+    : handler_(handler), link_(link), data_pipeline_manager_(data_pipeline_manager),
+      dynamic_service_manager_(dynamic_service_manager), channel_allocator_(channel_allocator), alarm_(handler) {
+  ASSERT(handler_ != nullptr);
+  ASSERT(link_ != nullptr);
+  signalling_channel_ = link_->AllocateFixedChannel(kLeSignallingCid, {});
+  signalling_channel_->GetQueueUpEnd()->RegisterDequeue(
+      handler_, common::Bind(&LeSignallingManager::on_incoming_packet, common::Unretained(this)));
+  enqueue_buffer_ =
+      std::make_unique<os::EnqueueBuffer<packet::BasePacketBuilder>>(signalling_channel_->GetQueueUpEnd());
+}
+
+LeSignallingManager::~LeSignallingManager() {
+  enqueue_buffer_.reset();
+  signalling_channel_->GetQueueUpEnd()->UnregisterDequeue();
+  signalling_channel_ = nullptr;
+}
+
+void LeSignallingManager::SendConnectionRequest(Psm psm, Cid local_cid, Mtu mtu) {
+  PendingCommand pending_command = {
+      next_signal_id_, LeCommandCode::LE_CREDIT_BASED_CONNECTION_REQUEST, psm, local_cid, {}, mtu, link_->GetMps(),
+      link_->GetInitialCredit()};
+  next_signal_id_++;
+  pending_commands_.push(pending_command);
+  if (pending_commands_.size() == 1) {
+    handle_send_next_command();
+  }
+}
+
+void LeSignallingManager::SendDisconnectRequest(Cid local_cid, Cid remote_cid) {
+  PendingCommand pending_command = {
+      next_signal_id_, LeCommandCode::DISCONNECTION_REQUEST, {}, local_cid, remote_cid, {}, {}, {}};
+  next_signal_id_++;
+  pending_commands_.push(pending_command);
+  if (pending_commands_.size() == 1) {
+    handle_send_next_command();
+  }
+}
+
+void LeSignallingManager::SendConnectionParameterUpdateRequest(uint16_t interval_min, uint16_t interval_max,
+                                                               uint16_t slave_latency, uint16_t timeout_multiplier) {
+  LOG_ERROR("Not implemented");
+}
+
+void LeSignallingManager::SendConnectionParameterUpdateResponse(SignalId signal_id,
+                                                                ConnectionParameterUpdateResponseResult result) {
+  auto builder = ConnectionParameterUpdateResponseBuilder::Create(signal_id.Value(), result);
+  enqueue_buffer_->Enqueue(std::move(builder), handler_);
+}
+
+void LeSignallingManager::SendCredit(Cid local_cid, uint16_t credits) {
+  auto builder = LeFlowControlCreditBuilder::Create(next_signal_id_.Value(), local_cid, credits);
+  next_signal_id_++;
+  enqueue_buffer_->Enqueue(std::move(builder), handler_);
+}
+
+void LeSignallingManager::CancelAlarm() {
+  alarm_.Cancel();
+}
+
+void LeSignallingManager::OnCommandReject(LeCommandRejectView command_reject_view) {
+  auto signal_id = command_reject_view.GetIdentifier();
+  if (signal_id != command_just_sent_.signal_id_ || command_just_sent_.command_code_ != command_reject_view.GetCode()) {
+    LOG_WARN("Unexpected response: no pending request");
+    return;
+  }
+  alarm_.Cancel();
+  handle_send_next_command();
+
+  LOG_WARN("Command rejected");
+}
+
+void LeSignallingManager::OnConnectionParameterUpdateRequest(uint16_t interval_min, uint16_t interval_max,
+                                                             uint16_t slave_latency, uint16_t timeout_multiplier) {
+  LOG_ERROR("Not implemented");
+}
+
+void LeSignallingManager::OnConnectionParameterUpdateResponse(ConnectionParameterUpdateResponseResult result) {
+  LOG_ERROR("Not implemented");
+}
+
+void LeSignallingManager::OnConnectionRequest(SignalId signal_id, Psm psm, Cid remote_cid, Mtu mtu, uint16_t mps,
+                                              uint16_t initial_credits) {
+  if (!IsPsmValid(psm)) {
+    LOG_WARN("Invalid psm received from remote psm:%d remote_cid:%d", psm, remote_cid);
+    send_connection_response(signal_id, kInvalidCid, 0, 0, 0,
+                             LeCreditBasedConnectionResponseResult::LE_PSM_NOT_SUPPORTED);
+    return;
+  }
+
+  if (remote_cid == kInvalidCid) {
+    LOG_WARN("Invalid remote cid received from remote psm:%d remote_cid:%d", psm, remote_cid);
+    send_connection_response(signal_id, kInvalidCid, 0, 0, 0,
+                             LeCreditBasedConnectionResponseResult::INVALID_SOURCE_CID);
+    return;
+  }
+
+  if (channel_allocator_->IsPsmUsed(psm)) {
+    LOG_WARN("Psm already exists");
+    send_connection_response(signal_id, kInvalidCid, 0, 0, 0,
+                             LeCreditBasedConnectionResponseResult::LE_PSM_NOT_SUPPORTED);
+    return;
+  }
+
+  if (!dynamic_service_manager_->IsServiceRegistered(psm)) {
+    LOG_INFO("Service for this psm (%d) is not registered", psm);
+    send_connection_response(signal_id, kInvalidCid, 0, 0, 0,
+                             LeCreditBasedConnectionResponseResult::LE_PSM_NOT_SUPPORTED);
+    return;
+  }
+
+  auto* service = dynamic_service_manager_->GetService(psm);
+  auto config = service->GetConfigOption();
+  auto local_mtu = config.mtu;
+  auto local_mps = link_->GetMps();
+
+  auto new_channel = link_->AllocateDynamicChannel(psm, remote_cid, {});
+  if (new_channel == nullptr) {
+    LOG_WARN("Can't allocate dynamic channel");
+    send_connection_response(signal_id, kInvalidCid, 0, 0, 0,
+                             LeCreditBasedConnectionResponseResult::NO_RESOURCES_AVAILABLE);
+
+    return;
+  }
+  send_connection_response(signal_id, remote_cid, local_mtu, local_mps, link_->GetInitialCredit(),
+                           LeCreditBasedConnectionResponseResult::SUCCESS);
+  auto* data_controller = reinterpret_cast<l2cap::internal::LeCreditBasedDataController*>(
+      data_pipeline_manager_->GetDataController(new_channel->GetCid()));
+  data_controller->SetMtu(std::min(mtu, local_mtu));
+  data_controller->SetMps(std::min(mps, local_mps));
+  data_controller->OnCredit(initial_credits);
+  auto user_channel = std::make_unique<DynamicChannel>(new_channel, handler_);
+  dynamic_service_manager_->GetService(psm)->NotifyChannelCreation(std::move(user_channel));
+}
+
+void LeSignallingManager::OnConnectionResponse(SignalId signal_id, Cid remote_cid, Mtu mtu, uint16_t mps,
+                                               uint16_t initial_credits, LeCreditBasedConnectionResponseResult result) {
+  if (signal_id != command_just_sent_.signal_id_) {
+    LOG_WARN("Unexpected response: no pending request");
+    return;
+  }
+  if (command_just_sent_.command_code_ != LeCommandCode::LE_CREDIT_BASED_CONNECTION_REQUEST) {
+    LOG_WARN("Unexpected response: no pending request");
+    return;
+  }
+  alarm_.Cancel();
+  command_just_sent_.signal_id_ = kInitialSignalId;
+  if (result != LeCreditBasedConnectionResponseResult::SUCCESS) {
+    LOG_WARN("Connection failed: %s", LeCreditBasedConnectionResponseResultText(result).data());
+    link_->OnOutgoingConnectionRequestFail(command_just_sent_.source_cid_);
+    handle_send_next_command();
+    return;
+  }
+  auto new_channel =
+      link_->AllocateReservedDynamicChannel(command_just_sent_.source_cid_, command_just_sent_.psm_, remote_cid, {});
+  if (new_channel == nullptr) {
+    LOG_WARN("Can't allocate dynamic channel");
+    link_->OnOutgoingConnectionRequestFail(command_just_sent_.source_cid_);
+    handle_send_next_command();
+    return;
+  }
+  auto* data_controller = reinterpret_cast<l2cap::internal::LeCreditBasedDataController*>(
+      data_pipeline_manager_->GetDataController(new_channel->GetCid()));
+  data_controller->SetMtu(std::min(mtu, command_just_sent_.mtu_));
+  data_controller->SetMps(std::min(mps, command_just_sent_.mps_));
+  data_controller->OnCredit(initial_credits);
+  std::unique_ptr<DynamicChannel> user_channel = std::make_unique<DynamicChannel>(new_channel, handler_);
+  dynamic_service_manager_->GetService(command_just_sent_.psm_)->NotifyChannelCreation(std::move(user_channel));
+}
+
+void LeSignallingManager::OnDisconnectionRequest(SignalId signal_id, Cid cid, Cid remote_cid) {
+  auto channel = channel_allocator_->FindChannelByCid(cid);
+  if (channel == nullptr) {
+    LOG_WARN("Disconnect request for an unknown channel");
+    return;
+  }
+  if (channel->GetRemoteCid() != remote_cid) {
+    LOG_WARN("Disconnect request for an unmatching channel");
+    return;
+  }
+  auto builder = LeDisconnectionResponseBuilder::Create(signal_id.Value(), cid, remote_cid);
+  enqueue_buffer_->Enqueue(std::move(builder), handler_);
+  channel->OnClosed(hci::ErrorCode::SUCCESS);
+  link_->FreeDynamicChannel(cid);
+}
+
+void LeSignallingManager::OnDisconnectionResponse(SignalId signal_id, Cid cid, Cid remote_cid) {
+  if (signal_id != command_just_sent_.signal_id_ ||
+      command_just_sent_.command_code_ != LeCommandCode::DISCONNECTION_REQUEST) {
+    LOG_WARN("Unexpected response: no pending request");
+    return;
+  }
+  if (command_just_sent_.source_cid_ != cid || command_just_sent_.destination_cid_ != remote_cid) {
+    LOG_WARN("Unexpected response: cid doesn't match. Expected scid %d dcid %d, got scid %d dcid %d",
+             command_just_sent_.source_cid_, command_just_sent_.destination_cid_, cid, remote_cid);
+    handle_send_next_command();
+    return;
+  }
+  alarm_.Cancel();
+  command_just_sent_.signal_id_ = kInitialSignalId;
+  auto channel = channel_allocator_->FindChannelByCid(cid);
+  if (channel == nullptr) {
+    LOG_WARN("Disconnect response for an unknown channel");
+    handle_send_next_command();
+    return;
+  }
+
+  channel->OnClosed(hci::ErrorCode::SUCCESS);
+  link_->FreeDynamicChannel(cid);
+  handle_send_next_command();
+}
+
+void LeSignallingManager::OnCredit(Cid remote_cid, uint16_t credits) {
+  auto channel = channel_allocator_->FindChannelByRemoteCid(remote_cid);
+  if (channel == nullptr) {
+    LOG_WARN("Received credit for invalid cid %d", channel->GetCid());
+    return;
+  }
+  auto* data_controller = reinterpret_cast<l2cap::internal::LeCreditBasedDataController*>(
+      data_pipeline_manager_->GetDataController(channel->GetCid()));
+  data_controller->OnCredit(credits);
+}
+
+void LeSignallingManager::on_incoming_packet() {
+  auto packet = signalling_channel_->GetQueueUpEnd()->TryDequeue();
+  LeControlView control_packet_view = LeControlView::Create(*packet);
+  if (!control_packet_view.IsValid()) {
+    LOG_WARN("Invalid signalling packet received");
+    return;
+  }
+  auto code = control_packet_view.GetCode();
+  switch (code) {
+    case LeCommandCode::COMMAND_REJECT: {
+      LeCommandRejectView command_reject_view = LeCommandRejectView::Create(control_packet_view);
+      if (!command_reject_view.IsValid()) {
+        return;
+      }
+      OnCommandReject(command_reject_view);
+      return;
+    }
+
+    case LeCommandCode::CONNECTION_PARAMETER_UPDATE_REQUEST: {
+      ConnectionParameterUpdateRequestView parameter_update_req_view =
+          ConnectionParameterUpdateRequestView::Create(control_packet_view);
+      if (!parameter_update_req_view.IsValid()) {
+        return;
+      }
+      OnConnectionParameterUpdateRequest(
+          parameter_update_req_view.GetIntervalMin(), parameter_update_req_view.GetIntervalMax(),
+          parameter_update_req_view.GetSlaveLatency(), parameter_update_req_view.GetTimeoutMultiplier());
+      return;
+    }
+    case LeCommandCode::CONNECTION_PARAMETER_UPDATE_RESPONSE: {
+      ConnectionParameterUpdateResponseView parameter_update_rsp_view =
+          ConnectionParameterUpdateResponseView::Create(control_packet_view);
+      if (!parameter_update_rsp_view.IsValid()) {
+        return;
+      }
+      OnConnectionParameterUpdateResponse(parameter_update_rsp_view.GetResult());
+      return;
+    }
+    case LeCommandCode::LE_CREDIT_BASED_CONNECTION_REQUEST: {
+      LeCreditBasedConnectionRequestView connection_request_view =
+          LeCreditBasedConnectionRequestView::Create(control_packet_view);
+      if (!connection_request_view.IsValid()) {
+        return;
+      }
+      OnConnectionRequest(connection_request_view.GetIdentifier(), connection_request_view.GetLePsm(),
+                          connection_request_view.GetSourceCid(), connection_request_view.GetMtu(),
+                          connection_request_view.GetMps(), connection_request_view.GetInitialCredits());
+      return;
+    }
+    case LeCommandCode::LE_CREDIT_BASED_CONNECTION_RESPONSE: {
+      LeCreditBasedConnectionResponseView connection_response_view =
+          LeCreditBasedConnectionResponseView::Create(control_packet_view);
+      if (!connection_response_view.IsValid()) {
+        return;
+      }
+      OnConnectionResponse(connection_response_view.GetIdentifier(), connection_response_view.GetDestinationCid(),
+                           connection_response_view.GetMtu(), connection_response_view.GetMps(),
+                           connection_response_view.GetInitialCredits(), connection_response_view.GetResult());
+      return;
+    }
+    case LeCommandCode::LE_FLOW_CONTROL_CREDIT: {
+      LeFlowControlCreditView credit_view = LeFlowControlCreditView::Create(control_packet_view);
+      if (!credit_view.IsValid()) {
+        return;
+      }
+      OnCredit(credit_view.GetCid(), credit_view.GetCredits());
+      return;
+    }
+    case LeCommandCode::DISCONNECTION_REQUEST: {
+      LeDisconnectionRequestView disconnection_request_view = LeDisconnectionRequestView::Create(control_packet_view);
+      if (!disconnection_request_view.IsValid()) {
+        return;
+      }
+      OnDisconnectionRequest(disconnection_request_view.GetIdentifier(), disconnection_request_view.GetDestinationCid(),
+                             disconnection_request_view.GetSourceCid());
+      return;
+    }
+    case LeCommandCode::DISCONNECTION_RESPONSE: {
+      LeDisconnectionResponseView disconnection_response_view =
+          LeDisconnectionResponseView::Create(control_packet_view);
+      if (!disconnection_response_view.IsValid()) {
+        return;
+      }
+      OnDisconnectionResponse(disconnection_response_view.GetIdentifier(),
+                              disconnection_response_view.GetDestinationCid(),
+                              disconnection_response_view.GetSourceCid());
+      return;
+    }
+    default:
+      LOG_WARN("Unhandled event 0x%x", static_cast<int>(code));
+      auto builder = LeCommandRejectNotUnderstoodBuilder::Create(control_packet_view.GetIdentifier());
+      enqueue_buffer_->Enqueue(std::move(builder), handler_);
+      return;
+  }
+}
+
+void LeSignallingManager::send_connection_response(SignalId signal_id, Cid local_cid, Mtu mtu, uint16_t mps,
+                                                   uint16_t initial_credit,
+                                                   LeCreditBasedConnectionResponseResult result) {
+  auto builder =
+      LeCreditBasedConnectionResponseBuilder::Create(signal_id.Value(), local_cid, mtu, mps, initial_credit, result);
+  enqueue_buffer_->Enqueue(std::move(builder), handler_);
+}
+
+void LeSignallingManager::on_command_timeout() {
+  LOG_WARN("Response time out");
+  if (command_just_sent_.signal_id_ == kInvalidSignalId) {
+    LOG_ERROR("No pending command");
+    return;
+  }
+  switch (command_just_sent_.command_code_) {
+    case LeCommandCode::CONNECTION_PARAMETER_UPDATE_REQUEST: {
+      link_->OnOutgoingConnectionRequestFail(command_just_sent_.source_cid_);
+      break;
+    }
+    default:
+      break;
+  }
+  handle_send_next_command();
+}
+
+void LeSignallingManager::handle_send_next_command() {
+  command_just_sent_.signal_id_ = kInvalidSignalId;
+  if (pending_commands_.empty()) {
+    return;
+  }
+
+  command_just_sent_ = pending_commands_.front();
+  pending_commands_.pop();
+  switch (command_just_sent_.command_code_) {
+    case LeCommandCode::LE_CREDIT_BASED_CONNECTION_REQUEST: {
+      auto builder = LeCreditBasedConnectionRequestBuilder::Create(
+          command_just_sent_.signal_id_.Value(), command_just_sent_.psm_, command_just_sent_.source_cid_,
+          command_just_sent_.mtu_, command_just_sent_.mps_, command_just_sent_.credits_);
+      enqueue_buffer_->Enqueue(std::move(builder), handler_);
+      alarm_.Schedule(common::BindOnce(&LeSignallingManager::on_command_timeout, common::Unretained(this)), kTimeout);
+      break;
+    }
+    case LeCommandCode::DISCONNECTION_REQUEST: {
+      auto builder = LeDisconnectionRequestBuilder::Create(
+          command_just_sent_.signal_id_.Value(), command_just_sent_.destination_cid_, command_just_sent_.source_cid_);
+      enqueue_buffer_->Enqueue(std::move(builder), handler_);
+      alarm_.Schedule(common::BindOnce(&LeSignallingManager::on_command_timeout, common::Unretained(this)), kTimeout);
+      break;
+    }
+    default: {
+      LOG_WARN("Unsupported command code 0x%x", static_cast<int>(command_just_sent_.command_code_));
+    }
+  }
+}
+}  // namespace internal
+}  // namespace le
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/le/internal/signalling_manager.h b/gd/l2cap/le/internal/signalling_manager.h
new file mode 100644
index 0000000..f218168
--- /dev/null
+++ b/gd/l2cap/le/internal/signalling_manager.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <queue>
+#include <vector>
+
+#include "l2cap/cid.h"
+#include "l2cap/internal/data_pipeline_manager.h"
+#include "l2cap/internal/dynamic_channel_allocator.h"
+#include "l2cap/l2cap_packets.h"
+#include "l2cap/le/internal/dynamic_channel_service_manager_impl.h"
+#include "l2cap/le/internal/fixed_channel_impl.h"
+#include "l2cap/le/internal/fixed_channel_service_manager_impl.h"
+#include "l2cap/mtu.h"
+#include "l2cap/psm.h"
+#include "l2cap/signal_id.h"
+#include "os/alarm.h"
+#include "os/handler.h"
+#include "os/queue.h"
+#include "packet/raw_builder.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace le {
+namespace internal {
+
+struct PendingCommand {
+  SignalId signal_id_ = kInvalidSignalId;
+  LeCommandCode command_code_;
+  Psm psm_;
+  Cid source_cid_;
+  Cid destination_cid_;
+  Mtu mtu_;
+  uint16_t mps_;
+  uint16_t credits_;
+};
+
+class Link;
+
+class LeSignallingManager {
+ public:
+  LeSignallingManager(os::Handler* handler, Link* link, l2cap::internal::DataPipelineManager* data_pipeline_manager,
+                      DynamicChannelServiceManagerImpl* dynamic_service_manager,
+                      l2cap::internal::DynamicChannelAllocator* channel_allocator);
+
+  virtual ~LeSignallingManager();
+
+  void SendConnectionRequest(Psm psm, Cid local_cid, Mtu mtu);
+
+  void SendDisconnectRequest(Cid local_cid, Cid remote_cid);
+
+  void SendConnectionParameterUpdateRequest(uint16_t interval_min, uint16_t interval_max, uint16_t slave_latency,
+                                            uint16_t timeout_multiplier);
+
+  void SendConnectionParameterUpdateResponse(SignalId signal_id, ConnectionParameterUpdateResponseResult result);
+
+  void SendCredit(Cid local_cid, uint16_t credits);
+
+  void CancelAlarm();
+
+  void OnCommandReject(LeCommandRejectView command_reject_view);
+
+  void OnConnectionParameterUpdateRequest(uint16_t interval_min, uint16_t interval_max, uint16_t slave_latency,
+                                          uint16_t timeout_multiplier);
+  void OnConnectionParameterUpdateResponse(ConnectionParameterUpdateResponseResult result);
+
+  void OnConnectionRequest(SignalId signal_id, Psm psm, Cid remote_cid, Mtu mtu, uint16_t mps,
+                           uint16_t initial_credits);
+
+  void OnConnectionResponse(SignalId signal_id, Cid remote_cid, Mtu mtu, uint16_t mps, uint16_t initial_credits,
+                            LeCreditBasedConnectionResponseResult result);
+
+  void OnDisconnectionRequest(SignalId signal_id, Cid cid, Cid remote_cid);
+
+  void OnDisconnectionResponse(SignalId signal_id, Cid cid, Cid remote_cid);
+
+  void OnCredit(Cid remote_cid, uint16_t credits);
+
+ private:
+  void on_incoming_packet();
+  void send_connection_response(SignalId signal_id, Cid local_cid, Mtu mtu, uint16_t mps, uint16_t initial_credit,
+                                LeCreditBasedConnectionResponseResult result);
+  void on_command_timeout();
+  void handle_send_next_command();
+
+  os::Handler* handler_;
+  Link* link_;
+  l2cap::internal::DataPipelineManager* data_pipeline_manager_;
+  std::shared_ptr<le::internal::FixedChannelImpl> signalling_channel_;
+  DynamicChannelServiceManagerImpl* dynamic_service_manager_;
+  l2cap::internal::DynamicChannelAllocator* channel_allocator_;
+  std::unique_ptr<os::EnqueueBuffer<packet::BasePacketBuilder>> enqueue_buffer_;
+  std::queue<PendingCommand> pending_commands_;
+  PendingCommand command_just_sent_;
+  os::Alarm alarm_;
+  SignalId next_signal_id_ = kInitialSignalId;
+};
+
+}  // namespace internal
+}  // namespace le
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/le/l2cap_le_module.cc b/gd/l2cap/le/l2cap_le_module.cc
new file mode 100644
index 0000000..4959547
--- /dev/null
+++ b/gd/l2cap/le/l2cap_le_module.cc
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_TAG "l2cap2"
+
+#include <memory>
+
+#include "common/bidi_queue.h"
+#include "hci/acl_manager.h"
+#include "hci/address.h"
+#include "hci/hci_layer.h"
+#include "hci/hci_packets.h"
+#include "l2cap/internal/parameter_provider.h"
+#include "l2cap/le/internal/fixed_channel_service_manager_impl.h"
+#include "l2cap/le/internal/link_manager.h"
+#include "module.h"
+#include "os/handler.h"
+#include "os/log.h"
+
+#include "l2cap/le/l2cap_le_module.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace le {
+
+const ModuleFactory L2capLeModule::Factory = ModuleFactory([]() { return new L2capLeModule(); });
+
+struct L2capLeModule::impl {
+  impl(os::Handler* l2cap_handler, hci::AclManager* acl_manager)
+      : l2cap_handler_(l2cap_handler), acl_manager_(acl_manager) {}
+  os::Handler* l2cap_handler_;
+  hci::AclManager* acl_manager_;
+  l2cap::internal::ParameterProvider parameter_provider_;
+  internal::FixedChannelServiceManagerImpl fixed_channel_service_manager_impl_{l2cap_handler_};
+  internal::LinkManager link_manager_{l2cap_handler_, acl_manager_, &fixed_channel_service_manager_impl_,
+                                      &parameter_provider_};
+};
+
+L2capLeModule::L2capLeModule() {}
+L2capLeModule::~L2capLeModule() {}
+
+void L2capLeModule::ListDependencies(ModuleList* list) {
+  list->add<hci::AclManager>();
+}
+
+void L2capLeModule::Start() {
+  pimpl_ = std::make_unique<impl>(GetHandler(), GetDependency<hci::AclManager>());
+}
+
+void L2capLeModule::Stop() {
+  pimpl_.reset();
+}
+
+std::string L2capLeModule::ToString() const {
+  return "L2cap Le Module";
+}
+
+std::unique_ptr<FixedChannelManager> L2capLeModule::GetFixedChannelManager() {
+  return std::unique_ptr<FixedChannelManager>(new FixedChannelManager(&pimpl_->fixed_channel_service_manager_impl_,
+                                                                      &pimpl_->link_manager_, pimpl_->l2cap_handler_));
+}
+
+}  // namespace le
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/le/l2cap_le_module.h b/gd/l2cap/le/l2cap_le_module.h
new file mode 100644
index 0000000..4ed3d9f
--- /dev/null
+++ b/gd/l2cap/le/l2cap_le_module.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <memory>
+
+#include "l2cap/le/fixed_channel_manager.h"
+#include "module.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace le {
+
+class L2capLeModule : public bluetooth::Module {
+ public:
+  L2capLeModule();
+  virtual ~L2capLeModule();
+
+  /**
+   * Get the api to the LE fixed channel l2cap module
+   */
+  virtual std::unique_ptr<FixedChannelManager> GetFixedChannelManager();
+
+  static const ModuleFactory Factory;
+
+ protected:
+  void ListDependencies(ModuleList* list) override;
+
+  void Start() override;
+
+  void Stop() override;
+
+  std::string ToString() const override;
+
+ private:
+  struct impl;
+  std::unique_ptr<impl> pimpl_;
+  DISALLOW_COPY_AND_ASSIGN(L2capLeModule);
+};
+
+}  // namespace le
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/le/l2cap_le_module_mock.h b/gd/l2cap/le/l2cap_le_module_mock.h
new file mode 100644
index 0000000..897ddb0
--- /dev/null
+++ b/gd/l2cap/le/l2cap_le_module_mock.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include "l2cap/le/l2cap_le_module.h"
+
+#include <gmock/gmock.h>
+
+// Unit test interfaces
+namespace bluetooth {
+namespace l2cap {
+namespace le {
+namespace testing {
+
+class MockL2capLeModule : public L2capLeModule {
+ public:
+  MOCK_METHOD(std::unique_ptr<FixedChannelManager>, GetFixedChannelManager, (), (override));
+};
+
+}  // namespace testing
+}  // namespace le
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/mtu.h b/gd/l2cap/mtu.h
new file mode 100644
index 0000000..fa9bc64
--- /dev/null
+++ b/gd/l2cap/mtu.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <cstdint>
+
+namespace bluetooth {
+namespace l2cap {
+
+using Mtu = uint16_t;
+
+constexpr Mtu kMinimumClassicMtu = 48;
+constexpr Mtu kMinimumLeMtu = 23;
+constexpr Mtu kDefaultClassicMtu = 672;
+
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/psm.h b/gd/l2cap/psm.h
new file mode 100644
index 0000000..0e89e62
--- /dev/null
+++ b/gd/l2cap/psm.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+
+namespace bluetooth {
+namespace l2cap {
+
+using Psm = uint16_t;
+constexpr Psm kDefaultPsm = 0;  // Invalid Psm as a default value
+
+constexpr bool IsPsmValid(Psm psm) {
+  // See Core spec 5.1 Vol 3 Part A 4.2 for definition
+  return (psm & 0x0101u) == 0x0001u;
+}
+
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/security_policy.h b/gd/l2cap/security_policy.h
new file mode 100644
index 0000000..257033a
--- /dev/null
+++ b/gd/l2cap/security_policy.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+namespace bluetooth {
+namespace l2cap {
+
+class SecurityPolicy {
+ public:
+  enum class Level {
+    LEVEL_0,  // Encryption not needed. Only applies to SDP.
+    LEVEL_2,  // Encryption desired. Only needs unauthenticated link key.
+    LEVEL_3,  // Encryption required and authenticated link key required.
+  };
+  Level security_level_ = Level::LEVEL_0;
+
+  bool RequiresAuthentication() const {
+    return security_level_ != SecurityPolicy::Level::LEVEL_0;
+  }
+};
+
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/signal_id.h b/gd/l2cap/signal_id.h
new file mode 100644
index 0000000..24372b1
--- /dev/null
+++ b/gd/l2cap/signal_id.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+
+namespace bluetooth {
+namespace l2cap {
+
+struct SignalId {
+ public:
+  constexpr SignalId(uint8_t value) : value_(value) {}
+  constexpr SignalId() : value_(1) {}
+  ~SignalId() = default;
+
+  uint8_t Value() const {
+    return value_;
+  }
+
+  bool IsValid() const {
+    return value_ != 0;
+  }
+
+  friend bool operator==(const SignalId& lhs, const SignalId& rhs);
+  friend bool operator!=(const SignalId& lhs, const SignalId& rhs);
+
+  struct SignalId& operator++();    // Prefix increment operator.
+  struct SignalId operator++(int);  // Postfix increment operator.
+  struct SignalId& operator--();    // Prefix decrement operator.
+  struct SignalId operator--(int);  // Postfix decrement operator.
+
+ private:
+  uint8_t value_;
+};
+
+constexpr SignalId kInvalidSignalId{0};
+constexpr SignalId kInitialSignalId{1};
+
+inline bool operator==(const SignalId& lhs, const SignalId& rhs) {
+  return lhs.value_ == rhs.value_;
+}
+
+inline bool operator!=(const SignalId& lhs, const SignalId& rhs) {
+  return !(lhs == rhs);
+}
+
+inline struct SignalId& SignalId::operator++() {
+  value_++;
+  if (value_ == 0) value_++;
+  return *this;
+}
+
+inline struct SignalId SignalId::operator++(int) {
+  struct SignalId tmp = *this;
+  ++*this;
+  return tmp;
+}
+
+inline struct SignalId& SignalId::operator--() {
+  value_--;
+  if (value_ == 0) value_ = 0xff;
+  return *this;
+}
+
+inline struct SignalId SignalId::operator--(int) {
+  struct SignalId tmp = *this;
+  --*this;
+  return tmp;
+}
+
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/signal_id_test.cc b/gd/l2cap/signal_id_test.cc
new file mode 100644
index 0000000..cd95425
--- /dev/null
+++ b/gd/l2cap/signal_id_test.cc
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+#include <cstdint>
+
+#include "l2cap/signal_id.h"
+
+namespace bluetooth {
+namespace l2cap {
+
+TEST(L2capSignalIdTest, valid_values) {
+  int valid = 0;
+  uint8_t i = 0;
+  while (++i != 0) {
+    SignalId signal_id(i);
+    if (signal_id.IsValid()) {
+      valid++;
+    }
+  }
+  ASSERT_TRUE(valid == 255);
+}
+
+TEST(L2capSignalIdTest, zero_invalid) {
+  SignalId signal_id(0);
+  ASSERT_FALSE(signal_id.IsValid());
+}
+
+TEST(L2capSignalIdTest, pre_increment) {
+  SignalId signal_id(0);
+  ASSERT_FALSE(signal_id.IsValid());
+
+  for (uint8_t i = 0; i != 0xff; ++i, ++signal_id) {
+    ASSERT_TRUE(i == signal_id.Value());
+  }
+}
+
+TEST(L2capSignalIdTest, post_increment) {
+  SignalId signal_id(0);
+  ASSERT_FALSE(signal_id.IsValid());
+
+  for (uint8_t i = 0; i != 0xff; i++, signal_id++) {
+    ASSERT_TRUE(i == signal_id.Value());
+  }
+}
+
+TEST(L2capSignalIdTest, almost_wrap_up) {
+  SignalId signal_id(0);
+  ASSERT_FALSE(signal_id.IsValid());
+
+  for (int i = 0; i < 255; i++) {
+    signal_id++;
+  }
+  ASSERT_EQ(0xff, signal_id.Value());
+}
+
+TEST(L2capSignalIdTest, wrap_up) {
+  SignalId signal_id(0);
+  ASSERT_FALSE(signal_id.IsValid());
+
+  for (int i = 0; i < 256; i++) {
+    signal_id++;
+  }
+  ASSERT_EQ(1, signal_id.Value());
+}
+
+TEST(L2capSignalIdTest, pre_decrement) {
+  SignalId signal_id(0);
+  ASSERT_FALSE(signal_id.IsValid());
+
+  for (uint8_t i = 0; i != 0xff; --i, --signal_id) {
+    ASSERT_TRUE(i == signal_id.Value());
+  }
+}
+
+TEST(L2capSignalIdTest, post_decrement) {
+  SignalId signal_id(0);
+  ASSERT_FALSE(signal_id.IsValid());
+
+  for (uint8_t i = 0; i != 0xff; i--, signal_id--) {
+    ASSERT_TRUE(i == signal_id.Value());
+  }
+}
+
+TEST(L2capSignalIdTest, almost_wrap_down) {
+  SignalId signal_id(0);
+  ASSERT_FALSE(signal_id.IsValid());
+
+  for (int i = 0; i < 255; i++) {
+    signal_id--;
+  }
+  ASSERT_EQ(1, signal_id.Value());
+}
+
+TEST(L2capSignalIdTest, wrap_down) {
+  SignalId signal_id(0);
+  ASSERT_FALSE(signal_id.IsValid());
+
+  for (int i = 0; i < 256; i++) {
+    signal_id--;
+  }
+  ASSERT_EQ(0xff, signal_id.Value());
+}
+
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/module.cc b/gd/module.cc
new file mode 100644
index 0000000..f12da97
--- /dev/null
+++ b/gd/module.cc
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "module.h"
+
+using ::bluetooth::os::Handler;
+using ::bluetooth::os::Thread;
+
+namespace bluetooth {
+
+constexpr std::chrono::milliseconds kModuleStopTimeout = std::chrono::milliseconds(20);
+
+ModuleFactory::ModuleFactory(std::function<Module*()> ctor) : ctor_(ctor) {
+}
+
+std::string Module::ToString() const {
+  return "Module";
+}
+
+Handler* Module::GetHandler() const {
+  ASSERT_LOG(handler_ != nullptr, "Can't get handler when it's not started");
+  return handler_;
+}
+
+const ModuleRegistry* Module::GetModuleRegistry() const {
+  return registry_;
+}
+
+Module* Module::GetDependency(const ModuleFactory* module) const {
+  for (auto& dependency : dependencies_.list_) {
+    if (dependency == module) {
+      return registry_->Get(module);
+    }
+  }
+
+  ASSERT_LOG(false, "Module was not listed as a dependency in ListDependencies");
+}
+
+Module* ModuleRegistry::Get(const ModuleFactory* module) const {
+  auto instance = started_modules_.find(module);
+  ASSERT(instance != started_modules_.end());
+  return instance->second;
+}
+
+bool ModuleRegistry::IsStarted(const ModuleFactory* module) const {
+  return started_modules_.find(module) != started_modules_.end();
+}
+
+void ModuleRegistry::Start(ModuleList* modules, Thread* thread) {
+  for (auto it = modules->list_.begin(); it != modules->list_.end(); it++) {
+    Start(*it, thread);
+  }
+}
+
+void ModuleRegistry::set_registry_and_handler(Module* instance, Thread* thread) const {
+  instance->registry_ = this;
+  instance->handler_ = new Handler(thread);
+}
+
+Module* ModuleRegistry::Start(const ModuleFactory* module, Thread* thread) {
+  auto started_instance = started_modules_.find(module);
+  if (started_instance != started_modules_.end()) {
+    return started_instance->second;
+  }
+
+  Module* instance = module->ctor_();
+  set_registry_and_handler(instance, thread);
+
+  instance->ListDependencies(&instance->dependencies_);
+  Start(&instance->dependencies_, thread);
+
+  instance->Start();
+  start_order_.push_back(module);
+  started_modules_[module] = instance;
+  return instance;
+}
+
+void ModuleRegistry::StopAll() {
+  // Since modules were brought up in dependency order, it is safe to tear down by going in reverse order.
+  for (auto it = start_order_.rbegin(); it != start_order_.rend(); it++) {
+    auto instance = started_modules_.find(*it);
+    ASSERT(instance != started_modules_.end());
+
+    // Clear the handler before stopping the module to allow it to shut down gracefully.
+    instance->second->handler_->Clear();
+    instance->second->handler_->WaitUntilStopped(kModuleStopTimeout);
+    instance->second->Stop();
+
+    delete instance->second->handler_;
+    delete instance->second;
+    started_modules_.erase(instance);
+  }
+
+  ASSERT(started_modules_.empty());
+  start_order_.clear();
+}
+
+os::Handler* ModuleRegistry::GetModuleHandler(const ModuleFactory* module) const {
+  auto started_instance = started_modules_.find(module);
+  if (started_instance != started_modules_.end()) {
+    return started_instance->second->GetHandler();
+  }
+  return nullptr;
+}
+}  // namespace bluetooth
diff --git a/gd/module.h b/gd/module.h
new file mode 100644
index 0000000..045865d
--- /dev/null
+++ b/gd/module.h
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <functional>
+#include <future>
+#include <map>
+#include <string>
+#include <vector>
+
+#include "common/bind.h"
+#include "os/handler.h"
+#include "os/log.h"
+#include "os/thread.h"
+
+namespace bluetooth {
+
+class Module;
+class ModuleRegistry;
+
+class ModuleFactory {
+ friend ModuleRegistry;
+ public:
+  ModuleFactory(std::function<Module*()> ctor);
+
+ private:
+  std::function<Module*()> ctor_;
+};
+
+class ModuleList {
+ friend ModuleRegistry;
+ friend Module;
+ public:
+  template <class T>
+  void add() {
+    list_.push_back(&T::Factory);
+  }
+
+ private:
+  std::vector<const ModuleFactory*> list_;
+};
+
+// Each leaf node module must have a factory like so:
+//
+// static const ModuleFactory Factory;
+//
+// which will provide a constructor for the module registry to call.
+// The module registry will also use the factory as the identifier
+// for that module.
+class Module {
+ friend ModuleRegistry;
+ public:
+  virtual ~Module() = default;
+ protected:
+  // Populate the provided list with modules that must start before yours
+  virtual void ListDependencies(ModuleList* list) = 0;
+
+  // You can grab your started dependencies during or after this call
+  // using GetDependency(), or access the module registry via GetModuleRegistry()
+  virtual void Start() = 0;
+
+  // Release all resources, you're about to be deleted
+  virtual void Stop() = 0;
+
+  virtual std::string ToString() const;
+
+  ::bluetooth::os::Handler* GetHandler() const;
+
+  const ModuleRegistry* GetModuleRegistry() const;
+
+  template <class T>
+  T* GetDependency() const {
+    return static_cast<T*>(GetDependency(&T::Factory));
+  }
+
+ private:
+  Module* GetDependency(const ModuleFactory* module) const;
+
+  ::bluetooth::os::Handler* handler_ = nullptr;
+  ModuleList dependencies_;
+  const ModuleRegistry* registry_;
+};
+
+class ModuleRegistry {
+ friend Module;
+ friend class StackManager;
+ public:
+  template <class T>
+  bool IsStarted() const {
+    return IsStarted(&T::Factory);
+  }
+
+  bool IsStarted(const ModuleFactory* factory) const;
+
+  // Start all the modules on this list and their dependencies
+  // in dependency order
+  void Start(ModuleList* modules, ::bluetooth::os::Thread* thread);
+
+  template <class T>
+  T* Start(::bluetooth::os::Thread* thread) {
+    return static_cast<T*>(Start(&T::Factory, thread));
+  }
+
+  Module* Start(const ModuleFactory* id, ::bluetooth::os::Thread* thread);
+
+  // Stop all running modules in reverse order of start
+  void StopAll();
+
+ protected:
+  Module* Get(const ModuleFactory* module) const;
+
+  void set_registry_and_handler(Module* instance, ::bluetooth::os::Thread* thread) const;
+
+  os::Handler* GetModuleHandler(const ModuleFactory* module) const;
+
+  std::map<const ModuleFactory*, Module*> started_modules_;
+  std::vector<const ModuleFactory*> start_order_;
+};
+
+class TestModuleRegistry : public ModuleRegistry {
+ public:
+  void InjectTestModule(const ModuleFactory* module, Module* instance) {
+    start_order_.push_back(module);
+    started_modules_[module] = instance;
+    set_registry_and_handler(instance, &test_thread);
+  }
+
+  Module* GetModuleUnderTest(const ModuleFactory* module) const {
+    return Get(module);
+  }
+
+  os::Handler* GetTestModuleHandler(const ModuleFactory* module) const {
+    return GetModuleHandler(module);
+  }
+
+  os::Thread& GetTestThread() {
+    return test_thread;
+  }
+
+  bool SynchronizeModuleHandler(const ModuleFactory* module, std::chrono::milliseconds timeout) const {
+    std::promise<void> promise;
+    auto future = promise.get_future();
+    os::Handler* handler = GetTestModuleHandler(module);
+    handler->Post(common::BindOnce(&std::promise<void>::set_value, common::Unretained(&promise)));
+    return future.wait_for(timeout) == std::future_status::ready;
+  }
+
+ private:
+  os::Thread test_thread{"test_thread", os::Thread::Priority::NORMAL};
+};
+
+}  // namespace bluetooth
diff --git a/gd/module_unittest.cc b/gd/module_unittest.cc
new file mode 100644
index 0000000..d0efb5d
--- /dev/null
+++ b/gd/module_unittest.cc
@@ -0,0 +1,203 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "module.h"
+
+#include "gtest/gtest.h"
+
+using ::bluetooth::os::Thread;
+
+namespace bluetooth {
+namespace {
+
+class ModuleTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    thread_ = new Thread("test_thread", Thread::Priority::NORMAL);
+    registry_ = new ModuleRegistry();
+  }
+
+  void TearDown() override {
+    delete registry_;
+    delete thread_;
+  }
+
+  ModuleRegistry* registry_;
+  Thread* thread_;
+};
+
+class TestModuleNoDependency : public Module {
+ public:
+  static const ModuleFactory Factory;
+
+ protected:
+  void ListDependencies(ModuleList* list) override {
+  }
+
+  void Start() override {
+    // A module is not considered started until Start() finishes
+    EXPECT_FALSE(GetModuleRegistry()->IsStarted<TestModuleNoDependency>());
+  }
+
+  void Stop() override {
+    // A module is not considered stopped until after Stop() finishes
+    EXPECT_TRUE(GetModuleRegistry()->IsStarted<TestModuleNoDependency>());
+  }
+};
+
+const ModuleFactory TestModuleNoDependency::Factory = ModuleFactory([]() {
+  return new TestModuleNoDependency();
+});
+
+class TestModuleOneDependency : public Module {
+ public:
+  static const ModuleFactory Factory;
+
+ protected:
+  void ListDependencies(ModuleList* list) override {
+    list->add<TestModuleNoDependency>();
+  }
+
+  void Start() override {
+    EXPECT_TRUE(GetModuleRegistry()->IsStarted<TestModuleNoDependency>());
+
+    // A module is not considered started until Start() finishes
+    EXPECT_FALSE(GetModuleRegistry()->IsStarted<TestModuleOneDependency>());
+  }
+
+  void Stop() override {
+    EXPECT_TRUE(GetModuleRegistry()->IsStarted<TestModuleNoDependency>());
+
+    // A module is not considered stopped until after Stop() finishes
+    EXPECT_TRUE(GetModuleRegistry()->IsStarted<TestModuleOneDependency>());
+  }
+};
+
+const ModuleFactory TestModuleOneDependency::Factory = ModuleFactory([]() {
+  return new TestModuleOneDependency();
+});
+
+
+class TestModuleNoDependencyTwo : public Module {
+ public:
+  static const ModuleFactory Factory;
+
+ protected:
+  void ListDependencies(ModuleList* list) override {
+  }
+
+  void Start() override {
+    // A module is not considered started until Start() finishes
+    EXPECT_FALSE(GetModuleRegistry()->IsStarted<TestModuleNoDependencyTwo>());
+  }
+
+  void Stop() override {
+    // A module is not considered stopped until after Stop() finishes
+    EXPECT_TRUE(GetModuleRegistry()->IsStarted<TestModuleNoDependencyTwo>());
+  }
+};
+
+const ModuleFactory TestModuleNoDependencyTwo::Factory = ModuleFactory([]() {
+  return new TestModuleNoDependencyTwo();
+});
+
+class TestModuleTwoDependencies : public Module {
+ public:
+  static const ModuleFactory Factory;
+
+ protected:
+  void ListDependencies(ModuleList* list) override {
+    list->add<TestModuleOneDependency>();
+    list->add<TestModuleNoDependencyTwo>();
+  }
+
+  void Start() override {
+    EXPECT_TRUE(GetModuleRegistry()->IsStarted<TestModuleOneDependency>());
+    EXPECT_TRUE(GetModuleRegistry()->IsStarted<TestModuleNoDependencyTwo>());
+
+    // A module is not considered started until Start() finishes
+    EXPECT_FALSE(GetModuleRegistry()->IsStarted<TestModuleTwoDependencies>());
+  }
+
+  void Stop() override {
+    EXPECT_TRUE(GetModuleRegistry()->IsStarted<TestModuleOneDependency>());
+    EXPECT_TRUE(GetModuleRegistry()->IsStarted<TestModuleNoDependencyTwo>());
+
+    // A module is not considered stopped until after Stop() finishes
+    EXPECT_TRUE(GetModuleRegistry()->IsStarted<TestModuleTwoDependencies>());
+  }
+};
+
+const ModuleFactory TestModuleTwoDependencies::Factory = ModuleFactory([]() {
+  return new TestModuleTwoDependencies();
+});
+
+TEST_F(ModuleTest, no_dependency) {
+  ModuleList list;
+  list.add<TestModuleNoDependency>();
+  registry_->Start(&list, thread_);
+
+  EXPECT_TRUE(registry_->IsStarted<TestModuleNoDependency>());
+  EXPECT_FALSE(registry_->IsStarted<TestModuleOneDependency>());
+  EXPECT_FALSE(registry_->IsStarted<TestModuleNoDependencyTwo>());
+  EXPECT_FALSE(registry_->IsStarted<TestModuleTwoDependencies>());
+
+  registry_->StopAll();
+
+  EXPECT_FALSE(registry_->IsStarted<TestModuleNoDependency>());
+  EXPECT_FALSE(registry_->IsStarted<TestModuleOneDependency>());
+  EXPECT_FALSE(registry_->IsStarted<TestModuleNoDependencyTwo>());
+  EXPECT_FALSE(registry_->IsStarted<TestModuleTwoDependencies>());
+}
+
+TEST_F(ModuleTest, one_dependency) {
+  ModuleList list;
+  list.add<TestModuleOneDependency>();
+  registry_->Start(&list, thread_);
+
+  EXPECT_TRUE(registry_->IsStarted<TestModuleNoDependency>());
+  EXPECT_TRUE(registry_->IsStarted<TestModuleOneDependency>());
+  EXPECT_FALSE(registry_->IsStarted<TestModuleNoDependencyTwo>());
+  EXPECT_FALSE(registry_->IsStarted<TestModuleTwoDependencies>());
+
+  registry_->StopAll();
+
+  EXPECT_FALSE(registry_->IsStarted<TestModuleNoDependency>());
+  EXPECT_FALSE(registry_->IsStarted<TestModuleOneDependency>());
+  EXPECT_FALSE(registry_->IsStarted<TestModuleNoDependencyTwo>());
+  EXPECT_FALSE(registry_->IsStarted<TestModuleTwoDependencies>());
+}
+
+TEST_F(ModuleTest, two_dependencies) {
+  ModuleList list;
+  list.add<TestModuleTwoDependencies>();
+  registry_->Start(&list, thread_);
+
+  EXPECT_TRUE(registry_->IsStarted<TestModuleNoDependency>());
+  EXPECT_TRUE(registry_->IsStarted<TestModuleOneDependency>());
+  EXPECT_TRUE(registry_->IsStarted<TestModuleNoDependencyTwo>());
+  EXPECT_TRUE(registry_->IsStarted<TestModuleTwoDependencies>());
+
+  registry_->StopAll();
+
+  EXPECT_FALSE(registry_->IsStarted<TestModuleNoDependency>());
+  EXPECT_FALSE(registry_->IsStarted<TestModuleOneDependency>());
+  EXPECT_FALSE(registry_->IsStarted<TestModuleNoDependencyTwo>());
+  EXPECT_FALSE(registry_->IsStarted<TestModuleTwoDependencies>());
+}
+
+}  // namespace
+}  // namespace bluetooth
diff --git a/gd/neighbor/Android.bp b/gd/neighbor/Android.bp
new file mode 100644
index 0000000..35ec925
--- /dev/null
+++ b/gd/neighbor/Android.bp
@@ -0,0 +1,26 @@
+filegroup {
+    name: "BluetoothNeighborSources",
+    srcs: [
+            "connectability.cc",
+            "discoverability.cc",
+            "inquiry.cc",
+            "name.cc",
+            "name_db.cc",
+            "page.cc",
+            "scan.cc",
+    ],
+}
+
+filegroup {
+    name: "BluetoothNeighborTestSources",
+    srcs: [
+            "inquiry_test.cc",
+    ],
+}
+
+filegroup {
+    name: "BluetoothFacade_neighbor",
+    srcs: [
+        "facade/facade.cc",
+    ],
+}
diff --git a/gd/neighbor/cert/neighbor_test.py b/gd/neighbor/cert/neighbor_test.py
new file mode 100644
index 0000000..e6003c2
--- /dev/null
+++ b/gd/neighbor/cert/neighbor_test.py
@@ -0,0 +1,185 @@
+#!/usr/bin/env python3
+#
+#   Copyright 2019 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import os
+import sys
+import logging
+
+from cert.gd_base_test_facade_only import GdFacadeOnlyBaseTestClass
+from cert.event_callback_stream import EventCallbackStream
+from cert.event_asserts import EventAsserts
+from google.protobuf import empty_pb2 as empty_proto
+from facade import rootservice_pb2 as facade_rootservice
+from hci.facade import facade_pb2 as hci_facade
+from hci.facade import controller_facade_pb2 as controller_facade
+from neighbor.facade import facade_pb2 as neighbor_facade
+from bluetooth_packets_python3 import hci_packets
+import bluetooth_packets_python3 as bt_packets
+
+
+class NeighborTest(GdFacadeOnlyBaseTestClass):
+
+    def setup_test(self):
+        self.device_under_test.rootservice.StartStack(
+            facade_rootservice.StartStackRequest(
+                module_under_test=facade_rootservice.BluetoothModule.Value(
+                    'HCI_INTERFACES'),))
+        self.cert_device.rootservice.StartStack(
+            facade_rootservice.StartStackRequest(
+                module_under_test=facade_rootservice.BluetoothModule.Value(
+                    'HCI'),))
+
+        self.device_under_test.wait_channel_ready()
+        self.cert_device.wait_channel_ready()
+
+    def teardown_test(self):
+        self.device_under_test.rootservice.StopStack(
+            facade_rootservice.StopStackRequest())
+        self.cert_device.rootservice.StopStack(
+            facade_rootservice.StopStackRequest())
+
+    def register_for_dut_event(self, event_code):
+        msg = hci_facade.EventCodeMsg(code=int(event_code))
+        self.device_under_test.hci.RegisterEventHandler(msg)
+
+    def register_for_event(self, event_code):
+        msg = hci_facade.EventCodeMsg(code=int(event_code))
+        self.cert_device.hci.RegisterEventHandler(msg)
+
+    def register_for_le_event(self, event_code):
+        msg = hci_facade.LeSubeventCodeMsg(code=int(event_code))
+        self.cert_device.hci.RegisterLeEventHandler(msg)
+
+    def enqueue_hci_command(self, command, expect_complete):
+        cmd_bytes = bytes(command.Serialize())
+        cmd = hci_facade.CommandMsg(command=cmd_bytes)
+        if (expect_complete):
+            self.cert_device.hci.EnqueueCommandWithComplete(cmd)
+        else:
+            self.cert_device.hci.EnqueueCommandWithStatus(cmd)
+
+    def test_inquiry_from_dut(self):
+        inquiry_msg = neighbor_facade.InquiryMsg(
+            inquiry_mode=neighbor_facade.DiscoverabilityMode.GENERAL,
+            result_mode=neighbor_facade.ResultMode.STANDARD,
+            length_1_28s=3,
+            max_results=0)
+        with EventCallbackStream(
+                self.device_under_test.neighbor.SetInquiryMode(
+                    inquiry_msg)) as inquiry_event_stream:
+            hci_event_asserts = EventAsserts(inquiry_event_stream)
+            self.enqueue_hci_command(
+                hci_packets.WriteScanEnableBuilder(
+                    hci_packets.ScanEnable.INQUIRY_AND_PAGE_SCAN), True)
+            hci_event_asserts.assert_event_occurs(
+                lambda msg: b'\x02\x0f' in msg.packet
+                # Expecting an HCI Event (code 0x02, length 0x0f)
+            )
+
+    def test_inquiry_rssi_from_dut(self):
+        inquiry_msg = neighbor_facade.InquiryMsg(
+            inquiry_mode=neighbor_facade.DiscoverabilityMode.GENERAL,
+            result_mode=neighbor_facade.ResultMode.RSSI,
+            length_1_28s=3,
+            max_results=0)
+        with EventCallbackStream(
+                self.device_under_test.neighbor.SetInquiryMode(
+                    inquiry_msg)) as inquiry_event_stream:
+            hci_event_asserts = EventAsserts(inquiry_event_stream)
+            self.enqueue_hci_command(
+                hci_packets.WriteScanEnableBuilder(
+                    hci_packets.ScanEnable.INQUIRY_AND_PAGE_SCAN), True)
+            hci_event_asserts.assert_event_occurs(
+                lambda msg: b'\x22\x0f' in msg.packet
+                # Expecting an HCI Event (code 0x22, length 0x0f)
+            )
+
+    def test_inquiry_extended_from_dut(self):
+        name_string = b'Im_A_Cert'
+        gap_name = hci_packets.GapData()
+        gap_name.data_type = hci_packets.GapDataType.COMPLETE_LOCAL_NAME
+        gap_name.data = list(bytes(name_string))
+        gap_data = list([gap_name])
+
+        self.enqueue_hci_command(
+            hci_packets.WriteExtendedInquiryResponseBuilder(
+                hci_packets.FecRequired.NOT_REQUIRED, gap_data), True)
+        inquiry_msg = neighbor_facade.InquiryMsg(
+            inquiry_mode=neighbor_facade.DiscoverabilityMode.GENERAL,
+            result_mode=neighbor_facade.ResultMode.EXTENDED,
+            length_1_28s=3,
+            max_results=0)
+        with EventCallbackStream(
+                self.device_under_test.neighbor.SetInquiryMode(
+                    inquiry_msg)) as inquiry_event_stream:
+            hci_event_asserts = EventAsserts(inquiry_event_stream)
+            self.enqueue_hci_command(
+                hci_packets.WriteScanEnableBuilder(
+                    hci_packets.ScanEnable.INQUIRY_AND_PAGE_SCAN), True)
+            hci_event_asserts.assert_event_occurs(
+                lambda msg: name_string in msg.packet)
+
+    def test_remote_name(self):
+        self.register_for_dut_event(
+            hci_packets.EventCode.REMOTE_HOST_SUPPORTED_FEATURES_NOTIFICATION)
+
+        with EventCallbackStream(self.cert_device.hci.FetchEvents(empty_proto.Empty())) as hci_event_stream, \
+            EventCallbackStream(self.device_under_test.neighbor.GetRemoteNameEvents(empty_proto.Empty())) as name_event_stream:
+            name_event_asserts = EventAsserts(name_event_stream)
+            hci_event_asserts = EventAsserts(hci_event_stream)
+
+            cert_name = b'Im_A_Cert'
+            padded_name = cert_name
+            while len(padded_name) < 248:
+                padded_name = padded_name + b'\0'
+            self.enqueue_hci_command(
+                hci_packets.WriteLocalNameBuilder(padded_name), True)
+
+            hci_event_asserts.assert_event_occurs(
+                lambda msg: b'\x0e\x04\x01\x13\x0c' in msg.event)
+
+            address = hci_packets.Address()
+
+            def get_address_from_complete(packet):
+                packet_bytes = packet.event
+                if b'\x0e\x0a\x01\x09\x10' in packet_bytes:
+                    nonlocal address
+                    addr_view = hci_packets.ReadBdAddrCompleteView(
+                        hci_packets.CommandCompleteView(
+                            hci_packets.EventPacketView(
+                                bt_packets.PacketViewLittleEndian(
+                                    list(packet_bytes)))))
+                    address = addr_view.GetBdAddr()
+                    return True
+                return False
+
+            # DUT Enables scans and gets its address
+            self.enqueue_hci_command(
+                hci_packets.WriteScanEnableBuilder(
+                    hci_packets.ScanEnable.INQUIRY_AND_PAGE_SCAN), True)
+            self.enqueue_hci_command(hci_packets.ReadBdAddrBuilder(), True)
+
+            hci_event_asserts.assert_event_occurs(get_address_from_complete)
+
+            cert_address = address.encode('utf8')
+
+            self.device_under_test.neighbor.ReadRemoteName(
+                neighbor_facade.RemoteNameRequestMsg(
+                    address=cert_address,
+                    page_scan_repetition_mode=1,
+                    clock_offset=0x6855))
+            name_event_asserts.assert_event_occurs(
+                lambda msg: cert_name in msg.name)
diff --git a/gd/neighbor/connectability.cc b/gd/neighbor/connectability.cc
new file mode 100644
index 0000000..8a60bdd
--- /dev/null
+++ b/gd/neighbor/connectability.cc
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_TAG "neighbor2"
+
+#include <memory>
+
+#include "hci/hci_layer.h"
+#include "hci/hci_packets.h"
+#include "module.h"
+#include "neighbor/connectability.h"
+#include "neighbor/scan.h"
+#include "os/handler.h"
+#include "os/log.h"
+
+namespace bluetooth {
+namespace neighbor {
+
+struct ConnectabilityModule::impl {
+  void StartConnectability();
+  void StopConnectability();
+  bool IsConnectable() const;
+
+  void Start();
+  void Stop();
+
+  impl(ConnectabilityModule& connectability_module);
+
+ private:
+  ConnectabilityModule& module_;
+
+  neighbor::ScanModule* scan_module_;
+};
+
+const ModuleFactory neighbor::ConnectabilityModule::Factory =
+    ModuleFactory([]() { return new ConnectabilityModule(); });
+
+neighbor::ConnectabilityModule::impl::impl(neighbor::ConnectabilityModule& module) : module_(module) {}
+
+void neighbor::ConnectabilityModule::impl::StartConnectability() {
+  scan_module_->SetPageScan();
+}
+
+void neighbor::ConnectabilityModule::impl::StopConnectability() {
+  scan_module_->ClearPageScan();
+}
+
+bool neighbor::ConnectabilityModule::impl::IsConnectable() const {
+  return scan_module_->IsPageEnabled();
+}
+
+void neighbor::ConnectabilityModule::impl::Start() {
+  scan_module_ = module_.GetDependency<neighbor::ScanModule>();
+}
+
+void neighbor::ConnectabilityModule::impl::Stop() {}
+
+neighbor::ConnectabilityModule::ConnectabilityModule() : pimpl_(std::make_unique<impl>(*this)) {}
+
+neighbor::ConnectabilityModule::~ConnectabilityModule() {
+  pimpl_.reset();
+}
+
+void neighbor::ConnectabilityModule::StartConnectability() {
+  pimpl_->StartConnectability();
+}
+
+void neighbor::ConnectabilityModule::StopConnectability() {
+  pimpl_->StopConnectability();
+}
+
+bool neighbor::ConnectabilityModule::IsConnectable() const {
+  return pimpl_->IsConnectable();
+}
+
+/**
+ * Module stuff
+ */
+void neighbor::ConnectabilityModule::ListDependencies(ModuleList* list) {
+  list->add<neighbor::ScanModule>();
+}
+
+void neighbor::ConnectabilityModule::Start() {
+  pimpl_->Start();
+}
+
+void neighbor::ConnectabilityModule::Stop() {
+  pimpl_->Stop();
+}
+
+}  // namespace neighbor
+}  // namespace bluetooth
diff --git a/gd/neighbor/connectability.h b/gd/neighbor/connectability.h
new file mode 100644
index 0000000..8cdd643
--- /dev/null
+++ b/gd/neighbor/connectability.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <memory>
+
+#include "module.h"
+
+namespace bluetooth {
+namespace neighbor {
+
+class ConnectabilityModule : public bluetooth::Module {
+ public:
+  void StartConnectability();
+  void StopConnectability();
+  bool IsConnectable() const;
+
+  ConnectabilityModule();
+  ~ConnectabilityModule();
+
+  static const ModuleFactory Factory;
+
+ protected:
+  void ListDependencies(ModuleList* list) override;
+  void Start() override;
+  void Stop() override;
+
+ private:
+  struct impl;
+  std::unique_ptr<impl> pimpl_;
+
+  DISALLOW_COPY_AND_ASSIGN(ConnectabilityModule);
+};
+
+}  // namespace neighbor
+}  // namespace bluetooth
diff --git a/gd/neighbor/discoverability.cc b/gd/neighbor/discoverability.cc
new file mode 100644
index 0000000..3514caf
--- /dev/null
+++ b/gd/neighbor/discoverability.cc
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_TAG "bt_gd_neigh"
+
+#include <memory>
+
+#include "common/bind.h"
+#include "hci/hci_layer.h"
+#include "hci/hci_packets.h"
+#include "module.h"
+#include "neighbor/discoverability.h"
+#include "neighbor/scan.h"
+#include "os/handler.h"
+#include "os/log.h"
+
+namespace bluetooth {
+namespace neighbor {
+
+static constexpr uint8_t kGeneralInquiryAccessCode = 0x33;
+static constexpr uint8_t kLimitedInquiryAccessCode = 0x00;
+
+struct DiscoverabilityModule::impl {
+  void StartDiscoverability(std::vector<hci::Lap>& laps);
+  void StopDiscoverability();
+
+  bool IsGeneralDiscoverabilityEnabled() const;
+  bool IsLimitedDiscoverabilityEnabled() const;
+
+  void Start();
+
+  impl(DiscoverabilityModule& discoverability_module);
+
+ private:
+  uint8_t num_supported_iac_;
+  std::vector<hci::Lap> laps_;
+
+  void OnCommandComplete(hci::CommandCompleteView status);
+
+  hci::HciLayer* hci_layer_;
+  neighbor::ScanModule* scan_module_;
+  os::Handler* handler_;
+
+  DiscoverabilityModule& module_;
+  void Dump() const;
+};
+
+const ModuleFactory neighbor::DiscoverabilityModule::Factory =
+    ModuleFactory([]() { return new neighbor::DiscoverabilityModule(); });
+
+neighbor::DiscoverabilityModule::impl::impl(neighbor::DiscoverabilityModule& module) : module_(module) {}
+
+void neighbor::DiscoverabilityModule::impl::OnCommandComplete(hci::CommandCompleteView status) {
+  switch (status.GetCommandOpCode()) {
+    case hci::OpCode::READ_CURRENT_IAC_LAP: {
+      auto packet = hci::ReadCurrentIacLapCompleteView::Create(status);
+      ASSERT(packet.IsValid());
+      ASSERT(packet.GetStatus() == hci::ErrorCode::SUCCESS);
+      laps_ = packet.GetLapsToRead();
+    } break;
+
+    case hci::OpCode::WRITE_CURRENT_IAC_LAP: {
+      auto packet = hci::WriteCurrentIacLapCompleteView::Create(status);
+      ASSERT(packet.IsValid());
+      ASSERT(packet.GetStatus() == hci::ErrorCode::SUCCESS);
+    } break;
+
+    case hci::OpCode::READ_NUMBER_OF_SUPPORTED_IAC: {
+      auto packet = hci::ReadNumberOfSupportedIacCompleteView::Create(status);
+      ASSERT(packet.IsValid());
+      ASSERT(packet.GetStatus() == hci::ErrorCode::SUCCESS);
+      num_supported_iac_ = packet.GetNumSupportIac();
+    } break;
+    default:
+      LOG_WARN("Unhandled command:%s", hci::OpCodeText(status.GetCommandOpCode()).c_str());
+      break;
+  }
+}
+
+void neighbor::DiscoverabilityModule::impl::StartDiscoverability(std::vector<hci::Lap>& laps) {
+  ASSERT(laps.size() <= num_supported_iac_);
+  hci_layer_->EnqueueCommand(hci::WriteCurrentIacLapBuilder::Create(laps),
+                             common::BindOnce(&impl::OnCommandComplete, common::Unretained(this)), handler_);
+  hci_layer_->EnqueueCommand(hci::ReadCurrentIacLapBuilder::Create(),
+                             common::BindOnce(&impl::OnCommandComplete, common::Unretained(this)), handler_);
+  scan_module_->SetInquiryScan();
+}
+
+void neighbor::DiscoverabilityModule::impl::StopDiscoverability() {
+  scan_module_->ClearInquiryScan();
+}
+
+bool neighbor::DiscoverabilityModule::impl::IsGeneralDiscoverabilityEnabled() const {
+  return scan_module_->IsInquiryEnabled() && laps_.size() == 1;
+}
+
+bool neighbor::DiscoverabilityModule::impl::IsLimitedDiscoverabilityEnabled() const {
+  return scan_module_->IsInquiryEnabled() && laps_.size() == 2;
+}
+
+void neighbor::DiscoverabilityModule::impl::Start() {
+  hci_layer_ = module_.GetDependency<hci::HciLayer>();
+  scan_module_ = module_.GetDependency<neighbor::ScanModule>();
+  handler_ = module_.GetHandler();
+
+  hci_layer_->EnqueueCommand(hci::ReadCurrentIacLapBuilder::Create(),
+                             common::BindOnce(&impl::OnCommandComplete, common::Unretained(this)), handler_);
+
+  hci_layer_->EnqueueCommand(hci::ReadNumberOfSupportedIacBuilder::Create(),
+                             common::BindOnce(&impl::OnCommandComplete, common::Unretained(this)), handler_);
+  LOG_DEBUG("Started discoverability module");
+}
+
+void neighbor::DiscoverabilityModule::impl::Dump() const {
+  LOG_DEBUG("Number of supported iacs:%hhd", num_supported_iac_);
+  LOG_DEBUG("Number of current iacs:%zd", laps_.size());
+  for (auto it : laps_) {
+    LOG_DEBUG("  discoverability lap:%x", it.lap_);
+  }
+}
+
+neighbor::DiscoverabilityModule::DiscoverabilityModule() : pimpl_(std::make_unique<impl>(*this)) {}
+
+neighbor::DiscoverabilityModule::~DiscoverabilityModule() {
+  pimpl_.reset();
+}
+
+void neighbor::DiscoverabilityModule::StartGeneralDiscoverability() {
+  std::vector<hci::Lap> laps;
+  {
+    hci::Lap lap;
+    lap.lap_ = kGeneralInquiryAccessCode;
+    laps.push_back(lap);
+  }
+  pimpl_->StartDiscoverability(laps);
+}
+
+void neighbor::DiscoverabilityModule::StartLimitedDiscoverability() {
+  std::vector<hci::Lap> laps;
+  {
+    hci::Lap lap;
+    lap.lap_ = kGeneralInquiryAccessCode;
+    laps.push_back(lap);
+  }
+
+  {
+    hci::Lap lap;
+    lap.lap_ = kLimitedInquiryAccessCode;
+    laps.push_back(lap);
+  }
+  pimpl_->StartDiscoverability(laps);
+}
+
+void neighbor::DiscoverabilityModule::StopDiscoverability() {
+  pimpl_->StopDiscoverability();
+}
+
+bool neighbor::DiscoverabilityModule::IsGeneralDiscoverabilityEnabled() const {
+  return pimpl_->IsGeneralDiscoverabilityEnabled();
+}
+
+bool neighbor::DiscoverabilityModule::IsLimitedDiscoverabilityEnabled() const {
+  return pimpl_->IsLimitedDiscoverabilityEnabled();
+}
+
+/**
+ * Module stuff
+ */
+void neighbor::DiscoverabilityModule::ListDependencies(ModuleList* list) {
+  list->add<hci::HciLayer>();
+  list->add<neighbor::ScanModule>();
+}
+
+void neighbor::DiscoverabilityModule::Start() {
+  pimpl_->Start();
+}
+
+void neighbor::DiscoverabilityModule::Stop() {}
+
+}  // namespace neighbor
+}  // namespace bluetooth
diff --git a/gd/neighbor/discoverability.h b/gd/neighbor/discoverability.h
new file mode 100644
index 0000000..124716c
--- /dev/null
+++ b/gd/neighbor/discoverability.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <memory>
+
+#include "module.h"
+
+namespace bluetooth {
+namespace neighbor {
+
+class DiscoverabilityModule : public bluetooth::Module {
+ public:
+  void StartGeneralDiscoverability();
+  void StartLimitedDiscoverability();
+  void StopDiscoverability();
+
+  bool IsGeneralDiscoverabilityEnabled() const;
+  bool IsLimitedDiscoverabilityEnabled() const;
+
+  static const ModuleFactory Factory;
+
+  DiscoverabilityModule();
+  ~DiscoverabilityModule();
+
+ protected:
+  void ListDependencies(ModuleList* list) override;
+  void Start() override;
+  void Stop() override;
+
+ private:
+  struct impl;
+  std::unique_ptr<impl> pimpl_;
+
+  DISALLOW_COPY_AND_ASSIGN(DiscoverabilityModule);
+};
+
+}  // namespace neighbor
+}  // namespace bluetooth
diff --git a/gd/neighbor/facade/facade.cc b/gd/neighbor/facade/facade.cc
new file mode 100644
index 0000000..4ede264
--- /dev/null
+++ b/gd/neighbor/facade/facade.cc
@@ -0,0 +1,223 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "neighbor/facade/facade.h"
+
+#include <memory>
+
+#include "common/bind.h"
+#include "grpc/grpc_event_queue.h"
+#include "hci/hci_packets.h"
+#include "neighbor/facade/facade.grpc.pb.h"
+
+using ::grpc::ServerAsyncResponseWriter;
+using ::grpc::ServerAsyncWriter;
+using ::grpc::ServerContext;
+
+namespace bluetooth {
+namespace neighbor {
+namespace facade {
+
+class NeighborFacadeService : public NeighborFacade::Service {
+ public:
+  NeighborFacadeService(ConnectabilityModule* connectability_module, DiscoverabilityModule* discoverability_module,
+                        InquiryModule* inquiry_module, NameModule* name_module, PageModule*, ScanModule* scan_module,
+                        ::bluetooth::os::Handler* facade_handler)
+      : connectability_module_(connectability_module), discoverability_module_(discoverability_module),
+        inquiry_module_(inquiry_module), name_module_(name_module), scan_module_(scan_module),
+        facade_handler_(facade_handler) {}
+
+  ::grpc::Status SetConnectability(::grpc::ServerContext* context, const ::bluetooth::neighbor::EnableMsg* request,
+                                   ::google::protobuf::Empty* response) override {
+    if (request->enabled()) {
+      connectability_module_->StartConnectability();
+    } else {
+      connectability_module_->StopConnectability();
+    }
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status SetDiscoverability(::grpc::ServerContext* context,
+                                    const ::bluetooth::neighbor::DiscoverabilitiyMsg* request,
+                                    ::google::protobuf::Empty* response) override {
+    switch (request->mode()) {
+      case DiscoverabilityMode::OFF:
+        discoverability_module_->StopDiscoverability();
+        break;
+      case DiscoverabilityMode::LIMITED:
+        discoverability_module_->StartLimitedDiscoverability();
+        break;
+      case DiscoverabilityMode::GENERAL:
+        discoverability_module_->StartGeneralDiscoverability();
+        break;
+      default:
+        LOG_ALWAYS_FATAL("Unknown discoverability mode %d", static_cast<int>(request->mode()));
+    }
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status SetInquiryMode(::grpc::ServerContext* context, const ::bluetooth::neighbor::InquiryMsg* request,
+                                ::grpc::ServerWriter<InquiryResultMsg>* writer) override {
+    inquiry_module_->RegisterCallbacks(inquiry_callbacks_);
+    switch (request->result_mode()) {
+      case ResultMode::STANDARD:
+        inquiry_module_->SetStandardInquiryResultMode();
+        break;
+      case ResultMode::RSSI:
+        inquiry_module_->SetInquiryWithRssiResultMode();
+        break;
+      case ResultMode::EXTENDED:
+        inquiry_module_->SetExtendedInquiryResultMode();
+        break;
+      default:
+        LOG_ALWAYS_FATAL("Unknown result mode %d", static_cast<int>(request->result_mode()));
+    }
+    switch (request->inquiry_mode()) {
+      case DiscoverabilityMode::OFF:
+        inquiry_module_->StopInquiry();
+        break;
+      case DiscoverabilityMode::LIMITED:
+        inquiry_module_->StartLimitedInquiry(request->length_1_28s(), request->max_results());
+        break;
+      case DiscoverabilityMode::GENERAL:
+        inquiry_module_->StartGeneralInquiry(request->length_1_28s(), request->max_results());
+        break;
+      default:
+        LOG_ALWAYS_FATAL("Unknown discoverability mode %d", static_cast<int>(request->inquiry_mode()));
+    }
+    return pending_events_.RunLoop(context, writer);
+  }
+
+  ::grpc::Status ReadRemoteName(::grpc::ServerContext* context,
+                                const ::bluetooth::neighbor::RemoteNameRequestMsg* request,
+                                ::google::protobuf::Empty* response) override {
+    hci::Address remote;
+    ASSERT(hci::Address::FromString(request->address(), remote));
+    hci::PageScanRepetitionMode mode;
+    switch (request->page_scan_repetition_mode()) {
+      case 0:
+        mode = hci::PageScanRepetitionMode::R0;
+        break;
+      case 1:
+        mode = hci::PageScanRepetitionMode::R1;
+        break;
+      case 2:
+        mode = hci::PageScanRepetitionMode::R2;
+        break;
+      default:
+        LOG_ALWAYS_FATAL("Unknown PageScanRepetition mode %d", static_cast<int>(request->page_scan_repetition_mode()));
+    }
+    name_module_->ReadRemoteNameRequest(
+        remote, mode, request->clock_offset(),
+        (request->clock_offset() != 0 ? hci::ClockOffsetValid::VALID : hci::ClockOffsetValid::INVALID),
+        common::Bind(&NeighborFacadeService::on_remote_name, common::Unretained(this)), facade_handler_);
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status GetRemoteNameEvents(::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
+                                     ::grpc::ServerWriter<RemoteNameResponseMsg>* writer) override {
+    return pending_remote_names_.RunLoop(context, writer);
+  }
+
+  ::grpc::Status EnableInquiryScan(::grpc::ServerContext* context, const ::bluetooth::neighbor::EnableMsg* request,
+                                   ::google::protobuf::Empty* response) override {
+    if (request->enabled()) {
+      scan_module_->SetInquiryScan();
+    } else {
+      scan_module_->ClearInquiryScan();
+    }
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status EnablePageScan(::grpc::ServerContext* context, const ::bluetooth::neighbor::EnableMsg* request,
+                                ::google::protobuf::Empty* response) override {
+    if (request->enabled()) {
+      scan_module_->SetPageScan();
+    } else {
+      scan_module_->ClearPageScan();
+    }
+    return ::grpc::Status::OK;
+  }
+
+ private:
+  void on_incoming_inquiry_result(hci::EventPacketView view) {
+    InquiryResultMsg inquiry_result_msg;
+    inquiry_result_msg.set_packet(std::string(view.begin(), view.end()));
+    pending_events_.OnIncomingEvent(std::move(inquiry_result_msg));
+  }
+
+  void on_incoming_inquiry_complete(hci::ErrorCode status) {
+    InquiryResultMsg inquiry_result_msg;
+    inquiry_result_msg.set_packet(hci::ErrorCodeText(status));
+    pending_events_.OnIncomingEvent(std::move(inquiry_result_msg));
+  }
+
+  InquiryCallbacks inquiry_callbacks_{
+      .result = [this](hci::InquiryResultView view) { on_incoming_inquiry_result(view); },
+      .result_with_rssi = [this](hci::InquiryResultWithRssiView view) { on_incoming_inquiry_result(view); },
+      .extended_result = [this](hci::ExtendedInquiryResultView view) { on_incoming_inquiry_result(view); },
+      .complete = [this](hci::ErrorCode status) { on_incoming_inquiry_complete(status); }};
+
+  void on_remote_name(hci::ErrorCode status, hci::Address address, std::array<uint8_t, 248> name) {
+    RemoteNameResponseMsg response;
+    response.set_status(static_cast<int>(status));
+    response.set_address(address.ToString());
+    response.set_name(name.begin(), name.size());
+    pending_remote_names_.OnIncomingEvent(response);
+  }
+
+  ConnectabilityModule* connectability_module_;
+  DiscoverabilityModule* discoverability_module_;
+  InquiryModule* inquiry_module_;
+  NameModule* name_module_;
+  ScanModule* scan_module_;
+  ::bluetooth::os::Handler* facade_handler_;
+  ::bluetooth::grpc::GrpcEventQueue<InquiryResultMsg> pending_events_{"InquiryResponses"};
+  ::bluetooth::grpc::GrpcEventQueue<RemoteNameResponseMsg> pending_remote_names_{"RemoteNameResponses"};
+};
+
+void NeighborFacadeModule::ListDependencies(ModuleList* list) {
+  ::bluetooth::grpc::GrpcFacadeModule::ListDependencies(list);
+  list->add<ConnectabilityModule>();
+  list->add<DiscoverabilityModule>();
+  list->add<InquiryModule>();
+  list->add<NameModule>();
+  list->add<PageModule>();
+  list->add<ScanModule>();
+}
+
+void NeighborFacadeModule::Start() {
+  ::bluetooth::grpc::GrpcFacadeModule::Start();
+  service_ = new NeighborFacadeService(GetDependency<ConnectabilityModule>(), GetDependency<DiscoverabilityModule>(),
+                                       GetDependency<InquiryModule>(), GetDependency<NameModule>(),
+                                       GetDependency<PageModule>(), GetDependency<ScanModule>(), GetHandler());
+}
+
+void NeighborFacadeModule::Stop() {
+  delete service_;
+  ::bluetooth::grpc::GrpcFacadeModule::Stop();
+}
+
+::grpc::Service* NeighborFacadeModule::GetService() const {
+  return service_;
+}
+
+const ModuleFactory NeighborFacadeModule::Factory =
+    ::bluetooth::ModuleFactory([]() { return new NeighborFacadeModule(); });
+
+}  // namespace facade
+}  // namespace neighbor
+}  // namespace bluetooth
diff --git a/gd/neighbor/facade/facade.h b/gd/neighbor/facade/facade.h
new file mode 100644
index 0000000..4f4549c
--- /dev/null
+++ b/gd/neighbor/facade/facade.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <grpc++/grpc++.h>
+
+#include "grpc/grpc_module.h"
+#include "neighbor/connectability.h"
+#include "neighbor/discoverability.h"
+#include "neighbor/inquiry.h"
+#include "neighbor/name.h"
+#include "neighbor/page.h"
+#include "neighbor/scan.h"
+
+namespace bluetooth {
+namespace neighbor {
+namespace facade {
+
+class NeighborFacadeService;
+
+class NeighborFacadeModule : public ::bluetooth::grpc::GrpcFacadeModule {
+ public:
+  static const ModuleFactory Factory;
+
+  void ListDependencies(ModuleList* list) override;
+  void Start() override;
+  void Stop() override;
+  ::grpc::Service* GetService() const override;
+
+ private:
+  NeighborFacadeService* service_;
+};
+
+}  // namespace facade
+}  // namespace neighbor
+}  // namespace bluetooth
diff --git a/gd/neighbor/facade/facade.proto b/gd/neighbor/facade/facade.proto
new file mode 100644
index 0000000..315ae32
--- /dev/null
+++ b/gd/neighbor/facade/facade.proto
@@ -0,0 +1,58 @@
+syntax = "proto3";
+
+package bluetooth.neighbor;
+
+import "google/protobuf/empty.proto";
+
+service NeighborFacade {
+  rpc SetConnectability(EnableMsg) returns (google.protobuf.Empty) {}
+  rpc SetDiscoverability(DiscoverabilitiyMsg) returns (google.protobuf.Empty) {}
+  rpc SetInquiryMode(InquiryMsg) returns (stream InquiryResultMsg) {}
+  rpc ReadRemoteName(RemoteNameRequestMsg) returns (google.protobuf.Empty) {}
+  rpc GetRemoteNameEvents(google.protobuf.Empty) returns (stream RemoteNameResponseMsg) {}
+  rpc EnableInquiryScan(EnableMsg) returns (google.protobuf.Empty) {}
+  rpc EnablePageScan(EnableMsg) returns (google.protobuf.Empty) {}
+}
+
+message EnableMsg {
+  bool enabled = 1;
+}
+
+enum DiscoverabilityMode {
+  OFF = 0;
+  LIMITED = 1;
+  GENERAL = 2;
+}
+
+message DiscoverabilitiyMsg {
+  DiscoverabilityMode mode = 1;
+}
+
+enum ResultMode {
+  STANDARD = 0;
+  RSSI = 1;
+  EXTENDED = 2;
+}
+
+message InquiryMsg {
+  DiscoverabilityMode inquiry_mode = 1;
+  ResultMode result_mode = 2;
+  uint32 length_1_28s = 3;
+  uint32 max_results = 4;  // 0 is unlimited
+}
+
+message InquiryResultMsg {
+  bytes packet = 1;
+}
+
+message RemoteNameRequestMsg {
+  bytes address = 1;
+  uint32 page_scan_repetition_mode = 2;  // r0, r1, r2
+  uint32 clock_offset = 3;
+}
+
+message RemoteNameResponseMsg {
+  uint32 status = 1;
+  bytes address = 2;
+  bytes name = 3;
+}
diff --git a/gd/neighbor/inquiry.cc b/gd/neighbor/inquiry.cc
new file mode 100644
index 0000000..ec734f3
--- /dev/null
+++ b/gd/neighbor/inquiry.cc
@@ -0,0 +1,470 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_TAG "bt_gd_neigh"
+
+#include "neighbor/inquiry.h"
+
+#include <memory>
+
+#include "common/bind.h"
+#include "hci/hci_layer.h"
+#include "hci/hci_packets.h"
+#include "module.h"
+#include "os/handler.h"
+#include "os/log.h"
+
+namespace bluetooth {
+namespace neighbor {
+
+static constexpr uint8_t kGeneralInquiryAccessCode = 0x33;
+static constexpr uint8_t kLimitedInquiryAccessCode = 0x00;
+
+struct InquiryModule::impl {
+  void RegisterCallbacks(InquiryCallbacks inquiry_callbacks);
+  void UnregisterCallbacks();
+
+  void StartOneShotInquiry(bool limited, InquiryLength inquiry_length, NumResponses num_responses);
+  void StopOneShotInquiry();
+
+  void StartPeriodicInquiry(bool limited, InquiryLength inquiry_length, NumResponses num_responses,
+                            PeriodLength max_delay, PeriodLength min_delay);
+  void StopPeriodicInquiry();
+
+  void SetScanActivity(ScanParameters params);
+
+  void SetScanType(hci::InquiryScanType scan_type);
+
+  void SetInquiryMode(hci::InquiryMode mode);
+
+  void Start();
+  void Stop();
+
+  bool HasCallbacks() const;
+
+  impl(InquiryModule& inquiry_module);
+
+ private:
+  InquiryCallbacks inquiry_callbacks_;
+
+  InquiryModule& module_;
+
+  bool active_general_one_shot_{false};
+  bool active_limited_one_shot_{false};
+  bool active_general_periodic_{false};
+  bool active_limited_periodic_{false};
+
+  ScanParameters inquiry_scan_;
+  hci::InquiryMode inquiry_mode_;
+  hci::InquiryScanType inquiry_scan_type_;
+  int8_t inquiry_response_tx_power_;
+
+  bool IsInquiryActive() const;
+
+  void EnqueueCommandComplete(std::unique_ptr<hci::CommandPacketBuilder> command);
+  void EnqueueCommandStatus(std::unique_ptr<hci::CommandPacketBuilder> command);
+  void OnCommandComplete(hci::CommandCompleteView view);
+  void OnCommandStatus(hci::CommandStatusView status);
+
+  void EnqueueCommandCompleteSync(std::unique_ptr<hci::CommandPacketBuilder> command);
+  void OnCommandCompleteSync(hci::CommandCompleteView view);
+
+  void OnEvent(hci::EventPacketView view);
+
+  std::promise<void>* command_sync_{nullptr};
+
+  hci::HciLayer* hci_layer_;
+  os::Handler* handler_;
+};
+
+const ModuleFactory neighbor::InquiryModule::Factory = ModuleFactory([]() { return new neighbor::InquiryModule(); });
+
+neighbor::InquiryModule::impl::impl(neighbor::InquiryModule& module) : module_(module) {}
+
+void neighbor::InquiryModule::impl::OnCommandCompleteSync(hci::CommandCompleteView view) {
+  OnCommandComplete(view);
+  ASSERT(command_sync_ != nullptr);
+  command_sync_->set_value();
+}
+
+void neighbor::InquiryModule::impl::OnCommandComplete(hci::CommandCompleteView view) {
+  switch (view.GetCommandOpCode()) {
+    case hci::OpCode::INQUIRY_CANCEL: {
+      auto packet = hci::InquiryCancelCompleteView::Create(view);
+      ASSERT(packet.IsValid());
+      ASSERT(packet.GetStatus() == hci::ErrorCode::SUCCESS);
+    } break;
+
+    case hci::OpCode::PERIODIC_INQUIRY_MODE: {
+      auto packet = hci::PeriodicInquiryModeCompleteView::Create(view);
+      ASSERT(packet.IsValid());
+      ASSERT(packet.GetStatus() == hci::ErrorCode::SUCCESS);
+    } break;
+
+    case hci::OpCode::EXIT_PERIODIC_INQUIRY_MODE: {
+      auto packet = hci::ExitPeriodicInquiryModeCompleteView::Create(view);
+      ASSERT(packet.IsValid());
+      ASSERT(packet.GetStatus() == hci::ErrorCode::SUCCESS);
+    } break;
+
+    case hci::OpCode::WRITE_INQUIRY_MODE: {
+      auto packet = hci::WriteInquiryModeCompleteView::Create(view);
+      ASSERT(packet.IsValid());
+      ASSERT(packet.GetStatus() == hci::ErrorCode::SUCCESS);
+    } break;
+
+    case hci::OpCode::READ_INQUIRY_MODE: {
+      auto packet = hci::ReadInquiryModeCompleteView::Create(view);
+      ASSERT(packet.IsValid());
+      ASSERT(packet.GetStatus() == hci::ErrorCode::SUCCESS);
+      inquiry_mode_ = packet.GetInquiryMode();
+    } break;
+
+    case hci::OpCode::READ_INQUIRY_RESPONSE_TRANSMIT_POWER_LEVEL: {
+      auto packet = hci::ReadInquiryResponseTransmitPowerLevelCompleteView::Create(view);
+      ASSERT(packet.IsValid());
+      ASSERT(packet.GetStatus() == hci::ErrorCode::SUCCESS);
+      inquiry_response_tx_power_ = packet.GetTxPower();
+    } break;
+
+    case hci::OpCode::WRITE_INQUIRY_SCAN_ACTIVITY: {
+      auto packet = hci::WriteInquiryScanActivityCompleteView::Create(view);
+      ASSERT(packet.IsValid());
+      ASSERT(packet.GetStatus() == hci::ErrorCode::SUCCESS);
+    } break;
+
+    case hci::OpCode::READ_INQUIRY_SCAN_ACTIVITY: {
+      auto packet = hci::ReadInquiryScanActivityCompleteView::Create(view);
+      ASSERT(packet.IsValid());
+      ASSERT(packet.GetStatus() == hci::ErrorCode::SUCCESS);
+      inquiry_scan_.interval = packet.GetInquiryScanInterval();
+      inquiry_scan_.window = packet.GetInquiryScanWindow();
+    } break;
+
+    case hci::OpCode::WRITE_INQUIRY_SCAN_TYPE: {
+      auto packet = hci::WriteInquiryScanTypeCompleteView::Create(view);
+      ASSERT(packet.IsValid());
+      ASSERT(packet.GetStatus() == hci::ErrorCode::SUCCESS);
+    } break;
+
+    case hci::OpCode::READ_INQUIRY_SCAN_TYPE: {
+      auto packet = hci::ReadInquiryScanTypeCompleteView::Create(view);
+      ASSERT(packet.IsValid());
+      ASSERT(packet.GetStatus() == hci::ErrorCode::SUCCESS);
+      inquiry_scan_type_ = packet.GetInquiryScanType();
+    } break;
+
+    default:
+      LOG_WARN("Unhandled command:%s", hci::OpCodeText(view.GetCommandOpCode()).c_str());
+      break;
+  }
+}
+
+void neighbor::InquiryModule::impl::OnCommandStatus(hci::CommandStatusView status) {
+  ASSERT(status.GetStatus() == hci::ErrorCode::SUCCESS);
+
+  switch (status.GetCommandOpCode()) {
+    case hci::OpCode::INQUIRY: {
+      auto packet = hci::InquiryStatusView::Create(status);
+      ASSERT(packet.IsValid());
+      if (active_limited_one_shot_ || active_general_one_shot_) {
+        LOG_DEBUG("Inquiry started lap: %s", active_limited_one_shot_ ? "Limited" : "General");
+      }
+    } break;
+
+    default:
+      LOG_WARN("Unhandled command:%s", hci::OpCodeText(status.GetCommandOpCode()).c_str());
+      break;
+  }
+}
+
+void neighbor::InquiryModule::impl::OnEvent(hci::EventPacketView view) {
+  switch (view.GetEventCode()) {
+    case hci::EventCode::INQUIRY_COMPLETE: {
+      auto packet = hci::InquiryCompleteView::Create(view);
+      ASSERT(packet.IsValid());
+      LOG_DEBUG("inquiry complete");
+      active_limited_one_shot_ = false;
+      active_general_one_shot_ = false;
+      inquiry_callbacks_.complete(packet.GetStatus());
+    } break;
+
+    case hci::EventCode::INQUIRY_RESULT: {
+      auto packet = hci::InquiryResultView::Create(view);
+      ASSERT(packet.IsValid());
+      LOG_DEBUG("Inquiry result size:%zd num_responses:%zu", packet.size(), packet.GetInquiryResults().size());
+      inquiry_callbacks_.result(packet);
+    } break;
+
+    case hci::EventCode::INQUIRY_RESULT_WITH_RSSI: {
+      auto packet = hci::InquiryResultWithRssiView::Create(view);
+      ASSERT(packet.IsValid());
+      LOG_DEBUG("Inquiry result with rssi num_responses:%zu", packet.GetInquiryResults().size());
+      inquiry_callbacks_.result_with_rssi(packet);
+    } break;
+
+    case hci::EventCode::EXTENDED_INQUIRY_RESULT: {
+      auto packet = hci::ExtendedInquiryResultView::Create(view);
+      ASSERT(packet.IsValid());
+      LOG_DEBUG("Extended inquiry result addr:%s repetition_mode:%s cod:%s clock_offset:%d rssi:%hhd",
+                packet.GetAddress().ToString().c_str(),
+                hci::PageScanRepetitionModeText(packet.GetPageScanRepetitionMode()).c_str(),
+                packet.GetClassOfDevice().ToString().c_str(), packet.GetClockOffset(), packet.GetRssi());
+      inquiry_callbacks_.extended_result(packet);
+    } break;
+
+    default:
+      LOG_ERROR("Unhandled event:%s", hci::EventCodeText(view.GetEventCode()).c_str());
+      break;
+  }
+}
+
+/**
+ * impl
+ */
+void neighbor::InquiryModule::impl::RegisterCallbacks(InquiryCallbacks callbacks) {
+  inquiry_callbacks_ = callbacks;
+
+  hci_layer_->RegisterEventHandler(hci::EventCode::INQUIRY_RESULT,
+                                   common::Bind(&InquiryModule::impl::OnEvent, common::Unretained(this)), handler_);
+  hci_layer_->RegisterEventHandler(hci::EventCode::INQUIRY_RESULT_WITH_RSSI,
+                                   common::Bind(&InquiryModule::impl::OnEvent, common::Unretained(this)), handler_);
+  hci_layer_->RegisterEventHandler(hci::EventCode::EXTENDED_INQUIRY_RESULT,
+                                   common::Bind(&InquiryModule::impl::OnEvent, common::Unretained(this)), handler_);
+  hci_layer_->RegisterEventHandler(hci::EventCode::INQUIRY_COMPLETE,
+                                   common::Bind(&InquiryModule::impl::OnEvent, common::Unretained(this)), handler_);
+}
+
+void neighbor::InquiryModule::impl::UnregisterCallbacks() {
+  hci_layer_->UnregisterEventHandler(hci::EventCode::INQUIRY_COMPLETE);
+  hci_layer_->UnregisterEventHandler(hci::EventCode::EXTENDED_INQUIRY_RESULT);
+  hci_layer_->UnregisterEventHandler(hci::EventCode::INQUIRY_RESULT_WITH_RSSI);
+  hci_layer_->UnregisterEventHandler(hci::EventCode::INQUIRY_RESULT);
+
+  inquiry_callbacks_ = {nullptr, nullptr, nullptr, nullptr};
+}
+
+void neighbor::InquiryModule::impl::EnqueueCommandComplete(std::unique_ptr<hci::CommandPacketBuilder> command) {
+  hci_layer_->EnqueueCommand(std::move(command), common::BindOnce(&impl::OnCommandComplete, common::Unretained(this)),
+                             handler_);
+}
+
+void neighbor::InquiryModule::impl::EnqueueCommandStatus(std::unique_ptr<hci::CommandPacketBuilder> command) {
+  hci_layer_->EnqueueCommand(std::move(command), common::BindOnce(&impl::OnCommandStatus, common::Unretained(this)),
+                             handler_);
+}
+
+void neighbor::InquiryModule::impl::EnqueueCommandCompleteSync(std::unique_ptr<hci::CommandPacketBuilder> command) {
+  ASSERT(command_sync_ == nullptr);
+  command_sync_ = new std::promise<void>();
+  auto command_received = command_sync_->get_future();
+  hci_layer_->EnqueueCommand(std::move(command),
+                             common::BindOnce(&impl::OnCommandCompleteSync, common::Unretained(this)), handler_);
+  command_received.wait();
+  delete command_sync_;
+  command_sync_ = nullptr;
+}
+
+void neighbor::InquiryModule::impl::StartOneShotInquiry(bool limited, InquiryLength inquiry_length,
+                                                        NumResponses num_responses) {
+  ASSERT(HasCallbacks());
+  ASSERT(!IsInquiryActive());
+  hci::Lap lap;
+  if (limited) {
+    active_limited_one_shot_ = true;
+    lap.lap_ = kLimitedInquiryAccessCode;
+  } else {
+    active_general_one_shot_ = true;
+    lap.lap_ = kGeneralInquiryAccessCode;
+  }
+  EnqueueCommandStatus(hci::InquiryBuilder::Create(lap, inquiry_length, num_responses));
+}
+
+void neighbor::InquiryModule::impl::StopOneShotInquiry() {
+  ASSERT(active_general_one_shot_ || active_limited_one_shot_);
+  active_general_one_shot_ = false;
+  active_limited_one_shot_ = false;
+  EnqueueCommandComplete(hci::InquiryCancelBuilder::Create());
+}
+
+void neighbor::InquiryModule::impl::StartPeriodicInquiry(bool limited, InquiryLength inquiry_length,
+                                                         NumResponses num_responses, PeriodLength max_delay,
+                                                         PeriodLength min_delay) {
+  ASSERT(HasCallbacks());
+  ASSERT(!IsInquiryActive());
+  hci::Lap lap;
+  if (limited) {
+    active_limited_periodic_ = true;
+    lap.lap_ = kLimitedInquiryAccessCode;
+  } else {
+    active_general_periodic_ = true;
+    lap.lap_ = kGeneralInquiryAccessCode;
+  }
+  EnqueueCommandComplete(
+      hci::PeriodicInquiryModeBuilder::Create(max_delay, min_delay, lap, inquiry_length, num_responses));
+}
+
+void neighbor::InquiryModule::impl::StopPeriodicInquiry() {
+  ASSERT(active_general_periodic_ || active_limited_periodic_);
+  active_general_periodic_ = false;
+  active_limited_periodic_ = false;
+  EnqueueCommandComplete(hci::ExitPeriodicInquiryModeBuilder::Create());
+}
+
+bool neighbor::InquiryModule::impl::IsInquiryActive() const {
+  return active_general_one_shot_ || active_limited_one_shot_ || active_limited_periodic_ || active_general_periodic_;
+}
+
+void neighbor::InquiryModule::impl::Start() {
+  hci_layer_ = module_.GetDependency<hci::HciLayer>();
+  handler_ = module_.GetHandler();
+
+  EnqueueCommandComplete(hci::ReadInquiryResponseTransmitPowerLevelBuilder::Create());
+  EnqueueCommandComplete(hci::ReadInquiryScanActivityBuilder::Create());
+  EnqueueCommandComplete(hci::ReadInquiryScanTypeBuilder::Create());
+  EnqueueCommandCompleteSync(hci::ReadInquiryModeBuilder::Create());
+
+  LOG_DEBUG("Started inquiry module");
+}
+
+void neighbor::InquiryModule::impl::Stop() {
+  LOG_INFO("Inquiry scan interval:%hu window:%hu", inquiry_scan_.interval, inquiry_scan_.window);
+  LOG_INFO("Inquiry mode:%s scan_type:%s", hci::InquiryModeText(inquiry_mode_).c_str(),
+           hci::InquiryScanTypeText(inquiry_scan_type_).c_str());
+  LOG_INFO("Inquiry response tx power:%hhd", inquiry_response_tx_power_);
+  LOG_DEBUG("Stopped inquiry module");
+}
+
+void neighbor::InquiryModule::impl::SetInquiryMode(hci::InquiryMode mode) {
+  EnqueueCommandComplete(hci::WriteInquiryModeBuilder::Create(mode));
+  inquiry_mode_ = mode;
+  LOG_DEBUG("Set inquiry mode:%s", hci::InquiryModeText(mode).c_str());
+}
+
+void neighbor::InquiryModule::impl::SetScanActivity(ScanParameters params) {
+  EnqueueCommandComplete(hci::WriteInquiryScanActivityBuilder::Create(params.interval, params.window));
+  inquiry_scan_ = params;
+  LOG_DEBUG("Set scan activity interval:0x%x/%.02fms window:0x%x/%.02fms", params.interval,
+            ScanIntervalTimeMs(params.interval), params.window, ScanWindowTimeMs(params.window));
+}
+
+void neighbor::InquiryModule::impl::SetScanType(hci::InquiryScanType scan_type) {
+  EnqueueCommandComplete(hci::WriteInquiryScanTypeBuilder::Create(scan_type));
+  LOG_DEBUG("Set scan type:%s", hci::InquiryScanTypeText(scan_type).c_str());
+}
+
+bool neighbor::InquiryModule::impl::HasCallbacks() const {
+  return inquiry_callbacks_.result != nullptr && inquiry_callbacks_.result_with_rssi != nullptr &&
+         inquiry_callbacks_.extended_result != nullptr && inquiry_callbacks_.complete != nullptr;
+}
+
+/**
+ * General API here
+ */
+neighbor::InquiryModule::InquiryModule() : pimpl_(std::make_unique<impl>(*this)) {}
+
+neighbor::InquiryModule::~InquiryModule() {
+  pimpl_.reset();
+}
+
+void neighbor::InquiryModule::RegisterCallbacks(InquiryCallbacks callbacks) {
+  pimpl_->RegisterCallbacks(callbacks);
+}
+
+void neighbor::InquiryModule::UnregisterCallbacks() {
+  pimpl_->UnregisterCallbacks();
+}
+
+void neighbor::InquiryModule::StartGeneralInquiry(InquiryLength inquiry_length, NumResponses num_responses) {
+  GetHandler()->Post(common::BindOnce(&neighbor::InquiryModule::impl::StartOneShotInquiry,
+                                      common::Unretained(pimpl_.get()), false, inquiry_length, num_responses));
+}
+
+void neighbor::InquiryModule::StartLimitedInquiry(InquiryLength inquiry_length, NumResponses num_responses) {
+  GetHandler()->Post(common::BindOnce(&neighbor::InquiryModule::impl::StartOneShotInquiry,
+                                      common::Unretained(pimpl_.get()), true, inquiry_length, num_responses));
+}
+
+void neighbor::InquiryModule::StopInquiry() {
+  GetHandler()->Post(
+      common::BindOnce(&neighbor::InquiryModule::impl::StopOneShotInquiry, common::Unretained(pimpl_.get())));
+}
+
+void neighbor::InquiryModule::StartGeneralPeriodicInquiry(InquiryLength inquiry_length, NumResponses num_responses,
+                                                          PeriodLength max_delay, PeriodLength min_delay) {
+  GetHandler()->Post(common::BindOnce(&neighbor::InquiryModule::impl::StartPeriodicInquiry,
+                                      common::Unretained(pimpl_.get()), false, inquiry_length, num_responses, max_delay,
+                                      min_delay));
+}
+
+void neighbor::InquiryModule::StartLimitedPeriodicInquiry(InquiryLength inquiry_length, NumResponses num_responses,
+                                                          PeriodLength max_delay, PeriodLength min_delay) {
+  GetHandler()->Post(common::BindOnce(&neighbor::InquiryModule::impl::StartPeriodicInquiry,
+                                      common::Unretained(pimpl_.get()), true, inquiry_length, num_responses, max_delay,
+                                      min_delay));
+}
+
+void neighbor::InquiryModule::StopPeriodicInquiry() {
+  GetHandler()->Post(
+      common::BindOnce(&neighbor::InquiryModule::impl::StopPeriodicInquiry, common::Unretained(pimpl_.get())));
+}
+
+void neighbor::InquiryModule::SetScanActivity(ScanParameters params) {
+  GetHandler()->Post(
+      common::BindOnce(&neighbor::InquiryModule::impl::SetScanActivity, common::Unretained(pimpl_.get()), params));
+}
+
+void neighbor::InquiryModule::SetInterlacedScan() {
+  GetHandler()->Post(common::BindOnce(&neighbor::InquiryModule::impl::SetScanType, common::Unretained(pimpl_.get()),
+                                      hci::InquiryScanType::INTERLACED));
+}
+
+void neighbor::InquiryModule::SetStandardScan() {
+  GetHandler()->Post(common::BindOnce(&neighbor::InquiryModule::impl::SetScanType, common::Unretained(pimpl_.get()),
+                                      hci::InquiryScanType::STANDARD));
+}
+
+void neighbor::InquiryModule::SetStandardInquiryResultMode() {
+  GetHandler()->Post(common::BindOnce(&neighbor::InquiryModule::impl::SetInquiryMode, common::Unretained(pimpl_.get()),
+                                      hci::InquiryMode::STANDARD));
+}
+
+void neighbor::InquiryModule::SetInquiryWithRssiResultMode() {
+  GetHandler()->Post(common::BindOnce(&neighbor::InquiryModule::impl::SetInquiryMode, common::Unretained(pimpl_.get()),
+                                      hci::InquiryMode::RSSI));
+}
+
+void neighbor::InquiryModule::SetExtendedInquiryResultMode() {
+  GetHandler()->Post(common::BindOnce(&neighbor::InquiryModule::impl::SetInquiryMode, common::Unretained(pimpl_.get()),
+                                      hci::InquiryMode::RSSI_OR_EXTENDED));
+}
+
+/**
+ * Module methods here
+ */
+void neighbor::InquiryModule::ListDependencies(ModuleList* list) {
+  list->add<hci::HciLayer>();
+}
+
+void neighbor::InquiryModule::Start() {
+  pimpl_->Start();
+}
+
+void neighbor::InquiryModule::Stop() {
+  pimpl_->Stop();
+}
+
+}  // namespace neighbor
+}  // namespace bluetooth
diff --git a/gd/neighbor/inquiry.h b/gd/neighbor/inquiry.h
new file mode 100644
index 0000000..33e3d6c
--- /dev/null
+++ b/gd/neighbor/inquiry.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <memory>
+
+#include "hci/hci_packets.h"
+#include "module.h"
+#include "neighbor/scan_parameters.h"
+
+namespace bluetooth {
+namespace neighbor {
+
+using InquiryLength = uint8_t;  // Range: 0x01 to 0x30, 1.28 to 61.44s
+using NumResponses = uint8_t;   // Range: 0x01 to 0xff, 0x00 is unlimited
+using PeriodLength = uint16_t;  // Time = N * 1.28 s
+
+using InquiryResultCallback = std::function<void(hci::InquiryResultView view)>;
+using InquiryResultWithRssiCallback = std::function<void(hci::InquiryResultWithRssiView view)>;
+using ExtendedInquiryResultCallback = std::function<void(hci::ExtendedInquiryResultView view)>;
+using InquiryCompleteCallback = std::function<void(hci::ErrorCode status)>;
+
+using InquiryCallbacks = struct {
+  InquiryResultCallback result;
+  InquiryResultWithRssiCallback result_with_rssi;
+  ExtendedInquiryResultCallback extended_result;
+  InquiryCompleteCallback complete;
+};
+
+class InquiryModule : public bluetooth::Module {
+ public:
+  void RegisterCallbacks(InquiryCallbacks inquiry_callbacks);
+  void UnregisterCallbacks();
+
+  void StartGeneralInquiry(InquiryLength inquiry_length, NumResponses num_responses);
+  void StartLimitedInquiry(InquiryLength inquiry_length, NumResponses num_responses);
+  void StopInquiry();
+
+  void StartGeneralPeriodicInquiry(InquiryLength inquiry_length, NumResponses num_responses, PeriodLength max_delay,
+                                   PeriodLength min_delay);
+  void StartLimitedPeriodicInquiry(InquiryLength inquiry_length, NumResponses num_responses, PeriodLength max_delay,
+                                   PeriodLength min_delay);
+  void StopPeriodicInquiry();
+
+  void SetScanActivity(ScanParameters parms);
+
+  void SetInterlacedScan();
+  void SetStandardScan();
+
+  void SetStandardInquiryResultMode();
+  void SetInquiryWithRssiResultMode();
+  void SetExtendedInquiryResultMode();
+
+  static const ModuleFactory Factory;
+
+  InquiryModule();
+  ~InquiryModule();
+
+ protected:
+  void ListDependencies(ModuleList* list) override;
+  void Start() override;
+  void Stop() override;
+
+ private:
+  struct impl;
+  std::unique_ptr<impl> pimpl_;
+
+  DISALLOW_COPY_AND_ASSIGN(InquiryModule);
+};
+
+}  // namespace neighbor
+}  // namespace bluetooth
diff --git a/gd/neighbor/inquiry_test.cc b/gd/neighbor/inquiry_test.cc
new file mode 100644
index 0000000..333b6df
--- /dev/null
+++ b/gd/neighbor/inquiry_test.cc
@@ -0,0 +1,471 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "neighbor/inquiry.h"
+
+#include <algorithm>
+#include <chrono>
+#include <future>
+#include <map>
+#include <memory>
+
+#include <unistd.h>
+
+#include <gtest/gtest.h>
+
+#include "common/bind.h"
+#include "common/callback.h"
+#include "hci/address.h"
+#include "hci/class_of_device.h"
+#include "hci/hci_layer.h"
+#include "hci/hci_packets.h"
+#include "os/thread.h"
+#include "packet/raw_builder.h"
+
+namespace bluetooth {
+namespace neighbor {
+namespace {
+
+static const uint8_t kNumberPacketsReadyToReceive = 1;
+
+/**
+ * This structure reflects the current state of the bluetooth chip
+ * at any given time.
+ */
+static const int8_t kInitialInquiryResponseTransmitPowerLevel = 123;
+static const uint16_t kInitialInquiryScanInterval = 1111;
+static const uint16_t kInitialInquiryScanWindow = 2222;
+
+struct HciRegister {
+  bool one_shot_inquiry_active;
+  bool periodic_inquiry_active;
+  int8_t inquiry_response_transmit_power_level;
+  uint16_t inquiry_scan_interval;
+  uint16_t inquiry_scan_window;
+  hci::InquiryScanType inquiry_scan_type;
+  hci::InquiryMode inquiry_mode;
+  uint8_t inquiry_length;
+  uint8_t num_responses;
+  uint16_t min_period_length;
+  uint16_t max_period_length;
+} hci_register_{
+    .one_shot_inquiry_active = false,
+    .periodic_inquiry_active = false,
+    .inquiry_response_transmit_power_level = kInitialInquiryResponseTransmitPowerLevel,
+    .inquiry_scan_interval = kInitialInquiryScanInterval,
+    .inquiry_scan_window = kInitialInquiryScanWindow,
+    .inquiry_scan_type = hci::InquiryScanType::STANDARD,
+    .inquiry_mode = hci::InquiryMode::STANDARD,
+    .inquiry_length = 0,
+    .num_responses = 0,
+    .min_period_length = 0,
+    .max_period_length = 0,
+};
+
+hci::PacketView<hci::kLittleEndian> GetPacketView(std::unique_ptr<packet::BasePacketBuilder> packet) {
+  auto bytes = std::make_shared<std::vector<uint8_t>>();
+  hci::BitInserter i(*bytes);
+  bytes->reserve(packet->size());
+  packet->Serialize(i);
+  return packet::PacketView<packet::kLittleEndian>(bytes);
+}
+
+class TestHciLayer : public hci::HciLayer {
+ public:
+  void EnqueueCommand(std::unique_ptr<hci::CommandPacketBuilder> command,
+                      common::OnceCallback<void(hci::CommandCompleteView)> on_complete, os::Handler* handler) override {
+    GetHandler()->Post(common::BindOnce(&TestHciLayer::HandleCommand, common::Unretained(this), std::move(command),
+                                        std::move(on_complete), common::Unretained(handler)));
+  }
+
+  void EnqueueCommand(std::unique_ptr<hci::CommandPacketBuilder> command,
+                      common::OnceCallback<void(hci::CommandStatusView)> on_status, os::Handler* handler) override {
+    GetHandler()->Post(common::BindOnce(&TestHciLayer::HandleStatus, common::Unretained(this), std::move(command),
+                                        std::move(on_status), common::Unretained(handler)));
+  }
+
+  void HandleCommand(std::unique_ptr<hci::CommandPacketBuilder> command_builder,
+                     common::OnceCallback<void(hci::CommandCompleteView)> on_complete, os::Handler* handler) {
+    hci::CommandPacketView command = hci::CommandPacketView::Create(GetPacketView(std::move(command_builder)));
+    ASSERT(command.IsValid());
+
+    std::unique_ptr<packet::BasePacketBuilder> event_builder;
+    switch (command.GetOpCode()) {
+      case hci::OpCode::INQUIRY_CANCEL:
+        event_builder =
+            hci::InquiryCancelCompleteBuilder::Create(kNumberPacketsReadyToReceive, hci::ErrorCode::SUCCESS);
+        hci_register_.one_shot_inquiry_active = false;
+        break;
+
+      case hci::OpCode::PERIODIC_INQUIRY_MODE: {
+        auto inquiry = hci::PeriodicInquiryModeView::Create(hci::DiscoveryCommandView::Create(command));
+        ASSERT(inquiry.IsValid());
+        event_builder =
+            hci::PeriodicInquiryModeCompleteBuilder::Create(kNumberPacketsReadyToReceive, hci::ErrorCode::SUCCESS);
+        hci_register_.periodic_inquiry_active = true;
+        hci_register_.inquiry_length = inquiry.GetInquiryLength();
+        hci_register_.num_responses = inquiry.GetNumResponses();
+        hci_register_.max_period_length = inquiry.GetMaxPeriodLength();
+        hci_register_.min_period_length = inquiry.GetMinPeriodLength();
+      } break;
+
+      case hci::OpCode::EXIT_PERIODIC_INQUIRY_MODE:
+        event_builder =
+            hci::ExitPeriodicInquiryModeCompleteBuilder::Create(kNumberPacketsReadyToReceive, hci::ErrorCode::SUCCESS);
+        hci_register_.periodic_inquiry_active = false;
+        break;
+
+      case hci::OpCode::WRITE_INQUIRY_MODE:
+        event_builder =
+            hci::WriteInquiryModeCompleteBuilder::Create(kNumberPacketsReadyToReceive, hci::ErrorCode::SUCCESS);
+        {
+          auto view = hci::WriteInquiryModeView::Create(hci::DiscoveryCommandView::Create(command));
+          ASSERT(view.IsValid());
+          hci_register_.inquiry_mode = view.GetInquiryMode();
+        }
+        break;
+
+      case hci::OpCode::READ_INQUIRY_MODE:
+        event_builder = hci::ReadInquiryModeCompleteBuilder::Create(
+            kNumberPacketsReadyToReceive, hci::ErrorCode::SUCCESS, hci_register_.inquiry_mode);
+        break;
+
+      case hci::OpCode::WRITE_INQUIRY_SCAN_ACTIVITY:
+        event_builder =
+            hci::WriteInquiryScanActivityCompleteBuilder::Create(kNumberPacketsReadyToReceive, hci::ErrorCode::SUCCESS);
+        {
+          auto view = hci::WriteInquiryScanActivityView::Create(hci::DiscoveryCommandView::Create(command));
+          ASSERT(view.IsValid());
+          hci_register_.inquiry_scan_interval = view.GetInquiryScanInterval();
+          hci_register_.inquiry_scan_window = view.GetInquiryScanWindow();
+        }
+        break;
+
+      case hci::OpCode::READ_INQUIRY_SCAN_ACTIVITY:
+        event_builder = hci::ReadInquiryScanActivityCompleteBuilder::Create(
+            kNumberPacketsReadyToReceive, hci::ErrorCode::SUCCESS, hci_register_.inquiry_scan_interval,
+            hci_register_.inquiry_scan_window);
+        break;
+
+      case hci::OpCode::WRITE_INQUIRY_SCAN_TYPE:
+        event_builder =
+            hci::WriteInquiryScanTypeCompleteBuilder::Create(kNumberPacketsReadyToReceive, hci::ErrorCode::SUCCESS);
+        {
+          auto view = hci::WriteInquiryScanTypeView::Create(hci::DiscoveryCommandView::Create(command));
+          ASSERT(view.IsValid());
+          hci_register_.inquiry_scan_type = view.GetInquiryScanType();
+        }
+        break;
+
+      case hci::OpCode::READ_INQUIRY_SCAN_TYPE:
+        event_builder = hci::ReadInquiryScanTypeCompleteBuilder::Create(
+            kNumberPacketsReadyToReceive, hci::ErrorCode::SUCCESS, hci_register_.inquiry_scan_type);
+        break;
+
+      case hci::OpCode::READ_INQUIRY_RESPONSE_TRANSMIT_POWER_LEVEL:
+        event_builder = hci::ReadInquiryResponseTransmitPowerLevelCompleteBuilder::Create(
+            kNumberPacketsReadyToReceive, hci::ErrorCode::SUCCESS, hci_register_.inquiry_response_transmit_power_level);
+        break;
+
+      default:
+        LOG_INFO("Dropping unhandled command:%s", hci::OpCodeText(command.GetOpCode()).c_str());
+        return;
+    }
+    hci::EventPacketView event = hci::EventPacketView::Create(GetPacketView(std::move(event_builder)));
+    ASSERT(event.IsValid());
+    hci::CommandCompleteView command_complete = hci::CommandCompleteView::Create(event);
+    ASSERT(command_complete.IsValid());
+    handler->Post(common::BindOnce(std::move(on_complete), std::move(command_complete)));
+
+    if (promise_sync_complete_ != nullptr) {
+      promise_sync_complete_->set_value(command.GetOpCode());
+    }
+  }
+
+  void HandleStatus(std::unique_ptr<hci::CommandPacketBuilder> command_builder,
+                    common::OnceCallback<void(hci::CommandStatusView)> on_status, os::Handler* handler) {
+    hci::CommandPacketView command = hci::CommandPacketView::Create(GetPacketView(std::move(command_builder)));
+    ASSERT(command.IsValid());
+
+    std::unique_ptr<packet::BasePacketBuilder> event_builder;
+    switch (command.GetOpCode()) {
+      case hci::OpCode::INQUIRY: {
+        auto inquiry = hci::InquiryView::Create(hci::DiscoveryCommandView::Create(command));
+        ASSERT(inquiry.IsValid());
+        event_builder = hci::InquiryStatusBuilder::Create(hci::ErrorCode::SUCCESS, kNumberPacketsReadyToReceive);
+        hci_register_.one_shot_inquiry_active = true;
+        hci_register_.num_responses = inquiry.GetNumResponses();
+        hci_register_.inquiry_length = inquiry.GetInquiryLength();
+      } break;
+      default:
+        LOG_INFO("Dropping unhandled status expecting command:%s", hci::OpCodeText(command.GetOpCode()).c_str());
+        return;
+    }
+    hci::EventPacketView event = hci::EventPacketView::Create(GetPacketView(std::move(event_builder)));
+    ASSERT(event.IsValid());
+    hci::CommandStatusView command_status = hci::CommandStatusView::Create(event);
+    ASSERT(command_status.IsValid());
+    handler->Post(common::BindOnce(std::move(on_status), std::move(command_status)));
+
+    if (promise_sync_complete_ != nullptr) {
+      promise_sync_complete_->set_value(command.GetOpCode());
+    }
+  }
+
+  void RegisterEventHandler(hci::EventCode event_code, common::Callback<void(hci::EventPacketView)> event_handler,
+                            os::Handler* handler) override {
+    switch (event_code) {
+      case hci::EventCode::INQUIRY_RESULT:
+        inquiry_result_handler_ = handler;
+        inquiry_result_callback_ = event_handler;
+        break;
+      case hci::EventCode::INQUIRY_RESULT_WITH_RSSI:
+        inquiry_result_with_rssi_handler_ = handler;
+        inquiry_result_with_rssi_callback_ = event_handler;
+        break;
+      case hci::EventCode::EXTENDED_INQUIRY_RESULT:
+        extended_inquiry_result_handler_ = handler;
+        extended_inquiry_result_callback_ = event_handler;
+        break;
+      case hci::EventCode::INQUIRY_COMPLETE:
+        inquiry_complete_handler_ = handler;
+        inquiry_complete_callback_ = event_handler;
+        break;
+      default:
+        ASSERT_TRUE(false) << "Unexpected event handler being registered";
+        break;
+    }
+  }
+
+  void UnregisterEventHandler(hci::EventCode event_code) override {
+    if (hci_register_.one_shot_inquiry_active || hci_register_.periodic_inquiry_active) {
+      LOG_ERROR("Event handlers may not be unregistered until inquiry is stopped");
+      return;
+    }
+
+    switch (event_code) {
+      case hci::EventCode::INQUIRY_RESULT:
+        inquiry_result_handler_ = nullptr;
+        inquiry_result_callback_ = {};
+        break;
+      case hci::EventCode::INQUIRY_RESULT_WITH_RSSI:
+        inquiry_result_with_rssi_handler_ = nullptr;
+        inquiry_result_with_rssi_callback_ = {};
+        break;
+      case hci::EventCode::EXTENDED_INQUIRY_RESULT:
+        extended_inquiry_result_handler_ = nullptr;
+        extended_inquiry_result_callback_ = {};
+        break;
+      case hci::EventCode::INQUIRY_COMPLETE:
+        inquiry_complete_handler_ = nullptr;
+        inquiry_complete_callback_ = {};
+        break;
+      default:
+        ASSERT_TRUE(false) << "Unexpected event handler being unregistered";
+        break;
+    }
+  }
+
+  void Synchronize(std::function<void()> func, hci::OpCode op_code) {
+    ASSERT(promise_sync_complete_ == nullptr);
+    promise_sync_complete_ = new std::promise<hci::OpCode>();
+    auto future = promise_sync_complete_->get_future();
+    func();
+    auto result = future.wait_for(std::chrono::milliseconds(100));
+    ASSERT_EQ(std::future_status::ready, result);
+    ASSERT_EQ(op_code, future.get());
+    delete promise_sync_complete_;
+    promise_sync_complete_ = nullptr;
+  }
+
+  void InjectInquiryResult(std::unique_ptr<hci::InquiryResultBuilder> result) {
+    if (inquiry_result_handler_ != nullptr) {
+      hci::EventPacketView view = hci::EventPacketView::Create(GetPacketView(std::move(result)));
+      ASSERT(view.IsValid());
+      inquiry_result_handler_->Post(common::BindOnce(inquiry_result_callback_, std::move(view)));
+    }
+  }
+
+  void ListDependencies(ModuleList* list) override {}
+  void Start() override {}
+  void Stop() override {}
+
+ private:
+  std::promise<hci::OpCode>* promise_sync_complete_{nullptr};
+
+  os::Handler* inquiry_result_handler_{nullptr};
+  common::Callback<void(hci::EventPacketView)> inquiry_result_callback_;
+  os::Handler* inquiry_result_with_rssi_handler_{nullptr};
+  common::Callback<void(hci::EventPacketView)> inquiry_result_with_rssi_callback_;
+  os::Handler* extended_inquiry_result_handler_{nullptr};
+  common::Callback<void(hci::EventPacketView)> extended_inquiry_result_callback_;
+  os::Handler* inquiry_complete_handler_{nullptr};
+  common::Callback<void(hci::EventPacketView)> inquiry_complete_callback_;
+};
+
+class InquiryTest : public ::testing::Test {
+ public:
+  void Result(hci::InquiryResultView view) {
+    ASSERT(view.size() >= sizeof(uint16_t));
+    promise_result_complete_->set_value(true);
+  }
+
+  void WaitForInquiryResult(std::function<void()> func) {
+    ASSERT(promise_result_complete_ == nullptr);
+    promise_result_complete_ = new std::promise<bool>();
+    auto future = promise_result_complete_->get_future();
+    func();
+    future.wait();
+    delete promise_result_complete_;
+    promise_result_complete_ = nullptr;
+  }
+
+  void ResultWithRssi(hci::InquiryResultWithRssiView view) {
+    ASSERT(view.size() >= sizeof(uint16_t));
+  }
+
+  void ExtendedResult(hci::ExtendedInquiryResultView view) {
+    ASSERT(view.size() >= sizeof(uint16_t));
+  }
+
+  void Complete(hci::ErrorCode status) {}
+
+ protected:
+  void SetUp() override {
+    test_hci_layer_ = new TestHciLayer;
+    fake_registry_.InjectTestModule(&hci::HciLayer::Factory, test_hci_layer_);
+    client_handler_ = fake_registry_.GetTestModuleHandler(&hci::HciLayer::Factory);
+    fake_registry_.Start<InquiryModule>(&thread_);
+
+    inquiry_module_ = static_cast<InquiryModule*>(fake_registry_.GetModuleUnderTest(&InquiryModule::Factory));
+
+    InquiryCallbacks inquiry_callbacks;
+    inquiry_callbacks.result = std::bind(&InquiryTest::Result, this, std::placeholders::_1);
+    inquiry_callbacks.result_with_rssi = std::bind(&InquiryTest::ResultWithRssi, this, std::placeholders::_1);
+    inquiry_callbacks.extended_result = std::bind(&InquiryTest::ExtendedResult, this, std::placeholders::_1);
+    inquiry_callbacks.complete = std::bind(&InquiryTest::Complete, this, std::placeholders::_1);
+    inquiry_module_->RegisterCallbacks(inquiry_callbacks);
+  }
+
+  void TearDown() override {
+    inquiry_module_->UnregisterCallbacks();
+    fake_registry_.StopAll();
+  }
+
+  TestModuleRegistry fake_registry_;
+  TestHciLayer* test_hci_layer_ = nullptr;
+  os::Thread& thread_ = fake_registry_.GetTestThread();
+  InquiryModule* inquiry_module_ = nullptr;
+  os::Handler* client_handler_ = nullptr;
+
+  std::promise<bool>* promise_result_complete_{nullptr};
+};
+
+TEST_F(InquiryTest, Module) {}
+
+TEST_F(InquiryTest, SetInquiryModes) {
+  test_hci_layer_->Synchronize([this] { inquiry_module_->SetInquiryWithRssiResultMode(); },
+                               hci::OpCode::WRITE_INQUIRY_MODE);
+  ASSERT_EQ(hci_register_.inquiry_mode, hci::InquiryMode::RSSI);
+
+  test_hci_layer_->Synchronize([this] { inquiry_module_->SetExtendedInquiryResultMode(); },
+                               hci::OpCode::WRITE_INQUIRY_MODE);
+  ASSERT_EQ(hci_register_.inquiry_mode, hci::InquiryMode::RSSI_OR_EXTENDED);
+
+  test_hci_layer_->Synchronize([this] { inquiry_module_->SetStandardInquiryResultMode(); },
+                               hci::OpCode::WRITE_INQUIRY_MODE);
+  ASSERT_EQ(hci_register_.inquiry_mode, hci::InquiryMode::STANDARD);
+}
+
+TEST_F(InquiryTest, SetScanType) {
+  test_hci_layer_->Synchronize([this] { inquiry_module_->SetInterlacedScan(); }, hci::OpCode::WRITE_INQUIRY_SCAN_TYPE);
+  ASSERT_EQ(hci_register_.inquiry_scan_type, hci::InquiryScanType::INTERLACED);
+
+  test_hci_layer_->Synchronize([this] { inquiry_module_->SetStandardScan(); }, hci::OpCode::WRITE_INQUIRY_SCAN_TYPE);
+  ASSERT_EQ(hci_register_.inquiry_scan_type, hci::InquiryScanType::STANDARD);
+}
+
+TEST_F(InquiryTest, ScanActivity) {
+  ScanParameters params{
+      .interval = 0x1234,
+      .window = 0x5678,
+  };
+
+  test_hci_layer_->Synchronize([this, params] { inquiry_module_->SetScanActivity(params); },
+                               hci::OpCode::WRITE_INQUIRY_SCAN_ACTIVITY);
+  ASSERT_EQ(params.interval, hci_register_.inquiry_scan_interval);
+  ASSERT_EQ(params.window, hci_register_.inquiry_scan_window);
+}
+
+TEST_F(InquiryTest, OneShotGeneralInquiry) {
+  uint8_t inquiry_length = 128;
+  uint8_t num_responses = 100;
+  test_hci_layer_->Synchronize(
+      [this, inquiry_length, num_responses] { inquiry_module_->StartGeneralInquiry(inquiry_length, num_responses); },
+      hci::OpCode::INQUIRY);
+  ASSERT_EQ(inquiry_length, hci_register_.inquiry_length);
+  ASSERT_EQ(num_responses, hci_register_.num_responses);
+
+  test_hci_layer_->Synchronize([this] { inquiry_module_->StopInquiry(); }, hci::OpCode::INQUIRY_CANCEL);
+}
+
+TEST_F(InquiryTest, OneShotLimitedInquiry) {
+  test_hci_layer_->Synchronize([this] { inquiry_module_->StartLimitedInquiry(128, 100); }, hci::OpCode::INQUIRY);
+
+  test_hci_layer_->Synchronize([this] { inquiry_module_->StopInquiry(); }, hci::OpCode::INQUIRY_CANCEL);
+}
+
+TEST_F(InquiryTest, GeneralPeriodicInquiry) {
+  uint8_t inquiry_length = 128;
+  uint8_t num_responses = 100;
+  uint16_t max_delay = 1100;
+  uint16_t min_delay = 200;
+  test_hci_layer_->Synchronize(
+      [this, inquiry_length, num_responses, max_delay, min_delay] {
+        inquiry_module_->StartGeneralPeriodicInquiry(inquiry_length, num_responses, max_delay, min_delay);
+      },
+      hci::OpCode::PERIODIC_INQUIRY_MODE);
+  ASSERT_EQ(inquiry_length, hci_register_.inquiry_length);
+  ASSERT_EQ(num_responses, hci_register_.num_responses);
+  ASSERT_EQ(max_delay, hci_register_.max_period_length);
+  ASSERT_EQ(min_delay, hci_register_.min_period_length);
+
+  test_hci_layer_->Synchronize([this] { inquiry_module_->StopPeriodicInquiry(); },
+                               hci::OpCode::EXIT_PERIODIC_INQUIRY_MODE);
+}
+
+TEST_F(InquiryTest, LimitedPeriodicInquiry) {
+  test_hci_layer_->Synchronize([this] { inquiry_module_->StartLimitedPeriodicInquiry(128, 100, 1100, 200); },
+                               hci::OpCode::PERIODIC_INQUIRY_MODE);
+
+  test_hci_layer_->Synchronize([this] { inquiry_module_->StopPeriodicInquiry(); },
+                               hci::OpCode::EXIT_PERIODIC_INQUIRY_MODE);
+}
+
+TEST_F(InquiryTest, InjectInquiryResult) {
+  test_hci_layer_->Synchronize([this] { inquiry_module_->StartGeneralInquiry(128, 100); }, hci::OpCode::INQUIRY);
+
+  WaitForInquiryResult([this] {
+    const std::vector<hci::InquiryResult> inquiry_results;
+    auto packet = hci::InquiryResultBuilder::Create(inquiry_results);
+    test_hci_layer_->InjectInquiryResult(std::move(packet));
+  });
+  test_hci_layer_->Synchronize([this] { inquiry_module_->StopInquiry(); }, hci::OpCode::INQUIRY_CANCEL);
+}
+
+}  // namespace
+}  // namespace neighbor
+}  // namespace bluetooth
diff --git a/gd/neighbor/name.cc b/gd/neighbor/name.cc
new file mode 100644
index 0000000..f373486
--- /dev/null
+++ b/gd/neighbor/name.cc
@@ -0,0 +1,229 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_TAG "bt_gd_neigh"
+
+#include "neighbor/name.h"
+
+#include <memory>
+#include <unordered_map>
+#include <utility>
+
+#include "common/bind.h"
+#include "hci/hci_layer.h"
+#include "hci/hci_packets.h"
+#include "module.h"
+#include "os/handler.h"
+#include "os/log.h"
+
+namespace bluetooth {
+namespace neighbor {
+
+struct ReadCallbackHandler {
+  ReadRemoteNameCallback callback;
+  os::Handler* handler;
+};
+
+struct CancelCallbackHandler {
+  CancelRemoteNameCallback callback;
+  os::Handler* handler;
+};
+
+constexpr RemoteName kEmptyName{};
+
+struct NameModule::impl {
+  void ReadRemoteNameRequest(hci::Address address, hci::PageScanRepetitionMode page_scan_repetition_mode,
+                             uint16_t clock_offset, hci::ClockOffsetValid clock_offset_valid,
+                             ReadRemoteNameCallback callback, os::Handler* handler);
+  void CancelRemoteNameRequest(hci::Address address, CancelRemoteNameCallback, os::Handler* handler);
+
+  void Start();
+  void Stop();
+
+  impl(const NameModule& name_module);
+
+ private:
+  const NameModule& module_;
+
+  void EnqueueCommandComplete(std::unique_ptr<hci::CommandPacketBuilder> command);
+  void EnqueueCommandStatus(std::unique_ptr<hci::CommandPacketBuilder> command);
+
+  void OnCommandComplete(hci::CommandCompleteView view);
+  void OnCommandStatus(hci::CommandStatusView status);
+  void OnEvent(hci::EventPacketView view);
+
+  std::unordered_map<hci::Address, std::unique_ptr<ReadCallbackHandler>> read_callback_handler_map_;
+  std::unordered_map<hci::Address, std::unique_ptr<CancelCallbackHandler>> cancel_callback_handler_map_;
+
+  hci::HciLayer* hci_layer_;
+  os::Handler* handler_;
+};
+
+const ModuleFactory neighbor::NameModule::Factory = ModuleFactory([]() { return new neighbor::NameModule(); });
+
+neighbor::NameModule::impl::impl(const neighbor::NameModule& module) : module_(module) {}
+
+void neighbor::NameModule::impl::EnqueueCommandComplete(std::unique_ptr<hci::CommandPacketBuilder> command) {
+  hci_layer_->EnqueueCommand(std::move(command), common::BindOnce(&impl::OnCommandComplete, common::Unretained(this)),
+                             handler_);
+}
+
+void neighbor::NameModule::impl::EnqueueCommandStatus(std::unique_ptr<hci::CommandPacketBuilder> command) {
+  hci_layer_->EnqueueCommand(std::move(command), common::BindOnce(&impl::OnCommandStatus, common::Unretained(this)),
+                             handler_);
+}
+
+void neighbor::NameModule::impl::OnCommandComplete(hci::CommandCompleteView view) {
+  switch (view.GetCommandOpCode()) {
+    case hci::OpCode::REMOTE_NAME_REQUEST_CANCEL: {
+      auto packet = hci::RemoteNameRequestCancelCompleteView::Create(view);
+      ASSERT(packet.IsValid());
+      ASSERT(packet.GetStatus() == hci::ErrorCode::SUCCESS);
+      hci::Address address = packet.GetBdAddr();
+      ASSERT(cancel_callback_handler_map_.find(address) != cancel_callback_handler_map_.end());
+      cancel_callback_handler_map_.erase(address);
+    } break;
+    default:
+      LOG_WARN("Unhandled command:%s", hci::OpCodeText(view.GetCommandOpCode()).c_str());
+      break;
+  }
+}
+
+void neighbor::NameModule::impl::OnCommandStatus(hci::CommandStatusView status) {
+  ASSERT(status.GetStatus() == hci::ErrorCode::SUCCESS);
+
+  switch (status.GetCommandOpCode()) {
+    case hci::OpCode::REMOTE_NAME_REQUEST: {
+      auto packet = hci::RemoteNameRequestStatusView::Create(status);
+      ASSERT(packet.IsValid());
+    } break;
+
+    default:
+      LOG_WARN("Unhandled command:%s", hci::OpCodeText(status.GetCommandOpCode()).c_str());
+      break;
+  }
+}
+
+void neighbor::NameModule::impl::OnEvent(hci::EventPacketView view) {
+  switch (view.GetEventCode()) {
+    case hci::EventCode::REMOTE_NAME_REQUEST_COMPLETE: {
+      auto packet = hci::RemoteNameRequestCompleteView::Create(view);
+      ASSERT(packet.IsValid());
+      hci::Address address = packet.GetBdAddr();
+      ASSERT(read_callback_handler_map_.find(address) != read_callback_handler_map_.end());
+      auto read_callback_handler = std::move(read_callback_handler_map_[address]);
+      read_callback_handler->handler->Post(common::BindOnce(std::move(read_callback_handler->callback),
+                                                            packet.GetStatus(), address, packet.GetRemoteName()));
+      read_callback_handler_map_.erase(address);
+    } break;
+    default:
+      LOG_ERROR("Unhandled event:%s", hci::EventCodeText(view.GetEventCode()).c_str());
+      break;
+  }
+}
+
+void neighbor::NameModule::impl::Start() {
+  hci_layer_ = module_.GetDependency<hci::HciLayer>();
+  handler_ = module_.GetHandler();
+
+  hci_layer_->RegisterEventHandler(hci::EventCode::REMOTE_NAME_REQUEST_COMPLETE,
+                                   common::Bind(&NameModule::impl::OnEvent, common::Unretained(this)), handler_);
+}
+
+void neighbor::NameModule::impl::Stop() {
+  hci_layer_->UnregisterEventHandler(hci::EventCode::REMOTE_NAME_REQUEST_COMPLETE);
+}
+
+void neighbor::NameModule::impl::ReadRemoteNameRequest(hci::Address address,
+                                                       hci::PageScanRepetitionMode page_scan_repetition_mode,
+                                                       uint16_t clock_offset, hci::ClockOffsetValid clock_offset_valid,
+                                                       ReadRemoteNameCallback callback, os::Handler* handler) {
+  LOG_DEBUG("%s Start read remote name request for %s", __func__, address.ToString().c_str());
+
+  if (read_callback_handler_map_.find(address) != read_callback_handler_map_.end()) {
+    LOG_WARN("Ignoring duplicate read remote name request to:%s", address.ToString().c_str());
+    handler->Post(common::BindOnce(std::move(callback), hci::ErrorCode::UNSPECIFIED_ERROR, address, kEmptyName));
+    return;
+  }
+  read_callback_handler_map_[address] = std::unique_ptr<ReadCallbackHandler>(new ReadCallbackHandler{
+      .callback = std::move(callback),
+      .handler = handler,
+  });
+
+  EnqueueCommandStatus(
+      hci::RemoteNameRequestBuilder::Create(address, page_scan_repetition_mode, clock_offset, clock_offset_valid));
+}
+
+void neighbor::NameModule::impl::CancelRemoteNameRequest(hci::Address address, CancelRemoteNameCallback callback,
+                                                         os::Handler* handler) {
+  LOG_DEBUG("%s Cancel remote name request for %s", __func__, address.ToString().c_str());
+
+  if (cancel_callback_handler_map_.find(address) != cancel_callback_handler_map_.end()) {
+    LOG_WARN("Ignoring duplicate cancel remote name request to:%s", address.ToString().c_str());
+    handler->Post(common::BindOnce(std::move(callback), hci::ErrorCode::UNSPECIFIED_ERROR, address));
+    return;
+  }
+  cancel_callback_handler_map_[address] = std::unique_ptr<CancelCallbackHandler>(new CancelCallbackHandler{
+      .callback = std::move(callback),
+      .handler = handler,
+  });
+  EnqueueCommandComplete(hci::RemoteNameRequestCancelBuilder::Create(address));
+}
+
+/**
+ * General API here
+ */
+neighbor::NameModule::NameModule() : pimpl_(std::make_unique<impl>(*this)) {}
+
+neighbor::NameModule::~NameModule() {
+  pimpl_.reset();
+}
+
+void neighbor::NameModule::ReadRemoteNameRequest(hci::Address address,
+                                                 hci::PageScanRepetitionMode page_scan_repetition_mode,
+                                                 uint16_t clock_offset, hci::ClockOffsetValid clock_offset_valid,
+                                                 ReadRemoteNameCallback callback, os::Handler* handler) {
+  ASSERT(callback);
+  ASSERT(handler != nullptr);
+  GetHandler()->Post(common::BindOnce(&NameModule::impl::ReadRemoteNameRequest, common::Unretained(pimpl_.get()),
+                                      address, page_scan_repetition_mode, clock_offset, clock_offset_valid,
+                                      std::move(callback), handler));
+}
+
+void neighbor::NameModule::CancelRemoteNameRequest(hci::Address address, CancelRemoteNameCallback callback,
+                                                   os::Handler* handler) {
+  ASSERT(callback);
+  ASSERT(handler != nullptr);
+  GetHandler()->Post(common::BindOnce(&NameModule::impl::CancelRemoteNameRequest, common::Unretained(pimpl_.get()),
+                                      address, std::move(callback), handler));
+}
+
+/**
+ * Module methods here
+ */
+void neighbor::NameModule::ListDependencies(ModuleList* list) {
+  list->add<hci::HciLayer>();
+}
+
+void neighbor::NameModule::Start() {
+  pimpl_->Start();
+}
+
+void neighbor::NameModule::Stop() {
+  pimpl_->Stop();
+}
+
+}  // namespace neighbor
+}  // namespace bluetooth
diff --git a/gd/neighbor/name.h b/gd/neighbor/name.h
new file mode 100644
index 0000000..c671ddc
--- /dev/null
+++ b/gd/neighbor/name.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <array>
+#include <cstdint>
+#include <memory>
+
+#include "common/bind.h"
+#include "hci/address.h"
+#include "hci/hci_packets.h"
+#include "module.h"
+
+namespace bluetooth {
+namespace neighbor {
+
+using RemoteName = std::array<uint8_t, 248>;
+using ReadRemoteNameCallback = common::OnceCallback<void(hci::ErrorCode status, hci::Address address, RemoteName name)>;
+using CancelRemoteNameCallback = common::OnceCallback<void(hci::ErrorCode status, hci::Address address)>;
+
+class NameModule : public bluetooth::Module {
+ public:
+  void ReadRemoteNameRequest(hci::Address address, hci::PageScanRepetitionMode page_scan_repetition_mode,
+                             uint16_t clock_offset, hci::ClockOffsetValid clock_offset_valid,
+                             ReadRemoteNameCallback on_read_name, os::Handler* handler);
+  void CancelRemoteNameRequest(hci::Address address, CancelRemoteNameCallback on_cancel, os::Handler* handler);
+
+  static const ModuleFactory Factory;
+
+  NameModule();
+  ~NameModule();
+
+ protected:
+  void ListDependencies(ModuleList* list) override;
+  void Start() override;
+  void Stop() override;
+
+ private:
+  struct impl;
+  std::unique_ptr<impl> pimpl_;
+
+  DISALLOW_COPY_AND_ASSIGN(NameModule);
+};
+
+}  // namespace neighbor
+}  // namespace bluetooth
diff --git a/gd/neighbor/name_db.cc b/gd/neighbor/name_db.cc
new file mode 100644
index 0000000..e82cc5e
--- /dev/null
+++ b/gd/neighbor/name_db.cc
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_TAG "bt_gd_neigh"
+
+#include "neighbor/name_db.h"
+
+#include <memory>
+#include <unordered_map>
+#include <utility>
+
+#include "common/bind.h"
+#include "module.h"
+#include "neighbor/name.h"
+#include "os/handler.h"
+#include "os/log.h"
+
+namespace bluetooth {
+namespace neighbor {
+
+namespace {
+struct PendingRemoteNameRead {
+  ReadRemoteNameDbCallback callback_;
+  os::Handler* handler_;
+};
+}  // namespace
+
+struct NameDbModule::impl {
+  void ReadRemoteNameRequest(hci::Address address, ReadRemoteNameDbCallback callback, os::Handler* handler);
+
+  bool IsNameCached(hci::Address address) const;
+  RemoteName ReadCachedRemoteName(hci::Address address) const;
+
+  impl(const NameDbModule& module);
+
+  void Start();
+  void Stop();
+
+ private:
+  std::unordered_map<hci::Address, PendingRemoteNameRead> address_to_pending_read_map_;
+  std::unordered_map<hci::Address, RemoteName> address_to_name_map_;
+
+  void OnRemoteNameResponse(hci::ErrorCode status, hci::Address address, RemoteName name);
+
+  neighbor::NameModule* name_module_;
+
+  const NameDbModule& module_;
+  os::Handler* handler_;
+};
+
+const ModuleFactory neighbor::NameDbModule::Factory = ModuleFactory([]() { return new neighbor::NameDbModule(); });
+
+neighbor::NameDbModule::impl::impl(const neighbor::NameDbModule& module) : module_(module) {}
+
+void neighbor::NameDbModule::impl::ReadRemoteNameRequest(hci::Address address, ReadRemoteNameDbCallback callback,
+                                                         os::Handler* handler) {
+  if (address_to_pending_read_map_.find(address) != address_to_pending_read_map_.end()) {
+    LOG_WARN("Already have remote read db in progress and currently can only have one outstanding");
+    return;
+  }
+
+  address_to_pending_read_map_[address] = {std::move(callback), std::move(handler)};
+
+  // TODO(cmanton) Use remote name request defaults for now
+  hci::PageScanRepetitionMode page_scan_repetition_mode = hci::PageScanRepetitionMode::R1;
+  uint16_t clock_offset = 0;
+  hci::ClockOffsetValid clock_offset_valid = hci::ClockOffsetValid::INVALID;
+  name_module_->ReadRemoteNameRequest(
+      address, page_scan_repetition_mode, clock_offset, clock_offset_valid,
+      common::BindOnce(&NameDbModule::impl::OnRemoteNameResponse, common::Unretained(this)), handler_);
+}
+
+void neighbor::NameDbModule::impl::OnRemoteNameResponse(hci::ErrorCode status, hci::Address address, RemoteName name) {
+  ASSERT(address_to_pending_read_map_.find(address) != address_to_pending_read_map_.end());
+  PendingRemoteNameRead callback_handler = std::move(address_to_pending_read_map_.at(address));
+
+  if (status == hci::ErrorCode::SUCCESS) {
+    address_to_name_map_[address] = name;
+  }
+  callback_handler.handler_->Post(
+      common::BindOnce(std::move(callback_handler.callback_), address, status == hci::ErrorCode::SUCCESS));
+}
+
+bool neighbor::NameDbModule::impl::IsNameCached(hci::Address address) const {
+  return address_to_name_map_.count(address) == 1;
+}
+
+RemoteName neighbor::NameDbModule::impl::ReadCachedRemoteName(hci::Address address) const {
+  ASSERT(IsNameCached(address));
+  return address_to_name_map_.at(address);
+}
+
+/**
+ * General API here
+ */
+neighbor::NameDbModule::NameDbModule() : pimpl_(std::make_unique<impl>(*this)) {}
+
+neighbor::NameDbModule::~NameDbModule() {
+  pimpl_.reset();
+}
+
+void neighbor::NameDbModule::ReadRemoteNameRequest(hci::Address address, ReadRemoteNameDbCallback callback,
+                                                   os::Handler* handler) {
+  GetHandler()->Post(common::BindOnce(&NameDbModule::impl::ReadRemoteNameRequest, common::Unretained(pimpl_.get()),
+                                      address, std::move(callback), handler));
+}
+
+bool neighbor::NameDbModule::IsNameCached(hci::Address address) const {
+  return pimpl_->IsNameCached(address);
+}
+
+RemoteName neighbor::NameDbModule::ReadCachedRemoteName(hci::Address address) const {
+  return pimpl_->ReadCachedRemoteName(address);
+}
+
+void neighbor::NameDbModule::impl::Start() {
+  name_module_ = module_.GetDependency<neighbor::NameModule>();
+  handler_ = module_.GetHandler();
+}
+
+void neighbor::NameDbModule::impl::Stop() {}
+
+/**
+ * Module methods here
+ */
+void neighbor::NameDbModule::ListDependencies(ModuleList* list) {
+  list->add<neighbor::NameModule>();
+}
+
+void neighbor::NameDbModule::Start() {
+  pimpl_->Start();
+}
+
+void neighbor::NameDbModule::Stop() {
+  pimpl_->Stop();
+}
+
+}  // namespace neighbor
+}  // namespace bluetooth
diff --git a/gd/neighbor/name_db.h b/gd/neighbor/name_db.h
new file mode 100644
index 0000000..010c494
--- /dev/null
+++ b/gd/neighbor/name_db.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <array>
+#include <cstdint>
+#include <memory>
+
+#include "common/bind.h"
+#include "hci/address.h"
+#include "hci/hci_packets.h"
+#include "module.h"
+#include "neighbor/name.h"
+
+namespace bluetooth {
+namespace neighbor {
+
+using ReadRemoteNameDbCallback = common::OnceCallback<void(hci::Address address, bool success)>;
+
+class NameDbModule : public bluetooth::Module {
+ public:
+  void ReadRemoteNameRequest(hci::Address address, ReadRemoteNameDbCallback callback, os::Handler* handler);
+
+  bool IsNameCached(hci::Address address) const;
+  RemoteName ReadCachedRemoteName(hci::Address address) const;
+
+  static const ModuleFactory Factory;
+
+  NameDbModule();
+  ~NameDbModule();
+
+ protected:
+  void ListDependencies(ModuleList* list) override;
+  void Start() override;
+  void Stop() override;
+
+ private:
+  struct impl;
+  std::unique_ptr<impl> pimpl_;
+
+  DISALLOW_COPY_AND_ASSIGN(NameDbModule);
+};
+
+}  // namespace neighbor
+}  // namespace bluetooth
diff --git a/gd/neighbor/page.cc b/gd/neighbor/page.cc
new file mode 100644
index 0000000..4631875
--- /dev/null
+++ b/gd/neighbor/page.cc
@@ -0,0 +1,206 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_TAG "bt_gd_neigh"
+
+#include <memory>
+
+#include "common/bind.h"
+#include "hci/hci_layer.h"
+#include "hci/hci_packets.h"
+#include "module.h"
+#include "neighbor/page.h"
+#include "neighbor/scan_parameters.h"
+#include "os/handler.h"
+#include "os/log.h"
+
+namespace bluetooth {
+namespace neighbor {
+
+struct PageModule::impl {
+  void SetScanActivity(ScanParameters params);
+  ScanParameters GetScanActivity() const;
+
+  void SetScanType(hci::PageScanType type);
+
+  void SetTimeout(PageTimeout timeout);
+
+  void Start();
+  void Stop();
+
+  impl(PageModule& page_module);
+
+ private:
+  PageModule& module_;
+
+  ScanParameters scan_parameters_;
+  hci::PageScanType scan_type_;
+  PageTimeout timeout_;
+
+  void OnCommandComplete(hci::CommandCompleteView status);
+
+  hci::HciLayer* hci_layer_;
+  os::Handler* handler_;
+};
+
+const ModuleFactory neighbor::PageModule::Factory = ModuleFactory([]() { return new neighbor::PageModule(); });
+
+neighbor::PageModule::impl::impl(neighbor::PageModule& module) : module_(module) {}
+
+void neighbor::PageModule::impl::OnCommandComplete(hci::CommandCompleteView view) {
+  switch (view.GetCommandOpCode()) {
+    case hci::OpCode::WRITE_PAGE_SCAN_ACTIVITY: {
+      auto packet = hci::WritePageScanActivityCompleteView::Create(view);
+      ASSERT(packet.IsValid());
+      ASSERT(packet.GetStatus() == hci::ErrorCode::SUCCESS);
+    } break;
+
+    case hci::OpCode::READ_PAGE_SCAN_ACTIVITY: {
+      auto packet = hci::ReadPageScanActivityCompleteView::Create(view);
+      ASSERT(packet.IsValid());
+      ASSERT(packet.GetStatus() == hci::ErrorCode::SUCCESS);
+      scan_parameters_.interval = packet.GetPageScanInterval();
+      scan_parameters_.window = packet.GetPageScanWindow();
+    } break;
+
+    case hci::OpCode::WRITE_PAGE_SCAN_TYPE: {
+      auto packet = hci::WritePageScanTypeCompleteView::Create(view);
+      ASSERT(packet.IsValid());
+      ASSERT(packet.GetStatus() == hci::ErrorCode::SUCCESS);
+    } break;
+
+    case hci::OpCode::READ_PAGE_SCAN_TYPE: {
+      auto packet = hci::ReadPageScanTypeCompleteView::Create(view);
+      ASSERT(packet.IsValid());
+      ASSERT(packet.GetStatus() == hci::ErrorCode::SUCCESS);
+      scan_type_ = packet.GetPageScanType();
+    } break;
+
+    case hci::OpCode::WRITE_PAGE_TIMEOUT: {
+      auto packet = hci::WritePageTimeoutCompleteView::Create(view);
+      ASSERT(packet.IsValid());
+      ASSERT(packet.GetStatus() == hci::ErrorCode::SUCCESS);
+    } break;
+
+    case hci::OpCode::READ_PAGE_TIMEOUT: {
+      auto packet = hci::ReadPageTimeoutCompleteView::Create(view);
+      ASSERT(packet.IsValid());
+      ASSERT(packet.GetStatus() == hci::ErrorCode::SUCCESS);
+      timeout_ = packet.GetPageTimeout();
+    } break;
+
+    default:
+      LOG_ERROR("Unhandled command %s", hci::OpCodeText(view.GetCommandOpCode()).c_str());
+      break;
+  }
+}
+
+void neighbor::PageModule::impl::Start() {
+  hci_layer_ = module_.GetDependency<hci::HciLayer>();
+  handler_ = module_.GetHandler();
+
+  hci_layer_->EnqueueCommand(hci::ReadPageScanActivityBuilder::Create(),
+                             common::BindOnce(&impl::OnCommandComplete, common::Unretained(this)), handler_);
+
+  hci_layer_->EnqueueCommand(hci::ReadPageScanTypeBuilder::Create(),
+                             common::BindOnce(&impl::OnCommandComplete, common::Unretained(this)), handler_);
+
+  hci_layer_->EnqueueCommand(hci::ReadPageTimeoutBuilder::Create(),
+                             common::BindOnce(&impl::OnCommandComplete, common::Unretained(this)), handler_);
+}
+
+void neighbor::PageModule::impl::Stop() {
+  LOG_DEBUG("Page scan interval:%hd window:%hd", scan_parameters_.interval, scan_parameters_.window);
+  LOG_DEBUG("Page scan_type:%s", hci::PageScanTypeText(scan_type_).c_str());
+}
+
+void neighbor::PageModule::impl::SetScanActivity(ScanParameters params) {
+  hci_layer_->EnqueueCommand(hci::WritePageScanActivityBuilder::Create(params.interval, params.window),
+                             common::BindOnce(&impl::OnCommandComplete, common::Unretained(this)), handler_);
+
+  hci_layer_->EnqueueCommand(hci::ReadPageScanActivityBuilder::Create(),
+                             common::BindOnce(&impl::OnCommandComplete, common::Unretained(this)), handler_);
+  LOG_DEBUG("Set page scan activity interval:0x%x/%.02fms window:0x%x/%.02fms", params.interval,
+            ScanIntervalTimeMs(params.interval), params.window, ScanWindowTimeMs(params.window));
+}
+
+ScanParameters neighbor::PageModule::impl::GetScanActivity() const {
+  return scan_parameters_;
+}
+
+void neighbor::PageModule::impl::SetScanType(hci::PageScanType scan_type) {
+  hci_layer_->EnqueueCommand(hci::WritePageScanTypeBuilder::Create(scan_type),
+                             common::BindOnce(&impl::OnCommandComplete, common::Unretained(this)), handler_);
+
+  hci_layer_->EnqueueCommand(hci::ReadPageScanTypeBuilder::Create(),
+                             common::BindOnce(&impl::OnCommandComplete, common::Unretained(this)), handler_);
+  LOG_DEBUG("Set page scan type:%s", hci::PageScanTypeText(scan_type).c_str());
+}
+
+void neighbor::PageModule::impl::SetTimeout(PageTimeout timeout) {
+  hci_layer_->EnqueueCommand(hci::WritePageTimeoutBuilder::Create(timeout),
+                             common::BindOnce(&impl::OnCommandComplete, common::Unretained(this)), handler_);
+
+  hci_layer_->EnqueueCommand(hci::ReadPageTimeoutBuilder::Create(),
+                             common::BindOnce(&impl::OnCommandComplete, common::Unretained(this)), handler_);
+  LOG_DEBUG("Set page scan timeout:0x%x/%.02fms", timeout, PageTimeoutMs(timeout));
+}
+
+/**
+ * General API here
+ */
+neighbor::PageModule::PageModule() : pimpl_(std::make_unique<impl>(*this)) {}
+
+neighbor::PageModule::~PageModule() {
+  pimpl_.reset();
+}
+
+void neighbor::PageModule::SetScanActivity(ScanParameters params) {
+  pimpl_->SetScanActivity(params);
+}
+
+ScanParameters neighbor::PageModule::GetScanActivity() const {
+  return pimpl_->GetScanActivity();
+}
+
+void neighbor::PageModule::SetInterlacedScan() {
+  pimpl_->SetScanType(hci::PageScanType::INTERLACED);
+}
+
+void neighbor::PageModule::SetStandardScan() {
+  pimpl_->SetScanType(hci::PageScanType::STANDARD);
+}
+
+void neighbor::PageModule::SetTimeout(PageTimeout timeout) {
+  pimpl_->SetTimeout(timeout);
+}
+
+/**
+ * Module methods here
+ */
+void neighbor::PageModule::ListDependencies(ModuleList* list) {
+  list->add<hci::HciLayer>();
+}
+
+void neighbor::PageModule::Start() {
+  pimpl_->Start();
+}
+
+void neighbor::PageModule::Stop() {
+  pimpl_->Stop();
+}
+
+}  // namespace neighbor
+}  // namespace bluetooth
diff --git a/gd/neighbor/page.h b/gd/neighbor/page.h
new file mode 100644
index 0000000..075217b
--- /dev/null
+++ b/gd/neighbor/page.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <memory>
+
+#include "hci/hci_packets.h"
+#include "module.h"
+#include "neighbor/scan_parameters.h"
+
+namespace bluetooth {
+namespace neighbor {
+
+using PageTimeout = uint16_t;  // Range = 0x0001 to 0xffff, Time N * 0.625ms
+
+inline double PageTimeoutMs(PageTimeout timeout) {
+  return kTimeTickMs * timeout;
+}
+
+class PageModule : public bluetooth::Module {
+ public:
+  void SetScanActivity(ScanParameters params);
+  ScanParameters GetScanActivity() const;
+
+  void SetInterlacedScan();
+  void SetStandardScan();
+
+  void SetTimeout(PageTimeout timeout);
+
+  static const ModuleFactory Factory;
+
+  PageModule();
+  ~PageModule();
+
+ protected:
+  void ListDependencies(ModuleList* list) override;
+  void Start() override;
+  void Stop() override;
+
+ private:
+  struct impl;
+  std::unique_ptr<impl> pimpl_;
+
+  DISALLOW_COPY_AND_ASSIGN(PageModule);
+};
+
+}  // namespace neighbor
+}  // namespace bluetooth
diff --git a/gd/neighbor/scan.cc b/gd/neighbor/scan.cc
new file mode 100644
index 0000000..5232b08
--- /dev/null
+++ b/gd/neighbor/scan.cc
@@ -0,0 +1,206 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_TAG "bt_gd_neigh"
+
+#include "neighbor/scan.h"
+#include <memory>
+#include "hci/hci_layer.h"
+#include "hci/hci_packets.h"
+#include "module.h"
+#include "os/handler.h"
+#include "os/log.h"
+
+namespace bluetooth {
+namespace neighbor {
+
+struct ScanModule::impl {
+  impl(ScanModule& module);
+
+  void SetInquiryScan(bool enabled);
+  bool IsInquiryEnabled() const;
+
+  void SetPageScan(bool enabled);
+  bool IsPageEnabled() const;
+
+  void Start();
+  void Stop();
+
+ private:
+  ScanModule& module_;
+
+  bool inquiry_scan_enabled_;
+  bool page_scan_enabled_;
+
+  void WriteScanEnable();
+  void ReadScanEnable(hci::ScanEnable);
+
+  void OnCommandComplete(hci::CommandCompleteView status);
+
+  hci::HciLayer* hci_layer_;
+  os::Handler* handler_;
+};
+
+const ModuleFactory neighbor::ScanModule::Factory = ModuleFactory([]() { return new neighbor::ScanModule(); });
+
+neighbor::ScanModule::impl::impl(neighbor::ScanModule& module)
+    : module_(module), inquiry_scan_enabled_(false), page_scan_enabled_(false) {}
+
+void neighbor::ScanModule::impl::OnCommandComplete(hci::CommandCompleteView view) {
+  switch (view.GetCommandOpCode()) {
+    case hci::OpCode::READ_SCAN_ENABLE: {
+      auto packet = hci::ReadScanEnableCompleteView::Create(view);
+      ASSERT(packet.IsValid());
+      ASSERT(packet.GetStatus() == hci::ErrorCode::SUCCESS);
+      ReadScanEnable(packet.GetScanEnable());
+    } break;
+
+    case hci::OpCode::WRITE_SCAN_ENABLE: {
+      auto packet = hci::WriteScanEnableCompleteView::Create(view);
+      ASSERT(packet.IsValid());
+      ASSERT(packet.GetStatus() == hci::ErrorCode::SUCCESS);
+    } break;
+
+    default:
+      LOG_ERROR("Unhandled command %s", hci::OpCodeText(view.GetCommandOpCode()).c_str());
+      break;
+  }
+}
+
+void neighbor::ScanModule::impl::WriteScanEnable() {
+  hci::ScanEnable scan_enable;
+
+  if (inquiry_scan_enabled_ && !page_scan_enabled_) {
+    scan_enable = hci::ScanEnable::INQUIRY_SCAN_ONLY;
+  } else if (!inquiry_scan_enabled_ && page_scan_enabled_) {
+    scan_enable = hci::ScanEnable::PAGE_SCAN_ONLY;
+  } else if (inquiry_scan_enabled_ && page_scan_enabled_) {
+    scan_enable = hci::ScanEnable::INQUIRY_AND_PAGE_SCAN;
+  } else {
+    scan_enable = hci::ScanEnable::NO_SCANS;
+  }
+
+  {
+    std::unique_ptr<hci::WriteScanEnableBuilder> packet = hci::WriteScanEnableBuilder::Create(scan_enable);
+    hci_layer_->EnqueueCommand(std::move(packet), common::BindOnce(&impl::OnCommandComplete, common::Unretained(this)),
+                               handler_);
+  }
+
+  {
+    std::unique_ptr<hci::ReadScanEnableBuilder> packet = hci::ReadScanEnableBuilder::Create();
+    hci_layer_->EnqueueCommand(std::move(packet), common::BindOnce(&impl::OnCommandComplete, common::Unretained(this)),
+                               handler_);
+  }
+}
+
+void neighbor::ScanModule::impl::ReadScanEnable(hci::ScanEnable scan_enable) {
+  switch (scan_enable) {
+    case hci::ScanEnable::INQUIRY_SCAN_ONLY:
+      inquiry_scan_enabled_ = true;
+      page_scan_enabled_ = false;
+      break;
+
+    case hci::ScanEnable::PAGE_SCAN_ONLY:
+      inquiry_scan_enabled_ = false;
+      page_scan_enabled_ = true;
+      break;
+
+    case hci::ScanEnable::INQUIRY_AND_PAGE_SCAN:
+      inquiry_scan_enabled_ = true;
+      page_scan_enabled_ = true;
+      break;
+
+    default:
+      inquiry_scan_enabled_ = false;
+      page_scan_enabled_ = false;
+      break;
+  }
+}
+
+void neighbor::ScanModule::impl::SetInquiryScan(bool enabled) {
+  inquiry_scan_enabled_ = enabled;
+  WriteScanEnable();
+}
+
+void neighbor::ScanModule::impl::SetPageScan(bool enabled) {
+  page_scan_enabled_ = enabled;
+  WriteScanEnable();
+}
+
+bool neighbor::ScanModule::impl::IsInquiryEnabled() const {
+  return inquiry_scan_enabled_;
+}
+
+bool neighbor::ScanModule::impl::IsPageEnabled() const {
+  return page_scan_enabled_;
+}
+
+void neighbor::ScanModule::impl::Start() {
+  hci_layer_ = module_.GetDependency<hci::HciLayer>();
+  handler_ = module_.GetHandler();
+
+  std::unique_ptr<hci::ReadScanEnableBuilder> packet = hci::ReadScanEnableBuilder::Create();
+  hci_layer_->EnqueueCommand(std::move(packet), common::BindOnce(&impl::OnCommandComplete, common::Unretained(this)),
+                             handler_);
+}
+
+void neighbor::ScanModule::impl::Stop() {
+  LOG_DEBUG("inquiry scan enabled:%d page scan enabled:%d", inquiry_scan_enabled_, page_scan_enabled_);
+}
+
+neighbor::ScanModule::ScanModule() : pimpl_(std::make_unique<impl>(*this)) {}
+
+neighbor::ScanModule::~ScanModule() {
+  pimpl_.reset();
+}
+
+void neighbor::ScanModule::SetInquiryScan() {
+  pimpl_->SetInquiryScan(true);
+}
+
+void neighbor::ScanModule::ClearInquiryScan() {
+  pimpl_->SetInquiryScan(false);
+}
+
+void neighbor::ScanModule::SetPageScan() {
+  pimpl_->SetPageScan(true);
+}
+
+void neighbor::ScanModule::ClearPageScan() {
+  pimpl_->SetPageScan(false);
+}
+
+bool neighbor::ScanModule::IsInquiryEnabled() const {
+  return pimpl_->IsInquiryEnabled();
+}
+
+bool neighbor::ScanModule::IsPageEnabled() const {
+  return pimpl_->IsPageEnabled();
+}
+
+void neighbor::ScanModule::ListDependencies(ModuleList* list) {
+  list->add<hci::HciLayer>();
+}
+
+void neighbor::ScanModule::Start() {
+  pimpl_->Start();
+}
+
+void neighbor::ScanModule::Stop() {
+  pimpl_->Stop();
+}
+
+}  // namespace neighbor
+}  // namespace bluetooth
diff --git a/gd/neighbor/scan.h b/gd/neighbor/scan.h
new file mode 100644
index 0000000..b6499d3
--- /dev/null
+++ b/gd/neighbor/scan.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <memory>
+
+#include "module.h"
+
+namespace bluetooth {
+namespace neighbor {
+
+class ScanModule : public bluetooth::Module {
+ public:
+  ScanModule();
+  ~ScanModule();
+
+  void SetInquiryScan();
+  void ClearInquiryScan();
+  bool IsInquiryEnabled() const;
+
+  void SetPageScan();
+  void ClearPageScan();
+  bool IsPageEnabled() const;
+
+  static const ModuleFactory Factory;
+
+ protected:
+  void ListDependencies(ModuleList* list) override;
+  void Start() override;
+  void Stop() override;
+
+ private:
+  struct impl;
+  std::unique_ptr<impl> pimpl_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScanModule);
+};
+
+}  // namespace neighbor
+}  // namespace bluetooth
diff --git a/gd/neighbor/scan_parameters.h b/gd/neighbor/scan_parameters.h
new file mode 100644
index 0000000..8927ee1
--- /dev/null
+++ b/gd/neighbor/scan_parameters.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <cstdint>
+
+namespace bluetooth {
+namespace neighbor {
+
+static constexpr double kTimeTickMs = 0.625;
+
+using ScanInterval = uint16_t;  // Range 0x0012 to 0x1000; only even values valid  11.25 to 2560ms
+using ScanWindow = uint16_t;    // Range 0x0011 to 0x1000;  10.625ms to 2560ms
+
+inline double ScanIntervalTimeMs(ScanInterval interval) {
+  return kTimeTickMs * interval;
+}
+
+inline double ScanWindowTimeMs(ScanWindow window) {
+  return kTimeTickMs * window;
+}
+
+using ScanParameters = struct {
+  ScanInterval interval;
+  ScanWindow window;
+};
+
+}  // namespace neighbor
+}  // namespace bluetooth
diff --git a/gd/os/Android.bp b/gd/os/Android.bp
index 535f144..b8ad91a 100644
--- a/gd/os/Android.bp
+++ b/gd/os/Android.bp
@@ -5,8 +5,9 @@
         "linux_generic/handler.cc",
         "linux_generic/reactor.cc",
         "linux_generic/repeating_alarm.cc",
+        "linux_generic/reactive_semaphore.cc",
         "linux_generic/thread.cc",
-    ]
+    ],
 }
 
 filegroup {
@@ -14,10 +15,11 @@
     srcs: [
         "linux_generic/alarm_unittest.cc",
         "linux_generic/handler_unittest.cc",
+        "linux_generic/queue_unittest.cc",
         "linux_generic/reactor_unittest.cc",
         "linux_generic/repeating_alarm_unittest.cc",
         "linux_generic/thread_unittest.cc",
-    ]
+    ],
 }
 
 filegroup {
@@ -25,5 +27,6 @@
     srcs: [
         "alarm_benchmark.cc",
         "thread_benchmark.cc",
-    ]
+        "queue_benchmark.cc",
+    ],
 }
diff --git a/gd/os/alarm.h b/gd/os/alarm.h
index 6c07474..e529fb9 100644
--- a/gd/os/alarm.h
+++ b/gd/os/alarm.h
@@ -20,6 +20,8 @@
 #include <memory>
 #include <mutex>
 
+#include "common/callback.h"
+#include "os/handler.h"
 #include "os/thread.h"
 #include "os/utils.h"
 
@@ -31,8 +33,8 @@
 // itself from the thread.
 class Alarm {
  public:
-  // Create and register a single-shot alarm on given thread
-  explicit Alarm(Thread* thread);
+  // Create and register a single-shot alarm on a given handler
+  explicit Alarm(Handler* handler);
 
   // Unregister this alarm from the thread and release resource
   ~Alarm();
@@ -40,14 +42,14 @@
   DISALLOW_COPY_AND_ASSIGN(Alarm);
 
   // Schedule the alarm with given delay
-  void Schedule(Closure task, std::chrono::milliseconds delay);
+  void Schedule(OnceClosure task, std::chrono::milliseconds delay);
 
   // Cancel the alarm. No-op if it's not armed.
   void Cancel();
 
  private:
-  Closure task_;
-  Thread* thread_;
+  OnceClosure task_;
+  Handler* handler_;
   int fd_ = 0;
   Reactor::Reactable* token_;
   mutable std::mutex mutex_;
diff --git a/gd/os/alarm_benchmark.cc b/gd/os/alarm_benchmark.cc
index 54b8a38..2150625 100644
--- a/gd/os/alarm_benchmark.cc
+++ b/gd/os/alarm_benchmark.cc
@@ -20,12 +20,15 @@
 
 #include "benchmark/benchmark.h"
 
+#include "common/bind.h"
 #include "os/alarm.h"
 #include "os/repeating_alarm.h"
 #include "os/thread.h"
 
 using ::benchmark::State;
+using ::bluetooth::common::Bind;
 using ::bluetooth::os::Alarm;
+using ::bluetooth::os::Handler;
 using ::bluetooth::os::RepeatingAlarm;
 using ::bluetooth::os::Thread;
 
@@ -34,8 +37,9 @@
   void SetUp(State& st) override {
     ::benchmark::Fixture::SetUp(st);
     thread_ = std::make_unique<Thread>("timer_benchmark", Thread::Priority::REAL_TIME);
-    alarm_ = std::make_unique<Alarm>(thread_.get());
-    repeating_alarm_ = std::make_unique<RepeatingAlarm>(thread_.get());
+    handler_ = std::make_unique<Handler>(thread_.get());
+    alarm_ = std::make_unique<Alarm>(handler_.get());
+    repeating_alarm_ = std::make_unique<RepeatingAlarm>(handler_.get());
     map_.clear();
     scheduled_tasks_ = 0;
     task_length_ = 0;
@@ -47,6 +51,7 @@
   void TearDown(State& st) override {
     alarm_ = nullptr;
     repeating_alarm_ = nullptr;
+    handler_ = nullptr;
     thread_->Stop();
     thread_ = nullptr;
     ::benchmark::Fixture::TearDown(st);
@@ -75,6 +80,7 @@
   std::promise<void> promise_;
   std::chrono::time_point<std::chrono::steady_clock> start_time_;
   std::unique_ptr<Thread> thread_;
+  std::unique_ptr<Handler> handler_;
   std::unique_ptr<Alarm> alarm_;
   std::unique_ptr<RepeatingAlarm> repeating_alarm_;
 };
@@ -83,7 +89,9 @@
   auto milliseconds = static_cast<int>(state.range(0));
   for (auto _ : state) {
     auto start_time_point = std::chrono::steady_clock::now();
-    alarm_->Schedule([this] { return TimerFire(); }, std::chrono::milliseconds(milliseconds));
+    alarm_->Schedule(
+        Bind(&BM_ReactableAlarm_timer_performance_ms_Benchmark::TimerFire, bluetooth::common::Unretained(this)),
+        std::chrono::milliseconds(milliseconds));
     promise_.get_future().get();
     auto end_time_point = std::chrono::steady_clock::now();
     auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(end_time_point - start_time_point);
@@ -109,7 +117,9 @@
     task_length_ = state.range(1);
     task_interval_ = state.range(2);
     start_time_ = std::chrono::steady_clock::now();
-    repeating_alarm_->Schedule([this] { AlarmSleepAndCountDelayedTime(); }, std::chrono::milliseconds(task_interval_));
+    repeating_alarm_->Schedule(Bind(&BM_ReactableAlarm_periodic_accuracy_Benchmark::AlarmSleepAndCountDelayedTime,
+                                    bluetooth::common::Unretained(this)),
+                               std::chrono::milliseconds(task_interval_));
     promise_.get_future().get();
     repeating_alarm_->Cancel();
   }
diff --git a/gd/os/handler.h b/gd/os/handler.h
index dc6d510..d9dea0b 100644
--- a/gd/os/handler.h
+++ b/gd/os/handler.h
@@ -21,6 +21,7 @@
 #include <mutex>
 #include <queue>
 
+#include "common/callback.h"
 #include "os/thread.h"
 #include "os/utils.h"
 
@@ -41,13 +42,26 @@
   DISALLOW_COPY_AND_ASSIGN(Handler);
 
   // Enqueue a closure to the queue of this handler
-  void Post(Closure closure);
+  void Post(OnceClosure closure);
 
   // Remove all pending events from the queue of this handler
   void Clear();
 
+  // Die if the current reactable doesn't stop before the timeout.  Must be called after Clear()
+  void WaitUntilStopped(std::chrono::milliseconds timeout);
+
+  template <typename T>
+  friend class Queue;
+
+  friend class Alarm;
+
+  friend class RepeatingAlarm;
+
  private:
-  std::queue<Closure> tasks_;
+  inline bool was_cleared() const {
+    return tasks_ == nullptr;
+  };
+  std::queue<OnceClosure>* tasks_;
   Thread* thread_;
   int fd_;
   Reactor::Reactable* reactable_;
diff --git a/gd/os/linux_generic/alarm.cc b/gd/os/linux_generic/alarm.cc
index b1ee5b0..a2d895e 100644
--- a/gd/os/linux_generic/alarm.cc
+++ b/gd/os/linux_generic/alarm.cc
@@ -20,6 +20,7 @@
 #include <cstring>
 #include <unistd.h>
 
+#include "common/bind.h"
 #include "os/log.h"
 #include "os/utils.h"
 
@@ -32,23 +33,22 @@
 namespace bluetooth {
 namespace os {
 
-Alarm::Alarm(Thread* thread)
-  : thread_(thread),
-    fd_(timerfd_create(ALARM_CLOCK, 0)) {
+Alarm::Alarm(Handler* handler) : handler_(handler), fd_(timerfd_create(ALARM_CLOCK, 0)) {
   ASSERT_LOG(fd_ != -1, "cannot create timerfd: %s", strerror(errno));
 
-  token_ = thread_->GetReactor()->Register(fd_, [this] { on_fire(); }, nullptr);
+  token_ = handler_->thread_->GetReactor()->Register(fd_, common::Bind(&Alarm::on_fire, common::Unretained(this)),
+                                                     Closure());
 }
 
 Alarm::~Alarm() {
-  thread_->GetReactor()->Unregister(token_);
+  handler_->thread_->GetReactor()->Unregister(token_);
 
   int close_status;
   RUN_NO_INTR(close_status = close(fd_));
   ASSERT(close_status != -1);
 }
 
-void Alarm::Schedule(Closure task, std::chrono::milliseconds delay) {
+void Alarm::Schedule(OnceClosure task, std::chrono::milliseconds delay) {
   std::lock_guard<std::mutex> lock(mutex_);
   long delay_ms = delay.count();
   itimerspec timer_itimerspec{
@@ -74,7 +74,7 @@
   uint64_t times_invoked;
   auto bytes_read = read(fd_, &times_invoked, sizeof(uint64_t));
   lock.unlock();
-  task();
+  std::move(task).Run();
   ASSERT(bytes_read == static_cast<ssize_t>(sizeof(uint64_t)));
   ASSERT(times_invoked == static_cast<uint64_t>(1));
 }
diff --git a/gd/os/linux_generic/alarm_unittest.cc b/gd/os/linux_generic/alarm_unittest.cc
index 3ffd0d9..b389a82 100644
--- a/gd/os/linux_generic/alarm_unittest.cc
+++ b/gd/os/linux_generic/alarm_unittest.cc
@@ -18,26 +18,33 @@
 
 #include <future>
 
+#include "common/bind.h"
 #include "gtest/gtest.h"
 
 namespace bluetooth {
 namespace os {
 namespace {
 
+using common::BindOnce;
+
 class AlarmTest : public ::testing::Test {
  protected:
   void SetUp() override {
     thread_ = new Thread("test_thread", Thread::Priority::NORMAL);
-    alarm_ = new Alarm(thread_);
+    handler_ = new Handler(thread_);
+    alarm_ = new Alarm(handler_);
   }
 
   void TearDown() override {
     delete alarm_;
+    handler_->Clear();
+    delete handler_;
     delete thread_;
   }
   Alarm* alarm_;
 
  private:
+  Handler* handler_;
   Thread* thread_;
 };
 
@@ -51,7 +58,8 @@
   auto before = std::chrono::steady_clock::now();
   int delay_ms = 10;
   int delay_error_ms = 3;
-  alarm_->Schedule([&promise]() { promise.set_value(); }, std::chrono::milliseconds(delay_ms));
+  alarm_->Schedule(BindOnce(&std::promise<void>::set_value, common::Unretained(&promise)),
+                   std::chrono::milliseconds(delay_ms));
   future.get();
   auto after = std::chrono::steady_clock::now();
   auto duration_ms = std::chrono::duration_cast<std::chrono::milliseconds>(after - before);
@@ -59,26 +67,27 @@
 }
 
 TEST_F(AlarmTest, cancel_alarm) {
-  alarm_->Schedule([]() { ASSERT_TRUE(false) << "Should not happen"; }, std::chrono::milliseconds(3));
+  alarm_->Schedule(BindOnce([]() { ASSERT_TRUE(false) << "Should not happen"; }), std::chrono::milliseconds(3));
   alarm_->Cancel();
   std::this_thread::sleep_for(std::chrono::milliseconds(5));
 }
 
 TEST_F(AlarmTest, cancel_alarm_from_callback) {
-  alarm_->Schedule([this]() { this->alarm_->Cancel(); }, std::chrono::milliseconds(1));
+  alarm_->Schedule(BindOnce(&Alarm::Cancel, common::Unretained(alarm_)), std::chrono::milliseconds(1));
   std::this_thread::sleep_for(std::chrono::milliseconds(5));
 }
 
 TEST_F(AlarmTest, schedule_while_alarm_armed) {
-  alarm_->Schedule([]() { ASSERT_TRUE(false) << "Should not happen"; }, std::chrono::milliseconds(1));
+  alarm_->Schedule(BindOnce([]() { ASSERT_TRUE(false) << "Should not happen"; }), std::chrono::milliseconds(1));
   std::promise<void> promise;
   auto future = promise.get_future();
-  alarm_->Schedule([&promise]() { promise.set_value(); }, std::chrono::milliseconds(10));
+  alarm_->Schedule(BindOnce(&std::promise<void>::set_value, common::Unretained(&promise)),
+                   std::chrono::milliseconds(10));
   future.get();
 }
 
 TEST_F(AlarmTest, delete_while_alarm_armed) {
-  alarm_->Schedule([]() { ASSERT_TRUE(false) << "Should not happen"; }, std::chrono::milliseconds(1));
+  alarm_->Schedule(BindOnce([]() { ASSERT_TRUE(false) << "Should not happen"; }), std::chrono::milliseconds(1));
   delete alarm_;
   alarm_ = nullptr;
   std::this_thread::sleep_for(std::chrono::milliseconds(10));
diff --git a/gd/os/linux_generic/handler.cc b/gd/os/linux_generic/handler.cc
index cdb3240..85cbcc3 100644
--- a/gd/os/linux_generic/handler.cc
+++ b/gd/os/linux_generic/handler.cc
@@ -17,9 +17,11 @@
 #include "os/handler.h"
 
 #include <sys/eventfd.h>
-#include <cstring>
 #include <unistd.h>
+#include <cstring>
 
+#include "common/bind.h"
+#include "common/callback.h"
 #include "os/log.h"
 #include "os/reactor.h"
 #include "os/utils.h"
@@ -32,26 +34,30 @@
 namespace os {
 
 Handler::Handler(Thread* thread)
-  : thread_(thread),
-    fd_(eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK)) {
+    : tasks_(new std::queue<OnceClosure>()), thread_(thread), fd_(eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK)) {
   ASSERT(fd_ != -1);
-
-  reactable_ = thread_->GetReactor()->Register(fd_, [this] { this->handle_next_event(); }, nullptr);
+  reactable_ = thread_->GetReactor()->Register(fd_, common::Bind(&Handler::handle_next_event, common::Unretained(this)),
+                                               common::Closure());
 }
 
 Handler::~Handler() {
-  thread_->GetReactor()->Unregister(reactable_);
-  reactable_ = nullptr;
+  {
+    std::lock_guard<std::mutex> lock(mutex_);
+    ASSERT_LOG(was_cleared(), "Handlers must be cleared before they are destroyed");
+  }
 
   int close_status;
   RUN_NO_INTR(close_status = close(fd_));
   ASSERT(close_status != -1);
 }
 
-void Handler::Post(Closure closure) {
+void Handler::Post(OnceClosure closure) {
   {
     std::lock_guard<std::mutex> lock(mutex_);
-    tasks_.emplace(std::move(closure));
+    if (was_cleared()) {
+      return;
+    }
+    tasks_->emplace(std::move(closure));
   }
   uint64_t val = 1;
   auto write_result = eventfd_write(fd_, val);
@@ -59,34 +65,43 @@
 }
 
 void Handler::Clear() {
-  std::lock_guard<std::mutex> lock(mutex_);
-
-  std::queue<Closure> empty;
-  std::swap(tasks_, empty);
+  std::queue<OnceClosure>* tmp = nullptr;
+  {
+    std::lock_guard<std::mutex> lock(mutex_);
+    ASSERT_LOG(!was_cleared(), "Handlers must only be cleared once");
+    std::swap(tasks_, tmp);
+  }
+  delete tmp;
 
   uint64_t val;
   while (eventfd_read(fd_, &val) == 0) {
   }
+
+  thread_->GetReactor()->Unregister(reactable_);
+  reactable_ = nullptr;
+}
+
+void Handler::WaitUntilStopped(std::chrono::milliseconds timeout) {
+  ASSERT(reactable_ == nullptr);
+  ASSERT(thread_->GetReactor()->WaitForUnregisteredReactable(timeout));
 }
 
 void Handler::handle_next_event() {
-  Closure closure;
-  uint64_t val = 0;
-  auto read_result = eventfd_read(fd_, &val);
-  if (read_result == -1 && errno == EAGAIN) {
-    // We were told there was an item, but it was removed before we got there
-    // (aka the queue was cleared). Not a fatal error, so just bail.
-    return;
-  }
-
-  ASSERT(read_result != -1);
-
+  common::OnceClosure closure;
   {
     std::lock_guard<std::mutex> lock(mutex_);
-    closure = std::move(tasks_.front());
-    tasks_.pop();
+    uint64_t val = 0;
+    auto read_result = eventfd_read(fd_, &val);
+
+    if (was_cleared()) {
+      return;
+    }
+    ASSERT_LOG(read_result != -1, "eventfd read error %d %s", errno, strerror(errno));
+
+    closure = std::move(tasks_->front());
+    tasks_->pop();
   }
-  closure();
+  std::move(closure).Run();
 }
 
 }  // namespace os
diff --git a/gd/os/linux_generic/handler_unittest.cc b/gd/os/linux_generic/handler_unittest.cc
index 7e0488f..80456db 100644
--- a/gd/os/linux_generic/handler_unittest.cc
+++ b/gd/os/linux_generic/handler_unittest.cc
@@ -17,9 +17,13 @@
 #include "os/handler.h"
 
 #include <sys/eventfd.h>
+#include <future>
 #include <thread>
 
+#include "common/bind.h"
+#include "common/callback.h"
 #include "gtest/gtest.h"
+#include "os/log.h"
 
 namespace bluetooth {
 namespace os {
@@ -40,31 +44,103 @@
   Thread* thread_;
 };
 
-TEST_F(HandlerTest, empty) {}
+TEST_F(HandlerTest, empty) {
+  handler_->Clear();
+}
 
 TEST_F(HandlerTest, post_task_invoked) {
   int val = 0;
-  Closure closure = [&val]() { val++; };
-  handler_->Post(closure);
-  std::this_thread::sleep_for(std::chrono::milliseconds(10));
-  EXPECT_EQ(val, 1);
+  std::promise<void> closure_ran;
+  auto future = closure_ran.get_future();
+  OnceClosure closure = common::BindOnce(
+      [](int* val, std::promise<void> closure_ran) {
+        *val = *val + 1;
+        closure_ran.set_value();
+      },
+      common::Unretained(&val), std::move(closure_ran));
+  handler_->Post(std::move(closure));
+  future.wait();
+  ASSERT_EQ(val, 1);
+  handler_->Clear();
 }
 
 TEST_F(HandlerTest, post_task_cleared) {
   int val = 0;
-  Closure closure = [&val]() {
-    val++;
-    std::this_thread::sleep_for(std::chrono::milliseconds(5));
-  };
-  handler_->Post(std::move(closure));
-  closure = []() {
-    ASSERT_TRUE(false);
-  };
-  std::this_thread::sleep_for(std::chrono::milliseconds(5));
-  handler_->Post(std::move(closure));
+  std::promise<void> closure_started;
+  auto closure_started_future = closure_started.get_future();
+  std::promise<void> closure_can_continue;
+  auto can_continue_future = closure_can_continue.get_future();
+  handler_->Post(common::BindOnce(
+      [](int* val, std::promise<void> closure_started, std::future<void> can_continue_future) {
+        closure_started.set_value();
+        *val = *val + 1;
+        can_continue_future.wait();
+      },
+      common::Unretained(&val), std::move(closure_started), std::move(can_continue_future)));
+  handler_->Post(common::BindOnce([]() { ASSERT_TRUE(false); }));
+  closure_started_future.wait();
   handler_->Clear();
-  std::this_thread::sleep_for(std::chrono::milliseconds(10));
-  EXPECT_EQ(val, 1);
+  closure_can_continue.set_value();
+  ASSERT_EQ(val, 1);
+}
+
+void check_int(std::unique_ptr<int> number, std::shared_ptr<int> to_change) {
+  *to_change = *number;
+}
+
+TEST_F(HandlerTest, once_callback) {
+  auto number = std::make_unique<int>(1);
+  auto to_change = std::make_shared<int>(0);
+  auto once_callback = common::BindOnce(&check_int, std::move(number), to_change);
+  std::move(once_callback).Run();
+  EXPECT_EQ(*to_change, 1);
+  handler_->Clear();
+}
+
+TEST_F(HandlerTest, callback_with_promise) {
+  std::promise<void> promise;
+  auto future = promise.get_future();
+  auto once_callback = common::BindOnce(&std::promise<void>::set_value, common::Unretained(&promise));
+  std::move(once_callback).Run();
+  future.wait();
+  handler_->Clear();
+}
+
+// For Death tests, all the threading needs to be done in the ASSERT_DEATH call
+class HandlerDeathTest : public ::testing::Test {
+ protected:
+  void ThreadSetUp() {
+    thread_ = new Thread("test_thread", Thread::Priority::NORMAL);
+    handler_ = new Handler(thread_);
+  }
+
+  void ThreadTearDown() {
+    delete handler_;
+    delete thread_;
+  }
+
+  void ClearTwice() {
+    ThreadSetUp();
+    handler_->Clear();
+    handler_->Clear();
+    ThreadTearDown();
+  }
+
+  void NotCleared() {
+    ThreadSetUp();
+    ThreadTearDown();
+  }
+
+  Handler* handler_;
+  Thread* thread_;
+};
+
+TEST_F(HandlerDeathTest, clear_after_handler_cleared) {
+  ASSERT_DEATH(ClearTwice(), "Handlers must only be cleared once");
+}
+
+TEST_F(HandlerDeathTest, not_cleared_before_destruction) {
+  ASSERT_DEATH(NotCleared(), "Handlers must be cleared");
 }
 
 }  // namespace
diff --git a/gd/os/linux_generic/linux.h b/gd/os/linux_generic/linux.h
new file mode 100644
index 0000000..524aa7d
--- /dev/null
+++ b/gd/os/linux_generic/linux.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#ifndef EFD_SEMAPHORE
+#define EFD_SEMAPHORE 1
+#endif
diff --git a/gd/os/linux_generic/queue.tpp b/gd/os/linux_generic/queue.tpp
new file mode 100644
index 0000000..bd9a292
--- /dev/null
+++ b/gd/os/linux_generic/queue.tpp
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+template <typename T>
+Queue<T>::Queue(size_t capacity) : enqueue_(capacity), dequeue_(0){};
+
+template <typename T>
+Queue<T>::~Queue() {
+  ASSERT(enqueue_.handler_ == nullptr);
+  ASSERT(dequeue_.handler_ == nullptr);
+};
+
+template <typename T>
+void Queue<T>::RegisterEnqueue(Handler* handler, EnqueueCallback callback) {
+  std::lock_guard<std::mutex> lock(mutex_);
+  ASSERT(enqueue_.handler_ == nullptr);
+  ASSERT(enqueue_.reactable_ == nullptr);
+  enqueue_.handler_ = handler;
+  enqueue_.reactable_ = enqueue_.handler_->thread_->GetReactor()->Register(
+      enqueue_.reactive_semaphore_.GetFd(),
+      base::Bind(&Queue<T>::EnqueueCallbackInternal, base::Unretained(this), std::move(callback)),
+      base::Closure());
+}
+
+template <typename T>
+void Queue<T>::UnregisterEnqueue() {
+  std::lock_guard<std::mutex> lock(mutex_);
+  ASSERT(enqueue_.reactable_ != nullptr);
+  enqueue_.handler_->thread_->GetReactor()->Unregister(enqueue_.reactable_);
+  enqueue_.reactable_ = nullptr;
+  enqueue_.handler_ = nullptr;
+}
+
+template <typename T>
+void Queue<T>::RegisterDequeue(Handler* handler, DequeueCallback callback) {
+  std::lock_guard<std::mutex> lock(mutex_);
+  ASSERT(dequeue_.handler_ == nullptr);
+  ASSERT(dequeue_.reactable_ == nullptr);
+  dequeue_.handler_ = handler;
+  dequeue_.reactable_ = dequeue_.handler_->thread_->GetReactor()->Register(dequeue_.reactive_semaphore_.GetFd(),
+                                                                           callback, base::Closure());
+}
+
+template <typename T>
+void Queue<T>::UnregisterDequeue() {
+  std::lock_guard<std::mutex> lock(mutex_);
+  ASSERT(dequeue_.reactable_ != nullptr);
+  dequeue_.handler_->thread_->GetReactor()->Unregister(dequeue_.reactable_);
+  dequeue_.reactable_ = nullptr;
+  dequeue_.handler_ = nullptr;
+}
+
+template <typename T>
+std::unique_ptr<T> Queue<T>::TryDequeue() {
+  std::lock_guard<std::mutex> lock(mutex_);
+
+  if (queue_.empty()) {
+    return nullptr;
+  }
+
+  dequeue_.reactive_semaphore_.Decrease();
+
+  std::unique_ptr<T> data = std::move(queue_.front());
+  queue_.pop();
+
+  enqueue_.reactive_semaphore_.Increase();
+
+  return data;
+}
+
+template <typename T>
+void Queue<T>::EnqueueCallbackInternal(EnqueueCallback callback) {
+  std::unique_ptr<T> data = callback.Run();
+  ASSERT(data != nullptr);
+  std::lock_guard<std::mutex> lock(mutex_);
+  enqueue_.reactive_semaphore_.Decrease();
+  queue_.push(std::move(data));
+  dequeue_.reactive_semaphore_.Increase();
+}
diff --git a/gd/os/linux_generic/queue_unittest.cc b/gd/os/linux_generic/queue_unittest.cc
new file mode 100644
index 0000000..f0ca2bd
--- /dev/null
+++ b/gd/os/linux_generic/queue_unittest.cc
@@ -0,0 +1,834 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "os/queue.h"
+
+#include <sys/eventfd.h>
+#include <future>
+#include <unordered_map>
+
+#include "common/bind.h"
+#include "gtest/gtest.h"
+#include "os/reactor.h"
+
+namespace bluetooth {
+namespace os {
+namespace {
+
+constexpr int kQueueSize = 10;
+constexpr int kHalfOfQueueSize = kQueueSize / 2;
+constexpr int kDoubleOfQueueSize = kQueueSize * 2;
+constexpr int kQueueSizeOne = 1;
+
+class QueueTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    enqueue_thread_ = new Thread("enqueue_thread", Thread::Priority::NORMAL);
+    enqueue_handler_ = new Handler(enqueue_thread_);
+    dequeue_thread_ = new Thread("dequeue_thread", Thread::Priority::NORMAL);
+    dequeue_handler_ = new Handler(dequeue_thread_);
+  }
+  void TearDown() override {
+    enqueue_handler_->Clear();
+    delete enqueue_handler_;
+    delete enqueue_thread_;
+    dequeue_handler_->Clear();
+    delete dequeue_handler_;
+    delete dequeue_thread_;
+    enqueue_handler_ = nullptr;
+    enqueue_thread_ = nullptr;
+    dequeue_handler_ = nullptr;
+    dequeue_thread_ = nullptr;
+  }
+
+  Thread* enqueue_thread_;
+  Handler* enqueue_handler_;
+  Thread* dequeue_thread_;
+  Handler* dequeue_handler_;
+};
+
+class TestEnqueueEnd {
+ public:
+  explicit TestEnqueueEnd(Queue<std::string>* queue, Handler* handler)
+      : count(0), handler_(handler), queue_(queue), delay_(0) {}
+
+  ~TestEnqueueEnd() {}
+
+  void RegisterEnqueue(std::unordered_map<int, std::promise<int>>* promise_map) {
+    promise_map_ = promise_map;
+    handler_->Post(common::BindOnce(&TestEnqueueEnd::handle_register_enqueue, common::Unretained(this)));
+  }
+
+  void UnregisterEnqueue() {
+    std::promise<void> promise;
+    auto future = promise.get_future();
+
+    handler_->Post(
+        common::BindOnce(&TestEnqueueEnd::handle_unregister_enqueue, common::Unretained(this), std::move(promise)));
+    future.wait();
+  }
+
+  std::unique_ptr<std::string> EnqueueCallbackForTest() {
+    if (delay_ != 0) {
+      std::this_thread::sleep_for(std::chrono::milliseconds(delay_));
+    }
+
+    count++;
+    std::unique_ptr<std::string> data = std::move(buffer_.front());
+    buffer_.pop();
+    std::string copy = *data;
+    if (buffer_.empty()) {
+      queue_->UnregisterEnqueue();
+    }
+
+    auto pair = promise_map_->find(buffer_.size());
+    if (pair != promise_map_->end()) {
+      pair->second.set_value(pair->first);
+      promise_map_->erase(pair->first);
+    }
+    return data;
+  }
+
+  void setDelay(int value) {
+    delay_ = value;
+  }
+
+  std::queue<std::unique_ptr<std::string>> buffer_;
+  int count;
+
+ private:
+  Handler* handler_;
+  Queue<std::string>* queue_;
+  std::unordered_map<int, std::promise<int>>* promise_map_;
+  int delay_;
+
+  void handle_register_enqueue() {
+    queue_->RegisterEnqueue(handler_, common::Bind(&TestEnqueueEnd::EnqueueCallbackForTest, common::Unretained(this)));
+  }
+
+  void handle_unregister_enqueue(std::promise<void> promise) {
+    queue_->UnregisterEnqueue();
+    promise.set_value();
+  }
+};
+
+class TestDequeueEnd {
+ public:
+  explicit TestDequeueEnd(Queue<std::string>* queue, Handler* handler, int capacity)
+      : count(0), handler_(handler), queue_(queue), capacity_(capacity), delay_(0) {}
+
+  ~TestDequeueEnd() {}
+
+  void RegisterDequeue(std::unordered_map<int, std::promise<int>>* promise_map) {
+    promise_map_ = promise_map;
+    handler_->Post(common::BindOnce(&TestDequeueEnd::handle_register_dequeue, common::Unretained(this)));
+  }
+
+  void UnregisterDequeue() {
+    std::promise<void> promise;
+    auto future = promise.get_future();
+
+    handler_->Post(
+        common::BindOnce(&TestDequeueEnd::handle_unregister_dequeue, common::Unretained(this), std::move(promise)));
+    future.wait();
+  }
+
+  void DequeueCallbackForTest() {
+    if (delay_ != 0) {
+      std::this_thread::sleep_for(std::chrono::milliseconds(delay_));
+    }
+
+    count++;
+    std::unique_ptr<std::string> data = queue_->TryDequeue();
+    buffer_.push(std::move(data));
+
+    if (buffer_.size() == capacity_) {
+      queue_->UnregisterDequeue();
+    }
+
+    auto pair = promise_map_->find(buffer_.size());
+    if (pair != promise_map_->end()) {
+      pair->second.set_value(pair->first);
+      promise_map_->erase(pair->first);
+    }
+  }
+
+  void setDelay(int value) {
+    delay_ = value;
+  }
+
+  std::queue<std::unique_ptr<std::string>> buffer_;
+  int count;
+
+ private:
+  Handler* handler_;
+  Queue<std::string>* queue_;
+  std::unordered_map<int, std::promise<int>>* promise_map_;
+  int capacity_;
+  int delay_;
+
+  void handle_register_dequeue() {
+    queue_->RegisterDequeue(handler_, common::Bind(&TestDequeueEnd::DequeueCallbackForTest, common::Unretained(this)));
+  }
+
+  void handle_unregister_dequeue(std::promise<void> promise) {
+    queue_->UnregisterDequeue();
+    promise.set_value();
+  }
+};
+
+// Enqueue end level : 0 -> queue is full, 1 - >  queue isn't full
+// Dequeue end level : 0 -> queue is empty, 1 - >  queue isn't empty
+
+// Test 1 : Queue is empty
+
+// Enqueue end level : 1
+// Dequeue end level : 0
+// Test 1-1 EnqueueCallback should continually be invoked when queue isn't full
+TEST_F(QueueTest, register_enqueue_with_empty_queue) {
+  Queue<std::string> queue(kQueueSize);
+  TestEnqueueEnd test_enqueue_end(&queue, enqueue_handler_);
+
+  // Push kQueueSize data to enqueue_end buffer
+  for (int i = 0; i < kQueueSize; i++) {
+    std::unique_ptr<std::string> data = std::make_unique<std::string>(std::to_string(i));
+    test_enqueue_end.buffer_.push(std::move(data));
+  }
+  EXPECT_EQ(test_enqueue_end.buffer_.size(), (size_t)kQueueSize);
+
+  // Register enqueue and expect data move to Queue
+  std::unordered_map<int, std::promise<int>> enqueue_promise_map;
+  enqueue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(0), std::forward_as_tuple());
+  auto enqueue_future = enqueue_promise_map[0].get_future();
+  test_enqueue_end.RegisterEnqueue(&enqueue_promise_map);
+  enqueue_future.wait();
+  EXPECT_EQ(enqueue_future.get(), 0);
+  std::this_thread::sleep_for(std::chrono::milliseconds(20));
+}
+
+// Enqueue end level : 1
+// Dequeue end level : 0
+// Test 1-2 DequeueCallback shouldn't be invoked when queue is empty
+TEST_F(QueueTest, register_dequeue_with_empty_queue) {
+  Queue<std::string> queue(kQueueSize);
+  TestDequeueEnd test_dequeue_end(&queue, dequeue_handler_, kQueueSize);
+
+  // Register dequeue, DequeueCallback shouldn't be invoked
+  std::unordered_map<int, std::promise<int>> dequeue_promise_map;
+  test_dequeue_end.RegisterDequeue(&dequeue_promise_map);
+  std::this_thread::sleep_for(std::chrono::milliseconds(20));
+  EXPECT_EQ(test_dequeue_end.count, 0);
+
+  test_dequeue_end.UnregisterDequeue();
+}
+
+// Test 2 : Queue is full
+
+// Enqueue end level : 0
+// Dequeue end level : 1
+// Test 2-1 EnqueueCallback shouldn't be invoked when queue is full
+TEST_F(QueueTest, register_enqueue_with_full_queue) {
+  Queue<std::string> queue(kQueueSize);
+  TestEnqueueEnd test_enqueue_end(&queue, enqueue_handler_);
+
+  // make Queue full
+  for (int i = 0; i < kQueueSize; i++) {
+    std::unique_ptr<std::string> data = std::make_unique<std::string>(std::to_string(i));
+    test_enqueue_end.buffer_.push(std::move(data));
+  }
+  std::unordered_map<int, std::promise<int>> enqueue_promise_map;
+  enqueue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(0), std::forward_as_tuple());
+  auto enqueue_future = enqueue_promise_map[0].get_future();
+  test_enqueue_end.RegisterEnqueue(&enqueue_promise_map);
+  enqueue_future.wait();
+  EXPECT_EQ(enqueue_future.get(), 0);
+
+  // push some data to enqueue_end buffer and register enqueue;
+  for (int i = 0; i < kHalfOfQueueSize; i++) {
+    std::unique_ptr<std::string> data = std::make_unique<std::string>(std::to_string(i));
+    test_enqueue_end.buffer_.push(std::move(data));
+  }
+  test_enqueue_end.RegisterEnqueue(&enqueue_promise_map);
+
+  // EnqueueCallback shouldn't be invoked
+  std::this_thread::sleep_for(std::chrono::milliseconds(20));
+  EXPECT_EQ(test_enqueue_end.buffer_.size(), (size_t)kHalfOfQueueSize);
+  EXPECT_EQ(test_enqueue_end.count, kQueueSize);
+
+  test_enqueue_end.UnregisterEnqueue();
+}
+
+// Enqueue end level : 0
+// Dequeue end level : 1
+// Test 2-2 DequeueCallback should continually be invoked when queue isn't empty
+TEST_F(QueueTest, register_dequeue_with_full_queue) {
+  Queue<std::string> queue(kQueueSize);
+  TestEnqueueEnd test_enqueue_end(&queue, enqueue_handler_);
+  TestDequeueEnd test_dequeue_end(&queue, dequeue_handler_, kDoubleOfQueueSize);
+
+  // make Queue full
+  for (int i = 0; i < kQueueSize; i++) {
+    std::unique_ptr<std::string> data = std::make_unique<std::string>(std::to_string(i));
+    test_enqueue_end.buffer_.push(std::move(data));
+  }
+  std::unordered_map<int, std::promise<int>> enqueue_promise_map;
+  enqueue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(0), std::forward_as_tuple());
+  auto enqueue_future = enqueue_promise_map[0].get_future();
+  test_enqueue_end.RegisterEnqueue(&enqueue_promise_map);
+  enqueue_future.wait();
+  EXPECT_EQ(enqueue_future.get(), 0);
+
+  // Register dequeue and expect data move to dequeue end buffer
+  std::unordered_map<int, std::promise<int>> dequeue_promise_map;
+  dequeue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(kQueueSize), std::forward_as_tuple());
+  auto dequeue_future = dequeue_promise_map[kQueueSize].get_future();
+  test_dequeue_end.RegisterDequeue(&dequeue_promise_map);
+  dequeue_future.wait();
+  EXPECT_EQ(dequeue_future.get(), kQueueSize);
+
+  test_dequeue_end.UnregisterDequeue();
+}
+
+// Test 3 : Queue is non-empty and non-full
+
+// Enqueue end level : 1
+// Dequeue end level : 1
+// Test 3-1 Register enqueue with half empty queue, EnqueueCallback should continually be invoked
+TEST_F(QueueTest, register_enqueue_with_half_empty_queue) {
+  Queue<std::string> queue(kQueueSize);
+  TestEnqueueEnd test_enqueue_end(&queue, enqueue_handler_);
+
+  // make Queue half empty
+  for (int i = 0; i < kHalfOfQueueSize; i++) {
+    std::unique_ptr<std::string> data = std::make_unique<std::string>(std::to_string(i));
+    test_enqueue_end.buffer_.push(std::move(data));
+  }
+  std::unordered_map<int, std::promise<int>> enqueue_promise_map;
+  enqueue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(0), std::forward_as_tuple());
+  auto enqueue_future = enqueue_promise_map[0].get_future();
+  test_enqueue_end.RegisterEnqueue(&enqueue_promise_map);
+  enqueue_future.wait();
+  EXPECT_EQ(enqueue_future.get(), 0);
+
+  // push some data to enqueue_end buffer and register enqueue;
+  for (int i = 0; i < kHalfOfQueueSize; i++) {
+    std::unique_ptr<std::string> data = std::make_unique<std::string>(std::to_string(i));
+    test_enqueue_end.buffer_.push(std::move(data));
+  }
+
+  // Register enqueue and expect data move to Queue
+  enqueue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(0), std::forward_as_tuple());
+  enqueue_future = enqueue_promise_map[0].get_future();
+  test_enqueue_end.RegisterEnqueue(&enqueue_promise_map);
+  enqueue_future.wait();
+  EXPECT_EQ(enqueue_future.get(), 0);
+}
+
+// Enqueue end level : 1
+// Dequeue end level : 1
+// Test 3-2 Register dequeue with half empty queue, DequeueCallback should continually be invoked
+TEST_F(QueueTest, register_dequeue_with_half_empty_queue) {
+  Queue<std::string> queue(kQueueSize);
+  TestEnqueueEnd test_enqueue_end(&queue, enqueue_handler_);
+  TestDequeueEnd test_dequeue_end(&queue, dequeue_handler_, kQueueSize);
+
+  // make Queue half empty
+  for (int i = 0; i < kHalfOfQueueSize; i++) {
+    std::unique_ptr<std::string> data = std::make_unique<std::string>(std::to_string(i));
+    test_enqueue_end.buffer_.push(std::move(data));
+  }
+  std::unordered_map<int, std::promise<int>> enqueue_promise_map;
+  enqueue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(0), std::forward_as_tuple());
+  auto enqueue_future = enqueue_promise_map[0].get_future();
+  test_enqueue_end.RegisterEnqueue(&enqueue_promise_map);
+  enqueue_future.wait();
+  EXPECT_EQ(enqueue_future.get(), 0);
+
+  // Register dequeue and expect data move to dequeue end buffer
+  std::unordered_map<int, std::promise<int>> dequeue_promise_map;
+  dequeue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(kHalfOfQueueSize),
+                              std::forward_as_tuple());
+  auto dequeue_future = dequeue_promise_map[kHalfOfQueueSize].get_future();
+  test_dequeue_end.RegisterDequeue(&dequeue_promise_map);
+  dequeue_future.wait();
+  EXPECT_EQ(dequeue_future.get(), kHalfOfQueueSize);
+
+  test_dequeue_end.UnregisterDequeue();
+}
+
+// Dynamic level test
+
+// Test 4 : Queue becomes full during test, EnqueueCallback should stop to be invoked
+
+// Enqueue end level : 1 -> 0
+// Dequeue end level : 1
+// Test 4-1 Queue becomes full due to only register EnqueueCallback
+TEST_F(QueueTest, queue_becomes_full_enqueue_callback_only) {
+  Queue<std::string> queue(kQueueSize);
+  TestEnqueueEnd test_enqueue_end(&queue, enqueue_handler_);
+
+  // push double of kQueueSize to enqueue end buffer
+  for (int i = 0; i < kDoubleOfQueueSize; i++) {
+    std::unique_ptr<std::string> data = std::make_unique<std::string>(std::to_string(i));
+    test_enqueue_end.buffer_.push(std::move(data));
+  }
+
+  // Register enqueue and expect kQueueSize data move to Queue
+  std::unordered_map<int, std::promise<int>> enqueue_promise_map;
+  enqueue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(kQueueSize), std::forward_as_tuple());
+  auto enqueue_future = enqueue_promise_map[kQueueSize].get_future();
+  test_enqueue_end.RegisterEnqueue(&enqueue_promise_map);
+  enqueue_future.wait();
+  EXPECT_EQ(enqueue_future.get(), kQueueSize);
+
+  // EnqueueCallback shouldn't be invoked and buffer size stay in kQueueSize
+  std::this_thread::sleep_for(std::chrono::milliseconds(20));
+  EXPECT_EQ(test_enqueue_end.buffer_.size(), (size_t)kQueueSize);
+  EXPECT_EQ(test_enqueue_end.count, kQueueSize);
+
+  test_enqueue_end.UnregisterEnqueue();
+}
+
+// Enqueue end level : 1 -> 0
+// Dequeue end level : 1
+// Test 4-2 Queue becomes full due to DequeueCallback unregister during test
+TEST_F(QueueTest, queue_becomes_full_dequeue_callback_unregister) {
+  Queue<std::string> queue(kQueueSize);
+  TestEnqueueEnd test_enqueue_end(&queue, enqueue_handler_);
+  TestDequeueEnd test_dequeue_end(&queue, dequeue_handler_, kHalfOfQueueSize);
+
+  // push double of kQueueSize to enqueue end buffer
+  for (int i = 0; i < kDoubleOfQueueSize; i++) {
+    std::unique_ptr<std::string> data = std::make_unique<std::string>(std::to_string(i));
+    test_enqueue_end.buffer_.push(std::move(data));
+  }
+
+  // Register dequeue
+  std::unordered_map<int, std::promise<int>> dequeue_promise_map;
+  dequeue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(kHalfOfQueueSize),
+                              std::forward_as_tuple());
+  auto dequeue_future = dequeue_promise_map[kHalfOfQueueSize].get_future();
+  test_dequeue_end.RegisterDequeue(&dequeue_promise_map);
+
+  // Register enqueue
+  std::unordered_map<int, std::promise<int>> enqueue_promise_map;
+  enqueue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(kHalfOfQueueSize),
+                              std::forward_as_tuple());
+  auto enqueue_future = enqueue_promise_map[kHalfOfQueueSize].get_future();
+  test_enqueue_end.RegisterEnqueue(&enqueue_promise_map);
+
+  // Dequeue end will unregister when buffer size is kHalfOfQueueSize
+  dequeue_future.wait();
+  EXPECT_EQ(dequeue_future.get(), kHalfOfQueueSize);
+
+  // EnqueueCallback shouldn't be invoked and buffer size stay in kHalfOfQueueSize
+  enqueue_future.wait();
+  EXPECT_EQ(enqueue_future.get(), kHalfOfQueueSize);
+  std::this_thread::sleep_for(std::chrono::milliseconds(20));
+  EXPECT_EQ(test_enqueue_end.buffer_.size(), (size_t)kHalfOfQueueSize);
+  EXPECT_EQ(test_enqueue_end.count, kQueueSize + kHalfOfQueueSize);
+
+  test_enqueue_end.UnregisterEnqueue();
+}
+
+// Enqueue end level : 1 -> 0
+// Dequeue end level : 1
+// Test 4-3 Queue becomes full due to DequeueCallback is slower
+TEST_F(QueueTest, queue_becomes_full_dequeue_callback_slower) {
+  Queue<std::string> queue(kQueueSize);
+  TestEnqueueEnd test_enqueue_end(&queue, enqueue_handler_);
+  TestDequeueEnd test_dequeue_end(&queue, dequeue_handler_, kDoubleOfQueueSize);
+
+  // push double of kDoubleOfQueueSize to enqueue end buffer
+  for (int i = 0; i < kDoubleOfQueueSize; i++) {
+    std::unique_ptr<std::string> data = std::make_unique<std::string>(std::to_string(i));
+    test_enqueue_end.buffer_.push(std::move(data));
+  }
+
+  // Set 20 ms delay for callback and register dequeue
+  std::unordered_map<int, std::promise<int>> dequeue_promise_map;
+  test_dequeue_end.setDelay(20);
+  auto dequeue_future = dequeue_promise_map[kHalfOfQueueSize].get_future();
+  test_dequeue_end.RegisterDequeue(&dequeue_promise_map);
+
+  // Register enqueue
+  std::unordered_map<int, std::promise<int>> enqueue_promise_map;
+  enqueue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(0), std::forward_as_tuple());
+  auto enqueue_future = enqueue_promise_map[0].get_future();
+  test_enqueue_end.RegisterEnqueue(&enqueue_promise_map);
+
+  // Wait for enqueue buffer empty and expect queue is full
+  enqueue_future.wait();
+  EXPECT_EQ(enqueue_future.get(), 0);
+  EXPECT_GE(test_dequeue_end.buffer_.size(), kQueueSize - 1);
+
+  test_dequeue_end.UnregisterDequeue();
+}
+
+// Enqueue end level : 0 -> 1
+// Dequeue end level : 1 -> 0
+// Test 5 Queue becomes full and non empty at same time.
+TEST_F(QueueTest, queue_becomes_full_and_non_empty_at_same_time) {
+  Queue<std::string> queue(kQueueSizeOne);
+  TestEnqueueEnd test_enqueue_end(&queue, enqueue_handler_);
+  TestDequeueEnd test_dequeue_end(&queue, dequeue_handler_, kDoubleOfQueueSize);
+
+  // push double of kQueueSize to enqueue end buffer
+  for (int i = 0; i < kQueueSize; i++) {
+    std::unique_ptr<std::string> data = std::make_unique<std::string>(std::to_string(i));
+    test_enqueue_end.buffer_.push(std::move(data));
+  }
+
+  // Register dequeue
+  std::unordered_map<int, std::promise<int>> dequeue_promise_map;
+  dequeue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(kQueueSize), std::forward_as_tuple());
+  auto dequeue_future = dequeue_promise_map[kQueueSize].get_future();
+  test_dequeue_end.RegisterDequeue(&dequeue_promise_map);
+
+  // Register enqueue
+  std::unordered_map<int, std::promise<int>> enqueue_promise_map;
+  auto enqueue_future = enqueue_promise_map[0].get_future();
+  test_enqueue_end.RegisterEnqueue(&enqueue_promise_map);
+
+  // Wait for all data move from enqueue end buffer to dequeue end buffer
+  dequeue_future.wait();
+  EXPECT_EQ(dequeue_future.get(), kQueueSize);
+
+  test_dequeue_end.UnregisterDequeue();
+}
+
+// Enqueue end level : 1 -> 0
+// Dequeue end level : 1
+// Test 6 Queue becomes not full during test, EnqueueCallback should start to be invoked
+TEST_F(QueueTest, queue_becomes_non_full_during_test) {
+  Queue<std::string> queue(kQueueSize);
+  TestEnqueueEnd test_enqueue_end(&queue, enqueue_handler_);
+  TestDequeueEnd test_dequeue_end(&queue, dequeue_handler_, kQueueSize * 3);
+
+  // make Queue full
+  for (int i = 0; i < kDoubleOfQueueSize; i++) {
+    std::unique_ptr<std::string> data = std::make_unique<std::string>(std::to_string(i));
+    test_enqueue_end.buffer_.push(std::move(data));
+  }
+  std::unordered_map<int, std::promise<int>> enqueue_promise_map;
+  enqueue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(kQueueSize), std::forward_as_tuple());
+  enqueue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(0), std::forward_as_tuple());
+  auto enqueue_future = enqueue_promise_map[kQueueSize].get_future();
+  test_enqueue_end.RegisterEnqueue(&enqueue_promise_map);
+  enqueue_future.wait();
+  EXPECT_EQ(enqueue_future.get(), kQueueSize);
+
+  // Expect kQueueSize data block in enqueue end buffer
+  std::this_thread::sleep_for(std::chrono::milliseconds(20));
+  EXPECT_EQ(test_enqueue_end.buffer_.size(), kQueueSize);
+
+  // Register dequeue
+  std::unordered_map<int, std::promise<int>> dequeue_promise_map;
+  test_dequeue_end.RegisterDequeue(&dequeue_promise_map);
+
+  // Expect enqueue end will empty
+  enqueue_future = enqueue_promise_map[0].get_future();
+  enqueue_future.wait();
+  EXPECT_EQ(enqueue_future.get(), 0);
+
+  test_dequeue_end.UnregisterDequeue();
+}
+
+// Enqueue end level : 0 -> 1
+// Dequeue end level : 1 -> 0
+// Test 7 Queue becomes non full and empty at same time. (Exactly same as Test 5)
+TEST_F(QueueTest, queue_becomes_non_full_and_empty_at_same_time) {
+  Queue<std::string> queue(kQueueSizeOne);
+  TestEnqueueEnd test_enqueue_end(&queue, enqueue_handler_);
+  TestDequeueEnd test_dequeue_end(&queue, dequeue_handler_, kDoubleOfQueueSize);
+
+  // push double of kQueueSize to enqueue end buffer
+  for (int i = 0; i < kQueueSize; i++) {
+    std::unique_ptr<std::string> data = std::make_unique<std::string>(std::to_string(i));
+    test_enqueue_end.buffer_.push(std::move(data));
+  }
+
+  // Register dequeue
+  std::unordered_map<int, std::promise<int>> dequeue_promise_map;
+  dequeue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(kQueueSize), std::forward_as_tuple());
+  auto dequeue_future = dequeue_promise_map[kQueueSize].get_future();
+  test_dequeue_end.RegisterDequeue(&dequeue_promise_map);
+
+  // Register enqueue
+  std::unordered_map<int, std::promise<int>> enqueue_promise_map;
+  auto enqueue_future = enqueue_promise_map[0].get_future();
+  test_enqueue_end.RegisterEnqueue(&enqueue_promise_map);
+
+  // Wait for all data move from enqueue end buffer to dequeue end buffer
+  dequeue_future.wait();
+  EXPECT_EQ(dequeue_future.get(), kQueueSize);
+
+  test_dequeue_end.UnregisterDequeue();
+}
+
+// Test 8 : Queue becomes empty during test, DequeueCallback should stop to be invoked
+
+// Enqueue end level : 1
+// Dequeue end level : 1 -> 0
+// Test 8-1 Queue becomes empty due to only register DequeueCallback
+TEST_F(QueueTest, queue_becomes_empty_dequeue_callback_only) {
+  Queue<std::string> queue(kQueueSize);
+  TestEnqueueEnd test_enqueue_end(&queue, enqueue_handler_);
+  TestDequeueEnd test_dequeue_end(&queue, dequeue_handler_, kHalfOfQueueSize);
+
+  // make Queue half empty
+  for (int i = 0; i < kHalfOfQueueSize; i++) {
+    std::unique_ptr<std::string> data = std::make_unique<std::string>(std::to_string(i));
+    test_enqueue_end.buffer_.push(std::move(data));
+  }
+  std::unordered_map<int, std::promise<int>> enqueue_promise_map;
+  enqueue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(0), std::forward_as_tuple());
+  auto enqueue_future = enqueue_promise_map[0].get_future();
+  test_enqueue_end.RegisterEnqueue(&enqueue_promise_map);
+  enqueue_future.wait();
+  EXPECT_EQ(enqueue_future.get(), 0);
+
+  // Register dequeue, expect kHalfOfQueueSize data move to dequeue end buffer
+  std::unordered_map<int, std::promise<int>> dequeue_promise_map;
+  dequeue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(kHalfOfQueueSize),
+                              std::forward_as_tuple());
+  auto dequeue_future = dequeue_promise_map[kHalfOfQueueSize].get_future();
+  test_dequeue_end.RegisterDequeue(&dequeue_promise_map);
+  dequeue_future.wait();
+  EXPECT_EQ(dequeue_future.get(), kHalfOfQueueSize);
+
+  // Expect DequeueCallback should stop to be invoked
+  std::this_thread::sleep_for(std::chrono::milliseconds(20));
+  EXPECT_EQ(test_dequeue_end.count, kHalfOfQueueSize);
+}
+
+// Enqueue end level : 1
+// Dequeue end level : 1 -> 0
+// Test 8-2 Queue becomes empty due to EnqueueCallback unregister during test
+TEST_F(QueueTest, queue_becomes_empty_enqueue_callback_unregister) {
+  Queue<std::string> queue(kQueueSize);
+  TestEnqueueEnd test_enqueue_end(&queue, enqueue_handler_);
+  TestDequeueEnd test_dequeue_end(&queue, dequeue_handler_, kQueueSize);
+
+  // make Queue half empty
+  for (int i = 0; i < kHalfOfQueueSize; i++) {
+    std::unique_ptr<std::string> data = std::make_unique<std::string>(std::to_string(i));
+    test_enqueue_end.buffer_.push(std::move(data));
+  }
+  std::unordered_map<int, std::promise<int>> enqueue_promise_map;
+  enqueue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(0), std::forward_as_tuple());
+  auto enqueue_future = enqueue_promise_map[0].get_future();
+  test_enqueue_end.RegisterEnqueue(&enqueue_promise_map);
+  enqueue_future.wait();
+  EXPECT_EQ(enqueue_future.get(), 0);
+
+  // push kHalfOfQueueSize to enqueue end buffer and register enqueue.
+  for (int i = 0; i < kHalfOfQueueSize; i++) {
+    std::unique_ptr<std::string> data = std::make_unique<std::string>(std::to_string(i));
+    test_enqueue_end.buffer_.push(std::move(data));
+  }
+  test_enqueue_end.RegisterEnqueue(&enqueue_promise_map);
+
+  // Register dequeue, expect kQueueSize move to dequeue end buffer
+  std::unordered_map<int, std::promise<int>> dequeue_promise_map;
+  dequeue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(kQueueSize), std::forward_as_tuple());
+  auto dequeue_future = dequeue_promise_map[kQueueSize].get_future();
+  test_dequeue_end.RegisterDequeue(&dequeue_promise_map);
+  dequeue_future.wait();
+  EXPECT_EQ(dequeue_future.get(), kQueueSize);
+
+  // Expect DequeueCallback should stop to be invoked
+  std::this_thread::sleep_for(std::chrono::milliseconds(20));
+  EXPECT_EQ(test_dequeue_end.count, kQueueSize);
+}
+
+// Enqueue end level : 1
+// Dequeue end level : 0 -> 1
+// Test 9 Queue becomes not empty during test, DequeueCallback should start to be invoked
+TEST_F(QueueTest, queue_becomes_non_empty_during_test) {
+  Queue<std::string> queue(kQueueSize);
+  TestEnqueueEnd test_enqueue_end(&queue, enqueue_handler_);
+  TestDequeueEnd test_dequeue_end(&queue, dequeue_handler_, kQueueSize);
+
+  // Register dequeue
+  std::unordered_map<int, std::promise<int>> dequeue_promise_map;
+  dequeue_promise_map.emplace(std::piecewise_construct, std::forward_as_tuple(kQueueSize), std::forward_as_tuple());
+  test_dequeue_end.RegisterDequeue(&dequeue_promise_map);
+
+  // push kQueueSize data to enqueue end buffer and register enqueue
+  for (int i = 0; i < kQueueSize; i++) {
+    std::unique_ptr<std::string> data = std::make_unique<std::string>(std::to_string(i));
+    test_enqueue_end.buffer_.push(std::move(data));
+  }
+  std::unordered_map<int, std::promise<int>> enqueue_promise_map;
+  test_enqueue_end.RegisterEnqueue(&enqueue_promise_map);
+
+  // Expect kQueueSize data move to dequeue end buffer
+  auto dequeue_future = dequeue_promise_map[kQueueSize].get_future();
+  dequeue_future.wait();
+  EXPECT_EQ(dequeue_future.get(), kQueueSize);
+}
+
+TEST_F(QueueTest, pass_smart_pointer_and_unregister) {
+  Queue<std::string>* queue = new Queue<std::string>(kQueueSize);
+
+  // Enqueue a string
+  std::string valid = "Valid String";
+  std::shared_ptr<std::string> shared = std::make_shared<std::string>(valid);
+  queue->RegisterEnqueue(enqueue_handler_, common::Bind(
+                                               [](Queue<std::string>* queue, std::shared_ptr<std::string> shared) {
+                                                 queue->UnregisterEnqueue();
+                                                 return std::make_unique<std::string>(*shared);
+                                               },
+                                               common::Unretained(queue), shared));
+
+  // Dequeue the string
+  queue->RegisterDequeue(dequeue_handler_, common::Bind(
+                                               [](Queue<std::string>* queue, std::string valid) {
+                                                 queue->UnregisterDequeue();
+                                                 auto answer = *queue->TryDequeue();
+                                                 ASSERT_EQ(answer, valid);
+                                               },
+                                               common::Unretained(queue), valid));
+
+  // Wait for both handlers to finish and delete the Queue
+  std::promise<void> promise;
+  auto future = promise.get_future();
+
+  enqueue_handler_->Post(common::BindOnce(
+      [](os::Handler* dequeue_handler, Queue<std::string>* queue, std::promise<void>* promise) {
+        dequeue_handler->Post(common::BindOnce(
+            [](Queue<std::string>* queue, std::promise<void>* promise) {
+              delete queue;
+              promise->set_value();
+            },
+            common::Unretained(queue), common::Unretained(promise)));
+      },
+      common::Unretained(dequeue_handler_), common::Unretained(queue), common::Unretained(&promise)));
+  future.wait();
+}
+
+// Create all threads for death tests in the function that dies
+class QueueDeathTest : public ::testing::Test {
+ public:
+  void RegisterEnqueueAndDelete() {
+    Thread* enqueue_thread = new Thread("enqueue_thread", Thread::Priority::NORMAL);
+    Handler* enqueue_handler = new Handler(enqueue_thread);
+    Queue<std::string>* queue = new Queue<std::string>(kQueueSizeOne);
+    queue->RegisterEnqueue(enqueue_handler,
+                           common::Bind([]() { return std::make_unique<std::string>("A string to fill the queue"); }));
+    delete queue;
+  }
+
+  void RegisterDequeueAndDelete() {
+    Thread* dequeue_thread = new Thread("dequeue_thread", Thread::Priority::NORMAL);
+    Handler* dequeue_handler = new Handler(dequeue_thread);
+    Queue<std::string>* queue = new Queue<std::string>(kQueueSizeOne);
+    queue->RegisterDequeue(dequeue_handler, common::Bind([](Queue<std::string>* queue) { queue->TryDequeue(); },
+                                                         common::Unretained(queue)));
+    delete queue;
+  }
+};
+
+TEST_F(QueueDeathTest, die_if_enqueue_not_unregistered) {
+  EXPECT_DEATH(RegisterEnqueueAndDelete(), "nqueue");
+}
+
+TEST_F(QueueDeathTest, die_if_dequeue_not_unregistered) {
+  EXPECT_DEATH(RegisterDequeueAndDelete(), "equeue");
+}
+
+class MockIQueueEnqueue : public IQueueEnqueue<int> {
+ public:
+  void RegisterEnqueue(Handler* handler, EnqueueCallback callback) override {
+    EXPECT_FALSE(registered_);
+    registered_ = true;
+    handler->Post(common::BindOnce(&MockIQueueEnqueue::handle_register_enqueue, common::Unretained(this), callback));
+  }
+
+  void handle_register_enqueue(EnqueueCallback callback) {
+    if (dont_handle_register_enqueue_) {
+      return;
+    }
+    while (registered_) {
+      std::unique_ptr<int> front = callback.Run();
+      queue_.push(*front);
+    }
+  }
+
+  void UnregisterEnqueue() override {
+    EXPECT_TRUE(registered_);
+    registered_ = false;
+  }
+
+  bool dont_handle_register_enqueue_ = false;
+  bool registered_ = false;
+  std::queue<int> queue_;
+};
+
+class EnqueueBufferTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    thread_ = new Thread("test_thread", Thread::Priority::NORMAL);
+    handler_ = new Handler(thread_);
+  }
+
+  void TearDown() override {
+    handler_->Clear();
+    delete handler_;
+    delete thread_;
+  }
+
+  void SynchronizeHandler() {
+    std::promise<void> promise;
+    auto future = promise.get_future();
+    handler_->Post(common::BindOnce([](std::promise<void> promise) { promise.set_value(); }, std::move(promise)));
+    future.wait();
+  }
+
+  MockIQueueEnqueue enqueue_;
+  EnqueueBuffer<int> enqueue_buffer_{&enqueue_};
+  Thread* thread_;
+  Handler* handler_;
+};
+
+TEST_F(EnqueueBufferTest, enqueue) {
+  int num_items = 10;
+  for (int i = 0; i < num_items; i++) {
+    enqueue_buffer_.Enqueue(std::make_unique<int>(i), handler_);
+  }
+  SynchronizeHandler();
+  for (int i = 0; i < num_items; i++) {
+    ASSERT_EQ(enqueue_.queue_.front(), i);
+    enqueue_.queue_.pop();
+  }
+  ASSERT_FALSE(enqueue_.registered_);
+}
+
+TEST_F(EnqueueBufferTest, clear) {
+  enqueue_.dont_handle_register_enqueue_ = true;
+  int num_items = 10;
+  for (int i = 0; i < num_items; i++) {
+    enqueue_buffer_.Enqueue(std::make_unique<int>(i), handler_);
+  }
+  ASSERT_TRUE(enqueue_.registered_);
+  enqueue_buffer_.Clear();
+  ASSERT_FALSE(enqueue_.registered_);
+}
+
+}  // namespace
+}  // namespace os
+}  // namespace bluetooth
diff --git a/gd/os/linux_generic/reactive_semaphore.cc b/gd/os/linux_generic/reactive_semaphore.cc
new file mode 100644
index 0000000..df0050a
--- /dev/null
+++ b/gd/os/linux_generic/reactive_semaphore.cc
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "reactive_semaphore.h"
+
+#include <error.h>
+#include <sys/eventfd.h>
+#include <unistd.h>
+#include <functional>
+
+#include "os/linux_generic/linux.h"
+#include "os/log.h"
+
+namespace bluetooth {
+namespace os {
+
+ReactiveSemaphore::ReactiveSemaphore(unsigned int value) : fd_(eventfd(value, EFD_SEMAPHORE | EFD_NONBLOCK)) {
+  ASSERT(fd_ != -1);
+}
+
+ReactiveSemaphore::~ReactiveSemaphore() {
+  int close_status;
+  RUN_NO_INTR(close_status = close(fd_));
+  ASSERT_LOG(close_status != -1, "close failed: %s", strerror(errno));
+}
+
+void ReactiveSemaphore::Decrease() {
+  uint64_t val = 0;
+  auto read_result = eventfd_read(fd_, &val);
+  ASSERT_LOG(read_result != -1, "decrease failed: %s", strerror(errno));
+}
+
+void ReactiveSemaphore::Increase() {
+  uint64_t val = 1;
+  auto write_result = eventfd_write(fd_, val);
+  ASSERT_LOG(write_result != -1, "increase failed: %s", strerror(errno));
+}
+
+int ReactiveSemaphore::GetFd() {
+  return fd_;
+}
+
+}  // namespace os
+}  // namespace bluetooth
diff --git a/gd/os/linux_generic/reactive_semaphore.h b/gd/os/linux_generic/reactive_semaphore.h
new file mode 100644
index 0000000..718666c
--- /dev/null
+++ b/gd/os/linux_generic/reactive_semaphore.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "os/utils.h"
+
+namespace bluetooth {
+namespace os {
+
+// A event_fd work in non-blocking and Semaphore mode
+class ReactiveSemaphore {
+ public:
+  // Creates a new ReactiveSemaphore with an initial value of |value|.
+  explicit ReactiveSemaphore(unsigned int value);
+  ~ReactiveSemaphore();
+  // Decrements the value of |fd_|, this will cause a crash if |fd_| unreadable.
+  void Decrease();
+  // Increase the value of |fd_|, this will cause a crash if |fd_| unwritable.
+  void Increase();
+  int GetFd();
+
+  DISALLOW_COPY_AND_ASSIGN(ReactiveSemaphore);
+
+ private:
+  int fd_;
+};
+
+}  // namespace os
+}  // namespace bluetooth
diff --git a/gd/os/linux_generic/reactor.cc b/gd/os/linux_generic/reactor.cc
index dfc6757..44b13d8 100644
--- a/gd/os/linux_generic/reactor.cc
+++ b/gd/os/linux_generic/reactor.cc
@@ -39,22 +39,21 @@
 class Reactor::Reactable {
  public:
   Reactable(int fd, Closure on_read_ready, Closure on_write_ready)
-      : fd_(fd),
-        on_read_ready_(std::move(on_read_ready)),
-        on_write_ready_(std::move(on_write_ready)),
-        is_executing_(false) {}
+      : fd_(fd), on_read_ready_(std::move(on_read_ready)), on_write_ready_(std::move(on_write_ready)),
+        is_executing_(false), removed_(false) {}
   const int fd_;
   Closure on_read_ready_;
   Closure on_write_ready_;
   bool is_executing_;
-  std::recursive_mutex lock_;
+  bool removed_;
+  std::mutex mutex_;
+  std::unique_ptr<std::promise<void>> finished_promise_;
 };
 
 Reactor::Reactor()
   : epoll_fd_(0),
     control_fd_(0),
-    is_running_(false),
-    reactable_removed_(false) {
+    is_running_(false) {
   RUN_NO_INTR(epoll_fd_ = epoll_create1(EPOLL_CLOEXEC));
   ASSERT_LOG(epoll_fd_ != -1, "could not create epoll fd: %s", strerror(errno));
 
@@ -80,11 +79,14 @@
 }
 
 void Reactor::Run() {
-  bool previously_running = is_running_.exchange(true);
-  ASSERT(!previously_running);
+  bool already_running = is_running_.exchange(true);
+  ASSERT(!already_running);
 
   for (;;) {
-    invalidation_list_.clear();
+    {
+      std::unique_lock<std::mutex> lock(mutex_);
+      invalidation_list_.clear();
+    }
     epoll_event events[kEpollMaxEvents];
     int count;
     RUN_NO_INTR(count = epoll_wait(epoll_fd_, events, kEpollMaxEvents, -1));
@@ -102,27 +104,30 @@
         return;
       }
       auto* reactable = static_cast<Reactor::Reactable*>(event.data.ptr);
-      {
-        std::unique_lock<std::mutex> lock(mutex_);
-        // See if this reactable has been removed in the meantime.
-        if (std::find(invalidation_list_.begin(), invalidation_list_.end(), reactable) != invalidation_list_.end()) {
-          continue;
-        }
-
-        std::lock_guard<std::recursive_mutex> reactable_lock(reactable->lock_);
-        lock.unlock();
-        reactable_removed_ = false;
-        reactable->is_executing_ = true;
-        if (event.events & (EPOLLIN | EPOLLHUP | EPOLLRDHUP | EPOLLERR) && reactable->on_read_ready_ != nullptr) {
-          reactable->on_read_ready_();
-        }
-        if (!reactable_removed_ && event.events & EPOLLOUT && reactable->on_write_ready_ != nullptr) {
-          reactable->on_write_ready_();
-        }
-        reactable->is_executing_ = false;
+      std::unique_lock<std::mutex> lock(mutex_);
+      // See if this reactable has been removed in the meantime.
+      if (std::find(invalidation_list_.begin(), invalidation_list_.end(), reactable) != invalidation_list_.end()) {
+        continue;
       }
-      if (reactable_removed_) {
-        delete reactable;
+
+      {
+        std::lock_guard<std::mutex> reactable_lock(reactable->mutex_);
+        lock.unlock();
+        reactable->is_executing_ = true;
+      }
+      if (event.events & (EPOLLIN | EPOLLHUP | EPOLLRDHUP | EPOLLERR) && !reactable->on_read_ready_.is_null()) {
+        reactable->on_read_ready_.Run();
+      }
+      if (event.events & EPOLLOUT && !reactable->on_write_ready_.is_null()) {
+        reactable->on_write_ready_.Run();
+      }
+      {
+        std::lock_guard<std::mutex> reactable_lock(reactable->mutex_);
+        reactable->is_executing_ = false;
+        if (reactable->removed_) {
+          reactable->finished_promise_->set_value();
+          delete reactable;
+        }
       }
     }
   }
@@ -133,25 +138,25 @@
     LOG_WARN("not running, will stop once it's started");
   }
   auto control = eventfd_write(control_fd_, 1);
-  ASSERT(control != -1)
+  ASSERT(control != -1);
 }
 
 Reactor::Reactable* Reactor::Register(int fd, Closure on_read_ready, Closure on_write_ready) {
   uint32_t poll_event_type = 0;
-  if (on_read_ready != nullptr) {
+  if (!on_read_ready.is_null()) {
     poll_event_type |= (EPOLLIN | EPOLLRDHUP);
   }
-  if (on_write_ready != nullptr) {
+  if (!on_write_ready.is_null()) {
     poll_event_type |= EPOLLOUT;
   }
   auto* reactable = new Reactable(fd, on_read_ready, on_write_ready);
   epoll_event event = {
       .events = poll_event_type,
-      {.ptr = reactable}
+      .data = {.ptr = reactable},
   };
   int register_fd;
   RUN_NO_INTR(register_fd = epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, fd, &event));
-  ASSERT(register_fd != -1)
+  ASSERT(register_fd != -1);
   return reactable;
 }
 
@@ -161,45 +166,58 @@
     std::lock_guard<std::mutex> lock(mutex_);
     invalidation_list_.push_back(reactable);
   }
+  bool delaying_delete_until_callback_finished = false;
   {
     int result;
-    std::lock_guard<std::recursive_mutex> reactable_lock(reactable->lock_);
+    std::lock_guard<std::mutex> reactable_lock(reactable->mutex_);
     RUN_NO_INTR(result = epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, reactable->fd_, nullptr));
     if (result == -1 && errno == ENOENT) {
       LOG_INFO("reactable is invalid or unregistered");
     } else {
       ASSERT(result != -1);
     }
-    // If we are unregistering during the callback event from this reactable, we delete it after the callback is executed.
-    // reactable->is_executing_ is protected by reactable->lock_, so it's thread safe.
+
+    // If we are unregistering during the callback event from this reactable, we delete it after the callback is
+    // executed. reactable->is_executing_ is protected by reactable->mutex_, so it's thread safe.
     if (reactable->is_executing_) {
-      reactable_removed_ = true;
+      reactable->removed_ = true;
+      reactable->finished_promise_ = std::make_unique<std::promise<void>>();
+      executing_reactable_finished_ = std::make_unique<std::future<void>>(reactable->finished_promise_->get_future());
+      delaying_delete_until_callback_finished = true;
     }
   }
   // If we are unregistering outside of the callback event from this reactable, we delete it now
-  if (!reactable_removed_) {
+  if (!delaying_delete_until_callback_finished) {
     delete reactable;
   }
 }
 
+bool Reactor::WaitForUnregisteredReactable(std::chrono::milliseconds timeout) {
+  if (executing_reactable_finished_ == nullptr) {
+    return true;
+  }
+  auto stop_status = executing_reactable_finished_->wait_for(timeout);
+  return stop_status == std::future_status::ready;
+}
+
 void Reactor::ModifyRegistration(Reactor::Reactable* reactable, Closure on_read_ready, Closure on_write_ready) {
   ASSERT(reactable != nullptr);
 
   uint32_t poll_event_type = 0;
-  if (on_read_ready != nullptr) {
+  if (!on_read_ready.is_null()) {
     poll_event_type |= (EPOLLIN | EPOLLRDHUP);
   }
-  if (on_write_ready != nullptr) {
+  if (!on_write_ready.is_null()) {
     poll_event_type |= EPOLLOUT;
   }
   {
-    std::lock_guard<std::recursive_mutex> reactable_lock(reactable->lock_);
+    std::lock_guard<std::mutex> reactable_lock(reactable->mutex_);
     reactable->on_read_ready_ = std::move(on_read_ready);
     reactable->on_write_ready_ = std::move(on_write_ready);
   }
   epoll_event event = {
       .events = poll_event_type,
-      {.ptr = reactable}
+      .data = {.ptr = reactable},
   };
   int modify_fd;
   RUN_NO_INTR(modify_fd = epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, reactable->fd_, &event));
diff --git a/gd/os/linux_generic/reactor_unittest.cc b/gd/os/linux_generic/reactor_unittest.cc
index 7fda234..b9cd07d 100644
--- a/gd/os/linux_generic/reactor_unittest.cc
+++ b/gd/os/linux_generic/reactor_unittest.cc
@@ -21,7 +21,10 @@
 #include <future>
 #include <thread>
 
+#include "common/bind.h"
+#include "common/callback.h"
 #include "gtest/gtest.h"
+#include "os/log.h"
 
 namespace bluetooth {
 namespace os {
@@ -29,6 +32,8 @@
 
 constexpr int kReadReadyValue = 100;
 
+using common::Bind;
+
 std::promise<int>* g_promise;
 
 class ReactorTest : public ::testing::Test {
@@ -51,7 +56,7 @@
 class SampleReactable {
  public:
   SampleReactable() : fd_(eventfd(0, EFD_NONBLOCK)) {
-    EXPECT_NE(fd_, 0);
+    EXPECT_NE(fd_, -1);
   }
 
   ~SampleReactable() {
@@ -74,11 +79,11 @@
     kSampleOutputValue,
   };
   FakeReactable() : fd_(eventfd(0, 0)), reactor_(nullptr) {
-    EXPECT_NE(fd_, 0);
+    EXPECT_NE(fd_, -1);
   }
 
   FakeReactable(Reactor* reactor) : fd_(eventfd(0, 0)), reactor_(reactor) {
-    EXPECT_NE(fd_, 0);
+    EXPECT_NE(fd_, -1);
   }
 
   ~FakeReactable() {
@@ -86,15 +91,18 @@
   }
 
   void OnReadReady() {
+    LOG_INFO();
     uint64_t value = 0;
     auto read_result = eventfd_read(fd_, &value);
+    LOG_INFO("value = %d", (int)value);
     EXPECT_EQ(read_result, 0);
     if (value == kSetPromise && g_promise != nullptr) {
       g_promise->set_value(kReadReadyValue);
     }
     if (value == kRegisterSampleReactable) {
-      reactable_ = reactor_->Register(sample_reactable_.fd_, [this] { this->sample_reactable_.OnReadReady(); },
-                                      [this] { this->sample_reactable_.OnWriteReady(); });
+      reactable_ =
+          reactor_->Register(sample_reactable_.fd_, Bind(&FakeReactable::OnReadReady, common::Unretained(this)),
+                             Bind(&FakeReactable::OnWriteReadyNoOp, common::Unretained(this)));
       g_promise->set_value(kReadReadyValue);
     }
     if (value == kUnregisterSampleReactable) {
@@ -109,6 +117,16 @@
     EXPECT_EQ(write_result, 0);
   }
 
+  void OnWriteReadyNoOp() {}
+
+  void UnregisterInCallback() {
+    uint64_t value = 0;
+    auto read_result = eventfd_read(fd_, &value);
+    EXPECT_EQ(read_result, 0);
+    g_promise->set_value(kReadReadyValue);
+    reactor_->Unregister(reactable_);
+  }
+
   SampleReactable sample_reactable_;
   Reactor::Reactable* reactable_ = nullptr;
   int fd_;
@@ -118,6 +136,33 @@
   uint64_t output_data_ = kSampleOutputValue;
 };
 
+class FakeRunningReactable {
+ public:
+  FakeRunningReactable() : fd_(eventfd(0, 0)) {
+    EXPECT_NE(fd_, -1);
+  }
+
+  ~FakeRunningReactable() {
+    close(fd_);
+  }
+
+  void OnReadReady() {
+    uint64_t value = 0;
+    auto read_result = eventfd_read(fd_, &value);
+    ASSERT_EQ(read_result, 0);
+    started.set_value();
+    can_finish.get_future().wait();
+    finished.set_value();
+  }
+
+  Reactor::Reactable* reactable_ = nullptr;
+  int fd_;
+
+  std::promise<void> started;
+  std::promise<void> can_finish;
+  std::promise<void> finished;
+};
+
 TEST_F(ReactorTest, start_and_stop) {
   auto reactor_thread = std::thread(&Reactor::Run, reactor_);
   reactor_->Stop();
@@ -141,16 +186,16 @@
 
 TEST_F(ReactorTest, cold_register_only) {
   FakeReactable fake_reactable;
-  auto* reactable =
-      reactor_->Register(fake_reactable.fd_, std::bind(&FakeReactable::OnReadReady, &fake_reactable), nullptr);
+  auto* reactable = reactor_->Register(
+      fake_reactable.fd_, Bind(&FakeReactable::OnReadReady, common::Unretained(&fake_reactable)), Closure());
 
   reactor_->Unregister(reactable);
 }
 
 TEST_F(ReactorTest, cold_register) {
   FakeReactable fake_reactable;
-  auto* reactable =
-      reactor_->Register(fake_reactable.fd_, std::bind(&FakeReactable::OnReadReady, &fake_reactable), nullptr);
+  auto* reactable = reactor_->Register(
+      fake_reactable.fd_, Bind(&FakeReactable::OnReadReady, common::Unretained(&fake_reactable)), Closure());
   auto reactor_thread = std::thread(&Reactor::Run, reactor_);
   auto future = g_promise->get_future();
 
@@ -167,8 +212,8 @@
   auto future = g_promise->get_future();
 
   FakeReactable fake_reactable;
-  auto* reactable =
-      reactor_->Register(fake_reactable.fd_, std::bind(&FakeReactable::OnReadReady, &fake_reactable), nullptr);
+  auto* reactable = reactor_->Register(
+      fake_reactable.fd_, Bind(&FakeReactable::OnReadReady, common::Unretained(&fake_reactable)), Closure());
   auto write_result = eventfd_write(fake_reactable.fd_, FakeReactable::kSetPromise);
   EXPECT_EQ(write_result, 0);
   EXPECT_EQ(future.get(), kReadReadyValue);
@@ -178,10 +223,62 @@
   reactor_->Unregister(reactable);
 }
 
+TEST_F(ReactorTest, unregister_from_different_thread_while_task_is_executing_) {
+  FakeRunningReactable fake_reactable;
+  auto* reactable = reactor_->Register(
+      fake_reactable.fd_, Bind(&FakeRunningReactable::OnReadReady, common::Unretained(&fake_reactable)), Closure());
+  auto reactor_thread = std::thread(&Reactor::Run, reactor_);
+  auto write_result = eventfd_write(fake_reactable.fd_, 1);
+  ASSERT_EQ(write_result, 0);
+  fake_reactable.started.get_future().wait();
+  reactor_->Unregister(reactable);
+  fake_reactable.can_finish.set_value();
+  fake_reactable.finished.get_future().wait();
+
+  reactor_->Stop();
+  reactor_thread.join();
+}
+
+TEST_F(ReactorTest, unregister_from_different_thread_while_task_is_executing_wait_fails) {
+  FakeRunningReactable fake_reactable;
+  auto* reactable = reactor_->Register(
+      fake_reactable.fd_, common::Bind(&FakeRunningReactable::OnReadReady, common::Unretained(&fake_reactable)),
+      common::Closure());
+  auto reactor_thread = std::thread(&Reactor::Run, reactor_);
+  auto write_result = eventfd_write(fake_reactable.fd_, 1);
+  ASSERT_EQ(write_result, 0);
+  fake_reactable.started.get_future().wait();
+  reactor_->Unregister(reactable);
+  ASSERT_FALSE(reactor_->WaitForUnregisteredReactable(std::chrono::milliseconds(1)));
+  fake_reactable.can_finish.set_value();
+  fake_reactable.finished.get_future().wait();
+
+  reactor_->Stop();
+  reactor_thread.join();
+}
+
+TEST_F(ReactorTest, unregister_from_different_thread_while_task_is_executing_wait_succeeds) {
+  FakeRunningReactable fake_reactable;
+  auto* reactable = reactor_->Register(
+      fake_reactable.fd_, common::Bind(&FakeRunningReactable::OnReadReady, common::Unretained(&fake_reactable)),
+      common::Closure());
+  auto reactor_thread = std::thread(&Reactor::Run, reactor_);
+  auto write_result = eventfd_write(fake_reactable.fd_, 1);
+  ASSERT_EQ(write_result, 0);
+  fake_reactable.started.get_future().wait();
+  reactor_->Unregister(reactable);
+  fake_reactable.can_finish.set_value();
+  fake_reactable.finished.get_future().wait();
+  ASSERT_TRUE(reactor_->WaitForUnregisteredReactable(std::chrono::milliseconds(1)));
+
+  reactor_->Stop();
+  reactor_thread.join();
+}
+
 TEST_F(ReactorTest, hot_unregister_from_different_thread) {
   FakeReactable fake_reactable;
-  auto* reactable =
-      reactor_->Register(fake_reactable.fd_, std::bind(&FakeReactable::OnReadReady, &fake_reactable), nullptr);
+  auto* reactable = reactor_->Register(
+      fake_reactable.fd_, Bind(&FakeReactable::OnReadReady, common::Unretained(&fake_reactable)), Closure());
   auto reactor_thread = std::thread(&Reactor::Run, reactor_);
   reactor_->Unregister(reactable);
   auto future = g_promise->get_future();
@@ -200,11 +297,16 @@
   auto future = g_promise->get_future();
 
   FakeReactable fake_reactable(reactor_);
-  auto* reactable =
-      reactor_->Register(fake_reactable.fd_, std::bind(&FakeReactable::OnReadReady, &fake_reactable), nullptr);
+  auto* reactable = reactor_->Register(
+      fake_reactable.fd_, Bind(&FakeReactable::OnReadReady, common::Unretained(&fake_reactable)), Closure());
   auto write_result = eventfd_write(fake_reactable.fd_, FakeReactable::kRegisterSampleReactable);
   EXPECT_EQ(write_result, 0);
   EXPECT_EQ(future.get(), kReadReadyValue);
+  delete g_promise;
+  g_promise = new std::promise<int>;
+  future = g_promise->get_future();
+  write_result = eventfd_write(fake_reactable.fd_, FakeReactable::kUnregisterSampleReactable);
+  EXPECT_EQ(write_result, 0);
   reactor_->Stop();
   reactor_thread.join();
 
@@ -216,23 +318,65 @@
   auto future = g_promise->get_future();
 
   FakeReactable fake_reactable(reactor_);
-  auto* reactable =
-      reactor_->Register(fake_reactable.fd_, std::bind(&FakeReactable::OnReadReady, &fake_reactable), nullptr);
+  auto* reactable = reactor_->Register(
+      fake_reactable.fd_, Bind(&FakeReactable::OnReadReady, common::Unretained(&fake_reactable)), Closure());
   auto write_result = eventfd_write(fake_reactable.fd_, FakeReactable::kRegisterSampleReactable);
   EXPECT_EQ(write_result, 0);
   EXPECT_EQ(future.get(), kReadReadyValue);
+  LOG_INFO();
   delete g_promise;
   g_promise = new std::promise<int>;
   future = g_promise->get_future();
   write_result = eventfd_write(fake_reactable.fd_, FakeReactable::kUnregisterSampleReactable);
   EXPECT_EQ(write_result, 0);
   EXPECT_EQ(future.get(), kReadReadyValue);
+  LOG_INFO();
   reactor_->Stop();
   reactor_thread.join();
 
   reactor_->Unregister(reactable);
 }
 
+TEST_F(ReactorTest, hot_unregister_from_callback) {
+  auto reactor_thread = std::thread(&Reactor::Run, reactor_);
+
+  FakeReactable fake_reactable1(reactor_);
+  auto* reactable1 = reactor_->Register(
+      fake_reactable1.fd_, Bind(&FakeReactable::OnReadReady, common::Unretained(&fake_reactable1)), Closure());
+
+  FakeReactable fake_reactable2(reactor_);
+  auto* reactable2 = reactor_->Register(
+      fake_reactable2.fd_, Bind(&FakeReactable::UnregisterInCallback, common::Unretained(&fake_reactable2)), Closure());
+  fake_reactable2.reactable_ = reactable2;
+  auto write_result = eventfd_write(fake_reactable2.fd_, 1);
+  EXPECT_EQ(write_result, 0);
+  reactor_->Stop();
+  reactor_thread.join();
+
+  reactor_->Unregister(reactable1);
+}
+
+TEST_F(ReactorTest, hot_unregister_during_unregister_from_callback) {
+  auto reactor_thread = std::thread(&Reactor::Run, reactor_);
+  auto future = g_promise->get_future();
+
+  FakeReactable fake_reactable1(reactor_);
+  auto* reactable1 = reactor_->Register(
+      fake_reactable1.fd_, Bind(&FakeReactable::OnReadReady, common::Unretained(&fake_reactable1)), Closure());
+
+  FakeReactable fake_reactable2(reactor_);
+  auto* reactable2 = reactor_->Register(
+      fake_reactable2.fd_, Bind(&FakeReactable::UnregisterInCallback, common::Unretained(&fake_reactable2)), Closure());
+  fake_reactable2.reactable_ = reactable2;
+  auto write_result = eventfd_write(fake_reactable2.fd_, 1);
+  EXPECT_EQ(write_result, 0);
+  EXPECT_EQ(future.get(), kReadReadyValue);
+  reactor_->Unregister(reactable1);
+
+  reactor_->Stop();
+  reactor_thread.join();
+}
+
 TEST_F(ReactorTest, start_and_stop_multi_times) {
   auto reactor_thread = std::thread(&Reactor::Run, reactor_);
   reactor_->Stop();
@@ -246,8 +390,8 @@
 
 TEST_F(ReactorTest, on_write_ready) {
   FakeReactable fake_reactable;
-  auto* reactable =
-      reactor_->Register(fake_reactable.fd_, nullptr, std::bind(&FakeReactable::OnWriteReady, &fake_reactable));
+  auto* reactable = reactor_->Register(fake_reactable.fd_, Closure(),
+                                       Bind(&FakeReactable::OnWriteReady, common::Unretained(&fake_reactable)));
   auto reactor_thread = std::thread(&Reactor::Run, reactor_);
   uint64_t value = 0;
   auto read_result = eventfd_read(fake_reactable.fd_, &value);
@@ -262,9 +406,10 @@
 
 TEST_F(ReactorTest, modify_registration) {
   FakeReactable fake_reactable;
-  auto* reactable =
-      reactor_->Register(fake_reactable.fd_, std::bind(&FakeReactable::OnReadReady, &fake_reactable), nullptr);
-  reactor_->ModifyRegistration(reactable, nullptr, std::bind(&FakeReactable::OnWriteReady, &fake_reactable));
+  auto* reactable = reactor_->Register(
+      fake_reactable.fd_, Bind(&FakeReactable::OnReadReady, common::Unretained(&fake_reactable)), Closure());
+  reactor_->ModifyRegistration(reactable, Closure(),
+                               Bind(&FakeReactable::OnWriteReady, common::Unretained(&fake_reactable)));
   auto reactor_thread = std::thread(&Reactor::Run, reactor_);
   uint64_t value = 0;
   auto read_result = eventfd_read(fake_reactable.fd_, &value);
@@ -273,6 +418,8 @@
 
   reactor_->Stop();
   reactor_thread.join();
+
+  reactor_->Unregister(reactable);
 }
 
 }  // namespace
diff --git a/gd/os/linux_generic/repeating_alarm.cc b/gd/os/linux_generic/repeating_alarm.cc
index 3258400..79cda6d 100644
--- a/gd/os/linux_generic/repeating_alarm.cc
+++ b/gd/os/linux_generic/repeating_alarm.cc
@@ -20,6 +20,7 @@
 #include <cstring>
 #include <unistd.h>
 
+#include "common/bind.h"
 #include "os/log.h"
 #include "os/utils.h"
 
@@ -32,16 +33,15 @@
 namespace bluetooth {
 namespace os {
 
-RepeatingAlarm::RepeatingAlarm(Thread* thread)
-  : thread_(thread),
-    fd_(timerfd_create(ALARM_CLOCK, 0)) {
+RepeatingAlarm::RepeatingAlarm(Handler* handler) : handler_(handler), fd_(timerfd_create(ALARM_CLOCK, 0)) {
   ASSERT(fd_ != -1);
 
-  token_ = thread_->GetReactor()->Register(fd_, [this] { on_fire(); }, nullptr);
+  token_ = handler_->thread_->GetReactor()->Register(
+      fd_, common::Bind(&RepeatingAlarm::on_fire, common::Unretained(this)), common::Closure());
 }
 
 RepeatingAlarm::~RepeatingAlarm() {
-  thread_->GetReactor()->Unregister(token_);
+  handler_->thread_->GetReactor()->Unregister(token_);
 
   int close_status;
   RUN_NO_INTR(close_status = close(fd_));
@@ -74,7 +74,7 @@
   uint64_t times_invoked;
   auto bytes_read = read(fd_, &times_invoked, sizeof(uint64_t));
   lock.unlock();
-  task();
+  task.Run();
   ASSERT(bytes_read == static_cast<ssize_t>(sizeof(uint64_t)));
 }
 
diff --git a/gd/os/linux_generic/repeating_alarm_unittest.cc b/gd/os/linux_generic/repeating_alarm_unittest.cc
index a017f66..d30bbd9 100644
--- a/gd/os/linux_generic/repeating_alarm_unittest.cc
+++ b/gd/os/linux_generic/repeating_alarm_unittest.cc
@@ -18,6 +18,7 @@
 
 #include <future>
 
+#include "common/bind.h"
 #include "gtest/gtest.h"
 
 namespace bluetooth {
@@ -30,11 +31,14 @@
  protected:
   void SetUp() override {
     thread_ = new Thread("test_thread", Thread::Priority::NORMAL);
-    alarm_ = new RepeatingAlarm(thread_);
+    handler_ = new Handler(thread_);
+    alarm_ = new RepeatingAlarm(handler_);
   }
 
   void TearDown() override {
     delete alarm_;
+    handler_->Clear();
+    delete handler_;
     delete thread_;
   }
 
@@ -43,26 +47,33 @@
     auto future = promise.get_future();
     auto start_time = std::chrono::steady_clock::now();
     int counter = 0;
-    alarm_->Schedule(
-        [&counter, &promise, start_time, scheduled_tasks, task_length_ms, interval_between_tasks_ms]() {
-          counter++;
-          auto time_now = std::chrono::steady_clock::now();
-          auto time_delta = time_now - start_time;
-          if (counter == scheduled_tasks) {
-            promise.set_value();
-          }
-          ASSERT_NEAR(time_delta.count(), interval_between_tasks_ms * 1000000 * counter, error_ms * 1000000);
-          std::this_thread::sleep_for(std::chrono::milliseconds(task_length_ms));
-        },
-        std::chrono::milliseconds(interval_between_tasks_ms));
+    alarm_->Schedule(common::Bind(&RepeatingAlarmTest::verify_delayed_tasks, common::Unretained(this),
+                                  common::Unretained(&counter), start_time, scheduled_tasks,
+                                  common::Unretained(&promise), task_length_ms, interval_between_tasks_ms),
+                     std::chrono::milliseconds(interval_between_tasks_ms));
     future.get();
     alarm_->Cancel();
   }
 
+  void verify_delayed_tasks(int* counter, std::chrono::steady_clock::time_point start_time, int scheduled_tasks,
+                            std::promise<void>* promise, int task_length_ms, int interval_between_tasks_ms) {
+    *counter = *counter + 1;
+    auto time_now = std::chrono::steady_clock::now();
+    auto time_delta = time_now - start_time;
+    if (*counter == scheduled_tasks) {
+      promise->set_value();
+    }
+    ASSERT_NEAR(time_delta.count(), interval_between_tasks_ms * 1000000 * *counter, error_ms * 1000000);
+    std::this_thread::sleep_for(std::chrono::milliseconds(task_length_ms));
+  }
+
   RepeatingAlarm* alarm_;
 
+  Closure should_not_happen_ = common::Bind([] { ASSERT_TRUE(false); });
+
  private:
   Thread* thread_;
+  Handler* handler_;
 };
 
 TEST_F(RepeatingAlarmTest, cancel_while_not_armed) {
@@ -74,7 +85,8 @@
   auto future = promise.get_future();
   auto before = std::chrono::steady_clock::now();
   int period_ms = 10;
-  alarm_->Schedule([&promise]() { promise.set_value(); }, std::chrono::milliseconds(period_ms));
+  alarm_->Schedule(common::Bind(&std::promise<void>::set_value, common::Unretained(&promise)),
+                   std::chrono::milliseconds(period_ms));
   future.get();
   alarm_->Cancel();
   auto after = std::chrono::steady_clock::now();
@@ -83,27 +95,29 @@
 }
 
 TEST_F(RepeatingAlarmTest, cancel_alarm) {
-  alarm_->Schedule([]() { ASSERT_TRUE(false); }, std::chrono::milliseconds(1));
+  alarm_->Schedule(should_not_happen_, std::chrono::milliseconds(1));
   alarm_->Cancel();
   std::this_thread::sleep_for(std::chrono::milliseconds(5));
 }
 
 TEST_F(RepeatingAlarmTest, cancel_alarm_from_callback) {
-  alarm_->Schedule([this]() { this->alarm_->Cancel(); }, std::chrono::milliseconds(1));
+  alarm_->Schedule(common::Bind(&RepeatingAlarm::Cancel, common::Unretained(this->alarm_)),
+                   std::chrono::milliseconds(1));
   std::this_thread::sleep_for(std::chrono::milliseconds(5));
 }
 
 TEST_F(RepeatingAlarmTest, schedule_while_alarm_armed) {
-  alarm_->Schedule([]() { ASSERT_TRUE(false); }, std::chrono::milliseconds(1));
+  alarm_->Schedule(should_not_happen_, std::chrono::milliseconds(1));
   std::promise<void> promise;
   auto future = promise.get_future();
-  alarm_->Schedule([&promise]() { promise.set_value(); }, std::chrono::milliseconds(10));
+  alarm_->Schedule(common::Bind(&std::promise<void>::set_value, common::Unretained(&promise)),
+                   std::chrono::milliseconds(10));
   future.get();
   alarm_->Cancel();
 }
 
 TEST_F(RepeatingAlarmTest, delete_while_alarm_armed) {
-  alarm_->Schedule([]() { ASSERT_TRUE(false); }, std::chrono::milliseconds(1));
+  alarm_->Schedule(should_not_happen_, std::chrono::milliseconds(1));
   delete alarm_;
   alarm_ = nullptr;
   std::this_thread::sleep_for(std::chrono::milliseconds(1));
diff --git a/gd/os/linux_generic/thread_unittest.cc b/gd/os/linux_generic/thread_unittest.cc
index 1e16edc..f0b8f24 100644
--- a/gd/os/linux_generic/thread_unittest.cc
+++ b/gd/os/linux_generic/thread_unittest.cc
@@ -18,6 +18,7 @@
 
 #include <sys/eventfd.h>
 
+#include "common/bind.h"
 #include "gtest/gtest.h"
 #include "os/reactor.h"
 
@@ -85,7 +86,8 @@
   Reactor* reactor = thread->GetReactor();
   SampleReactable sample_reactable(thread);
   auto* reactable =
-      reactor->Register(sample_reactable.fd_, std::bind(&SampleReactable::OnReadReady, &sample_reactable), nullptr);
+      reactor->Register(sample_reactable.fd_,
+                        common::Bind(&SampleReactable::OnReadReady, common::Unretained(&sample_reactable)), Closure());
   int fd = sample_reactable.fd_;
   int write_result = eventfd_write(fd, kCheckIsSameThread);
   EXPECT_EQ(write_result, 0);
diff --git a/gd/os/log.h b/gd/os/log.h
index 291b17b..29ff751 100644
--- a/gd/os/log.h
+++ b/gd/os/log.h
@@ -18,6 +18,8 @@
 
 #pragma once
 
+#include <cstdlib>
+
 #ifndef LOG_TAG
 #define LOG_TAG "bt"
 #endif
@@ -26,37 +28,66 @@
 
 #include <log/log.h>
 
-#define LOG_VERBOSE(fmt, args...) ALOGV("%s: " fmt, __PRETTY_FUNCTION__, ##args)
-#define LOG_DEBUG(fmt, args...) ALOGD("%s: " fmt, __PRETTY_FUNCTION__, ##args)
-#define LOG_INFO(fmt, args...) ALOGI("%s: " fmt, __PRETTY_FUNCTION__, ##args)
-#define LOG_WARN(fmt, args...) ALOGW("%s: " fmt, __PRETTY_FUNCTION__, ##args)
-#define LOG_ERROR(fmt, args...) ALOGE("%s: " fmt, __PRETTY_FUNCTION__, ##args)
+/* When including headers from legacy stack, this log definitions collide with existing logging system. Remove once we
+ * get rid of legacy stack. */
+#ifndef LOG_VERBOSE
+
+#define LOG_VERBOSE(fmt, args...) ALOGV("%s:%d %s: " fmt, __FILE__, __LINE__, __func__, ##args)
+#define LOG_DEBUG(fmt, args...) ALOGD("%s:%d %s: " fmt, __FILE__, __LINE__, __func__, ##args)
+#define LOG_INFO(fmt, args...) ALOGI("%s:%d %s: " fmt, __FILE__, __LINE__, __func__, ##args)
+#define LOG_WARN(fmt, args...) ALOGW("%s:%d %s: " fmt, __FILE__, __LINE__, __func__, ##args)
+#define LOG_ERROR(fmt, args...) ALOGE("%s:%d %s: " fmt, __FILE__, __LINE__, __func__, ##args)
+
+#endif /* LOG_VERBOSE*/
 
 #else
 
 /* syslog didn't work well here since we would be redefining LOG_DEBUG. */
-#include <stdio.h>
+#include <chrono>
+#include <cstdio>
+#include <ctime>
 
-#define LOGWRAPPER(fmt, args...) \
-  fprintf(stderr, "%s - %s: " fmt "\n", LOG_TAG, __PRETTY_FUNCTION__, ##args)
+/* When including headers from legacy stack, this log definitions collide with existing logging system. Remove once we
+ * get rid of legacy stack. */
+#ifndef LOG_VERBOSE
+
+#define LOGWRAPPER(fmt, args...)                                                                                      \
+  do {                                                                                                                \
+    auto now = std::chrono::system_clock::now();                                                                      \
+    auto now_ms = std::chrono::time_point_cast<std::chrono::milliseconds>(now);                                       \
+    auto now_t = std::chrono::system_clock::to_time_t(now);                                                           \
+    /* YYYY-MM-DD_HH:MM:SS.sss is 23 byte long, plus 1 for null terminator */                                         \
+    char buf[24];                                                                                                     \
+    auto l = std::strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", std::localtime(&now_t));                            \
+    snprintf(buf + l, sizeof(buf) - l, ".%03u", static_cast<unsigned int>(now_ms.time_since_epoch().count() % 1000)); \
+    fprintf(stderr, "%s %s - %s:%d - %s: " fmt "\n", buf, LOG_TAG, __FILE__, __LINE__, __func__, ##args);             \
+  } while (false)
 
 #define LOG_VERBOSE(...) LOGWRAPPER(__VA_ARGS__)
 #define LOG_DEBUG(...) LOGWRAPPER(__VA_ARGS__)
 #define LOG_INFO(...) LOGWRAPPER(__VA_ARGS__)
 #define LOG_WARN(...) LOGWRAPPER(__VA_ARGS__)
 #define LOG_ERROR(...) LOGWRAPPER(__VA_ARGS__)
+#define LOG_ALWAYS_FATAL(...) \
+  do {                        \
+    LOGWRAPPER(__VA_ARGS__);  \
+    abort();                  \
+  } while (false)
+
+#endif /* LOG_VERBOE */
 
 #endif /* defined(OS_ANDROID) */
 
-#define ASSERT(condition) \
-  if (!(condition)) { \
-    LOG_ERROR("%s:%d assertion '" #condition "' failed", __FILE__, __LINE__); \
-    abort(); \
-  }
+#define ASSERT(condition)                                    \
+  do {                                                       \
+    if (!(condition)) {                                      \
+      LOG_ALWAYS_FATAL("assertion '" #condition "' failed"); \
+    }                                                        \
+  } while (false)
 
-#define ASSERT_LOG(condition, fmt, args...) \
-  if (!(condition)) { \
-    LOG_ERROR("%s:%d assertion '" #condition "' failed - " fmt, __FILE__, __LINE__, ##args); \
-    abort(); \
-  }
-
+#define ASSERT_LOG(condition, fmt, args...)                                 \
+  do {                                                                      \
+    if (!(condition)) {                                                     \
+      LOG_ALWAYS_FATAL("assertion '" #condition "' failed - " fmt, ##args); \
+    }                                                                       \
+  } while (false)
diff --git a/gd/os/queue.h b/gd/os/queue.h
new file mode 100644
index 0000000..1d2af02
--- /dev/null
+++ b/gd/os/queue.h
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <unistd.h>
+#include <functional>
+#include <mutex>
+#include <queue>
+
+#include "common/bind.h"
+#include "common/callback.h"
+#include "os/handler.h"
+#ifdef OS_LINUX_GENERIC
+#include "os/linux_generic/reactive_semaphore.h"
+#endif
+#include "os/log.h"
+
+namespace bluetooth {
+namespace os {
+
+// See documentation for |Queue|
+template <typename T>
+class IQueueEnqueue {
+ public:
+  using EnqueueCallback = Callback<std::unique_ptr<T>()>;
+  virtual ~IQueueEnqueue() = default;
+  virtual void RegisterEnqueue(Handler* handler, EnqueueCallback callback) = 0;
+  virtual void UnregisterEnqueue() = 0;
+};
+
+// See documentation for |Queue|
+template <typename T>
+class IQueueDequeue {
+ public:
+  using DequeueCallback = Callback<void()>;
+  virtual ~IQueueDequeue() = default;
+  virtual void RegisterDequeue(Handler* handler, DequeueCallback callback) = 0;
+  virtual void UnregisterDequeue() = 0;
+  virtual std::unique_ptr<T> TryDequeue() = 0;
+};
+
+template <typename T>
+class Queue : public IQueueEnqueue<T>, public IQueueDequeue<T> {
+ public:
+  // A function moving data from enqueue end buffer to queue, it will be continually be invoked until queue
+  // is full. Enqueue end should make sure buffer isn't empty and UnregisterEnqueue when buffer become empty.
+  using EnqueueCallback = Callback<std::unique_ptr<T>()>;
+  // A function moving data form queue to dequeue end buffer, it will be continually be invoked until queue
+  // is empty. TryDequeue should be use in this function to get data from queue.
+  using DequeueCallback = Callback<void()>;
+  // Create a queue with |capacity| is the maximum number of messages a queue can contain
+  explicit Queue(size_t capacity);
+  ~Queue();
+  // Register |callback| that will be called on |handler| when the queue is able to enqueue one piece of data.
+  // This will cause a crash if handler or callback has already been registered before.
+  void RegisterEnqueue(Handler* handler, EnqueueCallback callback) override;
+  // Unregister current EnqueueCallback from this queue, this will cause a crash if not registered yet.
+  void UnregisterEnqueue() override;
+  // Register |callback| that will be called on |handler| when the queue has at least one piece of data ready
+  // for dequeue. This will cause a crash if handler or callback has already been registered before.
+  void RegisterDequeue(Handler* handler, DequeueCallback callback) override;
+  // Unregister current DequeueCallback from this queue, this will cause a crash if not registered yet.
+  void UnregisterDequeue() override;
+
+  // Try to dequeue an item from this queue. Return nullptr when there is nothing in the queue.
+  std::unique_ptr<T> TryDequeue() override;
+
+ private:
+  void EnqueueCallbackInternal(EnqueueCallback callback);
+  // An internal queue that holds at most |capacity| pieces of data
+  std::queue<std::unique_ptr<T>> queue_;
+  // A mutex that guards data in this queue
+  std::mutex mutex_;
+
+  class QueueEndpoint {
+   public:
+#ifdef OS_LINUX_GENERIC
+    explicit QueueEndpoint(unsigned int initial_value)
+        : reactive_semaphore_(initial_value), handler_(nullptr), reactable_(nullptr) {}
+    ReactiveSemaphore reactive_semaphore_;
+#endif
+    Handler* handler_;
+    Reactor::Reactable* reactable_;
+  };
+
+  QueueEndpoint enqueue_;
+  QueueEndpoint dequeue_;
+};
+
+template <typename T>
+class EnqueueBuffer {
+ public:
+  EnqueueBuffer(IQueueEnqueue<T>* queue) : queue_(queue) {}
+
+  void Enqueue(std::unique_ptr<T> t, os::Handler* handler) {
+    std::lock_guard<std::mutex> lock(mutex_);
+    buffer_.push(std::move(t));
+    if (buffer_.size() == 1) {
+      queue_->RegisterEnqueue(handler, common::Bind(&EnqueueBuffer<T>::enqueue_callback, common::Unretained(this)));
+    }
+  }
+
+  void Clear() {
+    std::lock_guard<std::mutex> lock(mutex_);
+    if (!buffer_.empty()) {
+      queue_->UnregisterEnqueue();
+      std::queue<std::unique_ptr<T>> empty;
+      std::swap(buffer_, empty);
+    }
+  }
+
+ private:
+  std::unique_ptr<T> enqueue_callback() {
+    std::lock_guard<std::mutex> lock(mutex_);
+    std::unique_ptr<T> enqueued_t = std::move(buffer_.front());
+    buffer_.pop();
+    if (buffer_.empty()) {
+      queue_->UnregisterEnqueue();
+    }
+    return enqueued_t;
+  }
+
+  mutable std::mutex mutex_;
+  IQueueEnqueue<T>* queue_;
+  std::queue<std::unique_ptr<T>> buffer_;
+};
+
+#ifdef OS_LINUX_GENERIC
+#include "os/linux_generic/queue.tpp"
+#endif
+
+}  // namespace os
+}  // namespace bluetooth
diff --git a/gd/os/queue_benchmark.cc b/gd/os/queue_benchmark.cc
new file mode 100644
index 0000000..0c90fe9
--- /dev/null
+++ b/gd/os/queue_benchmark.cc
@@ -0,0 +1,207 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "benchmark/benchmark.h"
+
+#include <future>
+
+#include "os/handler.h"
+#include "os/queue.h"
+#include "os/thread.h"
+
+using ::benchmark::State;
+
+namespace bluetooth {
+namespace os {
+
+class BM_QueuePerformance : public ::benchmark::Fixture {
+ protected:
+  void SetUp(State& st) override {
+    ::benchmark::Fixture::SetUp(st);
+    enqueue_thread_ = new Thread("enqueue_thread", Thread::Priority::NORMAL);
+    enqueue_handler_ = new Handler(enqueue_thread_);
+    dequeue_thread_ = new Thread("dequeue_thread", Thread::Priority::NORMAL);
+    dequeue_handler_ = new Handler(dequeue_thread_);
+  }
+
+  void TearDown(State& st) override {
+    delete enqueue_handler_;
+    delete enqueue_thread_;
+    delete dequeue_handler_;
+    delete dequeue_thread_;
+    enqueue_handler_ = nullptr;
+    enqueue_thread_ = nullptr;
+    dequeue_handler_ = nullptr;
+    dequeue_thread_ = nullptr;
+    benchmark::Fixture::TearDown(st);
+  }
+
+  Thread* enqueue_thread_;
+  Handler* enqueue_handler_;
+  Thread* dequeue_thread_;
+  Handler* dequeue_handler_;
+};
+
+class TestEnqueueEnd {
+ public:
+  explicit TestEnqueueEnd(int64_t count, Queue<std::string>* queue, Handler* handler, std::promise<void>* promise)
+      : count_(count), handler_(handler), queue_(queue), promise_(promise) {}
+
+  void RegisterEnqueue() {
+    handler_->Post(common::BindOnce(&TestEnqueueEnd::handle_register_enqueue, common::Unretained(this)));
+  }
+
+  void push(std::string data) {
+    {
+      std::lock_guard<std::mutex> lock(mutex_);
+      buffer_.push(std::move(data));
+    }
+    if (buffer_.size() == 1) {
+      RegisterEnqueue();
+    }
+  }
+
+  std::unique_ptr<std::string> EnqueueCallbackForTest() {
+    std::lock_guard<std::mutex> lock(mutex_);
+    std::unique_ptr<std::string> data = std::make_unique<std::string>(std::move(buffer_.front()));
+    buffer_.pop();
+
+    if (buffer_.empty()) {
+      queue_->UnregisterEnqueue();
+    }
+
+    count_--;
+    if (count_ == 0) {
+      promise_->set_value();
+    }
+
+    return data;
+  }
+
+  std::queue<std::string> buffer_;
+  int64_t count_;
+
+ private:
+  Handler* handler_;
+  Queue<std::string>* queue_;
+  std::promise<void>* promise_;
+  std::mutex mutex_;
+
+  void handle_register_enqueue() {
+    queue_->RegisterEnqueue(handler_, common::Bind(&TestEnqueueEnd::EnqueueCallbackForTest, common::Unretained(this)));
+  }
+};
+
+class TestDequeueEnd {
+ public:
+  explicit TestDequeueEnd(int64_t count, Queue<std::string>* queue, Handler* handler, std::promise<void>* promise)
+      : count_(count), handler_(handler), queue_(queue), promise_(promise) {}
+
+  void RegisterDequeue() {
+    handler_->Post(common::BindOnce(&TestDequeueEnd::handle_register_dequeue, common::Unretained(this)));
+  }
+
+  void DequeueCallbackForTest() {
+    std::string data = *(queue_->TryDequeue());
+    buffer_.push(data);
+
+    count_--;
+    if (count_ == 0) {
+      queue_->UnregisterDequeue();
+      promise_->set_value();
+    }
+  }
+
+  std::queue<std::string> buffer_;
+  int64_t count_;
+
+ private:
+  Handler* handler_;
+  Queue<std::string>* queue_;
+  std::promise<void>* promise_;
+
+  void handle_register_dequeue() {
+    queue_->RegisterDequeue(handler_, common::Bind(&TestDequeueEnd::DequeueCallbackForTest, common::Unretained(this)));
+  }
+};
+
+BENCHMARK_DEFINE_F(BM_QueuePerformance, send_packet_vary_by_packet_num)(State& state) {
+  for (auto _ : state) {
+    int64_t num_data_to_send_ = state.range(0);
+    Queue<std::string> queue(num_data_to_send_);
+
+    // register dequeue
+    std::promise<void> dequeue_promise;
+    auto dequeue_future = dequeue_promise.get_future();
+    TestDequeueEnd test_dequeue_end(num_data_to_send_, &queue, enqueue_handler_, &dequeue_promise);
+    test_dequeue_end.RegisterDequeue();
+
+    // Push data to enqueue end buffer and register enqueue
+    std::promise<void> enqueue_promise;
+    TestEnqueueEnd test_enqueue_end(num_data_to_send_, &queue, enqueue_handler_, &enqueue_promise);
+    for (int i = 0; i < num_data_to_send_; i++) {
+      std::string data = std::to_string(1);
+      test_enqueue_end.push(std::move(data));
+    }
+    dequeue_future.wait();
+  }
+
+  state.SetBytesProcessed(static_cast<int_fast64_t>(state.iterations()) * state.range(0));
+};
+
+BENCHMARK_REGISTER_F(BM_QueuePerformance, send_packet_vary_by_packet_num)
+    ->Arg(10)
+    ->Arg(100)
+    ->Arg(1000)
+    ->Arg(10000)
+    ->Arg(100000)
+    ->Iterations(100)
+    ->UseRealTime();
+
+BENCHMARK_DEFINE_F(BM_QueuePerformance, send_10000_packet_vary_by_packet_size)(State& state) {
+  for (auto _ : state) {
+    int64_t num_data_to_send_ = 10000;
+    int64_t packet_size = state.range(0);
+    Queue<std::string> queue(num_data_to_send_);
+
+    // register dequeue
+    std::promise<void> dequeue_promise;
+    auto dequeue_future = dequeue_promise.get_future();
+    TestDequeueEnd test_dequeue_end(num_data_to_send_, &queue, enqueue_handler_, &dequeue_promise);
+    test_dequeue_end.RegisterDequeue();
+
+    // Push data to enqueue end buffer and register enqueue
+    std::promise<void> enqueue_promise;
+    TestEnqueueEnd test_enqueue_end(num_data_to_send_, &queue, enqueue_handler_, &enqueue_promise);
+    for (int i = 0; i < num_data_to_send_; i++) {
+      std::string data = std::string(packet_size, 'x');
+      test_enqueue_end.push(std::move(data));
+    }
+    dequeue_future.wait();
+  }
+
+  state.SetBytesProcessed(static_cast<int_fast64_t>(state.iterations()) * state.range(0) * 10000);
+};
+
+BENCHMARK_REGISTER_F(BM_QueuePerformance, send_10000_packet_vary_by_packet_size)
+    ->Arg(10)
+    ->Arg(100)
+    ->Arg(1000)
+    ->Iterations(100)
+    ->UseRealTime();
+
+}  // namespace os
+}  // namespace bluetooth
diff --git a/gd/os/reactor.h b/gd/os/reactor.h
index a0de131..7e8476b 100644
--- a/gd/os/reactor.h
+++ b/gd/os/reactor.h
@@ -19,17 +19,21 @@
 #include <sys/epoll.h>
 #include <atomic>
 #include <functional>
+#include <future>
 #include <list>
 #include <mutex>
 #include <thread>
 
+#include "common/callback.h"
 #include "os/utils.h"
 
 namespace bluetooth {
 namespace os {
 
-// Format of closure to be used in the entire stack
-using Closure = std::function<void()>;
+using common::Callback;
+using common::Closure;
+using common::OnceCallback;
+using common::OnceClosure;
 
 // A simple implementation of reactor-style looper.
 // When a reactor is running, the main loop is polling and blocked until at least one registered reactable is ready to
@@ -62,6 +66,9 @@
   // Unregister a reactable from this reactor
   void Unregister(Reactable* reactable);
 
+  // Wait for up to timeout milliseconds, and return true if the reactable finished executing.
+  bool WaitForUnregisteredReactable(std::chrono::milliseconds timeout);
+
   // Modify the registration for a reactable with given reactable
   void ModifyRegistration(Reactable* reactable, Closure on_read_ready, Closure on_write_ready);
 
@@ -71,7 +78,7 @@
   int control_fd_;
   std::atomic<bool> is_running_;
   std::list<Reactable*> invalidation_list_;
-  bool reactable_removed_;
+  std::unique_ptr<std::future<void>> executing_reactable_finished_;
 };
 
 }  // namespace os
diff --git a/gd/os/repeating_alarm.h b/gd/os/repeating_alarm.h
index 776781a..cd91344 100644
--- a/gd/os/repeating_alarm.h
+++ b/gd/os/repeating_alarm.h
@@ -20,6 +20,8 @@
 #include <memory>
 #include <mutex>
 
+#include "common/callback.h"
+#include "os/handler.h"
 #include "os/thread.h"
 #include "os/utils.h"
 
@@ -31,8 +33,8 @@
 // itself from the thread.
 class RepeatingAlarm {
  public:
-  // Create and register a repeating alarm on given thread
-  explicit RepeatingAlarm(Thread* thread);
+  // Create and register a repeating alarm on a given handler
+  explicit RepeatingAlarm(Handler* handler);
 
   // Unregister this alarm from the thread and release resource
   ~RepeatingAlarm();
@@ -47,7 +49,7 @@
 
  private:
   Closure task_;
-  Thread* thread_;
+  Handler* handler_;
   int fd_ = 0;
   Reactor::Reactable* token_;
   mutable std::mutex mutex_;
diff --git a/gd/os/thread_benchmark.cc b/gd/os/thread_benchmark.cc
index dc22b9d..8d62889 100644
--- a/gd/os/thread_benchmark.cc
+++ b/gd/os/thread_benchmark.cc
@@ -20,10 +20,12 @@
 
 #include "benchmark/benchmark.h"
 
+#include "common/bind.h"
 #include "os/handler.h"
 #include "os/thread.h"
 
 using ::benchmark::State;
+using ::bluetooth::common::BindOnce;
 using ::bluetooth::os::Handler;
 using ::bluetooth::os::Thread;
 
@@ -79,7 +81,8 @@
     counter_promise_ = std::promise<void>();
     std::future<void> counter_future = counter_promise_.get_future();
     for (int i = 0; i < num_messages_to_send_; i++) {
-      handler_->Post([this]() { callback_batch(); });
+      handler_->Post(BindOnce(&BM_ReactorThread_batch_enque_dequeue_Benchmark::callback_batch,
+                              bluetooth::common::Unretained(this)));
     }
     counter_future.wait();
   }
@@ -99,7 +102,8 @@
     for (int i = 0; i < num_messages_to_send_; i++) {
       counter_promise_ = std::promise<void>();
       std::future<void> counter_future = counter_promise_.get_future();
-      handler_->Post([this]() { callback(); });
+      handler_->Post(
+          BindOnce(&BM_ReactorThread_sequential_execution_Benchmark::callback, bluetooth::common::Unretained(this)));
       counter_future.wait();
     }
   }
diff --git a/gd/os/utils.h b/gd/os/utils.h
index 26e8f23..89fca66 100644
--- a/gd/os/utils.h
+++ b/gd/os/utils.h
@@ -15,6 +15,7 @@
  */
 
 #pragma once
+#include <errno.h>
 
 // A macro to re-try a syscall when it receives EINTR
 #ifndef RUN_NO_INTR
diff --git a/gd/packet/Android.bp b/gd/packet/Android.bp
index be5a3c9..c9ddb7a 100644
--- a/gd/packet/Android.bp
+++ b/gd/packet/Android.bp
@@ -2,7 +2,10 @@
     name: "BluetoothPacketSources",
     srcs: [
         "bit_inserter.cc",
+        "byte_inserter.cc",
+        "byte_observer.cc",
         "iterator.cc",
+        "fragmenting_inserter.cc",
         "packet_view.cc",
         "raw_builder.cc",
         "view.cc",
@@ -13,6 +16,7 @@
     name: "BluetoothPacketTestSources",
     srcs: [
         "bit_inserter_unittest.cc",
+        "fragmenting_inserter_unittest.cc",
         "packet_builder_unittest.cc",
         "packet_view_unittest.cc",
         "raw_builder_unittest.cc",
diff --git a/gd/packet/base_struct.h b/gd/packet/base_struct.h
new file mode 100644
index 0000000..9983229
--- /dev/null
+++ b/gd/packet/base_struct.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <forward_list>
+#include <iterator>
+#include <memory>
+#include <vector>
+
+#include "packet/bit_inserter.h"
+
+namespace bluetooth {
+namespace packet {
+
+// A base struct to provide Serialize() and size() to be overridden.
+class BaseStruct {
+ public:
+  virtual ~BaseStruct() = default;
+
+  virtual size_t size() const = 0;
+
+  // Write to the vector with the given iterator.
+  virtual void Serialize(BitInserter& it) const = 0;
+
+ protected:
+  BaseStruct() = default;
+};
+
+}  // namespace packet
+}  // namespace bluetooth
diff --git a/gd/packet/bit_inserter.cc b/gd/packet/bit_inserter.cc
index 5167806..0a8f2bc 100644
--- a/gd/packet/bit_inserter.cc
+++ b/gd/packet/bit_inserter.cc
@@ -14,147 +14,35 @@
  * limitations under the License.
  */
 
-#include "packet/iterator.h"
+#include "packet/bit_inserter.h"
 
 #include "os/log.h"
 
 namespace bluetooth {
 namespace packet {
 
-template <bool little_endian>
-Iterator<little_endian>::Iterator(std::forward_list<View> data, size_t offset) {
-  data_ = data;
-  index_ = offset;
-  length_ = 0;
-  for (auto& view : data) {
-    length_ += view.size();
+BitInserter::BitInserter(std::vector<uint8_t>& vector) : ByteInserter(vector) {}
+
+BitInserter::~BitInserter() {
+  ASSERT(num_saved_bits_ == 0);
+}
+
+void BitInserter::insert_bits(uint8_t byte, size_t num_bits) {
+  size_t total_bits = num_bits + num_saved_bits_;
+  uint16_t new_value = static_cast<uint8_t>(saved_bits_) | (static_cast<uint16_t>(byte) << num_saved_bits_);
+  if (total_bits >= 8) {
+    ByteInserter::insert_byte(static_cast<uint8_t>(new_value));
+    total_bits -= 8;
+    new_value = new_value >> 8;
   }
+  num_saved_bits_ = total_bits;
+  uint8_t mask = static_cast<uint8_t>(0xff) >> (8 - num_saved_bits_);
+  saved_bits_ = static_cast<uint8_t>(new_value) & mask;
 }
 
-template <bool little_endian>
-Iterator<little_endian> Iterator<little_endian>::operator+(int offset) {
-  auto itr(*this);
-
-  return itr += offset;
+void BitInserter::insert_byte(uint8_t byte) {
+  insert_bits(byte, 8);
 }
 
-template <bool little_endian>
-Iterator<little_endian>& Iterator<little_endian>::operator+=(int offset) {
-  index_ += offset;
-  return *this;
-}
-
-template <bool little_endian>
-Iterator<little_endian> Iterator<little_endian>::operator++(int) {
-  auto itr(*this);
-  index_++;
-  return itr;
-}
-
-template <bool little_endian>
-Iterator<little_endian>& Iterator<little_endian>::operator++() {
-  index_++;
-  return *this;
-}
-
-template <bool little_endian>
-Iterator<little_endian> Iterator<little_endian>::operator-(int offset) {
-  auto itr(*this);
-
-  return itr -= offset;
-}
-
-template <bool little_endian>
-int Iterator<little_endian>::operator-(Iterator<little_endian>& itr) {
-  return index_ - itr.index_;
-}
-
-template <bool little_endian>
-Iterator<little_endian>& Iterator<little_endian>::operator-=(int offset) {
-  index_ -= offset;
-
-  return *this;
-}
-
-template <bool little_endian>
-Iterator<little_endian> Iterator<little_endian>::operator--(int) {
-  auto itr(*this);
-  if (index_ != 0) index_--;
-
-  return itr;
-}
-
-template <bool little_endian>
-Iterator<little_endian>& Iterator<little_endian>::operator--() {
-  if (index_ != 0) index_--;
-
-  return *this;
-}
-
-template <bool little_endian>
-Iterator<little_endian>& Iterator<little_endian>::operator=(const Iterator<little_endian>& itr) {
-  data_ = itr.data_;
-  index_ = itr.index_;
-
-  return *this;
-}
-
-template <bool little_endian>
-bool Iterator<little_endian>::operator==(const Iterator<little_endian>& itr) const {
-  return index_ == itr.index_;
-}
-
-template <bool little_endian>
-bool Iterator<little_endian>::operator!=(const Iterator<little_endian>& itr) const {
-  return !(*this == itr);
-}
-
-template <bool little_endian>
-bool Iterator<little_endian>::operator<(const Iterator<little_endian>& itr) const {
-  return index_ < itr.index_;
-}
-
-template <bool little_endian>
-bool Iterator<little_endian>::operator>(const Iterator<little_endian>& itr) const {
-  return index_ > itr.index_;
-}
-
-template <bool little_endian>
-bool Iterator<little_endian>::operator<=(const Iterator<little_endian>& itr) const {
-  return index_ <= itr.index_;
-}
-
-template <bool little_endian>
-bool Iterator<little_endian>::operator>=(const Iterator<little_endian>& itr) const {
-  return index_ >= itr.index_;
-}
-
-template <bool little_endian>
-uint8_t Iterator<little_endian>::operator*() const {
-  ASSERT_LOG(index_ < length_, "Index %zu out of bounds: %zu", index_, length_);
-  size_t index = index_;
-
-  for (auto view : data_) {
-    if (index < view.size()) {
-      return view[index];
-    }
-    index -= view.size();
-  }
-  ASSERT_LOG(false, "Out of fragments searching for index %zu", index_);
-  return 0;
-}
-
-template <bool little_endian>
-size_t Iterator<little_endian>::NumBytesRemaining() const {
-  if (length_ > index_) {
-    return length_ - index_;
-  } else {
-    return 0;
-  }
-}
-
-// Explicit instantiations for both types of Iterators.
-template class Iterator<true>;
-template class Iterator<false>;
 }  // namespace packet
 }  // namespace bluetooth
diff --git a/gd/packet/bit_inserter.h b/gd/packet/bit_inserter.h
index 9034e38..487eba5 100644
--- a/gd/packet/bit_inserter.h
+++ b/gd/packet/bit_inserter.h
@@ -21,41 +21,21 @@
 #include <memory>
 #include <vector>
 
-#include "os/log.h"
+#include "packet/byte_inserter.h"
 
 namespace bluetooth {
 namespace packet {
 
-class BitInserter : public std::back_insert_iterator<std::vector<uint8_t>> {
+class BitInserter : public ByteInserter {
  public:
-  BitInserter(std::vector<uint8_t>& vector) : std::back_insert_iterator<std::vector<uint8_t>>(vector) {}
-  virtual ~BitInserter() {
-    ASSERT(num_saved_bits_ == 0);
-  }
+  BitInserter(std::vector<uint8_t>& vector);
+  ~BitInserter() override;
 
-  void insert_bits(uint8_t byte, size_t num_bits) {
-    size_t total_bits = num_bits + num_saved_bits_;
-    uint16_t new_value = saved_bits_ | (static_cast<uint16_t>(byte) << num_saved_bits_);
-    if (total_bits >= 8) {
-      uint8_t new_byte = static_cast<uint8_t>(new_value);
-      std::back_insert_iterator<std::vector<uint8_t>>::operator=(new_byte);
-      total_bits -= 8;
-      new_value = new_value >> 8;
-    }
-    num_saved_bits_ = total_bits;
-    uint8_t mask = 0xff >> (8 - num_saved_bits_);
-    saved_bits_ = static_cast<uint8_t>(new_value) & mask;
-  }
+  virtual void insert_bits(uint8_t byte, size_t num_bits);
 
-  void insert_byte(uint8_t byte) {
-    insert_bits(byte, 8);
-  }
+  void insert_byte(uint8_t byte) override;
 
-  bool IsByteAligned() {
-    return num_saved_bits_ == 0;
-  }
-
- private:
+ protected:
   size_t num_saved_bits_{0};
   uint8_t saved_bits_{0};
 };
diff --git a/gd/packet/bit_inserter_unittest.cc b/gd/packet/bit_inserter_unittest.cc
index 6a3b668..fae19f8 100644
--- a/gd/packet/bit_inserter_unittest.cc
+++ b/gd/packet/bit_inserter_unittest.cc
@@ -27,12 +27,6 @@
 namespace bluetooth {
 namespace packet {
 
-class BitInserterTest : public ::testing::Test {
- public:
-  BitInserterTest() {}
-  ~BitInserterTest() = default;
-};
-
 TEST(BitInserterTest, addMoreBits) {
   std::vector<uint8_t> bytes;
   BitInserter it(bytes);
@@ -41,12 +35,8 @@
     it.insert_bits(static_cast<uint8_t>(i), i);
   }
   it.insert_bits(static_cast<uint8_t>(0b1010), 4);
-  std::vector<uint8_t> result = {
-      0b00011101 /* 3 2 1 */,
-      0b00010101 /* 5 4 */,
-      0b11100011 /* 7 6 */,
-      0b10000000 /* 8 */,
-      0b10100000 /* filled with 1010 */};
+  std::vector<uint8_t> result = {0b00011101 /* 3 2 1 */, 0b00010101 /* 5 4 */, 0b11100011 /* 7 6 */, 0b10000000 /* 8 */,
+                                 0b10100000 /* filled with 1010 */};
 
   ASSERT_EQ(result.size(), bytes.size());
   for (size_t i = 0; i < bytes.size(); i++) {
@@ -54,5 +44,39 @@
   }
 }
 
+TEST(BitInserterTest, observerTest) {
+  std::vector<uint8_t> bytes;
+  BitInserter it(bytes);
+  std::vector<uint8_t> copy;
+
+  uint64_t checksum = 0x0123456789abcdef;
+  it.RegisterObserver(ByteObserver([&copy](uint8_t byte) { copy.push_back(byte); }, [checksum]() { return checksum; }));
+
+  for (size_t i = 0; i < 9; i++) {
+    it.insert_bits(static_cast<uint8_t>(i), i);
+  }
+  it.insert_bits(static_cast<uint8_t>(0b1010), 4);
+  std::vector<uint8_t> result = {0b00011101 /* 3 2 1 */, 0b00010101 /* 5 4 */, 0b11100011 /* 7 6 */, 0b10000000 /* 8 */,
+                                 0b10100000 /* filled with 1010 */};
+
+  ASSERT_EQ(result.size(), bytes.size());
+  for (size_t i = 0; i < bytes.size(); i++) {
+    ASSERT_EQ(result[i], bytes[i]);
+  }
+
+  ASSERT_EQ(result.size(), copy.size());
+  for (size_t i = 0; i < copy.size(); i++) {
+    ASSERT_EQ(result[i], copy[i]);
+  }
+
+  ByteObserver observer = it.UnregisterObserver();
+  ASSERT_EQ(checksum, observer.GetValue());
+  uint8_t another_byte = 0xef;
+  it.insert_bits(another_byte, 8);
+  ASSERT_EQ(bytes.back(), another_byte);
+  ASSERT_EQ(result.size() + 1, bytes.size());
+  ASSERT_EQ(result.size(), copy.size());
+}
+
 }  // namespace packet
 }  // namespace bluetooth
diff --git a/gd/packet/byte_inserter.cc b/gd/packet/byte_inserter.cc
new file mode 100644
index 0000000..b872427
--- /dev/null
+++ b/gd/packet/byte_inserter.cc
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "packet/byte_inserter.h"
+
+#include "os/log.h"
+
+namespace bluetooth {
+namespace packet {
+
+ByteInserter::ByteInserter(std::vector<uint8_t>& vector) : std::back_insert_iterator<std::vector<uint8_t>>(vector) {}
+
+ByteInserter::~ByteInserter() {
+  ASSERT(registered_observers_.empty());
+}
+
+void ByteInserter::RegisterObserver(const ByteObserver& observer) {
+  registered_observers_.push_back(observer);
+}
+
+ByteObserver ByteInserter::UnregisterObserver() {
+  ByteObserver observer = registered_observers_.back();
+  registered_observers_.pop_back();
+  return observer;
+}
+
+void ByteInserter::on_byte(uint8_t byte) {
+  for (auto& observer : registered_observers_) {
+    observer.OnByte(byte);
+  }
+}
+
+void ByteInserter::insert_byte(uint8_t byte) {
+  on_byte(byte);
+  std::back_insert_iterator<std::vector<uint8_t>>::operator=(byte);
+}
+
+}  // namespace packet
+}  // namespace bluetooth
diff --git a/gd/packet/byte_inserter.h b/gd/packet/byte_inserter.h
new file mode 100644
index 0000000..6d33aee
--- /dev/null
+++ b/gd/packet/byte_inserter.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <iterator>
+#include <memory>
+#include <vector>
+
+#include "packet/byte_observer.h"
+
+namespace bluetooth {
+namespace packet {
+
+class ByteInserter : public std::back_insert_iterator<std::vector<uint8_t>> {
+ public:
+  ByteInserter(std::vector<uint8_t>& vector);
+  virtual ~ByteInserter();
+
+  virtual void insert_byte(uint8_t byte);
+
+  void RegisterObserver(const ByteObserver& observer);
+
+  ByteObserver UnregisterObserver();
+
+ protected:
+  void on_byte(uint8_t);
+
+ private:
+  std::vector<ByteObserver> registered_observers_;
+};
+
+}  // namespace packet
+}  // namespace bluetooth
diff --git a/gd/packet/byte_observer.cc b/gd/packet/byte_observer.cc
new file mode 100644
index 0000000..358a230
--- /dev/null
+++ b/gd/packet/byte_observer.cc
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "packet/byte_observer.h"
+
+namespace bluetooth {
+namespace packet {
+
+ByteObserver::ByteObserver(const std::function<void(uint8_t)>& on_byte, const std::function<uint64_t()>& get_value)
+    : on_byte_(on_byte), get_value_(get_value) {}
+
+void ByteObserver::OnByte(uint8_t byte) {
+  on_byte_(byte);
+}
+
+uint64_t ByteObserver::GetValue() {
+  return get_value_();
+}
+
+}  // namespace packet
+}  // namespace bluetooth
diff --git a/gd/packet/byte_observer.h b/gd/packet/byte_observer.h
new file mode 100644
index 0000000..adfcfc1
--- /dev/null
+++ b/gd/packet/byte_observer.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <functional>
+
+namespace bluetooth {
+namespace packet {
+
+class ByteObserver {
+ public:
+  ByteObserver(const std::function<void(uint8_t)>& on_byte_, const std::function<uint64_t()>& get_value_);
+
+  void OnByte(uint8_t byte);
+
+  uint64_t GetValue();
+
+ private:
+  std::function<void(uint8_t)> on_byte_;
+  std::function<uint64_t()> get_value_;
+};
+
+}  // namespace packet
+}  // namespace bluetooth
diff --git a/gd/packet/endian_inserter.h b/gd/packet/endian_inserter.h
new file mode 100644
index 0000000..317c12c
--- /dev/null
+++ b/gd/packet/endian_inserter.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <forward_list>
+#include <iterator>
+#include <memory>
+#include <vector>
+
+#include "os/log.h"
+#include "packet/bit_inserter.h"
+
+namespace bluetooth {
+namespace packet {
+
+// Abstract base class that is subclassed to provide insert() functions.
+// The template parameter little_endian controls the generation of insert().
+template <bool little_endian>
+class EndianInserter {
+ public:
+  EndianInserter() = default;
+  virtual ~EndianInserter() = default;
+
+ protected:
+  // Write sizeof(FixedWidthIntegerType) bytes using the iterator
+  template <typename FixedWidthPODType, typename std::enable_if<std::is_pod<FixedWidthPODType>::value, int>::type = 0>
+  void insert(FixedWidthPODType value, BitInserter& it) const {
+    uint8_t* raw_bytes = (uint8_t*)&value;
+    for (size_t i = 0; i < sizeof(FixedWidthPODType); i++) {
+      if (little_endian == true) {
+        it.insert_byte(raw_bytes[i]);
+      } else {
+        it.insert_byte(raw_bytes[sizeof(FixedWidthPODType) - i - 1]);
+      }
+    }
+  }
+
+  // Write num_bits bits using the iterator
+  template <typename FixedWidthIntegerType,
+            typename std::enable_if<std::is_pod<FixedWidthIntegerType>::value, int>::type = 0>
+  void insert(FixedWidthIntegerType value, BitInserter& it, size_t num_bits) const {
+    ASSERT(num_bits <= (sizeof(FixedWidthIntegerType) * 8));
+
+    for (size_t i = 0; i < num_bits / 8; i++) {
+      if (little_endian == true) {
+        it.insert_byte(static_cast<uint8_t>(value >> (i * 8)));
+      } else {
+        it.insert_byte(static_cast<uint8_t>(value >> (((num_bits / 8) - i - 1) * 8)));
+      }
+    }
+    if (num_bits % 8) {
+      it.insert_bits(static_cast<uint8_t>(value >> ((num_bits / 8) * 8)), num_bits % 8);
+    }
+  }
+
+  // Specialized insert that allows inserting enums without casting
+  template <typename Enum, typename std::enable_if<std::is_enum_v<Enum>, int>::type = 0>
+  inline void insert(Enum value, BitInserter& it) const {
+    using enum_type = typename std::underlying_type_t<Enum>;
+    static_assert(std::is_unsigned_v<enum_type>, "Enum type is signed. Did you forget to specify the enum size?");
+    insert<enum_type>(static_cast<enum_type>(value), it);
+  }
+
+  // Write a vector of FixedWidthIntegerType using the iterator
+  template <typename FixedWidthIntegerType>
+  void insert_vector(const std::vector<FixedWidthIntegerType>& vec, BitInserter& it) const {
+    static_assert(std::is_pod<FixedWidthIntegerType>::value,
+                  "EndianInserter::insert requires a vector with elements of a fixed-size.");
+    for (const auto& element : vec) {
+      insert(element, it);
+    }
+  }
+};
+
+}  // namespace packet
+}  // namespace bluetooth
diff --git a/gd/packet/fragmenting_inserter.cc b/gd/packet/fragmenting_inserter.cc
new file mode 100644
index 0000000..90f87c6
--- /dev/null
+++ b/gd/packet/fragmenting_inserter.cc
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "packet/fragmenting_inserter.h"
+
+#include "os/log.h"
+
+namespace bluetooth {
+namespace packet {
+
+FragmentingInserter::FragmentingInserter(size_t mtu,
+                                         std::back_insert_iterator<std::vector<std::unique_ptr<RawBuilder>>> iterator)
+    : BitInserter(to_construct_bit_inserter_), mtu_(mtu), curr_packet_(std::make_unique<RawBuilder>(mtu)),
+      iterator_(iterator) {}
+
+void FragmentingInserter::insert_bits(uint8_t byte, size_t num_bits) {
+  ASSERT(curr_packet_ != nullptr);
+  size_t total_bits = num_bits + num_saved_bits_;
+  uint16_t new_value = static_cast<uint8_t>(saved_bits_) | (static_cast<uint16_t>(byte) << num_saved_bits_);
+  if (total_bits >= 8) {
+    uint8_t new_byte = static_cast<uint8_t>(new_value);
+    on_byte(new_byte);
+    curr_packet_->AddOctets1(new_byte);
+    if (curr_packet_->size() >= mtu_) {
+      iterator_ = std::move(curr_packet_);
+      curr_packet_ = std::make_unique<RawBuilder>(mtu_);
+    }
+    total_bits -= 8;
+    new_value = new_value >> 8;
+  }
+  num_saved_bits_ = total_bits;
+  uint8_t mask = static_cast<uint8_t>(0xff) >> (8 - num_saved_bits_);
+  saved_bits_ = static_cast<uint8_t>(new_value) & mask;
+}
+
+void FragmentingInserter::finalize() {
+  if (curr_packet_->size() != 0) {
+    iterator_ = std::move(curr_packet_);
+  }
+  curr_packet_.reset();
+}
+
+}  // namespace packet
+}  // namespace bluetooth
diff --git a/gd/packet/fragmenting_inserter.h b/gd/packet/fragmenting_inserter.h
new file mode 100644
index 0000000..ff23a09
--- /dev/null
+++ b/gd/packet/fragmenting_inserter.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <iterator>
+#include <memory>
+#include <vector>
+
+#include "packet/bit_inserter.h"
+#include "packet/raw_builder.h"
+
+namespace bluetooth {
+namespace packet {
+
+class FragmentingInserter : public BitInserter {
+ public:
+  FragmentingInserter(size_t mtu, std::back_insert_iterator<std::vector<std::unique_ptr<RawBuilder>>> iterator);
+
+  void insert_bits(uint8_t byte, size_t num_bits) override;
+
+  void finalize();
+
+ protected:
+  std::vector<uint8_t> to_construct_bit_inserter_;
+  size_t mtu_;
+  std::unique_ptr<RawBuilder> curr_packet_;
+  std::back_insert_iterator<std::vector<std::unique_ptr<RawBuilder>>> iterator_;
+};
+
+}  // namespace packet
+}  // namespace bluetooth
diff --git a/gd/packet/fragmenting_inserter_unittest.cc b/gd/packet/fragmenting_inserter_unittest.cc
new file mode 100644
index 0000000..c2f1124
--- /dev/null
+++ b/gd/packet/fragmenting_inserter_unittest.cc
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "packet/fragmenting_inserter.h"
+
+#include <gtest/gtest.h>
+#include <memory>
+
+#include "os/log.h"
+
+using bluetooth::packet::FragmentingInserter;
+using std::vector;
+
+namespace bluetooth {
+namespace packet {
+
+TEST(FragmentingInserterTest, addMoreBits) {
+  std::vector<uint8_t> result = {0b00011101 /* 3 2 1 */, 0b00010101 /* 5 4 */, 0b11100011 /* 7 6 */, 0b10000000 /* 8 */,
+                                 0b10100000 /* filled with 1010 */};
+  std::vector<std::unique_ptr<RawBuilder>> fragments;
+
+  FragmentingInserter it(result.size(), std::back_insert_iterator(fragments));
+
+  for (size_t i = 0; i < 9; i++) {
+    it.insert_bits(static_cast<uint8_t>(i), i);
+  }
+  it.insert_bits(static_cast<uint8_t>(0b1010), 4);
+
+  it.finalize();
+
+  ASSERT_EQ(1, fragments.size());
+
+  std::vector<uint8_t> bytes;
+  BitInserter bit_inserter(bytes);
+  fragments[0]->Serialize(bit_inserter);
+
+  ASSERT_EQ(result.size(), bytes.size());
+  for (size_t i = 0; i < bytes.size(); i++) {
+    ASSERT_EQ(result[i], bytes[i]);
+  }
+}
+
+TEST(FragmentingInserterTest, observerTest) {
+  std::vector<uint8_t> result = {0b00011101 /* 3 2 1 */, 0b00010101 /* 5 4 */, 0b11100011 /* 7 6 */, 0b10000000 /* 8 */,
+                                 0b10100000 /* filled with 1010 */};
+  std::vector<std::unique_ptr<RawBuilder>> fragments;
+
+  FragmentingInserter it(result.size() + 1, std::back_insert_iterator(fragments));
+
+  std::vector<uint8_t> copy;
+
+  uint64_t checksum = 0x0123456789abcdef;
+  it.RegisterObserver(ByteObserver([&copy](uint8_t byte) { copy.push_back(byte); }, [checksum]() { return checksum; }));
+
+  for (size_t i = 0; i < 9; i++) {
+    it.insert_bits(static_cast<uint8_t>(i), i);
+  }
+  it.insert_bits(static_cast<uint8_t>(0b1010), 4);
+  it.finalize();
+
+  ASSERT_EQ(1, fragments.size());
+
+  std::vector<uint8_t> bytes;
+  BitInserter bit_inserter(bytes);
+  fragments[0]->Serialize(bit_inserter);
+
+  ASSERT_EQ(result.size(), bytes.size());
+  for (size_t i = 0; i < bytes.size(); i++) {
+    ASSERT_EQ(result[i], bytes[i]);
+  }
+
+  ASSERT_EQ(result.size(), copy.size());
+  for (size_t i = 0; i < copy.size(); i++) {
+    ASSERT_EQ(result[i], copy[i]);
+  }
+
+  ByteObserver observer = it.UnregisterObserver();
+  ASSERT_EQ(checksum, observer.GetValue());
+}
+
+TEST(FragmentingInserterTest, testMtuBoundaries) {
+  constexpr size_t kPacketSize = 1024;
+  auto counts = RawBuilder();
+  for (size_t i = 0; i < kPacketSize; i++) {
+    counts.AddOctets1(static_cast<uint8_t>(i));
+  }
+
+  std::vector<std::unique_ptr<RawBuilder>> fragments_mtu_is_kPacketSize;
+  FragmentingInserter it(kPacketSize, std::back_insert_iterator(fragments_mtu_is_kPacketSize));
+  counts.Serialize(it);
+  it.finalize();
+  ASSERT_EQ(1, fragments_mtu_is_kPacketSize.size());
+  ASSERT_EQ(kPacketSize, fragments_mtu_is_kPacketSize[0]->size());
+
+  std::vector<std::unique_ptr<RawBuilder>> fragments_mtu_is_less;
+  FragmentingInserter it_less(kPacketSize - 1, std::back_insert_iterator(fragments_mtu_is_less));
+  counts.Serialize(it_less);
+  it_less.finalize();
+  ASSERT_EQ(2, fragments_mtu_is_less.size());
+  ASSERT_EQ(kPacketSize - 1, fragments_mtu_is_less[0]->size());
+  ASSERT_EQ(1, fragments_mtu_is_less[1]->size());
+
+  std::vector<std::unique_ptr<RawBuilder>> fragments_mtu_is_more;
+  FragmentingInserter it_more(kPacketSize + 1, std::back_insert_iterator(fragments_mtu_is_more));
+  counts.Serialize(it_more);
+  it_more.finalize();
+  ASSERT_EQ(1, fragments_mtu_is_more.size());
+  ASSERT_EQ(kPacketSize, fragments_mtu_is_more[0]->size());
+}
+
+constexpr size_t kPacketSize = 128;
+class FragmentingTest : public ::testing::TestWithParam<size_t> {
+ public:
+  void StartUp() {
+    counts_.reserve(kPacketSize);
+    for (size_t i = 0; i < kPacketSize; i++) {
+      counts_.push_back(static_cast<uint8_t>(i));
+    }
+  }
+  void ShutDown() {
+    counts_.clear();
+  }
+  std::vector<uint8_t> counts_;
+};
+
+TEST_P(FragmentingTest, mtuFragmentTest) {
+  size_t mtu = GetParam();
+  std::vector<std::unique_ptr<RawBuilder>> fragments;
+  FragmentingInserter it(mtu, std::back_insert_iterator(fragments));
+
+  RawBuilder original_packet(counts_);
+  ASSERT_EQ(counts_.size(), original_packet.size());
+
+  original_packet.Serialize(it);
+  it.finalize();
+
+  size_t expected_fragments = counts_.size() / mtu;
+  if (counts_.size() % mtu != 0) {
+    expected_fragments++;
+  }
+  ASSERT_EQ(expected_fragments, fragments.size());
+
+  std::vector<std::vector<uint8_t>> serialized_fragments;
+  for (size_t f = 0; f < fragments.size(); f++) {
+    serialized_fragments.emplace_back(mtu);
+    BitInserter bit_inserter(serialized_fragments[f]);
+    fragments[f]->Serialize(bit_inserter);
+    if (f + 1 == fragments.size() && (counts_.size() % mtu != 0)) {
+      ASSERT_EQ(serialized_fragments[f].size(), counts_.size() % mtu);
+    } else {
+      ASSERT_EQ(serialized_fragments[f].size(), mtu);
+    }
+    for (size_t b = 0; b < serialized_fragments[f].size(); b++) {
+      EXPECT_EQ(counts_[f * mtu + b], serialized_fragments[f][b]);
+    }
+  }
+}
+
+INSTANTIATE_TEST_CASE_P(chopomatic, FragmentingTest, ::testing::Range<size_t>(1, kPacketSize + 1));
+
+}  // namespace packet
+}  // namespace bluetooth
diff --git a/gd/packet/iterator.cc b/gd/packet/iterator.cc
index 5167806..f924b05 100644
--- a/gd/packet/iterator.cc
+++ b/gd/packet/iterator.cc
@@ -25,9 +25,10 @@
 Iterator<little_endian>::Iterator(std::forward_list<View> data, size_t offset) {
   data_ = data;
   index_ = offset;
-  length_ = 0;
+  begin_ = 0;
+  end_ = 0;
   for (auto& view : data) {
-    length_ += view.size();
+    end_ += view.size();
   }
 }
 
@@ -93,9 +94,10 @@
 
 template <bool little_endian>
 Iterator<little_endian>& Iterator<little_endian>::operator=(const Iterator<little_endian>& itr) {
-  data_ = itr.data_;
-  index_ = itr.index_;
-
+  this->data_ = itr.data_;
+  this->begin_ = itr.begin_;
+  this->end_ = itr.end_;
+  this->index_ = itr.index_;
   return *this;
 }
 
@@ -131,7 +133,7 @@
 
 template <bool little_endian>
 uint8_t Iterator<little_endian>::operator*() const {
-  ASSERT_LOG(index_ < length_, "Index %zu out of bounds: %zu", index_, length_);
+  ASSERT_LOG(index_ < end_ && !(begin_ > index_), "Index %zu out of bounds: [%zu,%zu)", index_, begin_, end_);
   size_t index = index_;
 
   for (auto view : data_) {
@@ -146,13 +148,29 @@
 
 template <bool little_endian>
 size_t Iterator<little_endian>::NumBytesRemaining() const {
-  if (length_ > index_) {
-    return length_ - index_;
+  if (end_ > index_ && !(begin_ > index_)) {
+    return end_ - index_;
   } else {
     return 0;
   }
 }
 
+template <bool little_endian>
+Iterator<little_endian> Iterator<little_endian>::Subrange(size_t index, size_t length) const {
+  Iterator<little_endian> to_return(*this);
+  if (to_return.NumBytesRemaining() > index) {
+    to_return.index_ = to_return.index_ + index;
+    to_return.begin_ = to_return.index_;
+    if (to_return.NumBytesRemaining() >= length) {
+      to_return.end_ = to_return.index_ + length;
+    }
+  } else {
+    to_return.end_ = 0;
+  }
+
+  return to_return;
+}
+
 // Explicit instantiations for both types of Iterators.
 template class Iterator<true>;
 template class Iterator<false>;
diff --git a/gd/packet/iterator.h b/gd/packet/iterator.h
index 56d8708..8d927a0 100644
--- a/gd/packet/iterator.h
+++ b/gd/packet/iterator.h
@@ -18,6 +18,7 @@
 
 #include <cstdint>
 #include <forward_list>
+#include <memory>
 
 #include "packet/view.h"
 
@@ -60,10 +61,12 @@
 
   size_t NumBytesRemaining() const;
 
+  Iterator Subrange(size_t index, size_t length) const;
+
   // Get the next sizeof(FixedWidthPODType) bytes and return the filled type
   template <typename FixedWidthPODType>
   FixedWidthPODType extract() {
-    static_assert(std::is_pod<FixedWidthPODType>::value, "Iterator::extract requires an fixed type.");
+    static_assert(std::is_pod<FixedWidthPODType>::value, "Iterator::extract requires a fixed-width type.");
     FixedWidthPODType extracted_value;
     uint8_t* value_ptr = (uint8_t*)&extracted_value;
 
@@ -77,7 +80,8 @@
  private:
   std::forward_list<View> data_;
   size_t index_;
-  size_t length_;
+  size_t begin_;
+  size_t end_;
 };
 
 }  // namespace packet
diff --git a/gd/packet/packet_builder.h b/gd/packet/packet_builder.h
index 2a27cc4..3f6c627 100644
--- a/gd/packet/packet_builder.h
+++ b/gd/packet/packet_builder.h
@@ -22,11 +22,10 @@
 #include <memory>
 #include <vector>
 
-#include "common/address.h"
-#include "common/class_of_device.h"
 #include "os/log.h"
 #include "packet/base_packet_builder.h"
 #include "packet/bit_inserter.h"
+#include "packet/endian_inserter.h"
 
 namespace bluetooth {
 namespace packet {
@@ -34,75 +33,13 @@
 // Abstract base class that is subclassed to build specifc packets.
 // The template parameter little_endian controls the generation of insert().
 template <bool little_endian>
-class PacketBuilder : public BasePacketBuilder {
+class PacketBuilder : public BasePacketBuilder, protected EndianInserter<little_endian> {
  public:
   PacketBuilder() = default;
   virtual ~PacketBuilder() = default;
 
   // Classes which need fragmentation should define a function like this:
   // std::forward_list<DerivedBuilder>& Fragment(size_t max_size);
-
- protected:
-  // Write sizeof(FixedWidthIntegerType) bytes using the iterator
-  template <typename FixedWidthIntegerType,
-            typename std::enable_if<std::is_integral<FixedWidthIntegerType>::value, int>::type = 0>
-  void insert(FixedWidthIntegerType value, BitInserter& it) const {
-    for (size_t i = 0; i < sizeof(FixedWidthIntegerType); i++) {
-      if (little_endian == true) {
-        it.insert_byte(static_cast<uint8_t>(value >> (i * 8)));
-      } else {
-        it.insert_byte(static_cast<uint8_t>(value >> ((sizeof(FixedWidthIntegerType) - i - 1) * 8)));
-      }
-    }
-  }
-
-  // Write num_bits bits using the iterator
-  template <typename FixedWidthIntegerType,
-            typename std::enable_if<std::is_integral<FixedWidthIntegerType>::value, int>::type = 0>
-  void insert(FixedWidthIntegerType value, BitInserter& it, size_t num_bits) const {
-    ASSERT(num_bits <= (sizeof(FixedWidthIntegerType) * 8));
-
-    for (size_t i = 0; i < num_bits / 8; i++) {
-      if (little_endian == true) {
-        it.insert_byte(static_cast<uint8_t>(value >> (i * 8)));
-      } else {
-        it.insert_byte(static_cast<uint8_t>(value >> (((num_bits / 8) - i - 1) * 8)));
-      }
-    }
-    if (num_bits % 8) {
-      it.insert_bits(static_cast<uint8_t>(value >> ((num_bits / 8) * 8)), num_bits % 8);
-    }
-  }
-
-  // Specialized insert that allows inserting enums without casting
-  template <typename Enum, typename std::enable_if<std::is_enum_v<Enum>, int>::type = 0>
-  inline void insert(Enum value, BitInserter& it) const {
-    using enum_type = typename std::underlying_type_t<Enum>;
-    static_assert(std::is_unsigned_v<enum_type>, "Enum type is signed. Did you forget to specify the enum size?");
-    insert<enum_type>(static_cast<enum_type>(value), it);
-  }
-
-  // Write a vector of FixedWidthIntegerType using the iterator
-  template <typename FixedWidthIntegerType>
-  void insert_vector(const std::vector<FixedWidthIntegerType>& vec, BitInserter& it) const {
-    static_assert(std::is_integral<FixedWidthIntegerType>::value,
-                  "PacketBuilder::insert requires an integral type vector.");
-    for (const auto& element : vec) {
-      insert(element, it);
-    }
-  }
-
-  void insert_address(const common::Address& addr, BitInserter& it) const {
-    for (const auto& element : addr.address) {
-      insert(element, it);
-    }
-  }
-
-  void insert_class_of_device(const common::ClassOfDevice& cod, BitInserter& it) const {
-    for (const auto& element : cod.cod) {
-      insert(element, it);
-    }
-  }
 };
 
 }  // namespace packet
diff --git a/gd/packet/packet_builder_unittest.cc b/gd/packet/packet_builder_unittest.cc
index 74a27e6..46647a3 100644
--- a/gd/packet/packet_builder_unittest.cc
+++ b/gd/packet/packet_builder_unittest.cc
@@ -99,9 +99,9 @@
 template <typename T>
 class VectorBuilder : public PacketBuilder<true> {
  public:
-  VectorBuilder(std::vector<uint64_t> vect) {
+  VectorBuilder(const std::vector<uint64_t> vect) {
     for (uint64_t element : vect) {
-      vect.push_back(static_cast<T>(element));
+      vect_.push_back(static_cast<T>(element));
     }
   }
   ~VectorBuilder() = default;
@@ -129,9 +129,9 @@
 template <typename T>
 class InsertElementsBuilder : public PacketBuilder<true> {
  public:
-  InsertElementsBuilder(std::vector<uint64_t> vect) {
+  InsertElementsBuilder(const std::vector<uint64_t> vect) {
     for (uint64_t element : vect) {
-      vect.push_back(static_cast<T>(element));
+      vect_.push_back(static_cast<T>(element));
     }
   }
   virtual ~InsertElementsBuilder() = default;
diff --git a/gd/packet/packet_struct.h b/gd/packet/packet_struct.h
new file mode 100644
index 0000000..19bb0e5
--- /dev/null
+++ b/gd/packet/packet_struct.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <forward_list>
+#include <iterator>
+#include <memory>
+#include <vector>
+
+#include "os/log.h"
+#include "packet/base_struct.h"
+#include "packet/bit_inserter.h"
+#include "packet/endian_inserter.h"
+
+namespace bluetooth {
+namespace packet {
+
+// Abstract base class that is subclassed to build specifc structs.
+// The template parameter little_endian controls the generation of insert().
+template <bool little_endian>
+class PacketStruct : public BaseStruct, protected EndianInserter<little_endian> {
+ public:
+  PacketStruct() = default;
+  virtual ~PacketStruct() = default;
+};
+
+}  // namespace packet
+}  // namespace bluetooth
diff --git a/gd/packet/packet_view.cc b/gd/packet/packet_view.cc
index 94522ae..badd641 100644
--- a/gd/packet/packet_view.cc
+++ b/gd/packet/packet_view.cc
@@ -99,6 +99,24 @@
   return PacketView<false>(GetSubviewList(begin, end));
 }
 
+template <bool little_endian>
+void PacketView<little_endian>::Append(PacketView to_add) {
+  auto insertion_point = fragments_.begin();
+  size_t remaining_length = length_;
+  while (remaining_length > 0) {
+    remaining_length -= insertion_point->size();
+    if (remaining_length > 0) {
+      insertion_point++;
+    }
+  }
+  ASSERT(insertion_point != fragments_.end());
+  for (const auto& fragment : to_add.fragments_) {
+    fragments_.insert_after(insertion_point, fragment);
+    insertion_point++;
+  }
+  length_ += to_add.length_;
+}
+
 // Explicit instantiations for both types of PacketViews.
 template class PacketView<true>;
 template class PacketView<false>;
diff --git a/gd/packet/packet_view.h b/gd/packet/packet_view.h
index 4b2f191..304794c 100644
--- a/gd/packet/packet_view.h
+++ b/gd/packet/packet_view.h
@@ -52,6 +52,9 @@
 
   PacketView<false> GetBigEndianSubview(size_t begin, size_t end) const;
 
+ protected:
+  void Append(PacketView to_add);
+
  private:
   std::forward_list<View> fragments_;
   size_t length_;
diff --git a/gd/packet/packet_view_unittest.cc b/gd/packet/packet_view_unittest.cc
index e88c2cf..0ec2ef4 100644
--- a/gd/packet/packet_view_unittest.cc
+++ b/gd/packet/packet_view_unittest.cc
@@ -20,9 +20,9 @@
 #include <forward_list>
 #include <memory>
 
-#include "common/address.h"
+#include "hci/address.h"
 
-using bluetooth::common::Address;
+using bluetooth::hci::Address;
 using bluetooth::packet::PacketView;
 using bluetooth::packet::View;
 using std::vector;
@@ -91,6 +91,36 @@
  public:
   PacketViewMultiViewTest() = default;
   ~PacketViewMultiViewTest() = default;
+
+  const PacketView<true> single_view =
+      PacketView<true>({View(std::make_shared<const vector<uint8_t>>(count_all), 0, count_all.size())});
+  const PacketView<true> multi_view = PacketView<true>({
+      View(std::make_shared<const vector<uint8_t>>(count_1), 0, count_1.size()),
+      View(std::make_shared<const vector<uint8_t>>(count_2), 0, count_2.size()),
+      View(std::make_shared<const vector<uint8_t>>(count_3), 0, count_3.size()),
+  });
+};
+
+class PacketViewMultiViewAppendTest : public ::testing::Test {
+ public:
+  PacketViewMultiViewAppendTest() = default;
+  ~PacketViewMultiViewAppendTest() = default;
+
+  class AppendedPacketView : public PacketView<true> {
+   public:
+    AppendedPacketView(PacketView<true> first, std::forward_list<PacketView<true>> to_append)
+        : PacketView<true>(first) {
+      for (const auto& packet_view : to_append) {
+        Append(packet_view);
+      }
+    }
+  };
+  const PacketView<true> single_view =
+      PacketView<true>({View(std::make_shared<const vector<uint8_t>>(count_all), 0, count_all.size())});
+  const PacketView<true> multi_view = AppendedPacketView(
+      PacketView<true>({View(std::make_shared<const vector<uint8_t>>(count_1), 0, count_1.size())}),
+      {PacketView<true>({View(std::make_shared<const vector<uint8_t>>(count_2), 0, count_2.size())}),
+       PacketView<true>({View(std::make_shared<const vector<uint8_t>>(count_3), 0, count_3.size())})});
 };
 
 class ViewTest : public ::testing::Test {
@@ -273,7 +303,7 @@
   ASSERT_EQ(0x1f, (*(this->packet))[working_index]);
 }
 
-TYPED_TEST(PacketViewTest, numBytesRemainingTest) {
+TYPED_TEST(IteratorTest, numBytesRemainingTest) {
   auto all = this->packet->begin();
   size_t remaining = all.NumBytesRemaining();
   for (size_t n = remaining; n > 0; n--) {
@@ -288,6 +318,81 @@
   ASSERT_DEATH(*(all++), "");
 }
 
+TYPED_TEST(IteratorTest, subrangeTest) {
+  auto empty = this->packet->begin().Subrange(0, 0);
+  ASSERT_EQ(static_cast<size_t>(0), empty.NumBytesRemaining());
+  ASSERT_DEATH(*empty, "");
+
+  empty = this->packet->begin().Subrange(this->packet->size(), 1);
+  ASSERT_EQ(static_cast<size_t>(0), empty.NumBytesRemaining());
+  ASSERT_DEATH(*empty, "");
+
+  auto all = this->packet->begin();
+  auto fullrange = all.Subrange(0, all.NumBytesRemaining());
+  ASSERT_EQ(all.NumBytesRemaining(), fullrange.NumBytesRemaining());
+  ASSERT_EQ(*(all + 1), 1);
+
+  fullrange = all.Subrange(0, all.NumBytesRemaining() + 1);
+  ASSERT_EQ(all.NumBytesRemaining(), fullrange.NumBytesRemaining());
+  ASSERT_EQ(*(all + 1), 1);
+
+  fullrange = all.Subrange(0, all.NumBytesRemaining() + 10);
+  ASSERT_EQ(all.NumBytesRemaining(), fullrange.NumBytesRemaining());
+  ASSERT_EQ(*(all + 1), 1);
+
+  auto subrange = all.Subrange(0, 1);
+  ASSERT_EQ(1, subrange.NumBytesRemaining());
+  ASSERT_EQ(*(subrange), 0);
+
+  subrange = this->packet->begin().Subrange(0, 4);
+  ASSERT_EQ(4, subrange.NumBytesRemaining());
+  ASSERT_EQ(*(subrange + 1), 1);
+
+  subrange = all.Subrange(0, 3);
+  ASSERT_EQ(3, subrange.NumBytesRemaining());
+  ASSERT_EQ(*(subrange + 1), 1);
+
+  subrange = all.Subrange(0, all.NumBytesRemaining() - 1);
+  ASSERT_EQ(all.NumBytesRemaining() - 1, subrange.NumBytesRemaining());
+  ASSERT_EQ(*(subrange + 1), 1);
+
+  subrange = all.Subrange(0, all.NumBytesRemaining() - 2);
+  ASSERT_EQ(all.NumBytesRemaining() - 2, subrange.NumBytesRemaining());
+  ASSERT_EQ(*(subrange + 1), 1);
+
+  subrange = all.Subrange(1, all.NumBytesRemaining());
+  ASSERT_EQ(all.NumBytesRemaining() - 1, subrange.NumBytesRemaining());
+  ASSERT_EQ(*subrange, 1);
+
+  subrange = all.Subrange(2, all.NumBytesRemaining());
+  ASSERT_EQ(all.NumBytesRemaining() - 2, subrange.NumBytesRemaining());
+  ASSERT_EQ(*subrange, 2);
+
+  subrange = all.Subrange(1, all.NumBytesRemaining() - 1);
+  ASSERT_EQ(all.NumBytesRemaining() - 1, subrange.NumBytesRemaining());
+  ASSERT_EQ(*subrange, 1);
+
+  subrange = all.Subrange(2, all.NumBytesRemaining() - 2);
+  ASSERT_EQ(all.NumBytesRemaining() - 2, subrange.NumBytesRemaining());
+  ASSERT_EQ(*subrange, 2);
+
+  subrange = all.Subrange(1, 1);
+  ASSERT_EQ(1, subrange.NumBytesRemaining());
+  ASSERT_EQ(*(subrange), 1);
+
+  subrange = all.Subrange(1, 2);
+  ASSERT_EQ(2, subrange.NumBytesRemaining());
+  ASSERT_EQ(*(subrange), 1);
+
+  subrange = all.Subrange(2, 1);
+  ASSERT_EQ(1, subrange.NumBytesRemaining());
+  ASSERT_EQ(*(subrange), 2);
+
+  subrange = this->packet->begin().Subrange(this->packet->size() - 1, 2);
+  ASSERT_EQ(static_cast<size_t>(1), subrange.NumBytesRemaining());
+  ASSERT_EQ(*(subrange), this->packet->size() - 1);
+}
+
 using SubviewTestParam = std::pair<size_t, size_t>;
 class SubviewBaseTest : public ::testing::TestWithParam<SubviewTestParam> {
  public:
@@ -408,23 +513,11 @@
   }
 }
 
-TEST(PacketViewMultiViewTest, sizeTest) {
-  PacketView<true> single_view({View(std::make_shared<const vector<uint8_t>>(count_all), 0, count_all.size())});
-  PacketView<true> multi_view({
-      View(std::make_shared<const vector<uint8_t>>(count_1), 0, count_1.size()),
-      View(std::make_shared<const vector<uint8_t>>(count_2), 0, count_2.size()),
-      View(std::make_shared<const vector<uint8_t>>(count_3), 0, count_3.size()),
-  });
+TEST_F(PacketViewMultiViewTest, sizeTest) {
   ASSERT_EQ(single_view.size(), multi_view.size());
 }
 
-TEST(PacketViewMultiViewTest, dereferenceTestLittleEndian) {
-  PacketView<true> single_view({View(std::make_shared<const vector<uint8_t>>(count_all), 0, count_all.size())});
-  PacketView<true> multi_view({
-      View(std::make_shared<const vector<uint8_t>>(count_1), 0, count_1.size()),
-      View(std::make_shared<const vector<uint8_t>>(count_2), 0, count_2.size()),
-      View(std::make_shared<const vector<uint8_t>>(count_3), 0, count_3.size()),
-  });
+TEST_F(PacketViewMultiViewTest, dereferenceTestLittleEndian) {
   auto single_itr = single_view.begin();
   auto multi_itr = multi_view.begin();
   for (size_t i = 0; i < single_view.size(); i++) {
@@ -433,13 +526,7 @@
   ASSERT_DEATH(*multi_itr, "");
 }
 
-TEST(PacketViewMultiViewTest, dereferenceTestBigEndian) {
-  PacketView<false> single_view({View(std::make_shared<const vector<uint8_t>>(count_all), 0, count_all.size())});
-  PacketView<false> multi_view({
-      View(std::make_shared<const vector<uint8_t>>(count_1), 0, count_1.size()),
-      View(std::make_shared<const vector<uint8_t>>(count_2), 0, count_2.size()),
-      View(std::make_shared<const vector<uint8_t>>(count_3), 0, count_3.size()),
-  });
+TEST_F(PacketViewMultiViewTest, dereferenceTestBigEndian) {
   auto single_itr = single_view.begin();
   auto multi_itr = multi_view.begin();
   for (size_t i = 0; i < single_view.size(); i++) {
@@ -448,13 +535,36 @@
   ASSERT_DEATH(*multi_itr, "");
 }
 
-TEST(PacketViewMultiViewTest, arrayOperatorTest) {
-  PacketView<true> single_view({View(std::make_shared<const vector<uint8_t>>(count_all), 0, count_all.size())});
-  PacketView<true> multi_view({
-      View(std::make_shared<const vector<uint8_t>>(count_1), 0, count_1.size()),
-      View(std::make_shared<const vector<uint8_t>>(count_2), 0, count_2.size()),
-      View(std::make_shared<const vector<uint8_t>>(count_3), 0, count_3.size()),
-  });
+TEST_F(PacketViewMultiViewTest, arrayOperatorTest) {
+  for (size_t i = 0; i < single_view.size(); i++) {
+    ASSERT_EQ(single_view[i], multi_view[i]);
+  }
+  ASSERT_DEATH(multi_view[single_view.size()], "");
+}
+
+TEST_F(PacketViewMultiViewAppendTest, sizeTestAppend) {
+  ASSERT_EQ(single_view.size(), multi_view.size());
+}
+
+TEST_F(PacketViewMultiViewAppendTest, dereferenceTestLittleEndianAppend) {
+  auto single_itr = single_view.begin();
+  auto multi_itr = multi_view.begin();
+  for (size_t i = 0; i < single_view.size(); i++) {
+    ASSERT_EQ(*(single_itr++), *(multi_itr++));
+  }
+  ASSERT_DEATH(*multi_itr, "");
+}
+
+TEST_F(PacketViewMultiViewAppendTest, dereferenceTestBigEndianAppend) {
+  auto single_itr = single_view.begin();
+  auto multi_itr = multi_view.begin();
+  for (size_t i = 0; i < single_view.size(); i++) {
+    ASSERT_EQ(*(single_itr++), *(multi_itr++));
+  }
+  ASSERT_DEATH(*multi_itr, "");
+}
+
+TEST_F(PacketViewMultiViewAppendTest, arrayOperatorTestAppend) {
   for (size_t i = 0; i < single_view.size(); i++) {
     ASSERT_EQ(single_view[i], multi_view[i]);
   }
diff --git a/gd/packet/parser/Android.bp b/gd/packet/parser/Android.bp
new file mode 100644
index 0000000..97f323a
--- /dev/null
+++ b/gd/packet/parser/Android.bp
@@ -0,0 +1,52 @@
+cc_binary_host {
+    name: "bluetooth_packetgen",
+    srcs: [
+        "fields/array_field.cc",
+        "fields/vector_field.cc",
+        "fields/body_field.cc",
+        "fields/checksum_field.cc",
+        "fields/checksum_start_field.cc",
+        "fields/count_field.cc",
+        "fields/custom_field.cc",
+        "fields/custom_field_fixed_size.cc",
+        "fields/enum_field.cc",
+        "fields/fixed_enum_field.cc",
+        "fields/fixed_field.cc",
+        "fields/fixed_scalar_field.cc",
+        "fields/group_field.cc",
+        "fields/packet_field.cc",
+        "fields/padding_field.cc",
+        "fields/payload_field.cc",
+        "fields/reserved_field.cc",
+        "fields/scalar_field.cc",
+        "fields/size_field.cc",
+        "fields/struct_field.cc",
+        "fields/variable_length_struct_field.cc",
+        "checksum_def.cc",
+        "custom_field_def.cc",
+        "enum_def.cc",
+        "enum_gen.cc",
+        "packet_def.cc",
+        "parent_def.cc",
+        "struct_def.cc",
+        "struct_parser_generator.cc",
+        "main.cc",
+        "language_y.yy",
+        "language_l.ll",
+    ],
+    static_libs: [
+        "libc++fs",
+    ],
+    cppflags: [
+        "-fno-exceptions",
+        "-O0",
+    ],
+    ldflags: [
+        "-fuse-ld=ld",
+        "-O0",
+    ],
+    yacc: {
+        gen_location_hh: true,
+        gen_position_hh: true,
+    },
+}
diff --git a/gd/packet/parser/README b/gd/packet/parser/README
new file mode 100644
index 0000000..9006c94
--- /dev/null
+++ b/gd/packet/parser/README
@@ -0,0 +1,59 @@
+This file just contains some notes about the design and usage of the PDL language.
+
+-------
+ TERMS
+-------
+.pdl
+  The file type that defines packet definitions. You may think of each pdl file
+  as its own translation unit.
+
+Packet Views and Builders
+  Generated from a packet definition. Views are used to validate packets and
+  extract the fields that are defined in the pdl file.  Builders check the input
+  arguments and can be serialized.
+
+Checksum types
+  checksum MyChecksumClass : 16 "path/to/the/class/"
+  Checksum fields need to implement the following three static methods:
+    static void Initialize(MyChecksumClass&);
+    static void AddByte(MyChecksumClass&, uint8_t);
+    // Assuming a 16-bit (uint16_t) checksum:
+    static uint16_t GetChecksum(MyChecksumClass&);
+-------------
+ LIMITATIONS
+-------------
+  - Size fields for a variable length field MUST come before the definition
+    of said field.
+
+  - Payload fields must be byte-aligned unless they have an unknown size.
+    Body fields are allowed to not be byte aligned.
+
+  - No conditionals
+
+  - Can not have to fields with the same name anywhere in the in an inheritence chain
+
+  - Can't handle size for Body type fields yet since they might not be byte aligned.
+
+  - Currently no arrays of custom types (might change later)
+
+-------
+ NOTES
+-------
+All field names should be in snake_case.  Types should be in CamelCase.
+
+The _payload_ keyword generates a getter but _body_ doesn't. Therefore, a
+_payload_ must be byte aligned.
+
+Supports constraints on grandparents
+Supports multiple constraints
+Every field handles its own generation.
+One pdl file will result in one header file with all the packets
+
+Things to cover -
+  Constraints
+  Inheritence vs Contains
+
+Custom fields need the folowing functions:
+  static void Serialize(const Type&, MutableView&);
+  static std::optional<size_t> Size(Iterator);
+  static Type Parse(Iterator);
diff --git a/gd/packet/parser/checksum_def.cc b/gd/packet/parser/checksum_def.cc
new file mode 100644
index 0000000..8cb1803
--- /dev/null
+++ b/gd/packet/parser/checksum_def.cc
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "checksum_def.h"
+#include "checksum_type_checker.h"
+#include "fields/checksum_field.h"
+#include "util.h"
+
+ChecksumDef::ChecksumDef(std::string name, std::string include, int size) : CustomFieldDef(name, include, size) {}
+
+PacketField* ChecksumDef::GetNewField(const std::string& name, ParseLocation loc) const {
+  return new ChecksumField(name, name_, size_, loc);
+}
+
+TypeDef::Type ChecksumDef::GetDefinitionType() const {
+  return TypeDef::Type::CHECKSUM;
+}
+
+void ChecksumDef::GenChecksumCheck(std::ostream& s) const {
+  s << "static_assert(ChecksumTypeChecker<" << name_ << "," << util::GetTypeForSize(size_) << ">::value, \"";
+  s << name_ << " is not a valid checksum type. Please see README for more details.\");";
+}
diff --git a/gd/packet/parser/checksum_def.h b/gd/packet/parser/checksum_def.h
new file mode 100644
index 0000000..f82b8c3
--- /dev/null
+++ b/gd/packet/parser/checksum_def.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <iostream>
+
+#include "custom_field_def.h"
+#include "fields/packet_field.h"
+#include "parse_location.h"
+#include "type_def.h"
+
+class ChecksumDef : public CustomFieldDef {
+ public:
+  ChecksumDef(std::string name, std::string include, int size);
+
+  virtual PacketField* GetNewField(const std::string& name, ParseLocation loc) const override;
+
+  virtual TypeDef::Type GetDefinitionType() const override;
+
+  void GenChecksumCheck(std::ostream& s) const;
+
+  const std::string include_;
+};
diff --git a/gd/packet/parser/checksum_type_checker.h b/gd/packet/parser/checksum_type_checker.h
new file mode 100644
index 0000000..3e384b1
--- /dev/null
+++ b/gd/packet/parser/checksum_type_checker.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <optional>
+
+namespace bluetooth {
+namespace packet {
+namespace parser {
+
+// Checks for Initialize(), AddByte(), and GetChecksum().
+// T and TRET are the checksum class Type and the checksum return type
+// C and CRET are the substituted types for T and TRET
+template <typename T, typename TRET>
+class ChecksumTypeChecker {
+ public:
+  template <class C, void (C::*)()>
+  struct InitializeChecker {};
+
+  template <class C, void (C::*)(uint8_t byte)>
+  struct AddByteChecker {};
+
+  template <class C, typename CRET, CRET (C::*)() const>
+  struct GetChecksumChecker {};
+
+  // If all the methods are defined, this one matches
+  template <class C, typename CRET>
+  static int Test(InitializeChecker<C, &C::Initialize>*, AddByteChecker<C, &C::AddByte>*,
+                  GetChecksumChecker<C, CRET, &C::GetChecksum>*);
+
+  // This one matches everything else
+  template <class C, typename CRET>
+  static char Test(...);
+
+  // This checks which template was matched
+  static constexpr bool value = (sizeof(Test<T, TRET>(0, 0, 0)) == sizeof(int));
+};
+}  // namespace parser
+}  // namespace packet
+}  // namespace bluetooth
diff --git a/gd/packet/parser/custom_field_def.cc b/gd/packet/parser/custom_field_def.cc
new file mode 100644
index 0000000..fb027c2
--- /dev/null
+++ b/gd/packet/parser/custom_field_def.cc
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "custom_field_def.h"
+
+#include "util.h"
+
+CustomFieldDef::CustomFieldDef(std::string name, std::string include) : TypeDef(name), include_(include) {}
+
+CustomFieldDef::CustomFieldDef(std::string name, std::string include, int size)
+    : TypeDef(name, size), include_(include) {
+  if (size % 8 != 0) {
+    ERROR() << "Custom fields must be byte aligned.";
+  }
+}
+
+PacketField* CustomFieldDef::GetNewField(const std::string& name, ParseLocation loc) const {
+  if (size_ == -1) {
+    return new CustomField(name, name_, loc);
+  } else {
+    return new CustomFieldFixedSize(name, name_, size_, loc);
+  }
+}
+
+TypeDef::Type CustomFieldDef::GetDefinitionType() const {
+  return TypeDef::Type::CUSTOM;
+}
+
+void CustomFieldDef::GenInclude(std::ostream& s) const {
+  s << "#include \"" << include_ << util::CamelCaseToUnderScore(GetTypeName()) << ".h\"\n";
+}
+
+void CustomFieldDef::GenPyBind11Include(std::ostream& s) const {
+  s << "#include \"" << include_ << util::CamelCaseToUnderScore(GetTypeName()) << "_pybind11_type_caster.h\"\n";
+}
+
+void CustomFieldDef::GenUsing(std::ostream& s) const {
+  s << "using ::bluetooth::";
+  for (const auto& c : include_) {
+    switch (c) {
+      case '/':
+        s << "::";
+        break;
+      default:
+        s << c;
+    }
+  }
+  s << GetTypeName() << ";";
+}
+
+void CustomFieldDef::GenCustomFieldCheck(std::ostream& s, bool little_endian) const {
+  s << "static_assert(CustomTypeChecker<" << name_ << ", ";
+  s << (little_endian ? "" : "!") << "kLittleEndian>::value, \"";
+  s << name_ << " is not a valid custom field type. Please see README for more details.\");";
+}
diff --git a/gd/packet/parser/custom_field_def.h b/gd/packet/parser/custom_field_def.h
new file mode 100644
index 0000000..f28b116
--- /dev/null
+++ b/gd/packet/parser/custom_field_def.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <iostream>
+
+#include "fields/custom_field.h"
+#include "fields/custom_field_fixed_size.h"
+#include "parse_location.h"
+#include "type_def.h"
+
+class CustomFieldDef : public TypeDef {
+ public:
+  CustomFieldDef(std::string name, std::string include);
+
+  CustomFieldDef(std::string name, std::string include, int size);
+
+  virtual PacketField* GetNewField(const std::string& name, ParseLocation loc) const override;
+
+  virtual Type GetDefinitionType() const override;
+
+  void GenInclude(std::ostream& s) const;
+
+  void GenPyBind11Include(std::ostream& s) const;
+
+  void GenUsing(std::ostream& s) const;
+
+  void GenCustomFieldCheck(std::ostream& s, bool little_endian) const;
+
+  const std::string include_;
+};
diff --git a/gd/packet/parser/custom_type_checker.h b/gd/packet/parser/custom_type_checker.h
new file mode 100644
index 0000000..5d99622
--- /dev/null
+++ b/gd/packet/parser/custom_type_checker.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <array>
+
+#include "packet/bit_inserter.h"
+#include "packet/iterator.h"
+
+namespace bluetooth {
+namespace packet {
+// Checks a custom type has all the necessary functions with the correct signatures.
+template <typename T, bool packet_little_endian>
+class CustomTypeChecker {
+ public:
+  template <class C, void (C::*)(BitInserter&) const>
+  struct SerializeChecker {};
+
+  template <class C, size_t (C::*)() const>
+  struct SizeChecker {};
+
+  template <class C, bool little_endian, std::optional<Iterator<little_endian>> (*)(C* vec, Iterator<little_endian> it)>
+  struct ParseChecker {};
+
+  template <class C, bool little_endian>
+  static int Test(SerializeChecker<C, &C::Serialize>*, SizeChecker<C, &C::size>*,
+                  ParseChecker<C, little_endian, &C::Parse>*);
+
+  template <class C, bool little_endian>
+  static char Test(...);
+
+  static constexpr bool value = (sizeof(Test<T, packet_little_endian>(0, 0, 0)) == sizeof(int));
+};
+}  // namespace packet
+}  // namespace bluetooth
diff --git a/gd/packet/parser/declarations.h b/gd/packet/parser/declarations.h
new file mode 100644
index 0000000..549ddc4
--- /dev/null
+++ b/gd/packet/parser/declarations.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <deque>
+#include <map>
+#include <optional>
+
+#include "checksum_def.h"
+#include "custom_field_def.h"
+#include "enum_def.h"
+#include "enum_gen.h"
+#include "packet_def.h"
+#include "struct_def.h"
+
+class Declarations {
+ public:
+  void AddTypeDef(std::string name, TypeDef* def) {
+    auto it = type_defs_.find(name);
+    if (it != type_defs_.end()) {
+      ERROR() << "Redefinition of Type " << name;
+    }
+    type_defs_.insert(std::pair(name, def));
+    type_defs_queue_.push_back(std::pair(name, def));
+  }
+
+  TypeDef* GetTypeDef(const std::string& name) {
+    auto it = type_defs_.find(name);
+    if (it == type_defs_.end()) {
+      return nullptr;
+    }
+
+    return it->second;
+  }
+
+  void AddPacketDef(std::string name, PacketDef def) {
+    auto it = packet_defs_.find(name);
+    if (it != packet_defs_.end()) {
+      ERROR() << "Redefinition of Packet " << name;
+    }
+    packet_defs_.insert(std::pair(name, def));
+    packet_defs_queue_.push_back(std::pair(name, def));
+  }
+
+  PacketDef* GetPacketDef(const std::string& name) {
+    auto it = packet_defs_.find(name);
+    if (it == packet_defs_.end()) {
+      return nullptr;
+    }
+
+    return &(it->second);
+  }
+
+  void AddGroupDef(std::string name, FieldList* group_def) {
+    auto it = group_defs_.find(name);
+    if (it != group_defs_.end()) {
+      ERROR() << "Redefinition of group " << name;
+    }
+    group_defs_.insert(std::pair(name, group_def));
+  }
+
+  FieldList* GetGroupDef(std::string name) {
+    if (group_defs_.find(name) == group_defs_.end()) {
+      return nullptr;
+    }
+
+    return group_defs_.at(name);
+  }
+
+  std::map<std::string, FieldList*> group_defs_;
+
+  std::map<std::string, TypeDef*> type_defs_;
+  std::deque<std::pair<std::string, TypeDef*>> type_defs_queue_;
+  std::map<std::string, PacketDef> packet_defs_;
+  std::deque<std::pair<std::string, PacketDef>> packet_defs_queue_;
+  bool is_little_endian;
+};
diff --git a/gd/packet/parser/enum_def.cc b/gd/packet/parser/enum_def.cc
new file mode 100644
index 0000000..8b90426
--- /dev/null
+++ b/gd/packet/parser/enum_def.cc
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "enum_def.h"
+
+#include <iostream>
+#include <map>
+
+#include "fields/enum_field.h"
+#include "util.h"
+
+EnumDef::EnumDef(std::string name, int size) : TypeDef(name, size) {}
+
+void EnumDef::AddEntry(std::string name, uint32_t value) {
+  if (!util::IsEnumCase(name)) {
+    ERROR() << __func__ << ": Enum " << name << "(" << value << ") should be all uppercase with underscores";
+  }
+  if (value > util::GetMaxValueForBits(size_)) {
+    ERROR() << __func__ << ": Value of " << name << "(" << value << ") is greater than the max possible value for enum "
+            << name_ << "(" << util::GetMaxValueForBits(size_) << ")\n";
+  }
+
+  constants_.insert(std::pair(value, name));
+  entries_.insert(name);
+}
+
+PacketField* EnumDef::GetNewField(const std::string& name, ParseLocation loc) const {
+  return new EnumField(name, *this, "What is this for", loc);
+}
+
+bool EnumDef::HasEntry(std::string name) const {
+  return entries_.count(name) != 0;
+}
+
+TypeDef::Type EnumDef::GetDefinitionType() const {
+  return TypeDef::Type::ENUM;
+}
diff --git a/gd/packet/parser/enum_def.h b/gd/packet/parser/enum_def.h
new file mode 100644
index 0000000..5698d0f
--- /dev/null
+++ b/gd/packet/parser/enum_def.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <map>
+#include <set>
+#include <string>
+
+#include "fields/packet_field.h"
+#include "type_def.h"
+
+// Holds the definition of an enum.
+class EnumDef : public TypeDef {
+ public:
+  EnumDef(std::string name, int size);
+
+  virtual PacketField* GetNewField(const std::string& name, ParseLocation loc) const;
+
+  void AddEntry(std::string name, uint32_t value);
+
+  bool HasEntry(std::string name) const;
+
+  virtual Type GetDefinitionType() const override;
+
+  // data
+  std::map<uint32_t, std::string> constants_;
+  std::set<std::string> entries_;
+};
diff --git a/gd/packet/parser/enum_gen.cc b/gd/packet/parser/enum_gen.cc
new file mode 100644
index 0000000..4bd5a26
--- /dev/null
+++ b/gd/packet/parser/enum_gen.cc
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "enum_gen.h"
+
+#include <iostream>
+
+#include "util.h"
+
+EnumGen::EnumGen(EnumDef e) : e_(std::move(e)) {}
+
+void EnumGen::GenDefinition(std::ostream& stream) {
+  stream << "enum class ";
+  stream << e_.name_;
+  stream << " : " << util::GetTypeForSize(e_.size_);
+  stream << " {";
+  for (const auto& pair : e_.constants_) {
+    stream << pair.second << " = 0x" << std::hex << pair.first << std::dec << ",";
+  }
+  stream << "};\n";
+}
+
+void EnumGen::GenDefinitionPybind11(std::ostream& stream) {
+  stream << "py::enum_<" << e_.name_ << ">(m, \"" << e_.name_ << "\")";
+  for (const auto& pair : e_.constants_) {
+    stream << ".value(\"" << pair.second << "\", " << e_.name_ << "::" << pair.second << ")";
+  }
+  stream << ";\n";
+}
+
+void EnumGen::GenLogging(std::ostream& stream) {
+  // Print out the switch statement that converts all the constants to strings.
+  stream << "inline std::string " << e_.name_ << "Text(const " << e_.name_ << "& param) {";
+  stream << "switch (param) {";
+  for (const auto& pair : e_.constants_) {
+    stream << "case " << e_.name_ << "::" << pair.second << ":";
+    stream << "  return \"" << pair.second << "\";";
+  }
+  stream << "default:";
+  stream << "  return std::string(\"Unknown " << e_.name_ << ": \") + std::to_string(static_cast<int>(param));";
+  stream << "}";
+  stream << "}\n\n";
+
+  // Print out the stream operator so that the constant can be written to streams.
+  stream << "inline std::ostream& operator<<(std::ostream& os, const " << e_.name_ << "& param) {";
+  stream << "  return os << " << e_.name_ << "Text(param);";
+  stream << "}\n";
+}
diff --git a/gd/packet/parser/enum_gen.h b/gd/packet/parser/enum_gen.h
new file mode 100644
index 0000000..be16559
--- /dev/null
+++ b/gd/packet/parser/enum_gen.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <iostream>
+
+#include "enum_def.h"
+
+// Generates the C++ code for an enum.
+class EnumGen {
+ public:
+  EnumGen(EnumDef e);
+
+  void GenDefinition(std::ostream& stream);
+
+  void GenDefinitionPybind11(std::ostream& stream);
+
+  void GenLogging(std::ostream& stream);
+
+  EnumDef e_;
+};
diff --git a/gd/packet/parser/field_list.h b/gd/packet/parser/field_list.h
new file mode 100644
index 0000000..81fc0d7
--- /dev/null
+++ b/gd/packet/parser/field_list.h
@@ -0,0 +1,210 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <map>
+#include <set>
+#include <vector>
+
+#include "fields/all_fields.h"
+#include "fields/packet_field.h"
+
+using FieldListIterator = std::vector<PacketField*>::const_iterator;
+using ReverseFieldListIterator = std::vector<PacketField*>::const_reverse_iterator;
+
+class FieldList {
+ public:
+  FieldList() = default;
+
+  FieldList(std::vector<PacketField*> fields) {
+    for (PacketField* field : fields) {
+      AppendField(field);
+    }
+  }
+
+  template <class Iterator>
+  FieldList(Iterator begin, Iterator end) {
+    while (begin != end) {
+      AppendField(*begin);
+      begin++;
+    }
+  }
+
+  PacketField* operator[](int index) const {
+    return field_list_[index];
+  }
+
+  PacketField* GetField(std::string field_name) const {
+    auto it = field_map_.find(field_name);
+    if (it == field_map_.end()) {
+      return nullptr;
+    }
+
+    return it->second;
+  }
+
+  void AppendField(PacketField* field) {
+    AddField(field);
+    field_list_.push_back(field);
+  }
+
+  void PrependField(PacketField* field) {
+    AddField(field);
+    field_list_.insert(field_list_.begin(), field);
+  }
+
+  FieldList GetFieldsBeforePayloadOrBody() const {
+    FieldList ret;
+    for (auto it = begin(); it != end(); it++) {
+      const auto& field = *it;
+      if (field->GetFieldType() == PayloadField::kFieldType || field->GetFieldType() == BodyField::kFieldType) {
+        break;
+      }
+      ret.AppendField(*it);
+    }
+
+    return ret;
+  }
+
+  FieldList GetFieldsAfterPayloadOrBody() const {
+    FieldListIterator it;
+    for (it = begin(); it != end(); it++) {
+      const auto& field = *it;
+      if (field->GetFieldType() == PayloadField::kFieldType || field->GetFieldType() == BodyField::kFieldType) {
+        // Increment it once to get first field after payload/body.
+        it++;
+        break;
+      }
+    }
+
+    return FieldList(it, end());
+  }
+
+  FieldList GetFieldsWithTypes(std::set<std::string> field_types) const {
+    FieldList ret;
+
+    for (const auto& field : field_list_) {
+      if (field_types.find(field->GetFieldType()) != field_types.end()) {
+        ret.AppendField(field);
+      }
+    }
+
+    return ret;
+  }
+
+  FieldList GetFieldsWithoutTypes(std::set<std::string> field_types) const {
+    FieldList ret;
+
+    for (const auto& field : field_list_) {
+      if (field_types.find(field->GetFieldType()) == field_types.end()) {
+        ret.AppendField(field);
+      }
+    }
+
+    return ret;
+  }
+
+  // Appends header fields of param to header fields of the current and
+  // prepends footer fields of the param to footer fields of the current.
+  // Ex. Assuming field_list_X has the layout:
+  //   field_list_X_header
+  //   payload/body
+  //   field_list_X_footer
+  // The call to field_list_1.Merge(field_list_2) would result in
+  //   field_list_1_header
+  //   field_list_2_header
+  //   payload/body (uses whatever was in field_list_2)
+  //   field_list_2_footer
+  //   field_list_1_footer
+  FieldList Merge(FieldList nested) const {
+    FieldList ret;
+
+    for (const auto& field : GetFieldsBeforePayloadOrBody()) {
+      ret.AppendField(field);
+    }
+
+    for (const auto& field : nested) {
+      ret.AppendField(field);
+    }
+
+    for (const auto& field : GetFieldsAfterPayloadOrBody()) {
+      ret.AppendField(field);
+    }
+
+    return ret;
+  }
+
+  bool HasPayloadOrBody() const {
+    return has_payload_ || has_body_;
+  }
+
+  bool HasPayload() const {
+    return has_payload_;
+  }
+
+  bool HasBody() const {
+    return has_body_;
+  }
+
+  FieldListIterator begin() const {
+    return field_list_.begin();
+  }
+
+  FieldListIterator end() const {
+    return field_list_.end();
+  }
+
+  ReverseFieldListIterator rbegin() const {
+    return field_list_.rbegin();
+  }
+
+  ReverseFieldListIterator rend() const {
+    return field_list_.rend();
+  }
+
+  size_t size() const {
+    return field_list_.size();
+  }
+
+ private:
+  void AddField(PacketField* field) {
+    if (field_map_.find(field->GetName()) != field_map_.end()) {
+      ERROR(field) << "Field with name \"" << field->GetName() << "\" was previously defined.\n";
+    }
+
+    if (field->GetFieldType() == PayloadField::kFieldType) {
+      if (has_body_) {
+        ERROR(field) << "Packet already has a body.";
+      }
+      has_payload_ = true;
+    }
+
+    if (field->GetFieldType() == BodyField::kFieldType) {
+      if (has_payload_) {
+        ERROR(field) << "Packet already has a payload.";
+      }
+      has_body_ = true;
+    }
+
+    field_map_.insert(std::pair(field->GetName(), field));
+  }
+
+  std::vector<PacketField*> field_list_;
+  std::map<std::string, PacketField*> field_map_;
+  bool has_payload_ = false;
+  bool has_body_ = false;
+};
diff --git a/gd/packet/parser/fields/all_fields.h b/gd/packet/parser/fields/all_fields.h
new file mode 100644
index 0000000..8c0aeee
--- /dev/null
+++ b/gd/packet/parser/fields/all_fields.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "fields/array_field.h"
+#include "fields/body_field.h"
+#include "fields/checksum_field.h"
+#include "fields/checksum_start_field.h"
+#include "fields/count_field.h"
+#include "fields/custom_field.h"
+#include "fields/custom_field_fixed_size.h"
+#include "fields/enum_field.h"
+#include "fields/fixed_enum_field.h"
+#include "fields/fixed_scalar_field.h"
+#include "fields/group_field.h"
+#include "fields/padding_field.h"
+#include "fields/payload_field.h"
+#include "fields/reserved_field.h"
+#include "fields/scalar_field.h"
+#include "fields/size_field.h"
+#include "fields/struct_field.h"
+#include "fields/variable_length_struct_field.h"
+#include "fields/vector_field.h"
diff --git a/gd/packet/parser/fields/array_field.cc b/gd/packet/parser/fields/array_field.cc
new file mode 100644
index 0000000..37dea16
--- /dev/null
+++ b/gd/packet/parser/fields/array_field.cc
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "fields/array_field.h"
+
+#include "fields/custom_field.h"
+#include "fields/scalar_field.h"
+#include "util.h"
+
+const std::string ArrayField::kFieldType = "ArrayField";
+
+ArrayField::ArrayField(std::string name, int element_size, int array_size, ParseLocation loc)
+    : PacketField(name, loc), element_field_(new ScalarField("val", element_size, loc)), element_size_(element_size),
+      array_size_(array_size) {
+  if (element_size > 64 || element_size < 0)
+    ERROR(this) << __func__ << ": Not implemented for element size = " << element_size;
+  if (element_size % 8 != 0) {
+    ERROR(this) << "Can only have arrays with elements that are byte aligned (" << element_size << ")";
+  }
+}
+
+ArrayField::ArrayField(std::string name, TypeDef* type_def, int array_size, ParseLocation loc)
+    : PacketField(name, loc), element_field_(type_def->GetNewField("val", loc)),
+      element_size_(element_field_->GetSize()), array_size_(array_size) {
+  if (!element_size_.empty() && element_size_.bits() % 8 != 0) {
+    ERROR(this) << "Can only have arrays with elements that are byte aligned (" << element_size_ << ")";
+  }
+}
+
+const std::string& ArrayField::GetFieldType() const {
+  return ArrayField::kFieldType;
+}
+
+Size ArrayField::GetSize() const {
+  if (!element_size_.empty() && !element_size_.has_dynamic()) {
+    return Size(array_size_ * element_size_.bits());
+  }
+  return Size();
+}
+
+Size ArrayField::GetBuilderSize() const {
+  if (!element_size_.empty() && !element_size_.has_dynamic()) {
+    return GetSize();
+  } else if (element_field_->BuilderParameterMustBeMoved()) {
+    std::string ret = "[this](){ size_t length = 0; for (const auto& elem : " + GetName() +
+                      "_) { length += elem->size() * 8; } return length; }()";
+    return ret;
+  } else {
+    std::string ret = "[this](){ size_t length = 0; for (const auto& elem : " + GetName() +
+                      "_) { length += elem.size() * 8; } return length; }()";
+    return ret;
+  }
+}
+
+Size ArrayField::GetStructSize() const {
+  if (!element_size_.empty() && !element_size_.has_dynamic()) {
+    return GetSize();
+  } else if (element_field_->BuilderParameterMustBeMoved()) {
+    std::string ret = "[this](){ size_t length = 0; for (const auto& elem : to_fill->" + GetName() +
+                      "_) { length += elem->size() * 8; } return length; }()";
+    return ret;
+  } else {
+    std::string ret = "[this](){ size_t length = 0; for (const auto& elem : to_fill->" + GetName() +
+                      "_) { length += elem.size() * 8; } return length; }()";
+    return ret;
+  }
+}
+
+std::string ArrayField::GetDataType() const {
+  return "std::array<" + element_field_->GetDataType() + "," + std::to_string(array_size_) + ">";
+}
+
+void ArrayField::GenExtractor(std::ostream& s, int num_leading_bits, bool for_struct) const {
+  s << GetDataType() << "::iterator ret_it = " << GetName() << "_ptr->begin();";
+  s << "auto " << element_field_->GetName() << "_it = " << GetName() << "_it;";
+  if (!element_size_.empty()) {
+    s << "while (" << element_field_->GetName() << "_it.NumBytesRemaining() >= " << element_size_.bytes();
+    s << " && ret_it < " << GetName() << "_ptr->end()) {";
+  } else {
+    s << "while (" << element_field_->GetName() << "_it.NumBytesRemaining() > 0 ";
+    s << " && ret_it < " << GetName() << "_ptr->end()) {";
+  }
+  if (element_field_->BuilderParameterMustBeMoved()) {
+    s << element_field_->GetDataType() << " " << element_field_->GetName() << "_ptr;";
+  } else {
+    s << element_field_->GetDataType() << "* " << element_field_->GetName() << "_ptr = ret_it;";
+  }
+  element_field_->GenExtractor(s, num_leading_bits, for_struct);
+  if (element_field_->BuilderParameterMustBeMoved()) {
+    s << "*ret_it = std::move(" << element_field_->GetName() << "_ptr);";
+  }
+  s << "ret_it++;";
+  s << "}";
+}
+
+std::string ArrayField::GetGetterFunctionName() const {
+  std::stringstream ss;
+  ss << "Get" << util::UnderscoreToCamelCase(GetName());
+  return ss.str();
+}
+
+void ArrayField::GenGetter(std::ostream& s, Size start_offset, Size end_offset) const {
+  s << GetDataType() << " " << GetGetterFunctionName() << "() {";
+  s << "ASSERT(was_validated_);";
+  s << "size_t end_index = size();";
+  s << "auto to_bound = begin();";
+
+  int num_leading_bits = GenBounds(s, start_offset, end_offset, GetSize());
+  s << GetDataType() << " " << GetName() << "_value;";
+  s << GetDataType() << "* " << GetName() << "_ptr = &" << GetName() << "_value;";
+  GenExtractor(s, num_leading_bits, false);
+
+  s << "return " << GetName() << "_value;";
+  s << "}\n";
+}
+
+std::string ArrayField::GetBuilderParameterType() const {
+  std::stringstream ss;
+  if (element_field_->BuilderParameterMustBeMoved()) {
+    ss << "std::array<" << element_field_->GetDataType() << "," << array_size_ << ">";
+  } else {
+    ss << "const std::array<" << element_field_->GetDataType() << "," << array_size_ << ">&";
+  }
+  return ss.str();
+}
+
+bool ArrayField::BuilderParameterMustBeMoved() const {
+  return element_field_->BuilderParameterMustBeMoved();
+}
+
+bool ArrayField::GenBuilderMember(std::ostream& s) const {
+  s << "std::array<" << element_field_->GetDataType() << "," << array_size_ << "> " << GetName();
+  return true;
+}
+
+bool ArrayField::HasParameterValidator() const {
+  return false;
+}
+
+void ArrayField::GenParameterValidator(std::ostream&) const {
+  // Array length is validated by the compiler
+}
+
+void ArrayField::GenInserter(std::ostream& s) const {
+  s << "for (const auto& val_ : " << GetName() << "_) {";
+  element_field_->GenInserter(s);
+  s << "}\n";
+}
+
+void ArrayField::GenValidator(std::ostream&) const {
+  // NOTE: We could check if the element size divides cleanly into the array size, but we decided to forgo that
+  // in favor of just returning as many elements as possible in a best effort style.
+  //
+  // Other than that there is nothing that arrays need to be validated on other than length so nothing needs to
+  // be done here.
+}
+
+bool ArrayField::IsContainerField() const {
+  return true;
+}
+
+const PacketField* ArrayField::GetElementField() const {
+  return element_field_;
+}
diff --git a/gd/packet/parser/fields/array_field.h b/gd/packet/parser/fields/array_field.h
new file mode 100644
index 0000000..dd49f26
--- /dev/null
+++ b/gd/packet/parser/fields/array_field.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "fields/packet_field.h"
+#include "parse_location.h"
+#include "type_def.h"
+
+class ArrayField : public PacketField {
+ public:
+  ArrayField(std::string name, int element_size, int fixed_size, ParseLocation loc);
+
+  ArrayField(std::string name, TypeDef* type_def, int fixed_size, ParseLocation loc);
+
+  static const std::string kFieldType;
+
+  virtual const std::string& GetFieldType() const override;
+
+  virtual Size GetSize() const override;
+
+  virtual Size GetBuilderSize() const override;
+
+  virtual Size GetStructSize() const override;
+
+  virtual std::string GetDataType() const override;
+
+  virtual void GenExtractor(std::ostream& s, int num_leading_bits, bool for_struct) const override;
+
+  virtual std::string GetGetterFunctionName() const override;
+
+  virtual void GenGetter(std::ostream& s, Size start_offset, Size end_offset) const override;
+
+  virtual std::string GetBuilderParameterType() const override;
+
+  virtual bool BuilderParameterMustBeMoved() const override;
+
+  virtual bool GenBuilderMember(std::ostream& s) const override;
+
+  virtual bool HasParameterValidator() const override;
+
+  virtual void GenParameterValidator(std::ostream& s) const override;
+
+  virtual void GenInserter(std::ostream& s) const override;
+
+  virtual void GenValidator(std::ostream&) const override;
+
+  virtual bool IsContainerField() const override;
+
+  virtual const PacketField* GetElementField() const override;
+
+  const std::string name_;
+
+  const PacketField* element_field_{nullptr};
+
+  const Size element_size_{};
+
+  const int array_size_{-1};
+};
diff --git a/gd/packet/parser/fields/body_field.cc b/gd/packet/parser/fields/body_field.cc
new file mode 100644
index 0000000..5e00b68
--- /dev/null
+++ b/gd/packet/parser/fields/body_field.cc
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "fields/body_field.h"
+
+const std::string BodyField::kFieldType = "BodyField";
+
+BodyField::BodyField(ParseLocation loc) : PacketField("body", loc) {}
+
+const std::string& BodyField::GetFieldType() const {
+  return BodyField::kFieldType;
+}
+
+Size BodyField::GetSize() const {
+  return Size(0);
+}
+
+std::string BodyField::GetDataType() const {
+  ERROR(this) << "No need to know the type of a body field.";
+  return "BodyType";
+}
+
+void BodyField::GenExtractor(std::ostream&, int, bool) const {}
+
+std::string BodyField::GetGetterFunctionName() const {
+  return "";
+}
+
+void BodyField::GenGetter(std::ostream&, Size, Size) const {}
+
+std::string BodyField::GetBuilderParameterType() const {
+  return "";
+}
+
+bool BodyField::HasParameterValidator() const {
+  return false;
+}
+
+void BodyField::GenParameterValidator(std::ostream&) const {
+  // There is no validation needed for a payload
+}
+
+void BodyField::GenInserter(std::ostream&) const {
+  // Do nothing
+}
+
+void BodyField::GenValidator(std::ostream&) const {
+  // Do nothing
+}
diff --git a/gd/packet/parser/fields/body_field.h b/gd/packet/parser/fields/body_field.h
new file mode 100644
index 0000000..ce4ede7
--- /dev/null
+++ b/gd/packet/parser/fields/body_field.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "fields/packet_field.h"
+#include "fields/size_field.h"
+#include "parse_location.h"
+
+class BodyField : public PacketField {
+ public:
+  BodyField(ParseLocation loc);
+
+  static const std::string kFieldType;
+
+  virtual const std::string& GetFieldType() const override;
+
+  virtual Size GetSize() const override;
+
+  virtual std::string GetDataType() const override;
+
+  virtual void GenExtractor(std::ostream& s, int num_leading_bits, bool for_struct) const override;
+
+  virtual std::string GetGetterFunctionName() const override;
+
+  virtual void GenGetter(std::ostream&, Size, Size) const override;
+
+  virtual std::string GetBuilderParameterType() const override;
+
+  virtual bool HasParameterValidator() const override;
+
+  virtual void GenParameterValidator(std::ostream&) const override;
+
+  virtual void GenInserter(std::ostream&) const override;
+
+  virtual void GenValidator(std::ostream&) const override;
+};
diff --git a/gd/packet/parser/fields/checksum_field.cc b/gd/packet/parser/fields/checksum_field.cc
new file mode 100644
index 0000000..4647992
--- /dev/null
+++ b/gd/packet/parser/fields/checksum_field.cc
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "fields/checksum_field.h"
+#include "util.h"
+
+const std::string ChecksumField::kFieldType = "ChecksumField";
+
+ChecksumField::ChecksumField(std::string name, std::string type_name, int size, ParseLocation loc)
+    : ScalarField(name, size, loc), type_name_(type_name) {}
+
+const std::string& ChecksumField::GetFieldType() const {
+  return ChecksumField::kFieldType;
+}
+
+std::string ChecksumField::GetDataType() const {
+  return type_name_;
+}
+
+void ChecksumField::GenExtractor(std::ostream&, int, bool) const {}
+
+std::string ChecksumField::GetGetterFunctionName() const {
+  return "";
+}
+
+void ChecksumField::GenGetter(std::ostream&, Size, Size) const {}
+
+bool ChecksumField::GenBuilderParameter(std::ostream&) const {
+  return false;
+}
+
+bool ChecksumField::HasParameterValidator() const {
+  return false;
+}
+
+void ChecksumField::GenParameterValidator(std::ostream&) const {
+  // Do nothing.
+}
+
+void ChecksumField::GenInserter(std::ostream& s) const {
+  s << "packet::ByteObserver observer = i.UnregisterObserver();";
+  s << "insert(static_cast<" << util::GetTypeForSize(GetSize().bits()) << ">(observer.GetValue()), i);";
+}
+
+void ChecksumField::GenValidator(std::ostream&) const {
+  // Done in packet_def.cc
+}
diff --git a/gd/packet/parser/fields/checksum_field.h b/gd/packet/parser/fields/checksum_field.h
new file mode 100644
index 0000000..c15f023
--- /dev/null
+++ b/gd/packet/parser/fields/checksum_field.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "fields/packet_field.h"
+#include "fields/scalar_field.h"
+#include "parse_location.h"
+
+class ChecksumField : public ScalarField {
+ public:
+  ChecksumField(std::string name, std::string type_name, int size, ParseLocation loc);
+
+  static const std::string kFieldType;
+
+  virtual const std::string& GetFieldType() const override;
+
+  virtual std::string GetDataType() const override;
+
+  virtual void GenExtractor(std::ostream& s, int num_leading_bits, bool for_struct) const override;
+
+  virtual std::string GetGetterFunctionName() const override;
+
+  virtual void GenGetter(std::ostream& s, Size start_offset, Size end_offset) const override;
+
+  virtual bool GenBuilderParameter(std::ostream& s) const override;
+
+  virtual bool HasParameterValidator() const override;
+
+  virtual void GenParameterValidator(std::ostream&) const override;
+
+  virtual void GenInserter(std::ostream& s) const override;
+
+  virtual void GenValidator(std::ostream&) const override;
+
+ private:
+  std::string type_name_;
+};
diff --git a/gd/packet/parser/fields/checksum_start_field.cc b/gd/packet/parser/fields/checksum_start_field.cc
new file mode 100644
index 0000000..5531c5a
--- /dev/null
+++ b/gd/packet/parser/fields/checksum_start_field.cc
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "fields/checksum_start_field.h"
+#include "util.h"
+
+const std::string ChecksumStartField::kFieldType = "ChecksumStartField";
+
+ChecksumStartField::ChecksumStartField(std::string name, ParseLocation loc)
+    : PacketField(name + "_start", loc), started_field_name_(name) {}
+
+const std::string& ChecksumStartField::GetFieldType() const {
+  return ChecksumStartField::kFieldType;
+}
+
+Size ChecksumStartField::GetSize() const {
+  return Size(0);
+}
+
+std::string ChecksumStartField::GetDataType() const {
+  return "There's no type for Checksum Start fields";
+}
+
+void ChecksumStartField::GenExtractor(std::ostream&, int, bool) const {}
+
+std::string ChecksumStartField::GetGetterFunctionName() const {
+  return "";
+}
+
+void ChecksumStartField::GenGetter(std::ostream&, Size, Size) const {}
+
+std::string ChecksumStartField::GetBuilderParameterType() const {
+  return "";
+}
+
+bool ChecksumStartField::HasParameterValidator() const {
+  return false;
+}
+
+void ChecksumStartField::GenParameterValidator(std::ostream&) const {}
+
+void ChecksumStartField::GenInserter(std::ostream&) const {
+  ERROR(this) << __func__ << ": This should not be called for checksum start fields";
+}
+
+void ChecksumStartField::GenValidator(std::ostream&) const {}
+
+std::string ChecksumStartField::GetStartedFieldName() const {
+  return started_field_name_;
+}
diff --git a/gd/packet/parser/fields/checksum_start_field.h b/gd/packet/parser/fields/checksum_start_field.h
new file mode 100644
index 0000000..c63806b
--- /dev/null
+++ b/gd/packet/parser/fields/checksum_start_field.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "fields/packet_field.h"
+#include "parse_location.h"
+
+class ChecksumStartField : public PacketField {
+ public:
+  ChecksumStartField(std::string name, ParseLocation loc);
+
+  static const std::string kFieldType;
+
+  std::string GetField() const;
+
+  virtual const std::string& GetFieldType() const override;
+
+  virtual Size GetSize() const override;
+
+  virtual std::string GetDataType() const override;
+
+  virtual void GenExtractor(std::ostream& s, int num_leading_bits, bool for_struct) const override;
+
+  virtual std::string GetGetterFunctionName() const override;
+
+  virtual void GenGetter(std::ostream& s, Size start_offset, Size end_offset) const override;
+
+  virtual std::string GetBuilderParameterType() const override;
+
+  virtual bool HasParameterValidator() const override;
+
+  virtual void GenParameterValidator(std::ostream&) const override;
+
+  virtual void GenInserter(std::ostream&) const override;
+
+  virtual void GenValidator(std::ostream&) const override;
+
+  virtual std::string GetStartedFieldName() const;
+
+ private:
+  std::string started_field_name_;
+};
diff --git a/gd/packet/parser/fields/count_field.cc b/gd/packet/parser/fields/count_field.cc
new file mode 100644
index 0000000..8e1cf09
--- /dev/null
+++ b/gd/packet/parser/fields/count_field.cc
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "fields/count_field.h"
+#include "util.h"
+
+const std::string CountField::kFieldType = "CountField";
+
+CountField::CountField(std::string name, int size, ParseLocation loc)
+    : ScalarField(name + "_count", size, loc), sized_field_name_(name) {}
+
+const std::string& CountField::GetFieldType() const {
+  return CountField::kFieldType;
+}
+
+void CountField::GenGetter(std::ostream& s, Size start_offset, Size end_offset) const {
+  s << "protected:";
+  ScalarField::GenGetter(s, start_offset, end_offset);
+  s << "public:\n";
+}
+
+bool CountField::GenBuilderParameter(std::ostream&) const {
+  // There is no builder parameter for a size field
+  return false;
+}
+
+bool CountField::HasParameterValidator() const {
+  return false;
+}
+
+void CountField::GenParameterValidator(std::ostream&) const {
+  // There is no builder parameter for a size field
+  // TODO: Check if the payload fits in the packet?
+}
+
+void CountField::GenInserter(std::ostream&) const {
+  ERROR(this) << __func__ << ": This should not be called for count fields";
+}
+
+void CountField::GenValidator(std::ostream&) const {
+  // Do nothing since the fixed count fields will be handled specially.
+}
+
+std::string CountField::GetSizedFieldName() const {
+  return sized_field_name_;
+}
diff --git a/gd/packet/parser/fields/count_field.h b/gd/packet/parser/fields/count_field.h
new file mode 100644
index 0000000..c9cb673
--- /dev/null
+++ b/gd/packet/parser/fields/count_field.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "fields/packet_field.h"
+#include "fields/scalar_field.h"
+#include "parse_location.h"
+
+class CountField : public ScalarField {
+ public:
+  CountField(std::string name, int size, ParseLocation loc);
+
+  static const std::string kFieldType;
+
+  std::string GetField() const;
+
+  virtual const std::string& GetFieldType() const override;
+
+  virtual void GenGetter(std::ostream& s, Size start_offset, Size end_offset) const override;
+
+  virtual bool GenBuilderParameter(std::ostream&) const override;
+
+  virtual bool HasParameterValidator() const override;
+
+  virtual void GenParameterValidator(std::ostream&) const override;
+
+  virtual void GenInserter(std::ostream&) const override;
+
+  virtual void GenValidator(std::ostream&) const override;
+
+  virtual std::string GetSizedFieldName() const;
+
+ private:
+  int size_;
+  std::string sized_field_name_;
+};
diff --git a/gd/packet/parser/fields/custom_field.cc b/gd/packet/parser/fields/custom_field.cc
new file mode 100644
index 0000000..4e387f8
--- /dev/null
+++ b/gd/packet/parser/fields/custom_field.cc
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "fields/custom_field.h"
+#include "util.h"
+
+const std::string CustomField::kFieldType = "CustomField";
+
+CustomField::CustomField(std::string name, std::string type_name, ParseLocation loc)
+    : PacketField(name, loc), type_name_(type_name) {}
+
+const std::string& CustomField::GetFieldType() const {
+  return CustomField::kFieldType;
+}
+
+Size CustomField::GetSize() const {
+  return Size();
+}
+
+Size CustomField::GetBuilderSize() const {
+  std::string ret = "(" + GetName() + "_.size() * 8) ";
+  return ret;
+}
+
+std::string CustomField::GetDataType() const {
+  return type_name_;
+}
+
+void CustomField::GenExtractor(std::ostream& s, int, bool) const {
+  s << "auto optional_it = ";
+  s << GetDataType() << "::Parse( " << GetName() << "_ptr, " << GetName() << "_it);";
+  s << "if (optional_it) {";
+  s << GetName() << "_it = *optional_it;";
+  s << "} else {";
+  s << GetName() << "_it = " << GetName() << "_it + " << GetName() << "_it.NumBytesRemaining();";
+  s << GetName() << "_ptr = nullptr;";
+  s << "}";
+}
+
+std::string CustomField::GetGetterFunctionName() const {
+  std::stringstream ss;
+  ss << "Get" << util::UnderscoreToCamelCase(GetName());
+  return ss.str();
+}
+
+void CustomField::GenGetter(std::ostream& s, Size start_offset, Size end_offset) const {
+  s << "std::unique_ptr<" << GetDataType() << "> " << GetGetterFunctionName() << "() const {";
+  s << "ASSERT(was_validated_);";
+  s << "size_t end_index = size();";
+  s << "auto to_bound = begin();";
+
+  int num_leading_bits = GenBounds(s, start_offset, end_offset, GetSize());
+  s << "std::unique_ptr<" << GetDataType() << "> " << GetName() << "_value";
+  s << " = std::make_unique<" << GetDataType() << ">();";
+  s << GetDataType() << "* " << GetName() << "_ptr = " << GetName() << "_value.get();";
+  GenExtractor(s, num_leading_bits, false);
+  s << "if (" << GetName() << "_ptr == nullptr) {" << GetName() << "_value.reset(); }";
+  s << "return " << GetName() << "_value;";
+  s << "}\n";
+}
+
+std::string CustomField::GetBuilderParameterType() const {
+  return GetDataType();
+}
+
+bool CustomField::HasParameterValidator() const {
+  return false;
+}
+
+void CustomField::GenParameterValidator(std::ostream&) const {
+  // Do nothing.
+}
+
+void CustomField::GenInserter(std::ostream& s) const {
+  s << GetName() << "_.Serialize(i);";
+}
+
+void CustomField::GenValidator(std::ostream&) const {
+  // Do nothing.
+}
diff --git a/gd/packet/parser/fields/custom_field.h b/gd/packet/parser/fields/custom_field.h
new file mode 100644
index 0000000..621a3c8
--- /dev/null
+++ b/gd/packet/parser/fields/custom_field.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "fields/packet_field.h"
+#include "parse_location.h"
+
+class CustomField : public PacketField {
+ public:
+  CustomField(std::string name, std::string type_name, ParseLocation loc);
+
+  static const std::string kFieldType;
+
+  virtual const std::string& GetFieldType() const override;
+
+  virtual Size GetSize() const override;
+
+  virtual Size GetBuilderSize() const override;
+
+  virtual std::string GetDataType() const override;
+
+  virtual void GenExtractor(std::ostream& s, int num_leading_bits, bool for_struct) const override;
+
+  virtual std::string GetGetterFunctionName() const override;
+
+  virtual void GenGetter(std::ostream& s, Size start_offset, Size end_offset) const override;
+
+  virtual std::string GetBuilderParameterType() const override;
+
+  virtual bool HasParameterValidator() const override;
+
+  virtual void GenParameterValidator(std::ostream&) const override;
+
+  virtual void GenInserter(std::ostream& s) const override;
+
+  virtual void GenValidator(std::ostream&) const override;
+
+ private:
+  std::string type_name_;
+};
diff --git a/gd/packet/parser/fields/custom_field_fixed_size.cc b/gd/packet/parser/fields/custom_field_fixed_size.cc
new file mode 100644
index 0000000..687d48d
--- /dev/null
+++ b/gd/packet/parser/fields/custom_field_fixed_size.cc
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "fields/custom_field_fixed_size.h"
+#include "util.h"
+
+const std::string CustomFieldFixedSize::kFieldType = "CustomField";
+
+CustomFieldFixedSize::CustomFieldFixedSize(std::string name, std::string type_name, int size, ParseLocation loc)
+    : ScalarField(name, size, loc), type_name_(type_name) {}
+
+const std::string& CustomFieldFixedSize::GetFieldType() const {
+  return CustomFieldFixedSize::kFieldType;
+}
+
+std::string CustomFieldFixedSize::GetDataType() const {
+  return type_name_;
+}
+
+int CustomFieldFixedSize::GenBounds(std::ostream& s, Size start_offset, Size end_offset, Size size) const {
+  if (!start_offset.empty()) {
+    // Default to start if available.
+    s << "auto " << GetName() << "_it = to_bound + (" << start_offset << ") / 8;";
+  } else if (!end_offset.empty()) {
+    Size byte_offset = size + end_offset;
+    s << "auto " << GetName() << "_it = to_bound (+ to_bound.NumBytesRemaining() - (" << byte_offset << ") / 8);";
+  } else {
+    ERROR(this) << "Ambiguous offset for field.";
+  }
+  return 0;  // num_leading_bits
+}
+
+void CustomFieldFixedSize::GenExtractor(std::ostream& s, int, bool) const {
+  s << "*" << GetName() << "_ptr = " << GetName() << "_it.extract<" << GetDataType() << ">();";
+}
+
+bool CustomFieldFixedSize::HasParameterValidator() const {
+  return false;
+}
+
+void CustomFieldFixedSize::GenParameterValidator(std::ostream&) const {
+  // Do nothing.
+}
+
+void CustomFieldFixedSize::GenInserter(std::ostream& s) const {
+  s << "insert(" << GetName() << "_, i);";
+}
+
+void CustomFieldFixedSize::GenValidator(std::ostream&) const {
+  // Do nothing.
+}
diff --git a/gd/packet/parser/fields/custom_field_fixed_size.h b/gd/packet/parser/fields/custom_field_fixed_size.h
new file mode 100644
index 0000000..97acff9
--- /dev/null
+++ b/gd/packet/parser/fields/custom_field_fixed_size.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "fields/scalar_field.h"
+#include "parse_location.h"
+
+class CustomFieldFixedSize : public ScalarField {
+ public:
+  CustomFieldFixedSize(std::string name, std::string type_name, int size, ParseLocation loc);
+
+  static const std::string kFieldType;
+
+  virtual const std::string& GetFieldType() const override;
+
+  virtual std::string GetDataType() const override;
+
+  virtual int GenBounds(std::ostream& s, Size start_offset, Size end_offset, Size size) const override;
+
+  virtual void GenExtractor(std::ostream& s, int num_leading_bits, bool for_struct) const override;
+
+  virtual bool HasParameterValidator() const override;
+
+  virtual void GenParameterValidator(std::ostream&) const override;
+
+  virtual void GenInserter(std::ostream& s) const override;
+
+  virtual void GenValidator(std::ostream&) const override;
+
+  std::string type_name_;
+};
diff --git a/gd/packet/parser/fields/enum_field.cc b/gd/packet/parser/fields/enum_field.cc
new file mode 100644
index 0000000..3080d43
--- /dev/null
+++ b/gd/packet/parser/fields/enum_field.cc
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "fields/enum_field.h"
+
+#include "util.h"
+
+const std::string EnumField::kFieldType = "EnumField";
+
+EnumField::EnumField(std::string name, EnumDef enum_def, std::string value, ParseLocation loc)
+    : ScalarField(name, enum_def.size_, loc), enum_def_(enum_def), value_(value) {}
+
+const std::string& EnumField::GetFieldType() const {
+  return EnumField::kFieldType;
+}
+
+EnumDef EnumField::GetEnumDef() {
+  return enum_def_;
+}
+
+std::string EnumField::GetDataType() const {
+  return enum_def_.name_;
+}
+
+bool EnumField::HasParameterValidator() const {
+  return false;
+}
+
+void EnumField::GenParameterValidator(std::ostream&) const {
+  // Validated at compile time.
+}
+
+void EnumField::GenInserter(std::ostream& s) const {
+  s << "insert(static_cast<" << util::GetTypeForSize(GetSize().bits()) << ">(";
+  s << GetName() << "_), i, " << GetSize().bits() << ");";
+}
+
+void EnumField::GenValidator(std::ostream&) const {
+  // Do nothing
+}
diff --git a/gd/packet/parser/fields/enum_field.h b/gd/packet/parser/fields/enum_field.h
new file mode 100644
index 0000000..11c64e0
--- /dev/null
+++ b/gd/packet/parser/fields/enum_field.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "enum_def.h"
+#include "fields/packet_field.h"
+#include "fields/scalar_field.h"
+#include "parse_location.h"
+
+class EnumField : public ScalarField {
+ public:
+  EnumField(std::string name, EnumDef enum_def, std::string value, ParseLocation loc);
+
+  EnumDef GetEnumDef();
+
+  static const std::string kFieldType;
+
+  virtual const std::string& GetFieldType() const override;
+
+  virtual std::string GetDataType() const override;
+
+  virtual bool HasParameterValidator() const override;
+
+  virtual void GenParameterValidator(std::ostream&) const override;
+
+  virtual void GenInserter(std::ostream& s) const override;
+
+  virtual void GenValidator(std::ostream&) const override;
+
+ private:
+  EnumDef enum_def_;
+  std::string value_;
+};
diff --git a/gd/packet/parser/fields/fixed_enum_field.cc b/gd/packet/parser/fields/fixed_enum_field.cc
new file mode 100644
index 0000000..0cc35c0
--- /dev/null
+++ b/gd/packet/parser/fields/fixed_enum_field.cc
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "fields/fixed_enum_field.h"
+#include "util.h"
+
+const std::string FixedEnumField::kFieldType = "FixedEnumField";
+
+FixedEnumField::FixedEnumField(EnumDef* enum_def, std::string value, ParseLocation loc)
+    : FixedField("fixed_enum", enum_def->size_, loc), enum_(enum_def), value_(value) {}
+
+const std::string& FixedEnumField::GetFieldType() const {
+  return FixedEnumField::kFieldType;
+}
+
+std::string FixedEnumField::GetDataType() const {
+  return enum_->name_;
+}
+
+void FixedEnumField::GenValue(std::ostream& s) const {
+  s << enum_->name_ << "::" << value_;
+}
diff --git a/gd/packet/parser/fields/fixed_enum_field.h b/gd/packet/parser/fields/fixed_enum_field.h
new file mode 100644
index 0000000..2c6c3f3
--- /dev/null
+++ b/gd/packet/parser/fields/fixed_enum_field.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <variant>
+
+#include "enum_def.h"
+#include "fields/fixed_field.h"
+#include "fields/packet_field.h"
+#include "parse_location.h"
+
+class FixedEnumField : public FixedField {
+ public:
+  FixedEnumField(EnumDef* enum_def, std::string value, ParseLocation loc);
+
+  static const std::string kFieldType;
+
+  virtual const std::string& GetFieldType() const override;
+
+  virtual std::string GetDataType() const override;
+
+  static const std::string field_type;
+
+ private:
+  void GenValue(std::ostream& s) const;
+
+  EnumDef* enum_;
+  std::string value_;
+};
diff --git a/gd/packet/parser/fields/fixed_field.cc b/gd/packet/parser/fields/fixed_field.cc
new file mode 100644
index 0000000..a307653
--- /dev/null
+++ b/gd/packet/parser/fields/fixed_field.cc
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "fields/fixed_field.h"
+#include "util.h"
+
+int FixedField::unique_id_ = 0;
+
+FixedField::FixedField(std::string name, int size, ParseLocation loc)
+    : ScalarField(name + std::to_string(unique_id_++), size, loc) {}
+
+void FixedField::GenGetter(std::ostream& s, Size start_offset, Size end_offset) const {
+  s << "protected:";
+  ScalarField::GenGetter(s, start_offset, end_offset);
+  s << "public:\n";
+}
+
+std::string FixedField::GetBuilderParameterType() const {
+  return "";
+}
+
+bool FixedField::GenBuilderParameter(std::ostream&) const {
+  // No parameter needed for a fixed field.
+  return false;
+}
+
+bool FixedField::HasParameterValidator() const {
+  return false;
+}
+
+void FixedField::GenParameterValidator(std::ostream&) const {
+  // No parameter validator needed for a fixed field.
+}
+
+void FixedField::GenInserter(std::ostream& s) const {
+  s << "insert(";
+  GenValue(s);
+  s << ", i , " << GetSize().bits() << ");";
+}
+
+void FixedField::GenValidator(std::ostream& s) const {
+  s << "if (Get" << util::UnderscoreToCamelCase(GetName()) << "() != ";
+  GenValue(s);
+  s << ") return false;";
+}
diff --git a/gd/packet/parser/fields/fixed_field.h b/gd/packet/parser/fields/fixed_field.h
new file mode 100644
index 0000000..c2b2b7b
--- /dev/null
+++ b/gd/packet/parser/fields/fixed_field.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <variant>
+
+#include "enum_def.h"
+#include "fields/packet_field.h"
+#include "fields/scalar_field.h"
+#include "parse_location.h"
+
+class FixedField : public ScalarField {
+ public:
+  FixedField(std::string name, int size, ParseLocation loc);
+
+  static const std::string kFieldType;
+
+  virtual std::string GetDataType() const override = 0;
+
+  virtual void GenGetter(std::ostream& s, Size start_offset, Size end_offset) const override;
+
+  virtual std::string GetBuilderParameterType() const override;
+
+  virtual bool GenBuilderParameter(std::ostream&) const override;
+
+  virtual bool HasParameterValidator() const override;
+
+  virtual void GenParameterValidator(std::ostream&) const override;
+
+  virtual void GenInserter(std::ostream& s) const override;
+
+  virtual void GenValidator(std::ostream& s) const override;
+
+ private:
+  virtual void GenValue(std::ostream& s) const = 0;
+
+  static int unique_id_;
+};
diff --git a/gd/packet/parser/fields/fixed_scalar_field.cc b/gd/packet/parser/fields/fixed_scalar_field.cc
new file mode 100644
index 0000000..9bfdf0e
--- /dev/null
+++ b/gd/packet/parser/fields/fixed_scalar_field.cc
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "fields/fixed_scalar_field.h"
+#include "util.h"
+
+const std::string FixedScalarField::kFieldType = "FixedScalarField";
+
+FixedScalarField::FixedScalarField(int size, int64_t value, ParseLocation loc)
+    : FixedField("fixed_scalar", size, loc), value_(value) {}
+
+const std::string& FixedScalarField::GetFieldType() const {
+  return FixedScalarField::kFieldType;
+}
+
+std::string FixedScalarField::GetDataType() const {
+  return util::GetTypeForSize(GetSize().bits());
+}
+
+void FixedScalarField::GenValue(std::ostream& s) const {
+  s << value_;
+}
diff --git a/gd/packet/parser/fields/fixed_scalar_field.h b/gd/packet/parser/fields/fixed_scalar_field.h
new file mode 100644
index 0000000..0070f6c
--- /dev/null
+++ b/gd/packet/parser/fields/fixed_scalar_field.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <variant>
+
+#include "enum_def.h"
+#include "fields/fixed_field.h"
+#include "fields/packet_field.h"
+#include "parse_location.h"
+
+class FixedScalarField : public FixedField {
+ public:
+  FixedScalarField(int size, int64_t value, ParseLocation loc);
+
+  static const std::string kFieldType;
+
+  virtual const std::string& GetFieldType() const override;
+
+  virtual std::string GetDataType() const override;
+
+  static const std::string field_type;
+
+ private:
+  void GenValue(std::ostream& s) const;
+
+  const int64_t value_;
+};
diff --git a/gd/packet/parser/fields/group_field.cc b/gd/packet/parser/fields/group_field.cc
new file mode 100644
index 0000000..6c9ade9
--- /dev/null
+++ b/gd/packet/parser/fields/group_field.cc
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "fields/group_field.h"
+
+GroupField::GroupField(ParseLocation loc, std::list<PacketField*>* fields)
+    : PacketField("Groups have no name", loc), fields_(fields) {}
+
+GroupField::~GroupField() {
+  delete fields_;
+}
+
+const std::string GroupField::kFieldType = "GroupField";
+
+std::string GroupField::GetName() const {
+  ERROR(this) << "GetName should never be called.";
+  return "";
+}
+
+const std::string& GroupField::GetFieldType() const {
+  return GroupField::kFieldType;
+}
+
+Size GroupField::GetSize() const {
+  ERROR(this) << "GetSize should never be called.";
+  return Size();
+}
+
+std::string GroupField::GetDataType() const {
+  ERROR(this) << "GetType should never be called.";
+  return "";
+}
+
+void GroupField::GenExtractor(std::ostream&, int, bool) const {
+  ERROR(this) << "GenExtractor should never be called.";
+}
+
+std::string GroupField::GetGetterFunctionName() const {
+  ERROR(this) << "GetGetterFunctionName should never be called.";
+  return "";
+}
+
+void GroupField::GenGetter(std::ostream&, Size, Size) const {
+  ERROR(this) << "GenGetter should never be called.";
+}
+
+std::string GroupField::GetBuilderParameterType() const {
+  ERROR(this) << "GetBuilderParameterType should never be called";
+  return "";
+}
+
+bool GroupField::HasParameterValidator() const {
+  ERROR(this) << "HasParameterValidator should never be called";
+  return false;
+}
+
+void GroupField::GenParameterValidator(std::ostream&) const {
+  ERROR(this) << "Not implemented";
+}
+
+void GroupField::GenInserter(std::ostream&) const {
+  ERROR(this) << "GenInserter should never be called.";
+}
+
+void GroupField::GenValidator(std::ostream&) const {
+  ERROR(this) << "GenValidator should never be called.";
+}
+
+const std::list<PacketField*>* GroupField::GetFields() const {
+  return fields_;
+}
diff --git a/gd/packet/parser/fields/group_field.h b/gd/packet/parser/fields/group_field.h
new file mode 100644
index 0000000..57e0165
--- /dev/null
+++ b/gd/packet/parser/fields/group_field.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <list>
+
+#include "fields/packet_field.h"
+#include "parse_location.h"
+
+class GroupField : public PacketField {
+ public:
+  GroupField(ParseLocation loc, std::list<PacketField*>* fields);
+
+  ~GroupField();
+
+  virtual std::string GetName() const override;
+
+  static const std::string kFieldType;
+
+  virtual const std::string& GetFieldType() const override;
+
+  virtual Size GetSize() const override;
+
+  virtual std::string GetDataType() const override;
+
+  virtual void GenExtractor(std::ostream& s, int num_leading_bits, bool for_struct) const override;
+
+  virtual std::string GetGetterFunctionName() const override;
+
+  virtual void GenGetter(std::ostream&, Size, Size) const override;
+
+  virtual std::string GetBuilderParameterType() const override;
+
+  virtual bool HasParameterValidator() const override;
+
+  virtual void GenParameterValidator(std::ostream&) const override;
+
+  virtual void GenInserter(std::ostream&) const override;
+
+  virtual void GenValidator(std::ostream&) const override;
+
+  const std::list<PacketField*>* GetFields() const;
+
+ private:
+  std::list<PacketField*>* fields_;
+};
diff --git a/gd/packet/parser/fields/packet_field.cc b/gd/packet/parser/fields/packet_field.cc
new file mode 100644
index 0000000..7b0bdb7
--- /dev/null
+++ b/gd/packet/parser/fields/packet_field.cc
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "fields/packet_field.h"
+
+#include "util.h"
+
+PacketField::PacketField(std::string name, ParseLocation loc) : loc_(loc), name_(name) {}
+
+std::string PacketField::GetDebugName() const {
+  return "Field{Type:" + GetFieldType() + ", Name:" + GetName() + "}";
+}
+
+ParseLocation PacketField::GetLocation() const {
+  return loc_;
+}
+
+std::string PacketField::GetName() const {
+  return name_;
+}
+
+Size PacketField::GetBuilderSize() const {
+  return GetSize();
+}
+
+Size PacketField::GetStructSize() const {
+  return GetSize();
+}
+
+int PacketField::GenBounds(std::ostream& s, Size start_offset, Size end_offset, Size size) const {
+  // In order to find field_begin and field_end, we must have two of the three Sizes.
+  if ((start_offset.empty() && size.empty()) || (start_offset.empty() && end_offset.empty()) ||
+      (end_offset.empty() && size.empty())) {
+    ERROR(this) << "GenBounds called without enough information. " << start_offset << end_offset << size;
+  }
+
+  if (start_offset.bits() % 8 != 0 || end_offset.bits() % 8 != 0) {
+    ERROR(this) << "Can not find the bounds of a field at a non byte-aligned offset." << start_offset << end_offset;
+  }
+
+  if (!start_offset.empty()) {
+    s << "size_t field_begin = (" << start_offset << ") / 8;";
+  } else {
+    s << "size_t field_begin = end_index - (" << end_offset << " + " << size << ") / 8;";
+  }
+
+  if (!end_offset.empty()) {
+    s << "size_t field_end = end_index - (" << end_offset << ") / 8;";
+    // If the field has a known size, use the minimum for the end
+    if (!size.empty()) {
+      s << "size_t field_sized_end = field_begin + (" << size << ") / 8;";
+      s << "if (field_sized_end < field_end) { field_end = field_sized_end; }";
+    }
+  } else {
+    s << "size_t field_end = field_begin + (" << size << ") / 8;";
+    s << "if (field_end > end_index) { field_end = end_index; }";
+  }
+  s << "auto " << name_ << "_it = to_bound.Subrange(field_begin, field_end - field_begin); ";
+  return 0;  // num_leading_bits
+}
+
+bool PacketField::GenBuilderParameter(std::ostream& s) const {
+  auto param_type = GetBuilderParameterType();
+  if (param_type.empty()) {
+    return false;
+  }
+  s << param_type << " " << GetName();
+  return true;
+}
+
+bool PacketField::BuilderParameterMustBeMoved() const {
+  return false;
+}
+
+bool PacketField::GenBuilderMember(std::ostream& s) const {
+  return GenBuilderParameter(s);
+}
+
+void PacketField::GenBuilderParameterFromView(std::ostream& s) const {
+  s << "view.Get" << util::UnderscoreToCamelCase(GetName()) << "()";
+}
+
+bool PacketField::IsContainerField() const {
+  return false;
+}
+
+const PacketField* PacketField::GetElementField() const {
+  return nullptr;
+}
diff --git a/gd/packet/parser/fields/packet_field.h b/gd/packet/parser/fields/packet_field.h
new file mode 100644
index 0000000..d9cc019
--- /dev/null
+++ b/gd/packet/parser/fields/packet_field.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <iostream>
+#include <string>
+
+#include "logging.h"
+#include "parse_location.h"
+#include "size.h"
+
+// The base field that every packet needs to inherit from.
+class PacketField : public Loggable {
+ public:
+  virtual ~PacketField() = default;
+
+  PacketField(std::string name, ParseLocation loc);
+
+  // Get the type for this field.
+  virtual const std::string& GetFieldType() const = 0;
+
+  // Returns the size of the field in bits.
+  virtual Size GetSize() const = 0;
+
+  // Returns the size of the field in bits given the information in the builder.
+  // For most field types, this will be the same as GetSize();
+  virtual Size GetBuilderSize() const;
+
+  // Returns the size of the field in bits given the information in the parsed struct.
+  // For most field types, this will be the same as GetSize();
+  virtual Size GetStructSize() const;
+
+  // Get the type of the field to be used in the member variables.
+  virtual std::string GetDataType() const = 0;
+
+  // Given an iterator {name}_it, extract the type.
+  virtual void GenExtractor(std::ostream& s, int num_leading_bits, bool for_struct) const = 0;
+
+  // Calculate field_begin and field_end using the given offsets and size, return the number of leading bits
+  virtual int GenBounds(std::ostream& s, Size start_offset, Size end_offset, Size size) const;
+
+  // Get the name of the getter function, return empty string if there is a getter function
+  virtual std::string GetGetterFunctionName() const = 0;
+
+  // Get parser getter definition. Start_offset points to the first bit of the
+  // field. end_offset is the first bit after the field. If an offset is empty
+  // that means that there was a field with an unknown size when trying to
+  // calculate the offset.
+  virtual void GenGetter(std::ostream& s, Size start_offset, Size end_offset) const = 0;
+
+  // Get the type of parameter used in Create(), return empty string if a parameter type was NOT generated
+  virtual std::string GetBuilderParameterType() const = 0;
+
+  // Generate the parameter for Create(), return true if a parameter was added.
+  virtual bool GenBuilderParameter(std::ostream& s) const;
+
+  // Return true if the Builder parameter has to be moved.
+  virtual bool BuilderParameterMustBeMoved() const;
+
+  // Generate the actual storage for the parameter, return true if it was added.
+  virtual bool GenBuilderMember(std::ostream& s) const;
+
+  // Helper for reflection tests.
+  virtual void GenBuilderParameterFromView(std::ostream& s) const;
+
+  // Returns whether or not the field must be validated.
+  virtual bool HasParameterValidator() const = 0;
+
+  // Fail if the value doesn't fit in the field.
+  virtual void GenParameterValidator(std::ostream& s) const = 0;
+
+  // Generate the inserter for pushing the data in the builder.
+  virtual void GenInserter(std::ostream& s) const = 0;
+
+  // Generate the validator for a field for the IsValid() function.
+  //
+  // The way this function works is by assuming that there is an iterator |it|
+  // that was defined earlier. The implementer of the function will then move
+  // it forward based on the dynamic size of the field and then check to see if
+  // its past the end of the packet.
+  // It should be unused for fixed size fields unless special consideration is
+  // needed. This is because all fixed size fields are tallied together with
+  // GetSize() and used as an initial offset. One special consideration is for
+  // enums where instead of checking if they can be read, they are checked to
+  // see if they contain the correct value.
+  virtual void GenValidator(std::ostream& s) const = 0;
+
+  // Some fields are containers of other fields, e.g. array, vector, etc.
+  // Assume STL containers that support swap()
+  virtual bool IsContainerField() const;
+
+  // Get field of nested elements if this is a container field, nullptr if none
+  virtual const PacketField* GetElementField() const;
+
+  std::string GetDebugName() const override;
+
+  ParseLocation GetLocation() const override;
+
+  virtual std::string GetName() const;
+
+ private:
+  ParseLocation loc_;
+  std::string name_;
+};
diff --git a/gd/packet/parser/fields/padding_field.cc b/gd/packet/parser/fields/padding_field.cc
new file mode 100644
index 0000000..5f438f8
--- /dev/null
+++ b/gd/packet/parser/fields/padding_field.cc
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "fields/padding_field.h"
+#include "util.h"
+
+const std::string PaddingField::kFieldType = "PaddingField";
+
+PaddingField::PaddingField(int size, ParseLocation loc)
+    : PacketField("padding_" + std::to_string(size * 8), loc), size_(size * 8) {}
+
+const std::string& PaddingField::GetFieldType() const {
+  return PaddingField::kFieldType;
+}
+
+Size PaddingField::GetSize() const {
+  return size_;
+}
+
+Size PaddingField::GetBuilderSize() const {
+  return 0;
+}
+
+std::string PaddingField::GetDataType() const {
+  return "There's no type for Padding fields";
+}
+
+void PaddingField::GenExtractor(std::ostream&, int, bool) const {}
+
+std::string PaddingField::GetGetterFunctionName() const {
+  return "";
+}
+
+void PaddingField::GenGetter(std::ostream&, Size, Size) const {}
+
+std::string PaddingField::GetBuilderParameterType() const {
+  return "";
+}
+
+bool PaddingField::HasParameterValidator() const {
+  return false;
+}
+
+void PaddingField::GenParameterValidator(std::ostream&) const {}
+
+void PaddingField::GenInserter(std::ostream&) const {
+  ERROR(this) << __func__ << ": This should not be called for padding fields";
+}
+
+void PaddingField::GenValidator(std::ostream&) const {}
diff --git a/gd/packet/parser/fields/padding_field.h b/gd/packet/parser/fields/padding_field.h
new file mode 100644
index 0000000..bb99d42
--- /dev/null
+++ b/gd/packet/parser/fields/padding_field.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "fields/packet_field.h"
+#include "parse_location.h"
+
+class PaddingField : public PacketField {
+ public:
+  PaddingField(int size, ParseLocation loc);
+
+  static const std::string kFieldType;
+
+  std::string GetField() const;
+
+  virtual const std::string& GetFieldType() const override;
+
+  virtual Size GetSize() const override;
+
+  virtual Size GetBuilderSize() const override;
+
+  virtual std::string GetDataType() const override;
+
+  virtual void GenExtractor(std::ostream& s, int num_leading_bits, bool for_struct) const override;
+
+  virtual std::string GetGetterFunctionName() const override;
+
+  virtual void GenGetter(std::ostream& s, Size start_offset, Size end_offset) const override;
+
+  virtual std::string GetBuilderParameterType() const override;
+
+  virtual bool HasParameterValidator() const override;
+
+  virtual void GenParameterValidator(std::ostream&) const override;
+
+  virtual void GenInserter(std::ostream&) const override;
+
+  virtual void GenValidator(std::ostream&) const override;
+
+ private:
+  Size size_;
+};
diff --git a/gd/packet/parser/fields/payload_field.cc b/gd/packet/parser/fields/payload_field.cc
new file mode 100644
index 0000000..c075996
--- /dev/null
+++ b/gd/packet/parser/fields/payload_field.cc
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "fields/payload_field.h"
+#include "util.h"
+
+const std::string PayloadField::kFieldType = "PayloadField";
+
+PayloadField::PayloadField(std::string modifier, ParseLocation loc)
+    : PacketField("payload", loc), size_field_(nullptr), size_modifier_(modifier) {}
+
+void PayloadField::SetSizeField(const SizeField* size_field) {
+  if (size_field_ != nullptr) {
+    ERROR(this, size_field_, size_field) << "The size field for the payload has already been assigned.";
+  }
+
+  size_field_ = size_field;
+}
+
+const std::string& PayloadField::GetFieldType() const {
+  return PayloadField::kFieldType;
+}
+
+Size PayloadField::GetSize() const {
+  if (size_field_ == nullptr) {
+    if (!size_modifier_.empty()) {
+      ERROR(this) << "Missing size field for payload with size modifier.";
+    }
+    return Size();
+  }
+
+  std::string dynamic_size = "(Get" + util::UnderscoreToCamelCase(size_field_->GetName()) + "() * 8)";
+  if (!size_modifier_.empty()) {
+    dynamic_size += "- (" + size_modifier_ + ")";
+  }
+
+  return dynamic_size;
+}
+
+std::string PayloadField::GetDataType() const {
+  return "PacketView";
+}
+
+void PayloadField::GenExtractor(std::ostream&, int, bool) const {
+  ERROR(this) << __func__ << " should never be called. ";
+}
+
+std::string PayloadField::GetGetterFunctionName() const {
+  return "GetPayload";
+}
+
+void PayloadField::GenGetter(std::ostream& s, Size start_offset, Size end_offset) const {
+  s << "PacketView<kLittleEndian> " << GetGetterFunctionName() << "() const {";
+  s << "ASSERT(was_validated_);";
+  s << "size_t end_index = size();";
+  s << "auto to_bound = begin();";
+  GenBounds(s, start_offset, end_offset, GetSize());
+  s << "return GetLittleEndianSubview(field_begin, field_end);";
+  s << "}\n\n";
+
+  s << "PacketView<!kLittleEndian> " << GetGetterFunctionName() << "BigEndian() const {";
+  s << "ASSERT(was_validated_);";
+  s << "size_t end_index = size();";
+  s << "auto to_bound = begin();";
+  GenBounds(s, start_offset, end_offset, GetSize());
+  s << "return GetBigEndianSubview(field_begin, field_end);";
+  s << "}\n";
+}
+
+std::string PayloadField::GetBuilderParameterType() const {
+  return "std::unique_ptr<BasePacketBuilder>";
+}
+
+bool PayloadField::BuilderParameterMustBeMoved() const {
+  return true;
+}
+
+void PayloadField::GenBuilderParameterFromView(std::ostream& s) const {
+  s << "std::make_unique<RawBuilder>(std::vector<uint8_t>(view.GetPayload().begin(), view.GetPayload().end()))";
+}
+
+bool PayloadField::HasParameterValidator() const {
+  return false;
+}
+
+void PayloadField::GenParameterValidator(std::ostream&) const {
+  // There is no validation needed for a payload
+}
+
+void PayloadField::GenInserter(std::ostream&) const {
+  ERROR() << __func__ << " Should never be called.";
+}
+
+void PayloadField::GenValidator(std::ostream&) const {
+  // Do nothing
+}
diff --git a/gd/packet/parser/fields/payload_field.h b/gd/packet/parser/fields/payload_field.h
new file mode 100644
index 0000000..11e6267
--- /dev/null
+++ b/gd/packet/parser/fields/payload_field.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "fields/packet_field.h"
+#include "fields/size_field.h"
+#include "parse_location.h"
+
+class PayloadField : public PacketField {
+ public:
+  PayloadField(std::string modifier, ParseLocation loc);
+
+  static const std::string kFieldType;
+
+  virtual const std::string& GetFieldType() const override;
+
+  void SetSizeField(const SizeField* size_field);
+
+  virtual Size GetSize() const override;
+
+  virtual std::string GetDataType() const override;
+
+  virtual void GenExtractor(std::ostream& s, int num_leading_bits, bool for_struct) const override;
+
+  virtual std::string GetGetterFunctionName() const override;
+
+  virtual void GenGetter(std::ostream& s, Size start_offset, Size end_offset) const override;
+
+  virtual std::string GetBuilderParameterType() const override;
+
+  virtual bool BuilderParameterMustBeMoved() const override;
+
+  virtual void GenBuilderParameterFromView(std::ostream& s) const override;
+
+  virtual bool HasParameterValidator() const override;
+
+  virtual void GenParameterValidator(std::ostream&) const override;
+
+  virtual void GenInserter(std::ostream&) const override;
+
+  virtual void GenValidator(std::ostream&) const override;
+
+  // Payload fields can only be dynamically sized.
+  const SizeField* size_field_;
+  // Only used if the size of the payload is based on another field.
+  std::string size_modifier_;
+};
diff --git a/gd/packet/parser/fields/reserved_field.cc b/gd/packet/parser/fields/reserved_field.cc
new file mode 100644
index 0000000..0acf7b7
--- /dev/null
+++ b/gd/packet/parser/fields/reserved_field.cc
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "fields/reserved_field.h"
+#include "util.h"
+
+int ReservedField::unique_id_ = 0;
+
+const std::string ReservedField::kFieldType = "ReservedField";
+
+ReservedField::ReservedField(int size, ParseLocation loc)
+    : PacketField("ReservedScalar" + std::to_string(unique_id_++), loc), size_(size) {}
+
+const std::string& ReservedField::GetFieldType() const {
+  return ReservedField::kFieldType;
+}
+
+Size ReservedField::GetSize() const {
+  return size_;
+}
+
+std::string ReservedField::GetDataType() const {
+  return util::GetTypeForSize(size_);
+}
+
+void ReservedField::GenExtractor(std::ostream&, int, bool) const {}
+
+std::string ReservedField::GetGetterFunctionName() const {
+  return "";
+}
+
+void ReservedField::GenGetter(std::ostream&, Size, Size) const {
+  // There is no Getter for a reserved field
+}
+
+std::string ReservedField::GetBuilderParameterType() const {
+  // There is no builder parameter for a reserved field
+  return "";
+}
+
+bool ReservedField::HasParameterValidator() const {
+  return false;
+}
+
+void ReservedField::GenParameterValidator(std::ostream&) const {
+  // There is no builder parameter for a reserved field
+}
+
+void ReservedField::GenInserter(std::ostream& s) const {
+  s << "insert(static_cast<" << util::GetTypeForSize(GetSize().bits()) << ">(0) /* Reserved */, i, " << GetSize().bits()
+    << " );\n";
+}
+
+void ReservedField::GenValidator(std::ostream&) const {
+  // There is no need to validate the value of a reserved field
+}
diff --git a/gd/packet/parser/fields/reserved_field.h b/gd/packet/parser/fields/reserved_field.h
new file mode 100644
index 0000000..7d36352
--- /dev/null
+++ b/gd/packet/parser/fields/reserved_field.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "fields/packet_field.h"
+#include "parse_location.h"
+
+class ReservedField : public PacketField {
+ public:
+  ReservedField(int size, ParseLocation loc);
+
+  static const std::string kFieldType;
+
+  virtual const std::string& GetFieldType() const override;
+
+  virtual Size GetSize() const override;
+
+  virtual std::string GetDataType() const override;
+
+  virtual void GenExtractor(std::ostream& s, int num_leading_bits, bool for_struct) const override;
+
+  virtual std::string GetGetterFunctionName() const override;
+
+  virtual void GenGetter(std::ostream&, Size, Size) const override;
+
+  virtual std::string GetBuilderParameterType() const override;
+
+  virtual bool HasParameterValidator() const override;
+
+  virtual void GenParameterValidator(std::ostream&) const override;
+
+  virtual void GenInserter(std::ostream& s) const override;
+
+  virtual void GenValidator(std::ostream&) const override;
+
+ private:
+  std::string name_;
+  int size_;
+  static int unique_id_;
+};
diff --git a/gd/packet/parser/fields/scalar_field.cc b/gd/packet/parser/fields/scalar_field.cc
new file mode 100644
index 0000000..320534a
--- /dev/null
+++ b/gd/packet/parser/fields/scalar_field.cc
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "fields/scalar_field.h"
+
+#include "util.h"
+
+const std::string ScalarField::kFieldType = "ScalarField";
+
+ScalarField::ScalarField(std::string name, int size, ParseLocation loc) : PacketField(name, loc), size_(size) {
+  if (size_ > 64 || size_ < 0) {
+    ERROR(this) << "Not implemented for size_ = " << size_;
+  }
+}
+
+const std::string& ScalarField::GetFieldType() const {
+  return ScalarField::kFieldType;
+}
+
+Size ScalarField::GetSize() const {
+  return size_;
+}
+
+std::string ScalarField::GetDataType() const {
+  return util::GetTypeForSize(size_);
+}
+
+int GetShiftBits(int i) {
+  int bits_past_byte_boundary = i % 8;
+  if (bits_past_byte_boundary == 0) {
+    return 0;
+  } else {
+    return 8 - bits_past_byte_boundary;
+  }
+}
+
+int ScalarField::GenBounds(std::ostream& s, Size start_offset, Size end_offset, Size size) const {
+  int num_leading_bits = 0;
+
+  if (!start_offset.empty()) {
+    // Default to start if available.
+    num_leading_bits = start_offset.bits() % 8;
+    s << "auto " << GetName() << "_it = to_bound + (" << start_offset << ") / 8;";
+  } else if (!end_offset.empty()) {
+    num_leading_bits = GetShiftBits(end_offset.bits() + size.bits());
+    Size byte_offset = Size(num_leading_bits + size.bits()) + end_offset;
+    s << "auto " << GetName() << "_it = to_bound + (to_bound.NumBytesRemaining() - (" << byte_offset << ") / 8);";
+  } else {
+    ERROR(this) << "Ambiguous offset for field.";
+  }
+  return num_leading_bits;
+}
+
+void ScalarField::GenExtractor(std::ostream& s, int num_leading_bits, bool) const {
+  Size size = GetSize();
+  // Extract the correct number of bytes. The return type could be different
+  // from the extract type if an earlier field causes the beginning of the
+  // current field to start in the middle of a byte.
+  std::string extract_type = util::GetTypeForSize(size.bits() + num_leading_bits);
+  s << "auto extracted_value = " << GetName() << "_it.extract<" << extract_type << ">();";
+
+  // Right shift the result to remove leading bits.
+  if (num_leading_bits != 0) {
+    s << "extracted_value >>= " << num_leading_bits << ";";
+  }
+  // Mask the result if necessary.
+  if (util::RoundSizeUp(size.bits()) != size.bits()) {
+    uint64_t mask = 0;
+    for (int i = 0; i < size.bits(); i++) {
+      mask <<= 1;
+      mask |= 1;
+    }
+    s << "extracted_value &= 0x" << std::hex << mask << std::dec << ";";
+  }
+  s << "*" << GetName() << "_ptr = static_cast<" << GetDataType() << ">(extracted_value);";
+}
+
+std::string ScalarField::GetGetterFunctionName() const {
+  std::stringstream ss;
+  ss << "Get" << util::UnderscoreToCamelCase(GetName());
+  return ss.str();
+}
+
+void ScalarField::GenGetter(std::ostream& s, Size start_offset, Size end_offset) const {
+  s << GetDataType() << " " << GetGetterFunctionName() << "() const {";
+  s << "ASSERT(was_validated_);";
+  s << "auto to_bound = begin();";
+  int num_leading_bits = GenBounds(s, start_offset, end_offset, GetSize());
+  s << GetDataType() << " " << GetName() << "_value;";
+  s << GetDataType() << "* " << GetName() << "_ptr = &" << GetName() << "_value;";
+  GenExtractor(s, num_leading_bits, false);
+  s << "return " << GetName() << "_value;";
+  s << "}";
+}
+
+std::string ScalarField::GetBuilderParameterType() const {
+  return GetDataType();
+}
+
+bool ScalarField::HasParameterValidator() const {
+  return util::RoundSizeUp(GetSize().bits()) != GetSize().bits();
+}
+
+void ScalarField::GenParameterValidator(std::ostream& s) const {
+  s << "ASSERT(" << GetName() << " < (static_cast<uint64_t>(1) << " << GetSize().bits() << "));";
+}
+
+void ScalarField::GenInserter(std::ostream& s) const {
+  if (GetSize().bits() == 8) {
+    s << "i.insert_byte(" << GetName() << "_);";
+  } else {
+    s << "insert(" << GetName() << "_, i," << GetSize().bits() << ");";
+  }
+}
+
+void ScalarField::GenValidator(std::ostream&) const {
+  // Do nothing
+}
diff --git a/gd/packet/parser/fields/scalar_field.h b/gd/packet/parser/fields/scalar_field.h
new file mode 100644
index 0000000..65f897e
--- /dev/null
+++ b/gd/packet/parser/fields/scalar_field.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "fields/packet_field.h"
+#include "parse_location.h"
+
+class ScalarField : public PacketField {
+ public:
+  ScalarField(std::string name, int size, ParseLocation loc);
+
+  static const std::string kFieldType;
+
+  virtual const std::string& GetFieldType() const override;
+
+  virtual Size GetSize() const override;
+
+  virtual std::string GetDataType() const override;
+
+  virtual int GenBounds(std::ostream& s, Size start_offset, Size end_offset, Size size) const override;
+
+  virtual void GenExtractor(std::ostream& s, int num_leading_bits, bool for_struct) const override;
+
+  virtual std::string GetGetterFunctionName() const override;
+
+  virtual void GenGetter(std::ostream& s, Size start_offset, Size end_offset) const override;
+
+  virtual std::string GetBuilderParameterType() const override;
+
+  virtual bool HasParameterValidator() const override;
+
+  virtual void GenParameterValidator(std::ostream& s) const override;
+
+  virtual void GenInserter(std::ostream& s) const override;
+
+  virtual void GenValidator(std::ostream&) const override;
+
+ private:
+  const int size_;
+};
diff --git a/gd/packet/parser/fields/size_field.cc b/gd/packet/parser/fields/size_field.cc
new file mode 100644
index 0000000..0a1b80b
--- /dev/null
+++ b/gd/packet/parser/fields/size_field.cc
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "fields/size_field.h"
+#include "util.h"
+
+const std::string SizeField::kFieldType = "SizeField";
+
+SizeField::SizeField(std::string name, int size, ParseLocation loc)
+    : ScalarField(name + "_size", size, loc), sized_field_name_(name) {}
+
+const std::string& SizeField::GetFieldType() const {
+  return SizeField::kFieldType;
+}
+
+void SizeField::GenGetter(std::ostream& s, Size start_offset, Size end_offset) const {
+  s << "protected:";
+  ScalarField::GenGetter(s, start_offset, end_offset);
+  s << "public:\n";
+}
+
+std::string SizeField::GetBuilderParameterType() const {
+  return "";
+}
+
+bool SizeField::GenBuilderParameter(std::ostream&) const {
+  // There is no builder parameter for a size field
+  return false;
+}
+
+bool SizeField::HasParameterValidator() const {
+  return false;
+}
+
+void SizeField::GenParameterValidator(std::ostream&) const {
+  // There is no builder parameter for a size field
+  // TODO: Check if the payload fits in the packet?
+}
+
+void SizeField::GenInserter(std::ostream&) const {
+  ERROR(this) << __func__ << ": This should not be called for size fields";
+}
+
+void SizeField::GenValidator(std::ostream&) const {
+  // Do nothing since the fixed size fields will be handled specially.
+}
+
+std::string SizeField::GetSizedFieldName() const {
+  return sized_field_name_;
+}
diff --git a/gd/packet/parser/fields/size_field.h b/gd/packet/parser/fields/size_field.h
new file mode 100644
index 0000000..b6e8639
--- /dev/null
+++ b/gd/packet/parser/fields/size_field.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "fields/packet_field.h"
+#include "fields/scalar_field.h"
+#include "parse_location.h"
+
+class SizeField : public ScalarField {
+ public:
+  SizeField(std::string name, int size, ParseLocation loc);
+
+  static const std::string kFieldType;
+
+  std::string GetField() const;
+
+  virtual const std::string& GetFieldType() const override;
+
+  virtual void GenGetter(std::ostream& s, Size start_offset, Size end_offset) const override;
+
+  virtual std::string GetBuilderParameterType() const override;
+
+  virtual bool GenBuilderParameter(std::ostream&) const override;
+
+  virtual bool HasParameterValidator() const override;
+
+  virtual void GenParameterValidator(std::ostream&) const override;
+
+  virtual void GenInserter(std::ostream&) const override;
+
+  virtual void GenValidator(std::ostream&) const override;
+
+  virtual std::string GetSizedFieldName() const;
+
+ private:
+  int size_;
+  std::string sized_field_name_;
+};
diff --git a/gd/packet/parser/fields/struct_field.cc b/gd/packet/parser/fields/struct_field.cc
new file mode 100644
index 0000000..fbdafcf
--- /dev/null
+++ b/gd/packet/parser/fields/struct_field.cc
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "fields/struct_field.h"
+#include "util.h"
+
+const std::string StructField::kFieldType = "StructField";
+
+StructField::StructField(std::string name, std::string type_name, Size size, ParseLocation loc)
+    : PacketField(name, loc), type_name_(type_name), size_(size) {}
+
+const std::string& StructField::GetFieldType() const {
+  return StructField::kFieldType;
+}
+
+Size StructField::GetSize() const {
+  return size_;
+}
+
+Size StructField::GetBuilderSize() const {
+  std::string ret = "(" + GetName() + "_.size() * 8)";
+  return ret;
+}
+
+std::string StructField::GetDataType() const {
+  return type_name_;
+}
+
+void StructField::GenExtractor(std::ostream& s, int, bool) const {
+  s << GetName() << "_it = ";
+  s << GetDataType() << "::Parse(" << GetName() << "_ptr, " << GetName() << "_it);";
+}
+
+std::string StructField::GetGetterFunctionName() const {
+  std::stringstream ss;
+  ss << "Get" << util::UnderscoreToCamelCase(GetName());
+  return ss.str();
+}
+
+void StructField::GenGetter(std::ostream& s, Size start_offset, Size end_offset) const {
+  s << GetDataType() << " " << GetGetterFunctionName() << "() const {";
+  s << "ASSERT(was_validated_);";
+  s << "size_t end_index = size();";
+  s << "auto to_bound = begin();";
+  int num_leading_bits = GenBounds(s, start_offset, end_offset, GetSize());
+  s << GetDataType() << " " << GetName() << "_value;";
+  s << GetDataType() << "* " << GetName() << "_ptr = &" << GetName() << "_value;";
+  GenExtractor(s, num_leading_bits, false);
+
+  s << "return " << GetName() << "_value;";
+  s << "}\n";
+}
+
+std::string StructField::GetBuilderParameterType() const {
+  return GetDataType();
+}
+
+bool StructField::HasParameterValidator() const {
+  return false;
+}
+
+void StructField::GenParameterValidator(std::ostream&) const {
+  // Validated at compile time.
+}
+
+void StructField::GenInserter(std::ostream& s) const {
+  s << GetName() << "_.Serialize(i);";
+}
+
+void StructField::GenValidator(std::ostream&) const {
+  // Do nothing
+}
diff --git a/gd/packet/parser/fields/struct_field.h b/gd/packet/parser/fields/struct_field.h
new file mode 100644
index 0000000..1f4f100
--- /dev/null
+++ b/gd/packet/parser/fields/struct_field.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "fields/packet_field.h"
+#include "parse_location.h"
+
+class StructField : public PacketField {
+ public:
+  StructField(std::string name, std::string type_name, Size size, ParseLocation loc);
+
+  static const std::string kFieldType;
+
+  virtual const std::string& GetFieldType() const override;
+
+  virtual Size GetSize() const override;
+
+  virtual Size GetBuilderSize() const override;
+
+  virtual std::string GetDataType() const override;
+
+  virtual void GenExtractor(std::ostream& s, int num_leading_bits, bool for_struct) const override;
+
+  virtual std::string GetGetterFunctionName() const override;
+
+  virtual void GenGetter(std::ostream& s, Size start_offset, Size end_offset) const override;
+
+  virtual std::string GetBuilderParameterType() const override;
+
+  virtual bool HasParameterValidator() const override;
+
+  virtual void GenParameterValidator(std::ostream&) const override;
+
+  virtual void GenInserter(std::ostream& s) const override;
+
+  virtual void GenValidator(std::ostream&) const override;
+
+ private:
+  std::string type_name_;
+
+ public:
+  const Size size_{};
+};
diff --git a/gd/packet/parser/fields/variable_length_struct_field.cc b/gd/packet/parser/fields/variable_length_struct_field.cc
new file mode 100644
index 0000000..ceaf858
--- /dev/null
+++ b/gd/packet/parser/fields/variable_length_struct_field.cc
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "fields/variable_length_struct_field.h"
+#include "util.h"
+
+const std::string VariableLengthStructField::kFieldType = "VariableLengthStructField";
+
+VariableLengthStructField::VariableLengthStructField(std::string name, std::string type_name, ParseLocation loc)
+    : PacketField(name, loc), type_name_(type_name) {}
+
+const std::string& VariableLengthStructField::GetFieldType() const {
+  return VariableLengthStructField::kFieldType;
+}
+
+Size VariableLengthStructField::GetSize() const {
+  return Size();
+}
+
+Size VariableLengthStructField::GetBuilderSize() const {
+  std::string ret = "(" + GetName() + "_->size() * 8) ";
+  return ret;
+}
+
+std::string VariableLengthStructField::GetDataType() const {
+  std::string ret = "std::unique_ptr<" + type_name_ + ">";
+  return ret;
+}
+
+void VariableLengthStructField::GenExtractor(std::ostream& s, int, bool) const {
+  s << GetName() << "_ptr = Parse" << type_name_ << "(" << GetName() << "_it);";
+  s << "if (" << GetName() << "_ptr != nullptr) {";
+  s << GetName() << "_it = " << GetName() << "_it + " << GetName() << "_ptr->size();";
+  s << "} else {";
+  s << GetName() << "_it = " << GetName() << "_it + " << GetName() << "_it.NumBytesRemaining();";
+  s << "}";
+}
+
+std::string VariableLengthStructField::GetGetterFunctionName() const {
+  std::stringstream ss;
+  ss << "Get" << util::UnderscoreToCamelCase(GetName());
+  return ss.str();
+}
+
+void VariableLengthStructField::GenGetter(std::ostream& s, Size start_offset, Size end_offset) const {
+  s << GetDataType() << " " << GetGetterFunctionName() << "() const {";
+  s << "ASSERT(was_validated_);";
+  s << "size_t end_index = size();";
+  s << "auto to_bound = begin();";
+  int num_leading_bits = GenBounds(s, start_offset, end_offset, GetSize());
+  s << GetDataType() << " " << GetName() << "_ptr;";
+  GenExtractor(s, num_leading_bits, false);
+  s << "return " << GetName() << "_ptr;";
+  s << "}\n";
+}
+
+std::string VariableLengthStructField::GetBuilderParameterType() const {
+  return GetDataType();
+}
+
+bool VariableLengthStructField::BuilderParameterMustBeMoved() const {
+  return true;
+}
+
+bool VariableLengthStructField::HasParameterValidator() const {
+  return false;
+}
+
+void VariableLengthStructField::GenParameterValidator(std::ostream&) const {
+  // Validated at compile time.
+}
+
+void VariableLengthStructField::GenInserter(std::ostream& s) const {
+  s << GetName() << "_->Serialize(i);";
+}
+
+void VariableLengthStructField::GenValidator(std::ostream&) const {
+  // Do nothing
+}
diff --git a/gd/packet/parser/fields/variable_length_struct_field.h b/gd/packet/parser/fields/variable_length_struct_field.h
new file mode 100644
index 0000000..0b1b97f
--- /dev/null
+++ b/gd/packet/parser/fields/variable_length_struct_field.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "fields/packet_field.h"
+#include "parse_location.h"
+
+class VariableLengthStructField : public PacketField {
+ public:
+  VariableLengthStructField(std::string name, std::string type_name, ParseLocation loc);
+
+  static const std::string kFieldType;
+
+  virtual const std::string& GetFieldType() const override;
+
+  virtual Size GetSize() const override;
+
+  virtual Size GetBuilderSize() const override;
+
+  virtual std::string GetDataType() const override;
+
+  virtual void GenExtractor(std::ostream& s, int num_leading_bits, bool for_struct) const override;
+
+  virtual std::string GetGetterFunctionName() const override;
+
+  virtual void GenGetter(std::ostream& s, Size start_offset, Size end_offset) const override;
+
+  virtual std::string GetBuilderParameterType() const override;
+
+  virtual bool BuilderParameterMustBeMoved() const override;
+
+  virtual bool HasParameterValidator() const override;
+
+  virtual void GenParameterValidator(std::ostream&) const override;
+
+  virtual void GenInserter(std::ostream& s) const override;
+
+  virtual void GenValidator(std::ostream&) const override;
+
+ private:
+  std::string type_name_;
+};
diff --git a/gd/packet/parser/fields/vector_field.cc b/gd/packet/parser/fields/vector_field.cc
new file mode 100644
index 0000000..ab47c4f
--- /dev/null
+++ b/gd/packet/parser/fields/vector_field.cc
@@ -0,0 +1,232 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "fields/vector_field.h"
+
+#include "fields/count_field.h"
+#include "fields/custom_field.h"
+#include "util.h"
+
+const std::string VectorField::kFieldType = "VectorField";
+
+VectorField::VectorField(std::string name, int element_size, std::string size_modifier, ParseLocation loc)
+    : PacketField(name, loc), element_field_(new ScalarField("val", element_size, loc)), element_size_(element_size),
+      size_modifier_(size_modifier) {
+  if (element_size > 64 || element_size < 0)
+    ERROR(this) << __func__ << ": Not implemented for element size = " << element_size;
+  if (element_size % 8 != 0) {
+    ERROR(this) << "Can only have arrays with elements that are byte aligned (" << element_size << ")";
+  }
+}
+
+VectorField::VectorField(std::string name, TypeDef* type_def, std::string size_modifier, ParseLocation loc)
+    : PacketField(name, loc), element_field_(type_def->GetNewField("val", loc)),
+      element_size_(element_field_->GetSize()), size_modifier_(size_modifier) {
+  if (!element_size_.empty() && element_size_.bits() % 8 != 0) {
+    ERROR(this) << "Can only have arrays with elements that are byte aligned (" << element_size_ << ")";
+  }
+}
+
+const std::string& VectorField::GetFieldType() const {
+  return VectorField::kFieldType;
+}
+
+Size VectorField::GetSize() const {
+  // If there is no size field, then it is of unknown size.
+  if (size_field_ == nullptr) {
+    return Size();
+  }
+
+  // size_field_ is of type SIZE
+  if (size_field_->GetFieldType() == SizeField::kFieldType) {
+    std::string ret = "(static_cast<size_t>(Get" + util::UnderscoreToCamelCase(size_field_->GetName()) + "()) * 8)";
+    if (!size_modifier_.empty()) ret += size_modifier_;
+    return ret;
+  }
+
+  // size_field_ is of type COUNT and elements have a fixed size
+  if (!element_size_.empty() && !element_size_.has_dynamic()) {
+    return "(static_cast<size_t>(Get" + util::UnderscoreToCamelCase(size_field_->GetName()) + "()) * " +
+           std::to_string(element_size_.bits()) + ")";
+  }
+
+  return Size();
+}
+
+Size VectorField::GetBuilderSize() const {
+  if (!element_size_.empty() && !element_size_.has_dynamic()) {
+    std::string ret = "(static_cast<size_t>(" + GetName() + "_.size()) * " + std::to_string(element_size_.bits()) + ")";
+    return ret;
+  } else if (element_field_->BuilderParameterMustBeMoved()) {
+    std::string ret = "[this](){ size_t length = 0; for (const auto& elem : " + GetName() +
+                      "_) { length += elem->size() * 8; } return length; }()";
+    return ret;
+  } else {
+    std::string ret = "[this](){ size_t length = 0; for (const auto& elem : " + GetName() +
+                      "_) { length += elem.size() * 8; } return length; }()";
+    return ret;
+  }
+}
+
+Size VectorField::GetStructSize() const {
+  // If there is no size field, then it is of unknown size.
+  if (size_field_ == nullptr) {
+    return Size();
+  }
+
+  // size_field_ is of type SIZE
+  if (size_field_->GetFieldType() == SizeField::kFieldType) {
+    std::string ret = "(static_cast<size_t>(" + size_field_->GetName() + "_extracted) * 8)";
+    if (!size_modifier_.empty()) ret += "-" + size_modifier_;
+    return ret;
+  }
+
+  // size_field_ is of type COUNT and elements have a fixed size
+  if (!element_size_.empty() && !element_size_.has_dynamic()) {
+    return "(static_cast<size_t>(" + size_field_->GetName() + "_extracted) * " + std::to_string(element_size_.bits()) +
+           ")";
+  }
+
+  return Size();
+}
+
+std::string VectorField::GetDataType() const {
+  return "std::vector<" + element_field_->GetDataType() + ">";
+}
+
+void VectorField::GenExtractor(std::ostream& s, int num_leading_bits, bool for_struct) const {
+  s << "auto " << element_field_->GetName() << "_it = " << GetName() << "_it;";
+  if (size_field_ != nullptr && size_field_->GetFieldType() == CountField::kFieldType) {
+    s << "size_t " << element_field_->GetName() << "_count = ";
+    if (for_struct) {
+      s << size_field_->GetName() << "_extracted;";
+    } else {
+      s << "Get" << util::UnderscoreToCamelCase(size_field_->GetName()) << "();";
+    }
+  }
+  s << "while (";
+  if (size_field_ != nullptr && size_field_->GetFieldType() == CountField::kFieldType) {
+    s << "(" << element_field_->GetName() << "_count-- > 0) && ";
+  }
+  if (!element_size_.empty()) {
+    s << element_field_->GetName() << "_it.NumBytesRemaining() >= " << element_size_.bytes() << ") {";
+  } else {
+    s << element_field_->GetName() << "_it.NumBytesRemaining() > 0) {";
+  }
+  if (element_field_->BuilderParameterMustBeMoved()) {
+    s << element_field_->GetDataType() << " " << element_field_->GetName() << "_ptr;";
+  } else {
+    s << element_field_->GetDataType() << " " << element_field_->GetName() << "_value;";
+    s << element_field_->GetDataType() << "* " << element_field_->GetName() << "_ptr = &" << element_field_->GetName()
+      << "_value;";
+  }
+  element_field_->GenExtractor(s, num_leading_bits, for_struct);
+  s << "if (" << element_field_->GetName() << "_ptr != nullptr) { ";
+  if (element_field_->BuilderParameterMustBeMoved()) {
+    s << GetName() << "_ptr->push_back(std::move(" << element_field_->GetName() << "_ptr));";
+  } else {
+    s << GetName() << "_ptr->push_back(" << element_field_->GetName() << "_value);";
+  }
+  s << "}";
+  s << "}";
+}
+
+std::string VectorField::GetGetterFunctionName() const {
+  std::stringstream ss;
+  ss << "Get" << util::UnderscoreToCamelCase(GetName());
+  return ss.str();
+}
+
+void VectorField::GenGetter(std::ostream& s, Size start_offset, Size end_offset) const {
+  s << GetDataType() << " " << GetGetterFunctionName() << "() {";
+  s << "ASSERT(was_validated_);";
+  s << "size_t end_index = size();";
+  s << "auto to_bound = begin();";
+
+  int num_leading_bits = GenBounds(s, start_offset, end_offset, GetSize());
+  s << GetDataType() << " " << GetName() << "_value;";
+  s << GetDataType() << "* " << GetName() << "_ptr = &" << GetName() << "_value;";
+  GenExtractor(s, num_leading_bits, false);
+
+  s << "return " << GetName() << "_value;";
+  s << "}\n";
+}
+
+std::string VectorField::GetBuilderParameterType() const {
+  std::stringstream ss;
+  if (element_field_->BuilderParameterMustBeMoved()) {
+    ss << "std::vector<" << element_field_->GetDataType() << ">";
+  } else {
+    ss << "const std::vector<" << element_field_->GetDataType() << ">&";
+  }
+  return ss.str();
+}
+
+bool VectorField::BuilderParameterMustBeMoved() const {
+  return element_field_->BuilderParameterMustBeMoved();
+}
+
+bool VectorField::GenBuilderMember(std::ostream& s) const {
+  s << "std::vector<" << element_field_->GetDataType() << "> " << GetName();
+  return true;
+}
+
+bool VectorField::HasParameterValidator() const {
+  // Does not have parameter validator yet.
+  // TODO: See comment in GenParameterValidator
+  return false;
+}
+
+void VectorField::GenParameterValidator(std::ostream&) const {
+  // No Parameter validator if its dynamically size.
+  // TODO: Maybe add a validator to ensure that the size isn't larger than what the size field can hold.
+  return;
+}
+
+void VectorField::GenInserter(std::ostream& s) const {
+  s << "for (const auto& val_ : " << GetName() << "_) {";
+  element_field_->GenInserter(s);
+  s << "}\n";
+}
+
+void VectorField::GenValidator(std::ostream&) const {
+  // NOTE: We could check if the element size divides cleanly into the array size, but we decided to forgo that
+  // in favor of just returning as many elements as possible in a best effort style.
+  //
+  // Other than that there is nothing that arrays need to be validated on other than length so nothing needs to
+  // be done here.
+}
+
+void VectorField::SetSizeField(const SizeField* size_field) {
+  if (size_field->GetFieldType() == CountField::kFieldType && !size_modifier_.empty()) {
+    ERROR(this, size_field) << "Can not use count field to describe array with a size modifier."
+                            << " Use size instead";
+  }
+
+  size_field_ = size_field;
+}
+
+const std::string& VectorField::GetSizeModifier() const {
+  return size_modifier_;
+}
+
+bool VectorField::IsContainerField() const {
+  return true;
+}
+
+const PacketField* VectorField::GetElementField() const {
+  return element_field_;
+}
diff --git a/gd/packet/parser/fields/vector_field.h b/gd/packet/parser/fields/vector_field.h
new file mode 100644
index 0000000..b2ae95d
--- /dev/null
+++ b/gd/packet/parser/fields/vector_field.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "fields/packet_field.h"
+#include "fields/size_field.h"
+#include "parse_location.h"
+#include "type_def.h"
+
+class VectorField : public PacketField {
+ public:
+  VectorField(std::string name, int element_size, std::string size_modifier, ParseLocation loc);
+
+  VectorField(std::string name, TypeDef* type_def, std::string size_modifier, ParseLocation loc);
+
+  static const std::string kFieldType;
+
+  virtual const std::string& GetFieldType() const override;
+
+  virtual Size GetSize() const override;
+
+  virtual Size GetBuilderSize() const override;
+
+  virtual Size GetStructSize() const override;
+
+  virtual std::string GetDataType() const override;
+
+  virtual void GenExtractor(std::ostream& s, int num_leading_bits, bool for_struct) const override;
+
+  virtual std::string GetGetterFunctionName() const override;
+
+  virtual void GenGetter(std::ostream& s, Size start_offset, Size end_offset) const override;
+
+  virtual std::string GetBuilderParameterType() const override;
+
+  virtual bool BuilderParameterMustBeMoved() const override;
+
+  virtual bool GenBuilderMember(std::ostream& s) const override;
+
+  virtual bool HasParameterValidator() const override;
+
+  virtual void GenParameterValidator(std::ostream& s) const override;
+
+  virtual void GenInserter(std::ostream& s) const override;
+
+  virtual void GenValidator(std::ostream&) const override;
+
+  void SetSizeField(const SizeField* size_field);
+
+  const std::string& GetSizeModifier() const;
+
+  virtual bool IsContainerField() const override;
+
+  virtual const PacketField* GetElementField() const override;
+
+  const std::string name_;
+
+  const PacketField* element_field_{nullptr};
+
+  const Size element_size_{};
+
+  // Size is always in bytes, unless it is a count.
+  const SizeField* size_field_{nullptr};
+
+  // Size modifier is only used when size_field_ is of type SIZE and is not used with COUNT.
+  std::string size_modifier_{""};
+};
diff --git a/gd/packet/parser/language_l.ll b/gd/packet/parser/language_l.ll
new file mode 100644
index 0000000..5814ec1
--- /dev/null
+++ b/gd/packet/parser/language_l.ll
@@ -0,0 +1,121 @@
+%{
+
+#include <string>
+#include <map>
+#include <iostream>
+
+#include "declarations.h"
+#include "language_y.h"
+
+using token = yy::parser::token;
+
+#define YY_USER_ACTION yylloc->step(); yylloc->columns(yyleng);
+
+%}
+
+%option debug
+
+%option yylineno
+%option noyywrap
+%option nounput
+%option noinput
+%option reentrant
+%option bison-bridge
+%option bison-locations
+
+identifier [a-zA-Z][_a-zA-Z0-9]*
+size_modifier [+*/-][ +*/\-0-9]*
+intvalue (0|[1-9][0-9]*)
+hexvalue 0[x|X][0-9a-fA-F]+
+string_literal \".*\"
+
+%x COMMENT_STATE
+
+%%
+  /* NOTE:
+   * Rule ordering is important in order to establist priority. Some
+   * rules are a superset of other rules and will cause the sub rules to
+   * never match. Ex. Keywords must always go before identifiers, otherwise
+   * all keywords will be treated as an identifier.
+   */
+
+  /* Block Comment */
+"/*"                    { BEGIN(COMMENT_STATE); }
+<COMMENT_STATE>"*/"     { BEGIN(INITIAL); }
+<COMMENT_STATE>[\n]+    { yylloc->lines(yyleng); }
+<COMMENT_STATE>.        { /* do nothing */ }
+
+  /* Line Comment */
+"//"[^\r\n]*            { /* do nothing */ }
+
+  /* Begin reserved keyword definitions */
+  /* Fields */
+"_body_"                { return(token::BODY); }
+"_payload_"             { return(token::PAYLOAD); }
+"_size_"                { return(token::SIZE); }
+"_count_"               { return(token::COUNT); }
+"_fixed_"               { return(token::FIXED); }
+"_reserved_"            { return(token::RESERVED); }
+"_checksum_start_"      { return(token::CHECKSUM_START); }
+"_padding_"             { return(token::PADDING); }
+  /* Types */
+"checksum"              { return(token::CHECKSUM); }
+"custom_field"          { return(token::CUSTOM_FIELD); }
+"enum"                  { return(token::ENUM); }
+"group"                 { return(token::GROUP); }
+"packet"                { return(token::PACKET); }
+"struct"                { return(token::STRUCT); }
+"little_endian_packets" {
+                          yylval->integer = 1;
+                          return token::IS_LITTLE_ENDIAN;
+                        }
+"big_endian_packets"    {
+                          yylval->integer = 0;
+                          return token::IS_LITTLE_ENDIAN;
+                        }
+
+  /* Begin identifier definitions */
+{string_literal}        {
+                          std::string with_quotes = std::string(yytext);
+                          yylval->string = new std::string(with_quotes.begin() + 1, with_quotes.end() - 1);
+                          return token::STRING;
+                        }
+
+{size_modifier}         {
+                          yylval->string = new std::string(yytext);
+                          return token::SIZE_MODIFIER;
+                        }
+
+{identifier}            {
+                          yylval->string = new std::string(yytext);
+                          return token::IDENTIFIER;
+                        }
+
+{intvalue}              {
+                          yylval->integer = std::stoi(std::string(yytext), nullptr, 10);
+                          return token::INTEGER;
+                        }
+
+{hexvalue}              {
+                          yylval->integer = std::stoi(std::string(yytext), nullptr, 16);
+                          return token::INTEGER;
+                        }
+
+  /* Begin token definitions */
+":"            { return(':'); }
+"{"            { return('{'); }
+"}"            { return('}'); }
+"["            { return('['); }
+"]"            { return(']'); }
+"("            { return('('); }
+")"            { return(')'); }
+"<"            { return('<'); }
+">"            { return('>'); }
+"="            { return('='); }
+","            { return(','); }
+
+(\n|\r\n)+     { yylloc->lines(yyleng); }
+[ \t\f\v]+     { /* Ignore all other whitespace */ }
+
+%%
+
diff --git a/gd/packet/parser/language_y.yy b/gd/packet/parser/language_y.yy
new file mode 100644
index 0000000..caf8d81
--- /dev/null
+++ b/gd/packet/parser/language_y.yy
@@ -0,0 +1,725 @@
+%{
+  #include <iostream>
+  #include <vector>
+  #include <list>
+  #include <map>
+
+  #include "declarations.h"
+  #include "logging.h"
+  #include "language_y.h"
+  #include "field_list.h"
+  #include "fields/all_fields.h"
+
+  extern int yylex(yy::parser::semantic_type*, yy::parser::location_type*, void *);
+
+  ParseLocation toParseLocation(yy::parser::location_type loc) {
+    return ParseLocation(loc.begin.line);
+  }
+  #define LOC toParseLocation(yylloc)
+%}
+
+%parse-param { void* scanner }
+%parse-param { Declarations* decls }
+%lex-param { void* scanner }
+
+%glr-parser
+%skeleton "glr.cc"
+
+%expect-rr 0
+
+%debug
+%define parse.error verbose
+%locations
+%verbose
+
+%union {
+  int integer;
+  std::string* string;
+
+  EnumDef* enum_definition;
+  std::map<int, std::string>* enumeration_values;
+  std::pair<int, std::string>* enumeration_value;
+
+  PacketDef* packet_definition_value;
+  FieldList* packet_field_definitions;
+  PacketField* packet_field_type;
+
+  StructDef* struct_definition_value;
+
+  std::map<std::string, std::variant<int64_t, std::string>>* constraint_list_t;
+  std::pair<std::string, std::variant<int64_t, std::string>>* constraint_t;
+}
+
+%token <integer> INTEGER
+%token <integer> IS_LITTLE_ENDIAN
+%token <string> IDENTIFIER
+%token <string> SIZE_MODIFIER
+%token <string> STRING
+
+%token ENUM "enum"
+%token PACKET "packet"
+%token PAYLOAD "payload"
+%token BODY "body"
+%token STRUCT "struct"
+%token SIZE "size"
+%token COUNT "count"
+%token FIXED "fixed"
+%token RESERVED "reserved"
+%token GROUP "group"
+%token CUSTOM_FIELD "custom_field"
+%token CHECKSUM "checksum"
+%token CHECKSUM_START "checksum_start"
+%token PADDING "padding"
+
+%type<enum_definition> enum_definition
+%type<enumeration_values> enumeration_list
+%type<enumeration_value> enumeration
+
+%type<packet_definition_value> packet_definition;
+%type<packet_field_definitions> field_definition_list;
+%type<packet_field_type> field_definition;
+%type<packet_field_type> group_field_definition;
+%type<packet_field_type> type_def_field_definition;
+%type<packet_field_type> scalar_field_definition;
+%type<packet_field_type> checksum_start_field_definition;
+%type<packet_field_type> padding_field_definition;
+%type<packet_field_type> size_field_definition;
+%type<packet_field_type> payload_field_definition;
+%type<packet_field_type> body_field_definition;
+%type<packet_field_type> fixed_field_definition;
+%type<packet_field_type> reserved_field_definition;
+%type<packet_field_type> array_field_definition;
+
+%type<struct_definition_value> struct_definition;
+
+%type<constraint_list_t> constraint_list;
+%type<constraint_t> constraint;
+%destructor { std::cout << "DESTROYING STRING " << *$$ << "\n"; delete $$; } IDENTIFIER STRING SIZE_MODIFIER
+
+%%
+
+file
+  : IS_LITTLE_ENDIAN declarations
+  {
+    decls->is_little_endian = ($1 == 1);
+    if (decls->is_little_endian) {
+      DEBUG() << "LITTLE ENDIAN ";
+    } else {
+      DEBUG() << "BIG ENDIAN ";
+    }
+  }
+
+declarations
+  : /* empty */
+  | declarations declaration
+
+declaration
+  : enum_definition
+    {
+      DEBUG() << "FOUND ENUM\n\n";
+      decls->AddTypeDef($1->name_, $1);
+    }
+  | packet_definition
+    {
+      DEBUG() << "FOUND PACKET\n\n";
+      decls->AddPacketDef($1->name_, std::move(*$1));
+      delete $1;
+    }
+  | struct_definition
+    {
+      DEBUG() << "FOUND STRUCT\n\n";
+      decls->AddTypeDef($1->name_, $1);
+    }
+  | group_definition
+    {
+      // All actions are handled in group_definition
+    }
+  | checksum_definition
+    {
+      // All actions are handled in checksum_definition
+    }
+  | custom_field_definition
+    {
+      // All actions are handled in custom_field_definition
+    }
+
+enum_definition
+  : ENUM IDENTIFIER ':' INTEGER '{' enumeration_list ',' '}'
+    {
+      DEBUG() << "Enum Declared: name=" << *$2
+                << " size=" << $4 << "\n";
+
+      $$ = new EnumDef(std::move(*$2), $4);
+      for (const auto& e : *$6) {
+        $$->AddEntry(e.second, e.first);
+      }
+      delete $2;
+      delete $6;
+    }
+
+enumeration_list
+  : enumeration
+    {
+      DEBUG() << "Enumerator with comma\n";
+      $$ = new std::map<int, std::string>();
+      $$->insert(std::move(*$1));
+      delete $1;
+    }
+  | enumeration_list ',' enumeration
+    {
+      DEBUG() << "Enumerator with list\n";
+      $$ = $1;
+      $$->insert(std::move(*$3));
+      delete $3;
+    }
+
+enumeration
+  : IDENTIFIER '=' INTEGER
+    {
+      DEBUG() << "Enumerator: name=" << *$1
+                << " value=" << $3 << "\n";
+      $$ = new std::pair($3, std::move(*$1));
+      delete $1;
+    }
+
+group_definition
+  : GROUP IDENTIFIER '{' field_definition_list '}'
+    {
+      decls->AddGroupDef(*$2, $4);
+      delete $2;
+    }
+
+checksum_definition
+  : CHECKSUM IDENTIFIER ':' INTEGER STRING
+    {
+      DEBUG() << "Checksum field defined\n";
+      decls->AddTypeDef(*$2, new ChecksumDef(*$2, *$5, $4));
+      delete $2;
+      delete $5;
+    }
+
+custom_field_definition
+  : CUSTOM_FIELD IDENTIFIER ':' INTEGER STRING
+    {
+      decls->AddTypeDef(*$2, new CustomFieldDef(*$2, *$5, $4));
+      delete $2;
+      delete $5;
+    }
+  | CUSTOM_FIELD IDENTIFIER STRING
+    {
+      decls->AddTypeDef(*$2, new CustomFieldDef(*$2, *$3));
+      delete $2;
+      delete $3;
+    }
+
+struct_definition
+  : STRUCT IDENTIFIER '{' field_definition_list '}'
+    {
+      auto&& struct_name = *$2;
+      auto&& field_definition_list = *$4;
+
+      DEBUG() << "Struct " << struct_name << " with no parent";
+      DEBUG() << "STRUCT FIELD LIST SIZE: " << field_definition_list.size();
+      auto struct_definition = new StructDef(std::move(struct_name), std::move(field_definition_list));
+      struct_definition->AssignSizeFields();
+
+      $$ = struct_definition;
+      delete $2;
+      delete $4;
+    }
+  | STRUCT IDENTIFIER ':' IDENTIFIER '{' field_definition_list '}'
+    {
+      auto&& struct_name = *$2;
+      auto&& parent_struct_name = *$4;
+      auto&& field_definition_list = *$6;
+
+      DEBUG() << "Struct " << struct_name << " with parent " << parent_struct_name << "\n";
+      DEBUG() << "STRUCT FIELD LIST SIZE: " << field_definition_list.size() << "\n";
+
+      auto parent_struct = decls->GetTypeDef(parent_struct_name);
+      if (parent_struct == nullptr) {
+        ERRORLOC(LOC) << "Could not find struct " << parent_struct_name
+                  << " used as parent for " << struct_name;
+      }
+
+      if (parent_struct->GetDefinitionType() != TypeDef::Type::STRUCT) {
+        ERRORLOC(LOC) << parent_struct_name << " is not a struct";
+      }
+      auto struct_definition = new StructDef(std::move(struct_name), std::move(field_definition_list), (StructDef*)parent_struct);
+      struct_definition->AssignSizeFields();
+
+      $$ = struct_definition;
+      delete $2;
+      delete $4;
+      delete $6;
+    }
+  | STRUCT IDENTIFIER ':' IDENTIFIER '(' constraint_list ')' '{' field_definition_list '}'
+    {
+      auto&& struct_name = *$2;
+      auto&& parent_struct_name = *$4;
+      auto&& constraints = *$6;
+      auto&& field_definition_list = *$9;
+
+      auto parent_struct = decls->GetTypeDef(parent_struct_name);
+      if (parent_struct == nullptr) {
+        ERRORLOC(LOC) << "Could not find struct " << parent_struct_name
+                  << " used as parent for " << struct_name;
+      }
+
+      if (parent_struct->GetDefinitionType() != TypeDef::Type::STRUCT) {
+        ERRORLOC(LOC) << parent_struct_name << " is not a struct";
+      }
+
+      auto struct_definition = new StructDef(std::move(struct_name), std::move(field_definition_list), (StructDef*)parent_struct);
+      struct_definition->AssignSizeFields();
+
+      for (const auto& constraint : constraints) {
+        const auto& constraint_name = constraint.first;
+        const auto& constraint_value = constraint.second;
+        DEBUG() << "Parent constraint on " << constraint_name;
+        struct_definition->AddParentConstraint(constraint_name, constraint_value);
+      }
+
+      $$ = struct_definition;
+
+      delete $2;
+      delete $4;
+      delete $6;
+      delete $9;
+    }
+
+packet_definition
+  : PACKET IDENTIFIER '{' field_definition_list '}'  /* Packet with no parent */
+    {
+      auto&& packet_name = *$2;
+      auto&& field_definition_list = *$4;
+
+      DEBUG() << "Packet " << packet_name << " with no parent";
+      DEBUG() << "PACKET FIELD LIST SIZE: " << field_definition_list.size();
+      auto packet_definition = new PacketDef(std::move(packet_name), std::move(field_definition_list));
+      packet_definition->AssignSizeFields();
+
+      $$ = packet_definition;
+      delete $2;
+      delete $4;
+    }
+  | PACKET IDENTIFIER ':' IDENTIFIER '{' field_definition_list '}'
+    {
+      auto&& packet_name = *$2;
+      auto&& parent_packet_name = *$4;
+      auto&& field_definition_list = *$6;
+
+      DEBUG() << "Packet " << packet_name << " with parent " << parent_packet_name << "\n";
+      DEBUG() << "PACKET FIELD LIST SIZE: " << field_definition_list.size() << "\n";
+
+      auto parent_packet = decls->GetPacketDef(parent_packet_name);
+      if (parent_packet == nullptr) {
+        ERRORLOC(LOC) << "Could not find packet " << parent_packet_name
+                  << " used as parent for " << packet_name;
+      }
+
+      auto packet_definition = new PacketDef(std::move(packet_name), std::move(field_definition_list), parent_packet);
+      packet_definition->AssignSizeFields();
+
+      $$ = packet_definition;
+      delete $2;
+      delete $4;
+      delete $6;
+    }
+  | PACKET IDENTIFIER ':' IDENTIFIER '(' constraint_list ')' '{' field_definition_list '}'
+    {
+      auto&& packet_name = *$2;
+      auto&& parent_packet_name = *$4;
+      auto&& constraints = *$6;
+      auto&& field_definition_list = *$9;
+
+      DEBUG() << "Packet " << packet_name << " with parent " << parent_packet_name << "\n";
+      DEBUG() << "PACKET FIELD LIST SIZE: " << field_definition_list.size() << "\n";
+      DEBUG() << "CONSTRAINT LIST SIZE: " << constraints.size() << "\n";
+
+      auto parent_packet = decls->GetPacketDef(parent_packet_name);
+      if (parent_packet == nullptr) {
+        ERRORLOC(LOC) << "Could not find packet " << parent_packet_name
+                  << " used as parent for " << packet_name;
+      }
+
+      auto packet_definition = new PacketDef(std::move(packet_name), std::move(field_definition_list), parent_packet);
+      packet_definition->AssignSizeFields();
+
+      for (const auto& constraint : constraints) {
+        const auto& constraint_name = constraint.first;
+        const auto& constraint_value = constraint.second;
+        DEBUG() << "Parent constraint on " << constraint_name;
+        packet_definition->AddParentConstraint(constraint_name, constraint_value);
+      }
+
+      $$ = packet_definition;
+
+      delete $2;
+      delete $4;
+      delete $6;
+      delete $9;
+    }
+
+field_definition_list
+  : /* empty */
+    {
+      DEBUG() << "Empty Field definition\n";
+      $$ = new FieldList();
+    }
+  | field_definition
+    {
+      DEBUG() << "Field definition\n";
+      $$ = new FieldList();
+
+      if ($1->GetFieldType() == GroupField::kFieldType) {
+        auto group_fields = static_cast<GroupField*>($1)->GetFields();
+	FieldList reversed_fields(group_fields->rbegin(), group_fields->rend());
+        for (auto& field : reversed_fields) {
+          $$->PrependField(field);
+        }
+	delete $1;
+        break;
+      }
+
+      $$->PrependField($1);
+    }
+  | field_definition ',' field_definition_list
+    {
+      DEBUG() << "Field definition with list\n";
+      $$ = $3;
+
+      if ($1->GetFieldType() == GroupField::kFieldType) {
+        auto group_fields = static_cast<GroupField*>($1)->GetFields();
+	FieldList reversed_fields(group_fields->rbegin(), group_fields->rend());
+        for (auto& field : reversed_fields) {
+          $$->PrependField(field);
+        }
+	delete $1;
+        break;
+      }
+
+      $$->PrependField($1);
+    }
+
+field_definition
+  : group_field_definition
+    {
+      DEBUG() << "Group Field";
+      $$ = $1;
+    }
+  | type_def_field_definition
+    {
+      DEBUG() << "Field with a pre-defined type\n";
+      $$ = $1;
+    }
+  | scalar_field_definition
+    {
+      DEBUG() << "Scalar field\n";
+      $$ = $1;
+    }
+  | checksum_start_field_definition
+    {
+      DEBUG() << "Checksum start field\n";
+      $$ = $1;
+    }
+  | padding_field_definition
+    {
+      DEBUG() << "Padding field\n";
+      $$ = $1;
+    }
+  | size_field_definition
+    {
+      DEBUG() << "Size field\n";
+      $$ = $1;
+    }
+  | body_field_definition
+    {
+      DEBUG() << "Body field\n";
+      $$ = $1;
+    }
+  | payload_field_definition
+    {
+      DEBUG() << "Payload field\n";
+      $$ = $1;
+    }
+  | fixed_field_definition
+    {
+      DEBUG() << "Fixed field\n";
+      $$ = $1;
+    }
+  | reserved_field_definition
+    {
+      DEBUG() << "Reserved field\n";
+      $$ = $1;
+    }
+  | array_field_definition
+    {
+      DEBUG() << "ARRAY field\n";
+      $$ = $1;
+    }
+
+group_field_definition
+  : IDENTIFIER
+    {
+      auto group = decls->GetGroupDef(*$1);
+      if (group == nullptr) {
+        ERRORLOC(LOC) << "Could not find group with name " << *$1;
+      }
+
+      std::list<PacketField*>* expanded_fields;
+      expanded_fields = new std::list<PacketField*>(group->begin(), group->end());
+      $$ = new GroupField(LOC, expanded_fields);
+      delete $1;
+    }
+  | IDENTIFIER '{' constraint_list '}'
+    {
+      DEBUG() << "Group with fixed field(s) " << *$1 << "\n";
+      auto group = decls->GetGroupDef(*$1);
+      if (group == nullptr) {
+        ERRORLOC(LOC) << "Could not find group with name " << *$1;
+      }
+
+      std::list<PacketField*>* expanded_fields = new std::list<PacketField*>();
+      for (const auto field : *group) {
+        const auto constraint = $3->find(field->GetName());
+        if (constraint != $3->end()) {
+          if (field->GetFieldType() == ScalarField::kFieldType) {
+            DEBUG() << "Fixing group scalar value\n";
+            expanded_fields->push_back(new FixedScalarField(field->GetSize().bits(), std::get<int64_t>(constraint->second), LOC));
+          } else if (field->GetFieldType() == EnumField::kFieldType) {
+            DEBUG() << "Fixing group enum value\n";
+
+            auto type_def = decls->GetTypeDef(field->GetDataType());
+            EnumDef* enum_def = (type_def->GetDefinitionType() == TypeDef::Type::ENUM ? (EnumDef*)type_def : nullptr);
+            if (enum_def == nullptr) {
+              ERRORLOC(LOC) << "No enum found of type " << field->GetDataType();
+            }
+            if (!enum_def->HasEntry(std::get<std::string>(constraint->second))) {
+              ERRORLOC(LOC) << "Enum " << field->GetDataType() << " has no enumeration " << std::get<std::string>(constraint->second);
+            }
+
+            expanded_fields->push_back(new FixedEnumField(enum_def, std::get<std::string>(constraint->second), LOC));
+          } else {
+            ERRORLOC(LOC) << "Unimplemented constraint of type " << field->GetFieldType();
+          }
+          $3->erase(constraint);
+        } else {
+          expanded_fields->push_back(field);
+        }
+      }
+      if ($3->size() > 0) {
+        ERRORLOC(LOC) << "Could not find member " << $3->begin()->first << " in group " << *$1;
+      }
+
+      $$ = new GroupField(LOC, expanded_fields);
+      delete $1;
+      delete $3;
+    }
+
+constraint_list
+  : constraint ',' constraint_list
+    {
+      DEBUG() << "Group field value list\n";
+      $3->insert(*$1);
+      $$ = $3;
+      delete($1);
+    }
+  | constraint
+    {
+      DEBUG() << "Group field value\n";
+      $$ = new std::map<std::string, std::variant<int64_t, std::string>>();
+      $$->insert(*$1);
+      delete($1);
+    }
+
+constraint
+  : IDENTIFIER '=' INTEGER
+    {
+      DEBUG() << "Group with a fixed integer value=" << $1 << " value=" << $3 << "\n";
+
+      $$ = new std::pair(*$1, std::variant<int64_t,std::string>($3));
+      delete $1;
+    }
+  | IDENTIFIER '=' IDENTIFIER
+    {
+      DEBUG() << "Group with a fixed enum field value=" << *$3 << " enum=" << *$1;
+
+      $$ = new std::pair(*$1, std::variant<int64_t,std::string>(*$3));
+      delete $1;
+      delete $3;
+    }
+
+type_def_field_definition
+  : IDENTIFIER ':' IDENTIFIER
+    {
+      DEBUG() << "Predefined type field " << *$1 << " : " << *$3 << "\n";
+      if (auto type_def = decls->GetTypeDef(*$3)) {
+        $$ = type_def->GetNewField(*$1, LOC);
+      } else {
+        ERRORLOC(LOC) << "No type with this name\n";
+      }
+      delete $1;
+      delete $3;
+    }
+
+scalar_field_definition
+  : IDENTIFIER ':' INTEGER
+    {
+      DEBUG() << "Scalar field " << *$1 << " : " << $3 << "\n";
+      $$ = new ScalarField(*$1, $3, LOC);
+      delete $1;
+    }
+
+body_field_definition
+  : BODY
+    {
+      DEBUG() << "Body field\n";
+      $$ = new BodyField(LOC);
+    }
+
+payload_field_definition
+  : PAYLOAD ':' '[' SIZE_MODIFIER ']'
+    {
+      DEBUG() << "Payload field with modifier " << *$4 << "\n";
+      $$ = new PayloadField(*$4, LOC);
+      delete $4;
+    }
+  | PAYLOAD
+    {
+      DEBUG() << "Payload field\n";
+      $$ = new PayloadField("", LOC);
+    }
+
+checksum_start_field_definition
+  : CHECKSUM_START '(' IDENTIFIER ')'
+    {
+      DEBUG() << "ChecksumStart field defined\n";
+      $$ = new ChecksumStartField(*$3, LOC);
+      delete $3;
+    }
+
+padding_field_definition
+  : PADDING '[' INTEGER ']'
+    {
+      DEBUG() << "Padding field defined\n";
+      $$ = new PaddingField($3, LOC);
+    }
+
+size_field_definition
+  : SIZE '(' IDENTIFIER ')' ':' INTEGER
+    {
+      DEBUG() << "Size field defined\n";
+      $$ = new SizeField(*$3, $6, LOC);
+      delete $3;
+    }
+  | SIZE '(' PAYLOAD ')' ':' INTEGER
+    {
+      DEBUG() << "Size for payload defined\n";
+      $$ = new SizeField("payload", $6, LOC);
+    }
+  | COUNT '(' IDENTIFIER ')' ':' INTEGER
+    {
+      DEBUG() << "Count field defined\n";
+      $$ = new CountField(*$3, $6, LOC);
+      delete $3;
+    }
+
+fixed_field_definition
+  : FIXED '=' INTEGER ':' INTEGER
+    {
+      DEBUG() << "Fixed field defined value=" << $3 << " size=" << $5 << "\n";
+      $$ = new FixedScalarField($5, $3, LOC);
+    }
+  | FIXED '=' IDENTIFIER ':' IDENTIFIER
+    {
+      DEBUG() << "Fixed enum field defined value=" << *$3 << " enum=" << *$5;
+      auto type_def = decls->GetTypeDef(*$5);
+      if (type_def != nullptr) {
+        EnumDef* enum_def = (type_def->GetDefinitionType() == TypeDef::Type::ENUM ? (EnumDef*)type_def : nullptr);
+        if (!enum_def->HasEntry(*$3)) {
+          ERRORLOC(LOC) << "Previously defined enum " << enum_def->GetTypeName() << " has no entry for " << *$3;
+        }
+
+        $$ = new FixedEnumField(enum_def, *$3, LOC);
+      } else {
+        ERRORLOC(LOC) << "No enum found with name " << *$5;
+      }
+
+      delete $3;
+      delete $5;
+    }
+
+reserved_field_definition
+  : RESERVED ':' INTEGER
+    {
+      DEBUG() << "Reserved field of size=" << $3 << "\n";
+      $$ = new ReservedField($3, LOC);
+    }
+
+array_field_definition
+  : IDENTIFIER ':' INTEGER '[' ']'
+    {
+      DEBUG() << "Vector field defined name=" << *$1 << " element_size=" << $3;
+      $$ = new VectorField(*$1, $3, "", LOC);
+      delete $1;
+    }
+  | IDENTIFIER ':' INTEGER '[' SIZE_MODIFIER ']'
+    {
+      DEBUG() << "Vector field defined name=" << *$1 << " element_size=" << $3
+             << " size_modifier=" << *$5;
+      $$ = new VectorField(*$1, $3, *$5, LOC);
+      delete $1;
+      delete $5;
+    }
+  | IDENTIFIER ':' INTEGER '[' INTEGER ']'
+    {
+      DEBUG() << "Array field defined name=" << *$1 << " element_size=" << $3
+             << " fixed_size=" << $5;
+      $$ = new ArrayField(*$1, $3, $5, LOC);
+      delete $1;
+    }
+  | IDENTIFIER ':' IDENTIFIER '[' ']'
+    {
+      DEBUG() << "Vector field defined name=" << *$1 << " type=" << *$3;
+      if (auto type_def = decls->GetTypeDef(*$3)) {
+        $$ = new VectorField(*$1, type_def, "", LOC);
+      } else {
+        ERRORLOC(LOC) << "Can't find type used in array field.";
+      }
+      delete $1;
+      delete $3;
+    }
+  | IDENTIFIER ':' IDENTIFIER '[' SIZE_MODIFIER ']'
+    {
+      DEBUG() << "Vector field defined name=" << *$1 << " type=" << *$3
+             << " size_modifier=" << *$5;
+      if (auto type_def = decls->GetTypeDef(*$3)) {
+        $$ = new VectorField(*$1, type_def, *$5, LOC);
+      } else {
+        ERRORLOC(LOC) << "Can't find type used in array field.";
+      }
+      delete $1;
+      delete $3;
+      delete $5;
+    }
+  | IDENTIFIER ':' IDENTIFIER '[' INTEGER ']'
+    {
+      DEBUG() << "Array field defined name=" << *$1 << " type=" << *$3
+             << " fixed_size=" << $5;
+      if (auto type_def = decls->GetTypeDef(*$3)) {
+        $$ = new ArrayField(*$1, type_def, $5, LOC);
+      } else {
+        ERRORLOC(LOC) << "Can't find type used in array field.";
+      }
+      delete $1;
+      delete $3;
+    }
+
+%%
+
+
+void yy::parser::error(const yy::parser::location_type& loc, const std::string& error) {
+  ERROR() << error << " at location " << loc << "\n";
+  abort();
+}
diff --git a/gd/packet/parser/logging.h b/gd/packet/parser/logging.h
new file mode 100644
index 0000000..a2939ef
--- /dev/null
+++ b/gd/packet/parser/logging.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <initializer_list>
+#include <iostream>
+#include <sstream>
+#include <vector>
+
+#include "parse_location.h"
+
+class Loggable {
+ public:
+  virtual ~Loggable() = default;
+  virtual std::string GetDebugName() const = 0;
+  virtual ParseLocation GetLocation() const = 0;
+};
+
+class LogMessage {
+ public:
+  LogMessage(ParseLocation loc, std::initializer_list<const Loggable*> tokens)
+      : debug_(false), loc_(loc), tokens_(tokens) {
+    Init();
+  }
+
+  LogMessage(bool debug, std::initializer_list<const Loggable*> tokens) : debug_(debug), tokens_(tokens) {
+    Init();
+  }
+
+  void Init() {
+    if (loc_.GetLine() != -1) {
+      stream_ << "\033[1mLine " << loc_.GetLine() << ": ";
+    }
+
+    if (!debug_) {
+      stream_ << "\033[1;31m";
+      stream_ << "ERROR: ";
+      stream_ << "\033[0m";
+    } else {
+      stream_ << "\033[1;m";
+      stream_ << "DEBUG: ";
+      stream_ << "\033[0m";
+    }
+  }
+
+  ~LogMessage() {
+    if (debug_ && suppress_debug_) return;
+
+    std::cerr << stream_.str() << "\n";
+    for (const auto& token : tokens_) {
+      // Bold line number
+      std::cerr << "\033[1m";
+      std::cerr << "  Line " << token->GetLocation().GetLine() << ": ";
+      std::cerr << "\033[0m";
+      std::cerr << token->GetDebugName() << "\n";
+    }
+
+    if (!debug_) {
+      abort();
+    }
+  }
+
+  std::ostream& stream() {
+    return stream_;
+  }
+
+ private:
+  std::ostringstream stream_;
+  bool debug_;
+  bool suppress_debug_{true};
+  ParseLocation loc_;
+  std::vector<const Loggable*> tokens_;
+};
+
+// Error Log stream. Aborts the program after the message is printed.
+// The arguments to the macro is a list of Loggable objects that are printed when the error is printed.
+#define ERROR(...) LogMessage(false, {__VA_ARGS__}).stream()
+
+// ParseLocation error log, the first argument is a location.
+#define ERRORLOC(_1, ...) LogMessage(_1, {__VA_ARGS__}).stream()
+
+#define DEBUG(...) LogMessage(true, {__VA_ARGS__}).stream()
diff --git a/gd/packet/parser/main.cc b/gd/packet/parser/main.cc
new file mode 100644
index 0000000..8b6737a
--- /dev/null
+++ b/gd/packet/parser/main.cc
@@ -0,0 +1,449 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <unistd.h>
+#include <cerrno>
+#include <cstdio>
+#include <filesystem>
+#include <fstream>
+#include <iostream>
+#include <queue>
+#include <regex>
+#include <sstream>
+#include <vector>
+
+#include "declarations.h"
+#include "struct_parser_generator.h"
+
+#include "language_y.h"
+
+void yylex_init(void**);
+void yylex_destroy(void*);
+void yyset_debug(int, void*);
+void yyset_in(FILE*, void*);
+
+namespace {
+
+void parse_namespace(const std::string& root_namespace, const std::filesystem::path& input_file_relative_path,
+                     std::vector<std::string>* token) {
+  std::filesystem::path gen_namespace = root_namespace / input_file_relative_path;
+  std::string gen_namespace_str = gen_namespace;
+  std::regex path_tokenizer("/");
+  auto it = std::sregex_token_iterator(gen_namespace_str.cbegin(), gen_namespace_str.cend(), path_tokenizer, -1);
+  std::sregex_token_iterator it_end = {};
+  for (; it != it_end; ++it) {
+    token->push_back(it->str());
+  }
+}
+
+void generate_namespace_open(const std::vector<std::string>& token, std::ostream& output) {
+  for (const auto& ns : token) {
+    output << "namespace " << ns << " {" << std::endl;
+  }
+}
+
+void generate_namespace_close(const std::vector<std::string>& token, std::ostream& output) {
+  for (auto it = token.rbegin(); it != token.rend(); ++it) {
+    output << "}  //namespace " << *it << std::endl;
+  }
+}
+
+bool parse_declarations_one_file(const std::filesystem::path& input_file, Declarations* declarations) {
+  void* scanner;
+  yylex_init(&scanner);
+
+  FILE* in_file = fopen(input_file.string().c_str(), "r");
+  if (in_file == nullptr) {
+    std::cerr << "can't open " << input_file << ": " << strerror(errno) << std::endl;
+    return false;
+  }
+
+  yyset_in(in_file, scanner);
+
+  int ret = yy::parser(scanner, declarations).parse();
+  if (ret != 0) {
+    std::cerr << "yylex parsing failed: returned " << ret << std::endl;
+    return false;
+  }
+
+  yylex_destroy(scanner);
+
+  fclose(in_file);
+
+  // Set endianess before returning
+  for (auto& s : declarations->type_defs_queue_) {
+    if (s.second->GetDefinitionType() == TypeDef::Type::STRUCT) {
+      auto* struct_def = dynamic_cast<StructDef*>(s.second);
+      struct_def->SetEndianness(declarations->is_little_endian);
+    }
+  }
+
+  for (auto& packet_def : declarations->packet_defs_queue_) {
+    packet_def.second.SetEndianness(declarations->is_little_endian);
+  }
+
+  return true;
+}
+
+bool generate_cpp_headers_one_file(const Declarations& decls, const std::filesystem::path& input_file,
+                                   const std::filesystem::path& include_dir, const std::filesystem::path& out_dir,
+                                   const std::string& root_namespace) {
+  auto gen_relative_path = input_file.lexically_relative(include_dir).parent_path();
+
+  auto input_filename = input_file.filename().string().substr(0, input_file.filename().string().find(".pdl"));
+  auto gen_path = out_dir / gen_relative_path;
+
+  std::filesystem::create_directories(gen_path);
+
+  auto gen_file = gen_path / (input_filename + ".h");
+
+  std::ofstream out_file;
+  out_file.open(gen_file);
+  if (!out_file.is_open()) {
+    std::cerr << "can't open " << gen_file << std::endl;
+    return false;
+  }
+
+  out_file << "\n\n";
+  out_file << "#pragma once\n";
+  out_file << "\n\n";
+  out_file << "#include <stdint.h>\n";
+  out_file << "#include <string>\n";
+  out_file << "#include <functional>\n";
+  out_file << "\n\n";
+  out_file << "#include \"os/log.h\"\n";
+  out_file << "#include \"packet/base_packet_builder.h\"\n";
+  out_file << "#include \"packet/bit_inserter.h\"\n";
+  out_file << "#include \"packet/iterator.h\"\n";
+  out_file << "#include \"packet/packet_builder.h\"\n";
+  out_file << "#include \"packet/packet_struct.h\"\n";
+  out_file << "#include \"packet/packet_view.h\"\n";
+  out_file << "#include \"packet/parser/checksum_type_checker.h\"\n";
+  out_file << "#include \"packet/parser/custom_type_checker.h\"\n";
+  out_file << "\n\n";
+
+  for (const auto& c : decls.type_defs_queue_) {
+    if (c.second->GetDefinitionType() == TypeDef::Type::CUSTOM ||
+        c.second->GetDefinitionType() == TypeDef::Type::CHECKSUM) {
+      ((CustomFieldDef*)c.second)->GenInclude(out_file);
+    }
+  }
+  out_file << "\n\n";
+
+  std::vector<std::string> namespace_list;
+  parse_namespace(root_namespace, gen_relative_path, &namespace_list);
+  generate_namespace_open(namespace_list, out_file);
+  out_file << "\n\n";
+
+  for (const auto& c : decls.type_defs_queue_) {
+    if (c.second->GetDefinitionType() == TypeDef::Type::CUSTOM ||
+        c.second->GetDefinitionType() == TypeDef::Type::CHECKSUM) {
+      ((CustomFieldDef*)c.second)->GenUsing(out_file);
+    }
+  }
+  out_file << "\n\n";
+
+  out_file << "using ::bluetooth::packet::BasePacketBuilder;";
+  out_file << "using ::bluetooth::packet::BitInserter;";
+  out_file << "using ::bluetooth::packet::CustomTypeChecker;";
+  out_file << "using ::bluetooth::packet::Iterator;";
+  out_file << "using ::bluetooth::packet::kLittleEndian;";
+  out_file << "using ::bluetooth::packet::PacketBuilder;";
+  out_file << "using ::bluetooth::packet::PacketStruct;";
+  out_file << "using ::bluetooth::packet::PacketView;";
+  out_file << "using ::bluetooth::packet::parser::ChecksumTypeChecker;";
+  out_file << "\n\n";
+
+  for (const auto& e : decls.type_defs_queue_) {
+    if (e.second->GetDefinitionType() == TypeDef::Type::ENUM) {
+      const auto* enum_def = dynamic_cast<const EnumDef*>(e.second);
+      EnumGen gen(*enum_def);
+      gen.GenDefinition(out_file);
+      out_file << "\n\n";
+    }
+  }
+  for (const auto& e : decls.type_defs_queue_) {
+    if (e.second->GetDefinitionType() == TypeDef::Type::ENUM) {
+      const auto* enum_def = dynamic_cast<const EnumDef*>(e.second);
+      EnumGen gen(*enum_def);
+      gen.GenLogging(out_file);
+      out_file << "\n\n";
+    }
+  }
+  for (const auto& ch : decls.type_defs_queue_) {
+    if (ch.second->GetDefinitionType() == TypeDef::Type::CHECKSUM) {
+      const auto* checksum_def = dynamic_cast<const ChecksumDef*>(ch.second);
+      checksum_def->GenChecksumCheck(out_file);
+    }
+  }
+  out_file << "\n/* Done ChecksumChecks */\n";
+
+  for (const auto& c : decls.type_defs_queue_) {
+    if (c.second->GetDefinitionType() == TypeDef::Type::CUSTOM && c.second->size_ == -1 /* Variable Size */) {
+      const auto* custom_field_def = dynamic_cast<const CustomFieldDef*>(c.second);
+      custom_field_def->GenCustomFieldCheck(out_file, decls.is_little_endian);
+    }
+  }
+  out_file << "\n";
+
+  for (auto& s : decls.type_defs_queue_) {
+    if (s.second->GetDefinitionType() == TypeDef::Type::STRUCT) {
+      const auto* struct_def = dynamic_cast<const StructDef*>(s.second);
+      struct_def->GenDefinition(out_file);
+      out_file << "\n";
+    }
+  }
+
+  {
+    StructParserGenerator spg(decls);
+    spg.Generate(out_file);
+    out_file << "\n\n";
+  }
+
+  for (const auto& packet_def : decls.packet_defs_queue_) {
+    packet_def.second.GenParserDefinition(out_file);
+    out_file << "\n\n";
+  }
+
+  for (const auto& packet_def : decls.packet_defs_queue_) {
+    packet_def.second.GenBuilderDefinition(out_file);
+    out_file << "\n\n";
+  }
+
+  generate_namespace_close(namespace_list, out_file);
+
+  out_file.close();
+
+  return true;
+}
+
+// Get the out_file shard at a symbol_count
+std::ofstream& get_out_file(size_t symbol_count, size_t symbol_total, std::vector<std::ofstream>* out_files) {
+  auto symbols_per_shard = symbol_total / out_files->size();
+  auto file_index = std::min(symbol_count / symbols_per_shard, out_files->size() - 1);
+  return out_files->at(file_index);
+}
+
+bool generate_pybind11_sources_one_file(const Declarations& decls, const std::filesystem::path& input_file,
+                                        const std::filesystem::path& include_dir, const std::filesystem::path& out_dir,
+                                        const std::string& root_namespace, size_t num_shards) {
+  auto gen_relative_path = input_file.lexically_relative(include_dir).parent_path();
+
+  auto input_filename = input_file.filename().string().substr(0, input_file.filename().string().find(".pdl"));
+  auto gen_path = out_dir / gen_relative_path;
+
+  std::filesystem::create_directories(gen_path);
+
+  auto gen_relative_header = gen_relative_path / (input_filename + ".h");
+
+  std::vector<std::string> namespace_list;
+  parse_namespace(root_namespace, gen_relative_path, &namespace_list);
+
+  std::vector<std::ofstream> out_file_shards(num_shards);
+  for (size_t i = 0; i < out_file_shards.size(); i++) {
+    auto filename = gen_path / (input_filename + "_python3_shard_" + std::to_string(i) + ".cc");
+    auto& out_file = out_file_shards[i];
+    out_file.open(filename);
+    if (!out_file.is_open()) {
+      std::cerr << "can't open " << filename << std::endl;
+      return false;
+    }
+    out_file << "#include <pybind11/pybind11.h>\n";
+    out_file << "#include <pybind11/stl.h>\n";
+    out_file << "\n\n";
+    out_file << "#include " << gen_relative_header << "\n";
+    out_file << "\n\n";
+    out_file << "#include \"packet/raw_builder.h\"\n";
+    out_file << "\n\n";
+
+    for (const auto& c : decls.type_defs_queue_) {
+      if (c.second->GetDefinitionType() == TypeDef::Type::CUSTOM) {
+        const auto* custom_def = dynamic_cast<const CustomFieldDef*>(c.second);
+        custom_def->GenPyBind11Include(out_file);
+      }
+    }
+
+    out_file << "\n\n";
+
+    generate_namespace_open(namespace_list, out_file);
+    out_file << "\n\n";
+
+    for (const auto& c : decls.type_defs_queue_) {
+      if (c.second->GetDefinitionType() == TypeDef::Type::CUSTOM ||
+          c.second->GetDefinitionType() == TypeDef::Type::CHECKSUM) {
+        const auto* custom_def = dynamic_cast<const CustomFieldDef*>(c.second);
+        custom_def->GenUsing(out_file);
+      }
+    }
+    out_file << "\n\n";
+
+    out_file << "using ::bluetooth::packet::BasePacketBuilder;";
+    out_file << "using ::bluetooth::packet::BitInserter;";
+    out_file << "using ::bluetooth::packet::CustomTypeChecker;";
+    out_file << "using ::bluetooth::packet::Iterator;";
+    out_file << "using ::bluetooth::packet::kLittleEndian;";
+    out_file << "using ::bluetooth::packet::PacketBuilder;";
+    out_file << "using ::bluetooth::packet::BaseStruct;";
+    out_file << "using ::bluetooth::packet::PacketStruct;";
+    out_file << "using ::bluetooth::packet::PacketView;";
+    out_file << "using ::bluetooth::packet::RawBuilder;";
+    out_file << "using ::bluetooth::packet::parser::ChecksumTypeChecker;";
+    out_file << "\n\n";
+
+    out_file << "namespace py = pybind11;\n\n";
+
+    out_file << "void define_" << input_filename << "_submodule_shard_" << std::to_string(i) << "(py::module& m) {\n\n";
+  }
+  size_t symbol_total = 0;
+  // Only count types that will be generated
+  for (const auto& e : decls.type_defs_queue_) {
+    if (e.second->GetDefinitionType() == TypeDef::Type::ENUM) {
+      symbol_total++;
+    } else if (e.second->GetDefinitionType() == TypeDef::Type::STRUCT) {
+      symbol_total++;
+    }
+  }
+  // View and builder are counted separately
+  symbol_total += decls.packet_defs_queue_.size() * 2;
+  size_t symbol_count = 0;
+
+  for (const auto& e : decls.type_defs_queue_) {
+    if (e.second->GetDefinitionType() == TypeDef::Type::ENUM) {
+      const auto* enum_def = dynamic_cast<const EnumDef*>(e.second);
+      EnumGen gen(*enum_def);
+      auto& out_file = get_out_file(symbol_count, symbol_total, &out_file_shards);
+      gen.GenDefinitionPybind11(out_file);
+      out_file << "\n\n";
+      symbol_count++;
+    }
+  }
+
+  for (const auto& s : decls.type_defs_queue_) {
+    if (s.second->GetDefinitionType() == TypeDef::Type::STRUCT) {
+      const auto* struct_def = dynamic_cast<const StructDef*>(s.second);
+      auto& out_file = get_out_file(symbol_count, symbol_total, &out_file_shards);
+      struct_def->GenDefinitionPybind11(out_file);
+      out_file << "\n";
+      symbol_count++;
+    }
+  }
+
+  for (const auto& packet_def : decls.packet_defs_queue_) {
+    auto& out_file = get_out_file(symbol_count, symbol_total, &out_file_shards);
+    packet_def.second.GenParserDefinitionPybind11(out_file);
+    out_file << "\n\n";
+    symbol_count++;
+  }
+
+  for (const auto& p : decls.packet_defs_queue_) {
+    auto& out_file = get_out_file(symbol_count, symbol_total, &out_file_shards);
+    p.second.GenBuilderDefinitionPybind11(out_file);
+    out_file << "\n\n";
+    symbol_count++;
+  }
+
+  for (auto& out_file : out_file_shards) {
+    out_file << "}\n\n";
+    generate_namespace_close(namespace_list, out_file);
+  }
+
+  auto gen_file_main = gen_path / (input_filename + "_python3.cc");
+  std::ofstream out_file_main;
+  out_file_main.open(gen_file_main);
+  if (!out_file_main.is_open()) {
+    std::cerr << "can't open " << gen_file_main << std::endl;
+    return false;
+  }
+  out_file_main << "#include <pybind11/pybind11.h>\n";
+  generate_namespace_open(namespace_list, out_file_main);
+
+  out_file_main << "namespace py = pybind11;\n\n";
+
+  for (size_t i = 0; i < out_file_shards.size(); i++) {
+    out_file_main << "void define_" << input_filename << "_submodule_shard_" << std::to_string(i)
+                  << "(py::module& m);\n";
+  }
+
+  out_file_main << "void define_" << input_filename << "_submodule(py::module& m) {\n\n";
+  for (size_t i = 0; i < out_file_shards.size(); i++) {
+    out_file_main << "define_" << input_filename << "_submodule_shard_" << std::to_string(i) << "(m);\n";
+  }
+  out_file_main << "}\n\n";
+
+  generate_namespace_close(namespace_list, out_file_main);
+
+  return true;
+}
+
+}  // namespace
+
+// TODO(b/141583809): stop leaks
+extern "C" const char* __asan_default_options() {
+  return "detect_leaks=0";
+}
+
+int main(int argc, const char** argv) {
+  std::filesystem::path out_dir;
+  std::filesystem::path include_dir;
+  std::string root_namespace = "bluetooth";
+  // Number of shards per output pybind11 cc file
+  size_t num_shards = 1;
+  std::queue<std::filesystem::path> input_files;
+  const std::string arg_out = "--out=";
+  const std::string arg_include = "--include=";
+  const std::string arg_namespace = "--root_namespace=";
+  const std::string arg_num_shards = "--num_shards=";
+
+  for (int i = 1; i < argc; i++) {
+    std::string arg = argv[i];
+    if (arg.find(arg_out) == 0) {
+      out_dir = std::filesystem::current_path() / std::filesystem::path(arg.substr(arg_out.size()));
+    } else if (arg.find(arg_include) == 0) {
+      include_dir = std::filesystem::current_path() / std::filesystem::path(arg.substr(arg_include.size()));
+    } else if (arg.find(arg_namespace) == 0) {
+      root_namespace = arg.substr(arg_namespace.size());
+    } else if (arg.find(arg_num_shards) == 0) {
+      num_shards = std::stoul(arg.substr(arg_num_shards.size()));
+    } else {
+      input_files.emplace(std::filesystem::current_path() / std::filesystem::path(arg));
+    }
+  }
+  if (out_dir == std::filesystem::path() || include_dir == std::filesystem::path() || num_shards == 0) {
+    std::cerr << "Usage: bt-packetgen --out=OUT --include=INCLUDE --root_namespace=NAMESPACE --num_shards=NUM_SHARDS "
+              << "input_files..." << std::endl;
+    return 1;
+  }
+
+  while (!input_files.empty()) {
+    Declarations declarations;
+    if (!parse_declarations_one_file(input_files.front(), &declarations)) {
+      std::cerr << "Cannot parse " << input_files.front() << " correctly" << std::endl;
+      return 2;
+    }
+    if (!generate_cpp_headers_one_file(declarations, input_files.front(), include_dir, out_dir, root_namespace)) {
+      std::cerr << "Didn't generate cpp headers for " << input_files.front() << std::endl;
+      return 3;
+    }
+    if (!generate_pybind11_sources_one_file(declarations, input_files.front(), include_dir, out_dir, root_namespace,
+                                            num_shards)) {
+      std::cerr << "Didn't generate pybind11 sources for " << input_files.front() << std::endl;
+      return 4;
+    }
+    input_files.pop();
+  }
+
+  return 0;
+}
diff --git a/gd/packet/parser/packet_def.cc b/gd/packet/parser/packet_def.cc
new file mode 100644
index 0000000..c2e8ac0
--- /dev/null
+++ b/gd/packet/parser/packet_def.cc
@@ -0,0 +1,705 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "packet_def.h"
+
+#include <list>
+#include <set>
+
+#include "fields/all_fields.h"
+#include "util.h"
+
+PacketDef::PacketDef(std::string name, FieldList fields) : ParentDef(name, fields, nullptr) {}
+PacketDef::PacketDef(std::string name, FieldList fields, PacketDef* parent) : ParentDef(name, fields, parent) {}
+
+PacketField* PacketDef::GetNewField(const std::string&, ParseLocation) const {
+  return nullptr;  // Packets can't be fields
+}
+
+void PacketDef::GenParserDefinition(std::ostream& s) const {
+  s << "class " << name_ << "View";
+  if (parent_ != nullptr) {
+    s << " : public " << parent_->name_ << "View {";
+  } else {
+    s << " : public PacketView<" << (is_little_endian_ ? "" : "!") << "kLittleEndian> {";
+  }
+  s << " public:";
+
+  // Specialize function
+  if (parent_ != nullptr) {
+    s << "static " << name_ << "View Create(" << parent_->name_ << "View parent)";
+    s << "{ return " << name_ << "View(parent); }";
+  } else {
+    s << "static " << name_ << "View Create(PacketView<" << (is_little_endian_ ? "" : "!") << "kLittleEndian> packet) ";
+    s << "{ return " << name_ << "View(packet); }";
+  }
+
+  std::set<std::string> fixed_types = {
+      FixedScalarField::kFieldType,
+      FixedEnumField::kFieldType,
+  };
+
+  // Print all of the public fields which are all the fields minus the fixed fields.
+  const auto& public_fields = fields_.GetFieldsWithoutTypes(fixed_types);
+  bool has_fixed_fields = public_fields.size() != fields_.size();
+  for (const auto& field : public_fields) {
+    GenParserFieldGetter(s, field);
+    s << "\n";
+  }
+  GenValidator(s);
+  s << "\n";
+
+  s << " protected:\n";
+  // Constructor from a View
+  if (parent_ != nullptr) {
+    s << name_ << "View(" << parent_->name_ << "View parent)";
+    s << " : " << parent_->name_ << "View(parent) { was_validated_ = false; }";
+  } else {
+    s << name_ << "View(PacketView<" << (is_little_endian_ ? "" : "!") << "kLittleEndian> packet) ";
+    s << " : PacketView<" << (is_little_endian_ ? "" : "!") << "kLittleEndian>(packet) { was_validated_ = false;}";
+  }
+
+  // Print the private fields which are the fixed fields.
+  if (has_fixed_fields) {
+    const auto& private_fields = fields_.GetFieldsWithTypes(fixed_types);
+    s << " private:\n";
+    for (const auto& field : private_fields) {
+      GenParserFieldGetter(s, field);
+      s << "\n";
+    }
+  }
+  s << "};\n";
+}
+
+void PacketDef::GenParserDefinitionPybind11(std::ostream& s) const {
+  s << "py::class_<" << name_ << "View";
+  if (parent_ != nullptr) {
+    s << ", " << parent_->name_ << "View";
+  } else {
+    s << ", PacketView<" << (is_little_endian_ ? "" : "!") << "kLittleEndian>";
+  }
+  s << ">(m, \"" << name_ << "View\")";
+  if (parent_ != nullptr) {
+    s << ".def(py::init([](" << parent_->name_ << "View parent) {";
+  } else {
+    s << ".def(py::init([](PacketView<" << (is_little_endian_ ? "" : "!") << "kLittleEndian> parent) {";
+  }
+  s << "auto view =" << name_ << "View::Create(std::move(parent));";
+  s << "if (!view.IsValid()) { throw std::invalid_argument(\"Bad packet view\"); }";
+  s << "return view; }))";
+
+  s << ".def(py::init(&" << name_ << "View::Create))";
+  std::set<std::string> protected_field_types = {
+      FixedScalarField::kFieldType,
+      FixedEnumField::kFieldType,
+      SizeField::kFieldType,
+      CountField::kFieldType,
+  };
+  const auto& public_fields = fields_.GetFieldsWithoutTypes(protected_field_types);
+  for (const auto& field : public_fields) {
+    auto getter_func_name = field->GetGetterFunctionName();
+    if (getter_func_name.empty()) {
+      continue;
+    }
+    s << ".def(\"" << getter_func_name << "\", &" << name_ << "View::" << getter_func_name << ")";
+  }
+  s << ".def(\"IsValid\", &" << name_ << "View::IsValid)";
+  s << ";\n";
+}
+
+void PacketDef::GenParserFieldGetter(std::ostream& s, const PacketField* field) const {
+  // Start field offset
+  auto start_field_offset = GetOffsetForField(field->GetName(), false);
+  auto end_field_offset = GetOffsetForField(field->GetName(), true);
+
+  if (start_field_offset.empty() && end_field_offset.empty()) {
+    ERROR(field) << "Field location for " << field->GetName() << " is ambiguous, "
+                 << "no method exists to determine field location from begin() or end().\n";
+  }
+
+  field->GenGetter(s, start_field_offset, end_field_offset);
+}
+
+TypeDef::Type PacketDef::GetDefinitionType() const {
+  return TypeDef::Type::PACKET;
+}
+
+void PacketDef::GenValidator(std::ostream& s) const {
+  // Get the static offset for all of our fields.
+  int bits_size = 0;
+  for (const auto& field : fields_) {
+    if (field->GetFieldType() != PaddingField::kFieldType) {
+      bits_size += field->GetSize().bits();
+    }
+  }
+
+  // Write the function declaration.
+  s << "virtual bool IsValid() " << (parent_ != nullptr ? " override" : "") << " {";
+  s << "if (was_validated_) { return true; } ";
+  s << "else { was_validated_ = true; was_validated_ = IsValid_(); return was_validated_; }";
+  s << "}";
+
+  s << "protected:";
+  s << "virtual bool IsValid_() const {";
+
+  // Offset by the parents known size. We know that any dynamic fields can
+  // already be called since the parent must have already been validated by
+  // this point.
+  auto parent_size = Size(0);
+  if (parent_ != nullptr) {
+    parent_size = parent_->GetSize(true);
+  }
+
+  s << "auto it = begin() + (" << parent_size << ") / 8;";
+
+  // Check if you can extract the static fields.
+  // At this point you know you can use the size getters without crashing
+  // as long as they follow the instruction that size fields cant come before
+  // their corrisponding variable length field.
+  s << "it += " << ((bits_size + 7) / 8) << " /* Total size of the fixed fields */;";
+  s << "if (it > end()) return false;";
+
+  // For any variable length fields, use their size check.
+  for (const auto& field : fields_) {
+    if (field->GetFieldType() == ChecksumStartField::kFieldType) {
+      auto offset = GetOffsetForField(field->GetName(), false);
+      if (!offset.empty()) {
+        s << "size_t sum_index = (" << offset << ") / 8;";
+      } else {
+        offset = GetOffsetForField(field->GetName(), true);
+        if (offset.empty()) {
+          ERROR(field) << "Checksum Start Field offset can not be determined.";
+        }
+        s << "size_t sum_index = size() - (" << offset << ") / 8;";
+      }
+
+      const auto& field_name = ((ChecksumStartField*)field)->GetStartedFieldName();
+      const auto& started_field = fields_.GetField(field_name);
+      if (started_field == nullptr) {
+        ERROR(field) << __func__ << ": Can't find checksum field named " << field_name << "(" << field->GetName()
+                     << ")";
+      }
+      auto end_offset = GetOffsetForField(started_field->GetName(), false);
+      if (!end_offset.empty()) {
+        s << "size_t end_sum_index = (" << end_offset << ") / 8;";
+      } else {
+        end_offset = GetOffsetForField(started_field->GetName(), true);
+        if (end_offset.empty()) {
+          ERROR(started_field) << "Checksum Field end_offset can not be determined.";
+        }
+        s << "size_t end_sum_index = size() - (" << started_field->GetSize() << " - " << end_offset << ") / 8;";
+      }
+      if (is_little_endian_) {
+        s << "auto checksum_view = GetLittleEndianSubview(sum_index, end_sum_index);";
+      } else {
+        s << "auto checksum_view = GetBigEndianSubview(sum_index, end_sum_index);";
+      }
+      s << started_field->GetDataType() << " checksum;";
+      s << "checksum.Initialize();";
+      s << "for (uint8_t byte : checksum_view) { ";
+      s << "checksum.AddByte(byte);}";
+      s << "if (checksum.GetChecksum() != (begin() + end_sum_index).extract<"
+        << util::GetTypeForSize(started_field->GetSize().bits()) << ">()) { return false; }";
+
+      continue;
+    }
+
+    auto field_size = field->GetSize();
+    // Fixed size fields have already been handled.
+    if (!field_size.has_dynamic()) {
+      continue;
+    }
+
+    // Custom fields with dynamic size must have the offset for the field passed in as well
+    // as the end iterator so that they may ensure that they don't try to read past the end.
+    // Custom fields with fixed sizes will be handled in the static offset checking.
+    if (field->GetFieldType() == CustomField::kFieldType) {
+      // Check if we can determine offset from begin(), otherwise error because by this point,
+      // the size of the custom field is unknown and can't be subtracted from end() to get the
+      // offset.
+      auto offset = GetOffsetForField(field->GetName(), false);
+      if (offset.empty()) {
+        ERROR(field) << "Custom Field offset can not be determined from begin().";
+      }
+
+      if (offset.bits() % 8 != 0) {
+        ERROR(field) << "Custom fields must be byte aligned.";
+      }
+
+      // Custom fields are special as their size field takes an argument.
+      const auto& custom_size_var = field->GetName() + "_size";
+      s << "const auto& " << custom_size_var << " = " << field_size.dynamic_string();
+      s << "(begin() + (" << offset << ") / 8);";
+
+      s << "if (!" << custom_size_var << ".has_value()) { return false; }";
+      s << "it += *" << custom_size_var << ";";
+      s << "if (it > end()) return false;";
+      continue;
+    } else {
+      s << "it += (" << field_size.dynamic_string() << ") / 8;";
+      s << "if (it > end()) return false;";
+    }
+  }
+
+  // Validate constraints after validating the size
+  if (parent_constraints_.size() > 0 && parent_ == nullptr) {
+    ERROR() << "Can't have a constraint on a NULL parent";
+  }
+
+  for (const auto& constraint : parent_constraints_) {
+    s << "if (Get" << util::UnderscoreToCamelCase(constraint.first) << "() != ";
+    const auto& field = parent_->GetParamList().GetField(constraint.first);
+    if (field->GetFieldType() == ScalarField::kFieldType) {
+      s << std::get<int64_t>(constraint.second);
+    } else {
+      s << std::get<std::string>(constraint.second);
+    }
+    s << ") return false;";
+  }
+
+  // Validate the packets fields last
+  for (const auto& field : fields_) {
+    field->GenValidator(s);
+    s << "\n";
+  }
+
+  s << "return true;";
+  s << "}\n";
+  if (parent_ == nullptr) {
+    s << "bool was_validated_{false};\n";
+  }
+}
+
+void PacketDef::GenBuilderDefinition(std::ostream& s) const {
+  s << "class " << name_ << "Builder";
+  if (parent_ != nullptr) {
+    s << " : public " << parent_->name_ << "Builder";
+  } else {
+    if (is_little_endian_) {
+      s << " : public PacketBuilder<kLittleEndian>";
+    } else {
+      s << " : public PacketBuilder<!kLittleEndian>";
+    }
+  }
+  s << " {";
+  s << " public:";
+  s << "  virtual ~" << name_ << "Builder()" << (parent_ != nullptr ? " override" : "") << " = default;";
+
+  if (!fields_.HasBody()) {
+    GenBuilderCreate(s);
+    s << "\n";
+  }
+
+  GenSerialize(s);
+  s << "\n";
+
+  GenSize(s);
+  s << "\n";
+
+  s << " protected:\n";
+  GenBuilderConstructor(s);
+  s << "\n";
+
+  GenBuilderParameterChecker(s);
+  s << "\n";
+
+  GenMembers(s);
+  s << "};\n";
+
+  GenTestDefine(s);
+  s << "\n";
+
+  GenFuzzTestDefine(s);
+  s << "\n";
+}
+
+void PacketDef::GenBuilderDefinitionPybind11(std::ostream& s) const {
+  s << "py::class_<" << name_ << "Builder";
+  if (parent_ != nullptr) {
+    s << ", " << parent_->name_ << "Builder";
+  } else {
+    if (is_little_endian_) {
+      s << ", PacketBuilder<kLittleEndian>";
+    } else {
+      s << ", PacketBuilder<!kLittleEndian>";
+    }
+  }
+  s << ", std::shared_ptr<" << name_ << "Builder>";
+  s << ">(m, \"" << name_ << "Builder\")";
+  if (!fields_.HasBody()) {
+    GenBuilderCreatePybind11(s);
+  }
+  s << ".def(\"Serialize\", [](" << name_ << "Builder& builder){";
+  s << "std::vector<uint8_t> bytes;";
+  s << "BitInserter bi(bytes);";
+  s << "builder.Serialize(bi);";
+  s << "return bytes;})";
+  s << ";\n";
+}
+
+void PacketDef::GenTestDefine(std::ostream& s) const {
+  s << "#ifdef PACKET_TESTING\n";
+  s << "#define DEFINE_AND_INSTANTIATE_" << name_ << "ReflectionTest(...)";
+  s << "class " << name_ << "ReflectionTest : public testing::TestWithParam<std::vector<uint8_t>> { ";
+  s << "public: ";
+  s << "void CompareBytes(std::vector<uint8_t> captured_packet) {";
+  s << "auto vec = std::make_shared<std::vector<uint8_t>>(captured_packet.begin(), captured_packet.end());";
+  s << name_ << "View view = " << name_ << "View::Create(";
+  auto ancestor_ptr = parent_;
+  size_t parent_parens = 0;
+  while (ancestor_ptr != nullptr) {
+    s << ancestor_ptr->name_ << "View::Create(";
+    parent_parens++;
+    ancestor_ptr = ancestor_ptr->parent_;
+  }
+  s << "vec";
+  for (size_t i = 0; i < parent_parens; i++) {
+    s << ")";
+  }
+  s << ");";
+  s << "if (!view.IsValid()) { LOG_INFO(\"Invalid Packet Bytes (size = %zu)\", view.size());";
+  s << "for (size_t i = 0; i < view.size(); i++) { LOG_DEBUG(\"%5zd:%02X\", i, *(view.begin() + i)); }}";
+  s << "ASSERT_TRUE(view.IsValid());";
+  s << "auto packet = " << name_ << "Builder::Create(";
+  FieldList params = GetParamList().GetFieldsWithoutTypes({
+      BodyField::kFieldType,
+  });
+  for (int i = 0; i < params.size(); i++) {
+    params[i]->GenBuilderParameterFromView(s);
+    if (i != params.size() - 1) {
+      s << ", ";
+    }
+  }
+  s << ");";
+  s << "std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();";
+  s << "packet_bytes->reserve(packet->size());";
+  s << "BitInserter it(*packet_bytes);";
+  s << "packet->Serialize(it);";
+  s << "ASSERT_EQ(*packet_bytes, *vec);";
+  s << "}";
+  s << "};";
+  s << "TEST_P(" << name_ << "ReflectionTest, generatedReflectionTest) {";
+  s << "CompareBytes(GetParam());";
+  s << "}";
+  s << "INSTANTIATE_TEST_SUITE_P(" << name_ << "_reflection, ";
+  s << name_ << "ReflectionTest, testing::Values(__VA_ARGS__))";
+  s << "\n#endif";
+}
+
+void PacketDef::GenFuzzTestDefine(std::ostream& s) const {
+  s << "#if defined(PACKET_FUZZ_TESTING) || defined(PACKET_TESTING)\n";
+  s << "#define DEFINE_" << name_ << "ReflectionFuzzTest() ";
+  s << "void Run" << name_ << "ReflectionFuzzTest(const uint8_t* data, size_t size) {";
+  s << "auto vec = std::make_shared<std::vector<uint8_t>>(data, data + size);";
+  s << name_ << "View view = " << name_ << "View::Create(";
+  auto ancestor_ptr = parent_;
+  size_t parent_parens = 0;
+  while (ancestor_ptr != nullptr) {
+    s << ancestor_ptr->name_ << "View::Create(";
+    parent_parens++;
+    ancestor_ptr = ancestor_ptr->parent_;
+  }
+  s << "vec";
+  for (size_t i = 0; i < parent_parens; i++) {
+    s << ")";
+  }
+  s << ");";
+  s << "if (!view.IsValid()) { return; }";
+  s << "auto packet = " << name_ << "Builder::Create(";
+  FieldList params = GetParamList().GetFieldsWithoutTypes({
+      BodyField::kFieldType,
+  });
+  for (int i = 0; i < params.size(); i++) {
+    params[i]->GenBuilderParameterFromView(s);
+    if (i != params.size() - 1) {
+      s << ", ";
+    }
+  }
+  s << ");";
+  s << "std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();";
+  s << "packet_bytes->reserve(packet->size());";
+  s << "BitInserter it(*packet_bytes);";
+  s << "packet->Serialize(it);";
+  s << "}";
+  s << "\n#endif\n";
+  s << "#ifdef PACKET_FUZZ_TESTING\n";
+  s << "#define DEFINE_AND_REGISTER_" << name_ << "ReflectionFuzzTest(REGISTRY) ";
+  s << "DEFINE_" << name_ << "ReflectionFuzzTest();";
+  s << " class " << name_ << "ReflectionFuzzTestRegistrant {";
+  s << "public: ";
+  s << "explicit " << name_
+    << "ReflectionFuzzTestRegistrant(std::vector<void(*)(const uint8_t*, size_t)>& fuzz_test_registry) {";
+  s << "fuzz_test_registry.push_back(Run" << name_ << "ReflectionFuzzTest);";
+  s << "}}; ";
+  s << name_ << "ReflectionFuzzTestRegistrant " << name_ << "_reflection_fuzz_test_registrant(REGISTRY);";
+  s << "\n#endif";
+}
+
+FieldList PacketDef::GetParametersToValidate() const {
+  FieldList params_to_validate;
+  for (const auto& field : GetParamList()) {
+    if (field->HasParameterValidator()) {
+      params_to_validate.AppendField(field);
+    }
+  }
+  return params_to_validate;
+}
+
+void PacketDef::GenBuilderCreate(std::ostream& s) const {
+  s << "static std::unique_ptr<" << name_ << "Builder> Create(";
+
+  auto params = GetParamList();
+  for (int i = 0; i < params.size(); i++) {
+    params[i]->GenBuilderParameter(s);
+    if (i != params.size() - 1) {
+      s << ", ";
+    }
+  }
+  s << ") {";
+
+  // Call the constructor
+  s << "auto builder = std::unique_ptr<" << name_ << "Builder>(new " << name_ << "Builder(";
+
+  params = params.GetFieldsWithoutTypes({
+      PayloadField::kFieldType,
+      BodyField::kFieldType,
+  });
+  // Add the parameters.
+  for (int i = 0; i < params.size(); i++) {
+    if (params[i]->BuilderParameterMustBeMoved()) {
+      s << "std::move(" << params[i]->GetName() << ")";
+    } else {
+      s << params[i]->GetName();
+    }
+    if (i != params.size() - 1) {
+      s << ", ";
+    }
+  }
+
+  s << "));";
+  if (fields_.HasPayload()) {
+    s << "builder->payload_ = std::move(payload);";
+  }
+  s << "return builder;";
+  s << "}\n";
+}
+
+void PacketDef::GenBuilderCreatePybind11(std::ostream& s) const {
+  s << ".def(py::init([](";
+  auto params = GetParamList();
+  std::vector<std::string> constructor_args;
+  int i = 1;
+  for (const auto& param : params) {
+    i++;
+    std::stringstream ss;
+    auto param_type = param->GetBuilderParameterType();
+    if (param_type.empty()) {
+      continue;
+    }
+    // Use shared_ptr instead of unique_ptr for the Python interface
+    if (param->BuilderParameterMustBeMoved()) {
+      param_type = util::StringFindAndReplaceAll(param_type, "unique_ptr", "shared_ptr");
+    }
+    ss << param_type << " " << param->GetName();
+    constructor_args.push_back(ss.str());
+  }
+  s << util::StringJoin(",", constructor_args) << "){";
+
+  // Deal with move only args
+  for (const auto& param : params) {
+    std::stringstream ss;
+    auto param_type = param->GetBuilderParameterType();
+    if (param_type.empty()) {
+      continue;
+    }
+    if (!param->BuilderParameterMustBeMoved()) {
+      continue;
+    }
+    auto move_only_param_name = param->GetName() + "_move_only";
+    s << param_type << " " << move_only_param_name << ";";
+    if (param->IsContainerField()) {
+      // Assume single layer container and copy it
+      auto struct_type = param->GetElementField()->GetDataType();
+      struct_type = util::StringFindAndReplaceAll(struct_type, "std::unique_ptr<", "");
+      struct_type = util::StringFindAndReplaceAll(struct_type, ">", "");
+      s << "for (size_t i = 0; i < " << param->GetName() << ".size(); i++) {";
+      // Serialize each struct
+      s << "auto " << param->GetName() + "_bytes = std::make_shared<std::vector<uint8_t>>();";
+      s << param->GetName() + "_bytes->reserve(" << param->GetName() << "[i]->size());";
+      s << "auto " << param->GetName() + "_reparsed = std::make_unique<" << struct_type << ">();";
+      s << "BitInserter " << param->GetName() + "_bi(*" << param->GetName() << "_bytes);";
+      s << param->GetName() << "[i]->Serialize(" << param->GetName() << "_bi);";
+      // Parse it again
+      s << "auto " << param->GetName() << "_view = PacketView<kLittleEndian>(" << param->GetName() << "_bytes);";
+      s << "auto result = Parse" << struct_type << "(" << param->GetName() + "_view.begin());";
+      // Push it into a new container
+      if (param->GetFieldType() == VectorField::kFieldType) {
+        s << move_only_param_name << ".push_back(std::move(" << param->GetName() + "_reparsed));";
+      } else if (param->GetFieldType() == ArrayField::kFieldType) {
+        s << move_only_param_name << "[i] = " << param->GetName() << "_reparsed;";
+      } else {
+        ERROR() << param << " is not supported by Pybind11";
+      }
+      s << "}";
+    } else {
+      // Serialize the parameter and pass the bytes in a RawBuilder
+      s << "std::vector<uint8_t> " << param->GetName() + "_bytes;";
+      s << param->GetName() + "_bytes.reserve(" << param->GetName() << "->size());";
+      s << "BitInserter " << param->GetName() + "_bi(" << param->GetName() << "_bytes);";
+      s << param->GetName() << "->Serialize(" << param->GetName() + "_bi);";
+      s << move_only_param_name << " = ";
+      s << "std::make_unique<RawBuilder>(" << param->GetName() << "_bytes);";
+    }
+  }
+  s << "return " << name_ << "Builder::Create(";
+  std::vector<std::string> builder_vars;
+  for (const auto& param : params) {
+    std::stringstream ss;
+    auto param_type = param->GetBuilderParameterType();
+    if (param_type.empty()) {
+      continue;
+    }
+    auto param_name = param->GetName();
+    if (param->BuilderParameterMustBeMoved()) {
+      ss << "std::move(" << param_name << "_move_only)";
+    } else {
+      ss << param_name;
+    }
+    builder_vars.push_back(ss.str());
+  }
+  s << util::StringJoin(",", builder_vars) << ");}";
+  s << "))";
+}
+
+void PacketDef::GenBuilderParameterChecker(std::ostream& s) const {
+  FieldList params_to_validate = GetParametersToValidate();
+
+  // Skip writing this function if there is nothing to validate.
+  if (params_to_validate.size() == 0) {
+    return;
+  }
+
+  // Generate function arguments.
+  s << "void CheckParameterValues(";
+  for (int i = 0; i < params_to_validate.size(); i++) {
+    params_to_validate[i]->GenBuilderParameter(s);
+    if (i != params_to_validate.size() - 1) {
+      s << ", ";
+    }
+  }
+  s << ") {";
+
+  // Check the parameters.
+  for (const auto& field : params_to_validate) {
+    field->GenParameterValidator(s);
+  }
+  s << "}\n";
+}
+
+void PacketDef::GenBuilderConstructor(std::ostream& s) const {
+  s << name_ << "Builder(";
+
+  // Generate the constructor parameters.
+  auto params = GetParamList().GetFieldsWithoutTypes({
+      PayloadField::kFieldType,
+      BodyField::kFieldType,
+  });
+  for (int i = 0; i < params.size(); i++) {
+    params[i]->GenBuilderParameter(s);
+    if (i != params.size() - 1) {
+      s << ", ";
+    }
+  }
+  if (params.size() > 0 || parent_constraints_.size() > 0) {
+    s << ") :";
+  } else {
+    s << ")";
+  }
+
+  // Get the list of parent params to call the parent constructor with.
+  FieldList parent_params;
+  if (parent_ != nullptr) {
+    // Pass parameters to the parent constructor
+    s << parent_->name_ << "Builder(";
+    parent_params = parent_->GetParamList().GetFieldsWithoutTypes({
+        PayloadField::kFieldType,
+        BodyField::kFieldType,
+    });
+
+    // Go through all the fields and replace constrained fields with fixed values
+    // when calling the parent constructor.
+    for (int i = 0; i < parent_params.size(); i++) {
+      const auto& field = parent_params[i];
+      const auto& constraint = parent_constraints_.find(field->GetName());
+      if (constraint != parent_constraints_.end()) {
+        if (field->GetFieldType() == ScalarField::kFieldType) {
+          s << std::get<int64_t>(constraint->second);
+        } else if (field->GetFieldType() == EnumField::kFieldType) {
+          s << std::get<std::string>(constraint->second);
+        } else {
+          ERROR(field) << "Constraints on non enum/scalar fields should be impossible.";
+        }
+
+        s << "/* " << field->GetName() << "_ */";
+      } else {
+        s << field->GetName();
+      }
+
+      if (i != parent_params.size() - 1) {
+        s << ", ";
+      }
+    }
+    s << ") ";
+  }
+
+  // Build a list of parameters that excludes all parent parameters.
+  FieldList saved_params;
+  for (const auto& field : params) {
+    if (parent_params.GetField(field->GetName()) == nullptr) {
+      saved_params.AppendField(field);
+    }
+  }
+  if (parent_ != nullptr && saved_params.size() > 0) {
+    s << ",";
+  }
+  for (int i = 0; i < saved_params.size(); i++) {
+    const auto& saved_param_name = saved_params[i]->GetName();
+    if (saved_params[i]->BuilderParameterMustBeMoved()) {
+      s << saved_param_name << "_(std::move(" << saved_param_name << "))";
+    } else {
+      s << saved_param_name << "_(" << saved_param_name << ")";
+    }
+    if (i != saved_params.size() - 1) {
+      s << ",";
+    }
+  }
+  s << " {";
+
+  FieldList params_to_validate = GetParametersToValidate();
+
+  if (params_to_validate.size() > 0) {
+    s << "CheckParameterValues(";
+    for (int i = 0; i < params_to_validate.size(); i++) {
+      s << params_to_validate[i]->GetName() << "_";
+      if (i != params_to_validate.size() - 1) {
+        s << ", ";
+      }
+    }
+    s << ");";
+  }
+
+  s << "}\n";
+}
diff --git a/gd/packet/parser/packet_def.h b/gd/packet/parser/packet_def.h
new file mode 100644
index 0000000..e8acdc3
--- /dev/null
+++ b/gd/packet/parser/packet_def.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <map>
+#include <variant>
+
+#include "enum_def.h"
+#include "field_list.h"
+#include "fields/packet_field.h"
+#include "parent_def.h"
+
+class PacketDef : public ParentDef {
+ public:
+  PacketDef(std::string name, FieldList fields);
+  PacketDef(std::string name, FieldList fields, PacketDef* parent);
+
+  PacketField* GetNewField(const std::string& name, ParseLocation loc) const;
+
+  void GenParserDefinition(std::ostream& s) const;
+
+  void GenParserDefinitionPybind11(std::ostream& s) const;
+
+  void GenParserFieldGetter(std::ostream& s, const PacketField* field) const;
+
+  void GenValidator(std::ostream& s) const;
+
+  TypeDef::Type GetDefinitionType() const;
+
+  void GenBuilderDefinition(std::ostream& s) const;
+
+  void GenBuilderDefinitionPybind11(std::ostream& s) const;
+
+  void GenTestDefine(std::ostream& s) const;
+
+  void GenFuzzTestDefine(std::ostream& s) const;
+
+  FieldList GetParametersToValidate() const;
+
+  void GenBuilderCreate(std::ostream& s) const;
+
+  void GenBuilderCreatePybind11(std::ostream& s) const;
+
+  void GenBuilderParameterChecker(std::ostream& s) const;
+
+  void GenBuilderConstructor(std::ostream& s) const;
+};
diff --git a/gd/packet/parser/parent_def.cc b/gd/packet/parser/parent_def.cc
new file mode 100644
index 0000000..1557bf6
--- /dev/null
+++ b/gd/packet/parser/parent_def.cc
@@ -0,0 +1,461 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "parent_def.h"
+
+#include "fields/all_fields.h"
+#include "util.h"
+
+ParentDef::ParentDef(std::string name, FieldList fields) : ParentDef(name, fields, nullptr) {}
+ParentDef::ParentDef(std::string name, FieldList fields, ParentDef* parent)
+    : TypeDef(name), fields_(fields), parent_(parent) {}
+
+void ParentDef::AddParentConstraint(std::string field_name, std::variant<int64_t, std::string> value) {
+  // NOTE: This could end up being very slow if there are a lot of constraints.
+  const auto& parent_params = parent_->GetParamList();
+  const auto& constrained_field = parent_params.GetField(field_name);
+  if (constrained_field == nullptr) {
+    ERROR() << "Attempting to constrain field " << field_name << " in parent " << parent_->name_
+            << ", but no such field exists.";
+  }
+
+  if (constrained_field->GetFieldType() == ScalarField::kFieldType) {
+    if (!std::holds_alternative<int64_t>(value)) {
+      ERROR(constrained_field) << "Attempting to constrain a scalar field to an enum value in " << parent_->name_;
+    }
+  } else if (constrained_field->GetFieldType() == EnumField::kFieldType) {
+    if (!std::holds_alternative<std::string>(value)) {
+      ERROR(constrained_field) << "Attempting to constrain an enum field to a scalar value in " << parent_->name_;
+    }
+    const auto& enum_def = static_cast<EnumField*>(constrained_field)->GetEnumDef();
+    if (!enum_def.HasEntry(std::get<std::string>(value))) {
+      ERROR(constrained_field) << "No matching enumeration \"" << std::get<std::string>(value)
+                               << "\" for constraint on enum in parent " << parent_->name_ << ".";
+    }
+
+    // For enums, we have to qualify the value using the enum type name.
+    value = enum_def.GetTypeName() + "::" + std::get<std::string>(value);
+  } else {
+    ERROR(constrained_field) << "Field in parent " << parent_->name_ << " is not viable for constraining.";
+  }
+
+  parent_constraints_.insert(std::pair(field_name, value));
+}
+
+// Assign all size fields to their corresponding variable length fields.
+// Will crash if
+//  - there aren't any fields that don't match up to a field.
+//  - the size field points to a fixed size field.
+//  - if the size field comes after the variable length field.
+void ParentDef::AssignSizeFields() {
+  for (const auto& field : fields_) {
+    DEBUG() << "field name: " << field->GetName();
+
+    if (field->GetFieldType() != SizeField::kFieldType && field->GetFieldType() != CountField::kFieldType) {
+      continue;
+    }
+
+    const SizeField* size_field = static_cast<SizeField*>(field);
+    // Check to see if a corresponding field can be found.
+    const auto& var_len_field = fields_.GetField(size_field->GetSizedFieldName());
+    if (var_len_field == nullptr) {
+      ERROR(field) << "Could not find corresponding field for size/count field.";
+    }
+
+    // Do the ordering check to ensure the size field comes before the
+    // variable length field.
+    for (auto it = fields_.begin(); *it != size_field; it++) {
+      DEBUG() << "field name: " << (*it)->GetName();
+      if (*it == var_len_field) {
+        ERROR(var_len_field, size_field) << "Size/count field must come before the variable length field it describes.";
+      }
+    }
+
+    if (var_len_field->GetFieldType() == PayloadField::kFieldType) {
+      const auto& payload_field = static_cast<PayloadField*>(var_len_field);
+      payload_field->SetSizeField(size_field);
+      continue;
+    }
+
+    if (var_len_field->GetFieldType() == VectorField::kFieldType) {
+      const auto& vector_field = static_cast<VectorField*>(var_len_field);
+      vector_field->SetSizeField(size_field);
+      continue;
+    }
+
+    // If we've reached this point then the field wasn't a variable length field.
+    // Check to see if the field is a variable length field
+    ERROR(field, size_field) << "Can not use size/count in reference to a fixed size field.\n";
+  }
+}
+
+void ParentDef::SetEndianness(bool is_little_endian) {
+  is_little_endian_ = is_little_endian;
+}
+
+// Get the size. You scan specify without_payload in order to exclude payload fields as children will be overriding it.
+Size ParentDef::GetSize(bool without_payload) const {
+  auto size = Size(0);
+
+  for (const auto& field : fields_) {
+    if (without_payload &&
+        (field->GetFieldType() == PayloadField::kFieldType || field->GetFieldType() == BodyField::kFieldType)) {
+      continue;
+    }
+
+    // The offset to the field must be passed in as an argument for dynamically sized custom fields.
+    if (field->GetFieldType() == CustomField::kFieldType && field->GetSize().has_dynamic()) {
+      std::stringstream custom_field_size;
+
+      // Custom fields are special as their size field takes an argument.
+      custom_field_size << field->GetSize().dynamic_string() << "(begin()";
+
+      // Check if we can determine offset from begin(), otherwise error because by this point,
+      // the size of the custom field is unknown and can't be subtracted from end() to get the
+      // offset.
+      auto offset = GetOffsetForField(field->GetName(), false);
+      if (offset.empty()) {
+        ERROR(field) << "Custom Field offset can not be determined from begin().";
+      }
+
+      if (offset.bits() % 8 != 0) {
+        ERROR(field) << "Custom fields must be byte aligned.";
+      }
+      if (offset.has_bits()) custom_field_size << " + " << offset.bits() / 8;
+      if (offset.has_dynamic()) custom_field_size << " + " << offset.dynamic_string();
+      custom_field_size << ")";
+
+      size += custom_field_size.str();
+      continue;
+    }
+
+    size += field->GetSize();
+  }
+
+  if (parent_ != nullptr) {
+    size += parent_->GetSize(true);
+  }
+
+  return size;
+}
+
+// Get the offset until the field is reached, if there is no field
+// returns an empty Size. from_end requests the offset to the field
+// starting from the end() iterator. If there is a field with an unknown
+// size along the traversal, then an empty size is returned.
+Size ParentDef::GetOffsetForField(std::string field_name, bool from_end) const {
+  // Check first if the field exists.
+  if (fields_.GetField(field_name) == nullptr) {
+    ERROR() << "Can't find a field offset for nonexistent field named: " << field_name << " in " << name_;
+  }
+
+  // We have to use a generic lambda to conditionally change iteration direction
+  // due to iterator and reverse_iterator being different types.
+  auto size_lambda = [&](auto from, auto to) -> Size {
+    auto size = Size(0);
+    for (auto it = from; it != to; it++) {
+      // We've reached the field, end the loop.
+      if ((*it)->GetName() == field_name) break;
+      const auto& field = *it;
+      // If there is a field with an unknown size before the field, return an empty Size.
+      if (field->GetSize().empty()) {
+        return Size();
+      }
+      if (field->GetFieldType() != PaddingField::kFieldType || !from_end) {
+        size += field->GetSize();
+      }
+    }
+    return size;
+  };
+
+  // Change iteration direction based on from_end.
+  auto size = Size();
+  if (from_end)
+    size = size_lambda(fields_.rbegin(), fields_.rend());
+  else
+    size = size_lambda(fields_.begin(), fields_.end());
+  if (size.empty()) return size;
+
+  // We need the offset until a payload or body field.
+  if (parent_ != nullptr) {
+    if (parent_->fields_.HasPayload()) {
+      auto parent_payload_offset = parent_->GetOffsetForField("payload", from_end);
+      if (parent_payload_offset.empty()) {
+        ERROR() << "Empty offset for payload in " << parent_->name_ << " finding the offset for field: " << field_name;
+      }
+      size += parent_payload_offset;
+    } else {
+      auto parent_body_offset = parent_->GetOffsetForField("body", from_end);
+      if (parent_body_offset.empty()) {
+        ERROR() << "Empty offset for body in " << parent_->name_ << " finding the offset for field: " << field_name;
+      }
+      size += parent_body_offset;
+    }
+  }
+
+  return size;
+}
+
+FieldList ParentDef::GetParamList() const {
+  FieldList params;
+
+  std::set<std::string> param_types = {
+      ScalarField::kFieldType,
+      EnumField::kFieldType,
+      ArrayField::kFieldType,
+      VectorField::kFieldType,
+      CustomField::kFieldType,
+      StructField::kFieldType,
+      VariableLengthStructField::kFieldType,
+      PayloadField::kFieldType,
+  };
+
+  if (parent_ != nullptr) {
+    auto parent_params = parent_->GetParamList().GetFieldsWithTypes(param_types);
+
+    // Do not include constrained fields in the params
+    for (const auto& field : parent_params) {
+      if (parent_constraints_.find(field->GetName()) == parent_constraints_.end()) {
+        params.AppendField(field);
+      }
+    }
+  }
+  // Add our parameters.
+  return params.Merge(fields_.GetFieldsWithTypes(param_types));
+}
+
+void ParentDef::GenMembers(std::ostream& s) const {
+  // Add the parameter list.
+  for (int i = 0; i < fields_.size(); i++) {
+    if (fields_[i]->GenBuilderMember(s)) {
+      s << "_;";
+    }
+  }
+}
+
+void ParentDef::GenSize(std::ostream& s) const {
+  auto header_fields = fields_.GetFieldsBeforePayloadOrBody();
+  auto footer_fields = fields_.GetFieldsAfterPayloadOrBody();
+
+  s << "protected:";
+  s << "size_t BitsOfHeader() const {";
+  s << "return 0";
+
+  if (parent_ != nullptr) {
+    if (parent_->GetDefinitionType() == Type::PACKET) {
+      s << " + " << parent_->name_ << "Builder::BitsOfHeader() ";
+    } else {
+      s << " + " << parent_->name_ << "::BitsOfHeader() ";
+    }
+  }
+
+  for (const auto& field : header_fields) {
+    s << " + " << field->GetBuilderSize();
+  }
+  s << ";";
+
+  s << "}\n\n";
+
+  s << "size_t BitsOfFooter() const {";
+  s << "return 0";
+  for (const auto& field : footer_fields) {
+    s << " + " << field->GetBuilderSize();
+  }
+
+  if (parent_ != nullptr) {
+    if (parent_->GetDefinitionType() == Type::PACKET) {
+      s << " + " << parent_->name_ << "Builder::BitsOfFooter() ";
+    } else {
+      s << " + " << parent_->name_ << "::BitsOfFooter() ";
+    }
+  }
+  s << ";";
+  s << "}\n\n";
+
+  if (fields_.HasPayload()) {
+    s << "size_t GetPayloadSize() const {";
+    s << "if (payload_ != nullptr) {return payload_->size();}";
+    s << "else { return size() - (BitsOfHeader() + BitsOfFooter()) / 8;}";
+    s << ";}\n\n";
+  }
+
+  Size padded_size;
+  for (const auto& field : header_fields) {
+    if (field->GetFieldType() == PaddingField::kFieldType) {
+      if (!padded_size.empty()) {
+        ERROR() << "Only one padding field is allowed.  Second field: " << field->GetName();
+      }
+      padded_size = field->GetSize();
+    }
+  }
+
+  s << "public:";
+  s << "virtual size_t size() const override {";
+  if (!padded_size.empty()) {
+    s << "return " << padded_size.bytes() << ";}";
+    s << "size_t unpadded_size() const {";
+  }
+  s << "return (BitsOfHeader() / 8)";
+  if (fields_.HasPayload()) {
+    s << "+ payload_->size()";
+  }
+  s << " + (BitsOfFooter() / 8);";
+  s << "}\n";
+}
+
+void ParentDef::GenSerialize(std::ostream& s) const {
+  auto header_fields = fields_.GetFieldsBeforePayloadOrBody();
+  auto footer_fields = fields_.GetFieldsAfterPayloadOrBody();
+
+  s << "protected:";
+  s << "void SerializeHeader(BitInserter&";
+  if (parent_ != nullptr || header_fields.size() != 0) {
+    s << " i ";
+  }
+  s << ") const {";
+
+  if (parent_ != nullptr) {
+    if (parent_->GetDefinitionType() == Type::PACKET) {
+      s << parent_->name_ << "Builder::SerializeHeader(i);";
+    } else {
+      s << parent_->name_ << "::SerializeHeader(i);";
+    }
+  }
+
+  for (const auto& field : header_fields) {
+    if (field->GetFieldType() == SizeField::kFieldType) {
+      const auto& field_name = ((SizeField*)field)->GetSizedFieldName();
+      const auto& sized_field = fields_.GetField(field_name);
+      if (sized_field == nullptr) {
+        ERROR(field) << __func__ << ": Can't find sized field named " << field_name;
+      }
+      if (sized_field->GetFieldType() == PayloadField::kFieldType) {
+        s << "size_t payload_bytes = GetPayloadSize();";
+        std::string modifier = ((PayloadField*)sized_field)->size_modifier_;
+        if (modifier != "") {
+          s << "static_assert((" << modifier << ")%8 == 0, \"Modifiers must be byte-aligned\");";
+          s << "payload_bytes = payload_bytes + (" << modifier << ") / 8;";
+        }
+        s << "ASSERT(payload_bytes < (static_cast<size_t>(1) << " << field->GetSize().bits() << "));";
+        s << "insert(static_cast<" << field->GetDataType() << ">(payload_bytes), i," << field->GetSize().bits() << ");";
+      } else {
+        if (sized_field->GetFieldType() != VectorField::kFieldType) {
+          ERROR(field) << __func__ << ": Unhandled sized field type for " << field_name;
+        }
+        const auto& vector_name = field_name + "_";
+        const VectorField* vector = (VectorField*)sized_field;
+        s << "size_t " << vector_name + "bytes = 0;";
+        if (vector->element_size_.empty() || vector->element_size_.has_dynamic()) {
+          s << "for (auto elem : " << vector_name << ") {";
+          s << vector_name + "bytes += elem.size(); }";
+        } else {
+          s << vector_name + "bytes = ";
+          s << vector_name << ".size() * ((" << vector->element_size_ << ") / 8);";
+        }
+        std::string modifier = vector->GetSizeModifier();
+        if (modifier != "") {
+          s << "static_assert((" << modifier << ")%8 == 0, \"Modifiers must be byte-aligned\");";
+          s << vector_name << "bytes = ";
+          s << vector_name << "bytes + (" << modifier << ") / 8;";
+        }
+        s << "ASSERT(" << vector_name + "bytes < (1 << " << field->GetSize().bits() << "));";
+        s << "insert(" << vector_name << "bytes, i, ";
+        s << field->GetSize().bits() << ");";
+      }
+    } else if (field->GetFieldType() == ChecksumStartField::kFieldType) {
+      const auto& field_name = ((ChecksumStartField*)field)->GetStartedFieldName();
+      const auto& started_field = fields_.GetField(field_name);
+      if (started_field == nullptr) {
+        ERROR(field) << __func__ << ": Can't find checksum field named " << field_name << "(" << field->GetName()
+                     << ")";
+      }
+      s << "auto shared_checksum_ptr = std::make_shared<" << started_field->GetDataType() << ">();";
+      s << "shared_checksum_ptr->Initialize();";
+      s << "i.RegisterObserver(packet::ByteObserver(";
+      s << "[shared_checksum_ptr](uint8_t byte){ shared_checksum_ptr->AddByte(byte);},";
+      s << "[shared_checksum_ptr](){ return static_cast<uint64_t>(shared_checksum_ptr->GetChecksum());}));";
+    } else if (field->GetFieldType() == PaddingField::kFieldType) {
+      s << "ASSERT(unpadded_size() <= " << field->GetSize().bytes() << ");";
+      s << "size_t padding_bytes = ";
+      s << field->GetSize().bytes() << " - unpadded_size();";
+      s << "for (size_t padding = 0; padding < padding_bytes; padding++) {i.insert_byte(0);}";
+    } else if (field->GetFieldType() == CountField::kFieldType) {
+      const auto& vector_name = ((SizeField*)field)->GetSizedFieldName() + "_";
+      s << "insert(" << vector_name << ".size(), i, " << field->GetSize().bits() << ");";
+    } else {
+      field->GenInserter(s);
+    }
+  }
+  s << "}\n\n";
+
+  s << "void SerializeFooter(BitInserter&";
+  if (parent_ != nullptr || footer_fields.size() != 0) {
+    s << " i ";
+  }
+  s << ") const {";
+
+  for (const auto& field : footer_fields) {
+    field->GenInserter(s);
+  }
+  if (parent_ != nullptr) {
+    if (parent_->GetDefinitionType() == Type::PACKET) {
+      s << parent_->name_ << "Builder::SerializeFooter(i);";
+    } else {
+      s << parent_->name_ << "::SerializeFooter(i);";
+    }
+  }
+  s << "}\n\n";
+
+  s << "public:";
+  s << "virtual void Serialize(BitInserter& i) const override {";
+  s << "SerializeHeader(i);";
+  if (fields_.HasPayload()) {
+    s << "payload_->Serialize(i);";
+  }
+  s << "SerializeFooter(i);";
+
+  s << "}\n";
+}
+
+void ParentDef::GenInstanceOf(std::ostream& s) const {
+  if (parent_ != nullptr && parent_constraints_.size() > 0) {
+    s << "static bool IsInstance(const " << parent_->name_ << "& parent) {";
+    // Get the list of parent params.
+    FieldList parent_params = parent_->GetParamList().GetFieldsWithoutTypes({
+        PayloadField::kFieldType,
+        BodyField::kFieldType,
+    });
+
+    // Check if constrained parent fields are set to their correct values.
+    for (int i = 0; i < parent_params.size(); i++) {
+      const auto& field = parent_params[i];
+      const auto& constraint = parent_constraints_.find(field->GetName());
+      if (constraint != parent_constraints_.end()) {
+        s << "if (parent." << field->GetName() << "_ != ";
+        if (field->GetFieldType() == ScalarField::kFieldType) {
+          s << std::get<int64_t>(constraint->second) << ")";
+          s << "{ return false;}";
+        } else if (field->GetFieldType() == EnumField::kFieldType) {
+          s << std::get<std::string>(constraint->second) << ")";
+          s << "{ return false;}";
+        } else {
+          ERROR(field) << "Constraints on non enum/scalar fields should be impossible.";
+        }
+      }
+    }
+    s << "return true;}";
+  }
+}
diff --git a/gd/packet/parser/parent_def.h b/gd/packet/parser/parent_def.h
new file mode 100644
index 0000000..a293a26
--- /dev/null
+++ b/gd/packet/parser/parent_def.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <map>
+#include <variant>
+
+#include "enum_def.h"
+#include "field_list.h"
+#include "fields/all_fields.h"
+#include "fields/packet_field.h"
+#include "parse_location.h"
+#include "type_def.h"
+
+class ParentDef : public TypeDef {
+ public:
+  ParentDef(std::string name, FieldList fields);
+  ParentDef(std::string name, FieldList fields, ParentDef* parent);
+
+  void AddParentConstraint(std::string field_name, std::variant<int64_t, std::string> value);
+
+  // Assign all size fields to their corresponding variable length fields.
+  // Will crash if
+  //  - there aren't any fields that don't match up to a field.
+  //  - the size field points to a fixed size field.
+  //  - if the size field comes after the variable length field.
+  void AssignSizeFields();
+
+  void SetEndianness(bool is_little_endian);
+
+  // Get the size. You scan specify without_payload to exclude payload and body fields as children override them.
+  Size GetSize(bool without_payload = false) const;
+
+  // Get the offset until the field is reached, if there is no field
+  // returns an empty Size. from_end requests the offset to the field
+  // starting from the end() iterator. If there is a field with an unknown
+  // size along the traversal, then an empty size is returned.
+  Size GetOffsetForField(std::string field_name, bool from_end = false) const;
+
+  FieldList GetParamList() const;
+
+  void GenMembers(std::ostream& s) const;
+
+  void GenSize(std::ostream& s) const;
+
+  void GenSerialize(std::ostream& s) const;
+
+  void GenInstanceOf(std::ostream& s) const;
+
+  FieldList fields_;
+
+  ParentDef* parent_{nullptr};
+
+  std::map<std::string, std::variant<int64_t, std::string>> parent_constraints_;
+  bool is_little_endian_;
+};
diff --git a/gd/packet/parser/parse_location.h b/gd/packet/parser/parse_location.h
new file mode 100644
index 0000000..80bdc72
--- /dev/null
+++ b/gd/packet/parser/parse_location.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+class ParseLocation {
+ public:
+  ParseLocation() : line_(-1) {}
+
+  ParseLocation(int line) : line_(line) {}
+
+  int GetLine() {
+    return line_;
+  }
+
+ private:
+  int line_;
+};
diff --git a/gd/packet/parser/size.h b/gd/packet/parser/size.h
new file mode 100644
index 0000000..fed3c51
--- /dev/null
+++ b/gd/packet/parser/size.h
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <iterator>
+#include <sstream>
+#include <string>
+#include <vector>
+
+class Size {
+ public:
+  Size() {}
+
+  Size(int bits) {
+    is_valid_ = true;
+    bits_ = bits;
+  }
+
+  Size(std::string dynamic) {
+    is_valid_ = true;
+    dynamic_.push_back(dynamic);
+  }
+
+  Size(int bits, std::string dynamic) {
+    is_valid_ = true;
+    bits_ = bits;
+    dynamic_.push_back(dynamic);
+  }
+
+  Size(const Size& size) {
+    is_valid_ = size.is_valid_;
+    bits_ = size.bits_;
+    dynamic_ = size.dynamic_;
+  }
+
+  std::string dynamic_string() const {
+    if (dynamic_.empty()) return "0";
+
+    std::stringstream result;
+    // Print everything but the last element then append it manually to avoid
+    // the trailing "+" operator.
+    std::copy(dynamic_.begin(), dynamic_.end() - 1, std::ostream_iterator<std::string>(result, " + "));
+    result << dynamic_.back();
+    return result.str();
+  }
+
+  std::vector<std::string> dynamic_string_list() {
+    return dynamic_;
+  }
+
+  bool empty() const {
+    return !is_valid_;
+  }
+
+  bool has_bits() const {
+    return bits_ != 0;
+  }
+
+  bool has_dynamic() const {
+    return !dynamic_.empty();
+  }
+
+  int bits() const {
+    return bits_;
+  }
+
+  int bytes() const {
+    // Round up to the nearest byte
+    return (bits_ + 7) / 8;
+  }
+
+  Size operator+(int rhs) {
+    return Size(bits_ + rhs);
+  }
+
+  Size operator+(std::string rhs) {
+    auto ret = Size();
+    ret.is_valid_ = true;
+    ret.dynamic_.insert(ret.dynamic_.end(), dynamic_.begin(), dynamic_.end());
+    ret.dynamic_.push_back(rhs);
+    return ret;
+  }
+
+  Size operator+(const Size& rhs) {
+    auto ret = Size(bits_ + rhs.bits_);
+    ret.is_valid_ = is_valid_ && rhs.is_valid_;
+    ret.dynamic_.insert(ret.dynamic_.end(), dynamic_.begin(), dynamic_.end());
+    ret.dynamic_.insert(ret.dynamic_.end(), rhs.dynamic_.begin(), rhs.dynamic_.end());
+    return ret;
+  }
+
+  Size& operator+=(int rhs) {
+    is_valid_ = true;
+    bits_ += rhs;
+    return *this;
+  }
+
+  Size& operator+=(std::string rhs) {
+    is_valid_ = true;
+    dynamic_.push_back(rhs);
+    return *this;
+  }
+
+  Size& operator+=(const Size& rhs) {
+    is_valid_ = is_valid_ && rhs.is_valid_;
+    bits_ += rhs.bits_;
+    dynamic_.insert(dynamic_.end(), rhs.dynamic_.begin(), rhs.dynamic_.end());
+    return *this;
+  }
+
+  std::string ToString() const {
+    std::stringstream str;
+    str << "/* Bits: */ " << bits_ << " + /* Dynamic: */ " << dynamic_string();
+    if (!is_valid_) {
+      str << " (invalid) ";
+    }
+    return str.str();
+  }
+
+  friend std::ostream& operator<<(std::ostream& os, const Size& rhs) {
+    return os << rhs.ToString();
+  }
+
+ private:
+  bool is_valid_ = false;
+  int bits_ = 0;
+  std::vector<std::string> dynamic_;
+};
diff --git a/gd/packet/parser/struct_def.cc b/gd/packet/parser/struct_def.cc
new file mode 100644
index 0000000..ff9cb78
--- /dev/null
+++ b/gd/packet/parser/struct_def.cc
@@ -0,0 +1,283 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "struct_def.h"
+
+#include "fields/all_fields.h"
+#include "util.h"
+
+StructDef::StructDef(std::string name, FieldList fields) : StructDef(name, fields, nullptr) {}
+StructDef::StructDef(std::string name, FieldList fields, StructDef* parent)
+    : ParentDef(name, fields, parent), total_size_(GetSize(true)) {}
+
+PacketField* StructDef::GetNewField(const std::string& name, ParseLocation loc) const {
+  if (fields_.HasBody()) {
+    return new VariableLengthStructField(name, name_, loc);
+  } else {
+    return new StructField(name, name_, total_size_, loc);
+  }
+}
+
+TypeDef::Type StructDef::GetDefinitionType() const {
+  return TypeDef::Type::STRUCT;
+}
+
+void StructDef::GenSpecialize(std::ostream& s) const {
+  if (parent_ == nullptr) {
+    return;
+  }
+  s << "static " << name_ << "* Specialize(" << parent_->name_ << "* parent) {";
+  s << "ASSERT(" << name_ << "::IsInstance(*parent));";
+  s << "return static_cast<" << name_ << "*>(parent);";
+  s << "}";
+}
+
+void StructDef::GenParse(std::ostream& s) const {
+  std::string iterator = (is_little_endian_ ? "Iterator<kLittleEndian>" : "Iterator<!kLittleEndian>");
+
+  if (fields_.HasBody()) {
+    s << "static std::optional<" << iterator << ">";
+  } else {
+    s << "static " << iterator;
+  }
+
+  s << " Parse(" << name_ << "* to_fill, " << iterator << " struct_begin_it ";
+
+  if (parent_ != nullptr) {
+    s << ", bool fill_parent = true) {";
+  } else {
+    s << ") {";
+  }
+  s << "auto to_bound = struct_begin_it;";
+
+  if (parent_ != nullptr) {
+    s << "if (fill_parent) {";
+    std::string parent_param = (parent_->parent_ == nullptr ? "" : ", true");
+    if (parent_->fields_.HasBody()) {
+      s << "auto parent_optional_it = " << parent_->name_ << "::Parse(to_fill, to_bound" << parent_param << ");";
+      if (fields_.HasBody()) {
+        s << "if (!parent_optional_it) { return {}; }";
+      } else {
+        s << "ASSERT(parent_optional_it);";
+      }
+    } else {
+      s << parent_->name_ << "::Parse(to_fill, to_bound" << parent_param << ");";
+    }
+    s << "}";
+  }
+
+  if (!fields_.HasBody()) {
+    s << "size_t end_index = struct_begin_it.NumBytesRemaining();";
+    if (parent_ != nullptr) {
+      s << "if (end_index < " << GetSize().bytes() << " - to_fill->" << parent_->name_ << "::size())";
+    } else {
+      s << "if (end_index < " << GetSize().bytes() << ")";
+    }
+    s << "{ return struct_begin_it.Subrange(0,0);}";
+  }
+
+  Size total_bits{0};
+  for (const auto& field : fields_) {
+    if (field->GetFieldType() != ReservedField::kFieldType && field->GetFieldType() != BodyField::kFieldType &&
+        field->GetFieldType() != FixedScalarField::kFieldType && field->GetFieldType() != SizeField::kFieldType &&
+        field->GetFieldType() != ChecksumStartField::kFieldType && field->GetFieldType() != ChecksumField::kFieldType &&
+        field->GetFieldType() != CountField::kFieldType) {
+      total_bits += field->GetSize().bits();
+    }
+  }
+  s << "{";
+  s << "if (to_bound.NumBytesRemaining() < " << total_bits.bytes() << ")";
+  if (!fields_.HasBody()) {
+    s << "{ return to_bound.Subrange(to_bound.NumBytesRemaining(),0);}";
+  } else {
+    s << "{ return {};}";
+  }
+  s << "}";
+  for (const auto& field : fields_) {
+    if (field->GetFieldType() != ReservedField::kFieldType && field->GetFieldType() != BodyField::kFieldType &&
+        field->GetFieldType() != FixedScalarField::kFieldType && field->GetFieldType() != SizeField::kFieldType &&
+        field->GetFieldType() != ChecksumStartField::kFieldType && field->GetFieldType() != ChecksumField::kFieldType &&
+        field->GetFieldType() != CountField::kFieldType) {
+      s << "{";
+      int num_leading_bits =
+          field->GenBounds(s, GetStructOffsetForField(field->GetName()), Size(), field->GetStructSize());
+      s << "auto " << field->GetName() << "_ptr = &to_fill->" << field->GetName() << "_;";
+      field->GenExtractor(s, num_leading_bits, true);
+      s << "}";
+    }
+    if (field->GetFieldType() == CountField::kFieldType || field->GetFieldType() == SizeField::kFieldType) {
+      s << field->GetDataType() << " " << field->GetName() << "_extracted;";
+      s << "{";
+      s << "if (to_bound.NumBytesRemaining() < " << field->GetSize().bytes() << ")";
+      if (!fields_.HasBody()) {
+        s << "{ return to_bound.Subrange(to_bound.NumBytesRemaining(),0);}";
+      } else {
+        s << "{ return {};}";
+      }
+      int num_leading_bits =
+          field->GenBounds(s, GetStructOffsetForField(field->GetName()), Size(), field->GetStructSize());
+      s << "auto " << field->GetName() << "_ptr = &" << field->GetName() << "_extracted;";
+      field->GenExtractor(s, num_leading_bits, true);
+      s << "}";
+    }
+  }
+  s << "return struct_begin_it + to_fill->" << name_ << "::size();";
+  s << "}";
+}
+
+void StructDef::GenParseFunctionPrototype(std::ostream& s) const {
+  s << "std::unique_ptr<" << name_ << "> Parse" << name_ << "(";
+  if (is_little_endian_) {
+    s << "Iterator<kLittleEndian>";
+  } else {
+    s << "Iterator<!kLittleEndian>";
+  }
+  s << "it);";
+}
+
+void StructDef::GenDefinition(std::ostream& s) const {
+  s << "class " << name_;
+  if (parent_ != nullptr) {
+    s << " : public " << parent_->name_;
+  } else {
+    if (is_little_endian_) {
+      s << " : public PacketStruct<kLittleEndian>";
+    } else {
+      s << " : public PacketStruct<!kLittleEndian>";
+    }
+  }
+  s << " {";
+  s << " public:";
+
+  GenConstructor(s);
+
+  s << " public:\n";
+  s << "  virtual ~" << name_ << "() override = default;\n";
+
+  GenSerialize(s);
+  s << "\n";
+
+  GenParse(s);
+  s << "\n";
+
+  GenSize(s);
+  s << "\n";
+
+  GenInstanceOf(s);
+  s << "\n";
+
+  GenSpecialize(s);
+  s << "\n";
+
+  GenMembers(s);
+  s << "};\n";
+
+  if (fields_.HasBody()) {
+    GenParseFunctionPrototype(s);
+  }
+  s << "\n";
+}
+
+void StructDef::GenDefinitionPybind11(std::ostream& s) const {
+  s << "py::class_<" << name_;
+  if (parent_ != nullptr) {
+    s << ", " << parent_->name_;
+  } else {
+    if (is_little_endian_) {
+      s << ", PacketStruct<kLittleEndian>";
+    } else {
+      s << ", PacketStruct<!kLittleEndian>";
+    }
+  }
+  s << ", std::shared_ptr<" << name_ << ">";
+  s << ">(m, \"" << name_ << "\")";
+  s << ".def(py::init<>())";
+  s << ".def(\"Serialize\", [](" << GetTypeName() << "& obj){";
+  s << "std::vector<uint8_t> bytes;";
+  s << "BitInserter bi(bytes);";
+  s << "obj.Serialize(bi);";
+  s << "return bytes;})";
+  s << ".def(\"Parse\", &" << name_ << "::Parse)";
+  s << ".def(\"size\", &" << name_ << "::size)";
+  for (const auto& field : fields_) {
+    if (field->GetBuilderParameterType().empty()) {
+      continue;
+    }
+    s << ".def_readwrite(\"" << field->GetName() << "\", &" << name_ << "::" << field->GetName() << "_)";
+  }
+  s << ";\n";
+}
+
+void StructDef::GenConstructor(std::ostream& s) const {
+  if (parent_ != nullptr) {
+    s << name_ << "(const " << parent_->name_ << "& parent) : " << parent_->name_ << "(parent) {}";
+    s << name_ << "() : " << parent_->name_ << "() {";
+  } else {
+    s << name_ << "() {";
+  }
+
+  // Get the list of parent params.
+  FieldList parent_params;
+  if (parent_ != nullptr) {
+    parent_params = parent_->GetParamList().GetFieldsWithoutTypes({
+        PayloadField::kFieldType,
+        BodyField::kFieldType,
+    });
+
+    // Set constrained parent fields to their correct values.
+    for (int i = 0; i < parent_params.size(); i++) {
+      const auto& field = parent_params[i];
+      const auto& constraint = parent_constraints_.find(field->GetName());
+      if (constraint != parent_constraints_.end()) {
+        s << parent_->name_ << "::" << field->GetName() << "_ = ";
+        if (field->GetFieldType() == ScalarField::kFieldType) {
+          s << std::get<int64_t>(constraint->second) << ";";
+        } else if (field->GetFieldType() == EnumField::kFieldType) {
+          s << std::get<std::string>(constraint->second) << ";";
+        } else {
+          ERROR(field) << "Constraints on non enum/scalar fields should be impossible.";
+        }
+      }
+    }
+  }
+
+  s << "}\n";
+}
+
+Size StructDef::GetStructOffsetForField(std::string field_name) const {
+  auto size = Size(0);
+  for (auto it = fields_.begin(); it != fields_.end(); it++) {
+    // We've reached the field, end the loop.
+    if ((*it)->GetName() == field_name) break;
+    const auto& field = *it;
+    // When we need to parse this field, all previous fields should already be parsed.
+    if (field->GetStructSize().empty()) {
+      ERROR() << "Empty size for field " << (*it)->GetName() << " finding the offset for field: " << field_name;
+    }
+    size += field->GetStructSize();
+  }
+
+  // We need the offset until a body field.
+  if (parent_ != nullptr) {
+    auto parent_body_offset = static_cast<StructDef*>(parent_)->GetStructOffsetForField("body");
+    if (parent_body_offset.empty()) {
+      ERROR() << "Empty offset for body in " << parent_->name_ << " finding the offset for field: " << field_name;
+    }
+    size += parent_body_offset;
+  }
+
+  return size;
+}
diff --git a/gd/packet/parser/struct_def.h b/gd/packet/parser/struct_def.h
new file mode 100644
index 0000000..74c1b04
--- /dev/null
+++ b/gd/packet/parser/struct_def.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <map>
+#include <variant>
+
+#include "field_list.h"
+#include "fields/packet_field.h"
+#include "parent_def.h"
+#include "parse_location.h"
+#include "type_def.h"
+
+class StructDef : public ParentDef {
+ public:
+  StructDef(std::string name, FieldList fields);
+  StructDef(std::string name, FieldList fields, StructDef* parent);
+
+  PacketField* GetNewField(const std::string& name, ParseLocation loc) const;
+
+  TypeDef::Type GetDefinitionType() const;
+
+  void GenSpecialize(std::ostream& s) const;
+
+  void GenParse(std::ostream& s) const;
+
+  void GenParseFunctionPrototype(std::ostream& s) const;
+
+  void GenDefinition(std::ostream& s) const;
+
+  void GenDefinitionPybind11(std::ostream& s) const;
+
+  void GenConstructor(std::ostream& s) const;
+
+  Size GetStructOffsetForField(std::string field_name) const;
+
+ private:
+  Size total_size_;
+};
diff --git a/gd/packet/parser/struct_parser_generator.cc b/gd/packet/parser/struct_parser_generator.cc
new file mode 100644
index 0000000..352219e
--- /dev/null
+++ b/gd/packet/parser/struct_parser_generator.cc
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "struct_parser_generator.h"
+
+StructParserGenerator::StructParserGenerator(const Declarations& decls) {
+  is_little_endian = decls.is_little_endian;
+  for (const auto& s : decls.type_defs_queue_) {
+    if (s.second->GetDefinitionType() == TypeDef::Type::STRUCT) {
+      const auto* struct_def = dynamic_cast<const StructDef*>(s.second);
+      variable_struct_fields_.emplace_back(struct_def);
+    }
+  }
+  for (const auto& node : variable_struct_fields_) {
+    if (node.struct_def_->parent_ != nullptr) {
+      for (auto& parent : variable_struct_fields_) {
+        if (node.struct_def_->parent_->name_ == parent.struct_def_->name_) {
+          parent.children_.push_back(&node);
+        }
+      }
+    }
+  }
+}
+
+void StructParserGenerator::explore_children(const TreeNode& node, std::ostream& s) const {
+  auto field = node.packet_field_;
+  if (!node.children_.empty()) {
+    s << "bool " << field->GetName() << "_child_found = false; /* Greedy match */";
+  }
+  for (const auto& child : node.children_) {
+    s << "if (!" << field->GetName() << "_child_found && ";
+    s << child->struct_def_->name_ << "::IsInstance(*" << field->GetName() << "_value.get())) {";
+    s << field->GetName() << "_child_found = true;";
+    s << "std::unique_ptr<" << child->struct_def_->name_ << "> " << child->packet_field_->GetName() << "_value;";
+    s << child->packet_field_->GetName() << "_value.reset(new ";
+    s << child->struct_def_->name_ << "(*" << field->GetName() << "_value));";
+    if (child->struct_def_->fields_.HasBody()) {
+      s << "auto optional_it = ";
+      s << child->struct_def_->name_ << "::Parse( " << child->packet_field_->GetName() << "_value.get(), ";
+      s << "to_bound, false);";
+      s << "if (optional_it) {";
+      s << "} else { return " << field->GetName() << "_value;}";
+    } else {
+      s << child->struct_def_->name_ << "::Parse( " << child->packet_field_->GetName() << "_value.get(), ";
+      s << "to_bound, false);";
+    }
+    explore_children(*child, s);
+    s << field->GetName() << "_value = std::move(" << child->packet_field_->GetName() << "_value);";
+
+    s << " }";
+  }
+}
+
+void StructParserGenerator::Generate(std::ostream& s) const {
+  for (const auto& node : variable_struct_fields_) {
+    if (node.children_.empty()) {
+      continue;
+    }
+    auto field = node.packet_field_;
+    s << "inline std::unique_ptr<" << node.struct_def_->name_ << "> Parse" << node.struct_def_->name_;
+    if (is_little_endian) {
+      s << "(Iterator<kLittleEndian> to_bound) {";
+    } else {
+      s << "(Iterator<!kLittleEndian> to_bound) {";
+    }
+    s << field->GetDataType() << " " << field->GetName() << "_value = ";
+    s << "std::make_unique<" << node.struct_def_->name_ << ">();";
+
+    s << "auto " << field->GetName() << "_it = to_bound;";
+    s << "auto optional_it = ";
+    s << node.struct_def_->name_ << "::Parse( " << field->GetName() << "_value.get(), ";
+    s << field->GetName() << "_it";
+    if (node.struct_def_->parent_ != nullptr) {
+      s << ", true);";
+    } else {
+      s << ");";
+    }
+    s << "if (optional_it) {";
+    s << field->GetName() << "_it = *optional_it;";
+    s << "} else { return nullptr; }";
+
+    explore_children(node, s);
+    s << "return " << field->GetName() << "_value; }";
+  }
+}
diff --git a/gd/packet/parser/struct_parser_generator.h b/gd/packet/parser/struct_parser_generator.h
new file mode 100644
index 0000000..0315460
--- /dev/null
+++ b/gd/packet/parser/struct_parser_generator.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <iostream>
+
+#include "declarations.h"
+#include "fields/packet_field.h"
+#include "parse_location.h"
+
+class StructParserGenerator {
+ public:
+  explicit StructParserGenerator(const Declarations& declarations);
+
+  void Generate(std::ostream& s) const;
+
+ private:
+  class TreeNode {
+   public:
+    explicit TreeNode(const StructDef* s)
+        : struct_def_(s), packet_field_(s->GetNewField(s->name_ + "_parse", ParseLocation())) {}
+    const StructDef* struct_def_;
+    const PacketField* packet_field_;
+    std::list<const TreeNode*> children_;
+  };
+  std::list<TreeNode> variable_struct_fields_;
+  bool is_little_endian;
+
+  void explore_children(const TreeNode& node, std::ostream& s) const;
+};
diff --git a/gd/packet/parser/test/Android.bp b/gd/packet/parser/test/Android.bp
new file mode 100644
index 0000000..65b2f3e
--- /dev/null
+++ b/gd/packet/parser/test/Android.bp
@@ -0,0 +1,24 @@
+genrule {
+    name: "BluetoothPacketParserTestPacketPdlGen_h",
+    tools: [
+        "bluetooth_packetgen",
+    ],
+    cmd: "$(location bluetooth_packetgen) --include=system/bt/gd --out=$(genDir) $(in)",
+    srcs: [
+        "test_packets.pdl",
+        "big_endian_test_packets.pdl",
+    ],
+    out: [
+        "packet/parser/test/test_packets.h",
+        "packet/parser/test/big_endian_test_packets.h",
+    ],
+}
+
+filegroup {
+    name: "BluetoothPacketParserTestPacketTestSources",
+    srcs: [
+        "generated_packet_test.cc",
+        "six_bytes.cc",
+        "variable.cc",
+    ],
+}
diff --git a/gd/packet/parser/test/big_endian_test_packets.pdl b/gd/packet/parser/test/big_endian_test_packets.pdl
new file mode 100644
index 0000000..5647ea6
--- /dev/null
+++ b/gd/packet/parser/test/big_endian_test_packets.pdl
@@ -0,0 +1,266 @@
+big_endian_packets
+
+custom_field SixBytes : 48 "packet/parser/test/"
+custom_field Variable "packet/parser/test/"
+
+packet ParentBe {
+  _fixed_ = 0x12 : 8,
+  _size_(_payload_) : 8,
+  _payload_,
+  footer : 8,
+}
+
+packet ChildBe  : ParentBe {
+  field_name : 16,
+}
+
+enum FourBitsBe : 4 {
+  ONE = 1,
+  TWO = 2,
+  THREE = 3,
+  FIVE = 5,
+  TEN = 10,
+  LAZY_ME = 15,
+}
+
+packet ParentTwoBe {
+  _reserved_ : 4,
+  four_bits : FourBitsBe,
+  _payload_,
+}
+
+packet ChildTwoThreeBe  : ParentTwoBe (four_bits = THREE) {
+  more_bits : FourBitsBe,
+  _reserved_ : 4,
+  sixteen_bits : 16
+}
+
+packet ChildTwoTwoBe  : ParentTwoBe (four_bits = TWO) {
+  more_bits : FourBitsBe,
+  _reserved_ : 4,
+}
+
+packet ChildTwoTwoThreeBe  :ChildTwoTwoBe (more_bits = THREE) {
+}
+
+enum TwoBitsBe : 2 {
+  ZERO = 0,
+  ONE = 1,
+  TWO = 2,
+  THREE = 3,
+}
+
+packet MiddleFourBitsBe {
+  low_two : TwoBitsBe,
+  next_four : FourBitsBe,
+  straddle : FourBitsBe,
+  four_more : FourBitsBe,
+  high_two : TwoBitsBe,
+}
+
+packet ParentWithSixBytesBe {
+  two_bytes : 16,
+  six_bytes : SixBytes,
+  _payload_,
+}
+
+packet ChildWithSixBytesBe  : ParentWithSixBytesBe (two_bytes = 0x1234) {
+  child_six_bytes : SixBytes,
+}
+
+checksum SimpleSum : 16 "packet/parser/test/"
+
+packet ParentWithSumBe {
+  two_bytes : 16,
+  _checksum_start_(example_checksum),
+  sum_bytes : 16,
+  _payload_,
+  example_checksum : SimpleSum,
+}
+
+packet ChildWithSumBe  : ParentWithSumBe {
+  more_bytes : 32,
+  another_byte : 8,
+}
+
+packet ChildWithNestedSumBe  : ParentWithSumBe {
+  _checksum_start_(nested_checksum),
+  more_bytes : 32,
+  nested_checksum : SimpleSum,
+}
+
+packet ParentSizeModifierBe {
+  _size_(_payload_) : 8,
+  _payload_ : [+2*8], // Include two_bytes in the size
+  two_bytes : 16,
+}
+
+packet ChildSizeModifierBe  : ParentSizeModifierBe (two_bytes = 0x1211) {
+  more_bytes : 32,
+}
+
+enum ForArraysBe : 16 {
+  ONE = 0x0001,
+  TWO = 0x0002,
+  ONE_TWO = 0x0201,
+  TWO_THREE = 0x0302,
+  FFFF = 0xffff,
+}
+
+packet FixedArrayEnumBe {
+  enum_array : ForArraysBe[5],
+}
+
+packet SizedArrayEnumBe {
+  _size_(enum_array) : 16,
+  enum_array : ForArraysBe[],
+}
+
+packet CountArrayEnumBe {
+  _count_(enum_array) : 8,
+  enum_array : ForArraysBe[],
+}
+
+packet SizedArrayCustomBe {
+  _size_(six_bytes_array) : 8,
+  an_extra_byte : 8,
+  six_bytes_array : SixBytes[+1*8],
+}
+
+packet FixedArrayCustomBe {
+  six_bytes_array : SixBytes[5],
+}
+
+packet CountArrayCustomBe {
+  _count_(six_bytes_array) : 8,
+  six_bytes_array : SixBytes[],
+}
+
+packet PacketWithFixedArraysOfBytesBe {
+  fixed_256bit_in_bytes : 8[32],
+  fixed_256bit_in_words : 32[8],
+}
+
+packet OneVariableBe {
+  one : Variable,
+}
+
+packet SizedArrayVariableBe {
+  _size_(variable_array) : 8,
+  variable_array : Variable[],
+}
+
+packet FixedArrayVariableBe {
+  variable_array : Variable[5],
+}
+
+packet CountArrayVariableBe {
+  _count_(variable_array) : 8,
+  variable_array : Variable[],
+}
+
+struct TwoRelatedNumbersBe {
+  id : 8,
+  count : 16,
+}
+
+packet OneStructBe {
+  one : TwoRelatedNumbersBe,
+}
+
+packet TwoStructsBe {
+  one : TwoRelatedNumbersBe,
+  two : TwoRelatedNumbersBe,
+}
+
+packet ArrayOfStructBe {
+  _count_(array) : 8,
+  array : TwoRelatedNumbersBe[],
+}
+
+struct StructWithFixedTypesBe {
+  four_bits : FourBitsBe,
+  _reserved_ : 4,
+  _checksum_start_(example_checksum),
+  _fixed_ = 0xf3 : 8,
+  id : 8,
+  array : 8[3],
+  example_checksum : SimpleSum,
+  six_bytes : SixBytes,
+}
+
+packet OneFixedTypesStructBe {
+  one : StructWithFixedTypesBe,
+}
+
+packet ArrayOfStructAndAnotherBe {
+  _count_(array) : 8,
+  array : TwoRelatedNumbersBe[],
+  another : TwoRelatedNumbersBe,
+}
+
+group BitFieldGroupBe {
+  seven_bits : 7,
+  straddle : 4,
+  five_bits : 5,
+}
+
+packet BitFieldGroupPacketBe {
+  BitFieldGroupBe,
+}
+
+packet BitFieldGroupAfterPayloadPacketBe {
+  _payload_,
+  BitFieldGroupBe,
+}
+
+packet BitFieldGroupAfterUnsizedArrayPacketBe  : BitFieldGroupAfterPayloadPacketBe {
+  array : 8[],
+}
+
+struct BitFieldBe {
+  seven_bits : 7,
+  straddle : 4,
+  five_bits : 5,
+}
+
+packet BitFieldPacketBe {
+  bit_field : BitFieldBe,
+}
+
+packet BitFieldAfterPayloadPacketBe {
+  _payload_,
+  bit_field : BitFieldBe,
+}
+
+packet BitFieldAfterUnsizedArrayPacketBe  : BitFieldAfterPayloadPacketBe {
+  array : 8[],
+}
+
+packet BitFieldArrayPacketBe {
+  _size_(array): 8,
+  array : BitFieldBe[],
+}
+
+struct VersionlessStructBe {
+  one_number : 8,
+}
+
+packet OneVersionlessStructPacketBe {
+  versionless : VersionlessStructBe,
+  _payload_,
+}
+
+packet OneVersionedStructPacketBe  : OneVersionlessStructPacketBe {
+  version : 8,
+  _payload_,
+}
+
+packet OneVersionOneStructPacketBe  : OneVersionedStructPacketBe (version = 0x01) {
+  just_one_number : 8,
+}
+
+packet OneVersionTwoStructPacketBe  : OneVersionedStructPacketBe (version = 0x02) {
+  one_number : 8,
+  another_number : 8,
+}
diff --git a/gd/packet/parser/test/generated_packet_test.cc b/gd/packet/parser/test/generated_packet_test.cc
new file mode 100644
index 0000000..4feb11e
--- /dev/null
+++ b/gd/packet/parser/test/generated_packet_test.cc
@@ -0,0 +1,1904 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define PACKET_TESTING
+#include "packet/parser/test/big_endian_test_packets.h"
+#include "packet/parser/test/test_packets.h"
+
+#include <gtest/gtest.h>
+#include <forward_list>
+#include <memory>
+
+#include "os/log.h"
+#include "packet/bit_inserter.h"
+#include "packet/parser/test/six_bytes.h"
+#include "packet/raw_builder.h"
+
+using ::bluetooth::packet::BitInserter;
+using ::bluetooth::packet::kLittleEndian;
+using ::bluetooth::packet::RawBuilder;
+using ::bluetooth::packet::parser::test::SixBytes;
+using std::vector;
+
+namespace {
+vector<uint8_t> child_two_two_three = {
+    0x20 /* Reserved : 4, FourBits::TWO */,
+    0x03 /* FourBits::THREE, Reserved : 4 */,
+};
+vector<uint8_t> child = {
+    0x12 /* fixed */, 0x02 /* Size of the payload */, 0xa1 /* First byte of the payload */, 0xa2, 0xb1 /* footer */,
+};
+vector<uint8_t> child_with_six_bytes = {
+    0x34 /* TwoBytes */,
+    0x12,
+    0xa1 /* First byte of the six_bytes */,
+    0xa2,
+    0xa3,
+    0xa4,
+    0xa5,
+    0xa6,
+    0xb1 /* Second six_bytes*/,
+    0xb2,
+    0xb3,
+    0xb4,
+    0xb5,
+    0xb6,
+};
+
+}  // namespace
+
+namespace bluetooth {
+namespace packet {
+namespace parser {
+using namespace test;
+
+TEST(GeneratedPacketTest, testChildTwoTwoThree) {
+  auto packet = ChildTwoTwoThreeBuilder::Create();
+
+  ASSERT_EQ(child_two_two_three.size(), packet->size());
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  packet->Serialize(it);
+
+  ASSERT_EQ(packet_bytes->size(), child_two_two_three.size());
+  for (size_t i = 0; i < child_two_two_three.size(); i++) {
+    ASSERT_EQ(packet_bytes->at(i), child_two_two_three[i]);
+  }
+
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  ParentView wrong_view = ParentView::Create(packet_bytes_view);
+  ASSERT_FALSE(wrong_view.IsValid());
+
+  ParentTwoView parent_view = ParentTwoView::Create(packet_bytes_view);
+  ASSERT_TRUE(parent_view.IsValid());
+  ASSERT_EQ(FourBits::TWO, parent_view.GetFourBits());
+
+  ChildTwoTwoView child_view = ChildTwoTwoView::Create(parent_view);
+  ASSERT_TRUE(child_view.IsValid());
+  ASSERT_EQ(FourBits::THREE, child_view.GetMoreBits());
+
+  ChildTwoTwoThreeView grandchild_view = ChildTwoTwoThreeView::Create(child_view);
+  ASSERT_TRUE(grandchild_view.IsValid());
+}
+
+TEST(GeneratedPacketTest, testChild) {
+  uint16_t field_name = 0xa2a1;
+  uint8_t footer = 0xb1;
+  auto packet = ChildBuilder::Create(field_name, footer);
+
+  ASSERT_EQ(child.size(), packet->size());
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  packet->Serialize(it);
+
+  ASSERT_EQ(packet_bytes->size(), child.size());
+  for (size_t i = 0; i < child.size(); i++) {
+    ASSERT_EQ(packet_bytes->at(i), child[i]);
+  }
+
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  ParentView parent_view = ParentView::Create(packet_bytes_view);
+  ASSERT_TRUE(parent_view.IsValid());
+  auto payload = parent_view.GetPayload();
+
+  ASSERT_EQ(child[1 /* skip fixed field */], payload.size());
+  for (size_t i = 0; i < payload.size(); i++) {
+    ASSERT_EQ(child[i + 2 /* fixed & size */], payload[i]);
+  }
+
+  ChildView child_view = ChildView::Create(parent_view);
+  ASSERT_TRUE(child_view.IsValid());
+
+  ASSERT_EQ(field_name, child_view.GetFieldName());
+}
+
+TEST(GeneratedPacketTest, testValidateWayTooSmall) {
+  std::vector<uint8_t> too_small_bytes = {0x34};
+  auto too_small = std::make_shared<std::vector<uint8_t>>(too_small_bytes.begin(), too_small_bytes.end());
+
+  ParentWithSixBytesView invalid_parent = ParentWithSixBytesView::Create(too_small);
+  ASSERT_FALSE(invalid_parent.IsValid());
+  ChildWithSixBytesView invalid = ChildWithSixBytesView::Create(ParentWithSixBytesView::Create(too_small));
+  ASSERT_FALSE(invalid.IsValid());
+}
+
+TEST(GeneratedPacketTest, testValidateTooSmall) {
+  std::vector<uint8_t> too_small_bytes = {0x34, 0x12, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x11};
+  auto too_small = std::make_shared<std::vector<uint8_t>>(too_small_bytes.begin(), too_small_bytes.end());
+
+  ParentWithSixBytesView valid_parent = ParentWithSixBytesView::Create(too_small);
+  ASSERT_TRUE(valid_parent.IsValid());
+  ChildWithSixBytesView invalid = ChildWithSixBytesView::Create(ParentWithSixBytesView::Create(too_small));
+  ASSERT_FALSE(invalid.IsValid());
+}
+
+TEST(GeneratedPacketTest, testValidateJustRight) {
+  std::vector<uint8_t> just_right_bytes = {0x34, 0x12, 0x01, 0x02, 0x03, 0x04, 0x05,
+                                           0x06, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16};
+  auto just_right = std::make_shared<std::vector<uint8_t>>(just_right_bytes.begin(), just_right_bytes.end());
+
+  ChildWithSixBytesView valid = ChildWithSixBytesView::Create(ParentWithSixBytesView::Create(just_right));
+  ASSERT_TRUE(valid.IsValid());
+}
+
+TEST(GeneratedPacketTest, testValidateTooBig) {
+  std::vector<uint8_t> too_big_bytes = {0x34, 0x12, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+                                        0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x20};
+  auto too_big = std::make_shared<std::vector<uint8_t>>(too_big_bytes.begin(), too_big_bytes.end());
+
+  ChildWithSixBytesView lenient = ChildWithSixBytesView::Create(ParentWithSixBytesView::Create(too_big));
+  ASSERT_TRUE(lenient.IsValid());
+}
+
+TEST(GeneratedPacketTest, testValidateDeath) {
+  auto packet = ChildTwoTwoThreeBuilder::Create();
+
+  ASSERT_EQ(child_two_two_three.size(), packet->size());
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  packet->Serialize(it);
+
+  ASSERT_EQ(packet_bytes->size(), child_two_two_three.size());
+  for (size_t i = 0; i < child_two_two_three.size(); i++) {
+    ASSERT_EQ(packet_bytes->at(i), child_two_two_three[i]);
+  }
+
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  ParentView wrong_view = ParentView::Create(packet_bytes_view);
+  ASSERT_DEATH(wrong_view.GetPayload(), "validated");
+}
+
+TEST(GeneratedPacketTest, testValidatedParentDeath) {
+  uint16_t field_name = 0xa2a1;
+  uint8_t footer = 0xb1;
+  auto packet = ChildBuilder::Create(field_name, footer);
+
+  ASSERT_EQ(child.size(), packet->size());
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  packet->Serialize(it);
+
+  ASSERT_EQ(packet_bytes->size(), child.size());
+  for (size_t i = 0; i < child.size(); i++) {
+    ASSERT_EQ(packet_bytes->at(i), child[i]);
+  }
+
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  ParentView parent_view = ParentView::Create(packet_bytes_view);
+  ASSERT_TRUE(parent_view.IsValid());
+  auto payload = parent_view.GetPayload();
+
+  ASSERT_EQ(child[1 /* skip fixed field */], payload.size());
+  for (size_t i = 0; i < payload.size(); i++) {
+    ASSERT_EQ(child[i + 2 /* fixed & size */], payload[i]);
+  }
+
+  ChildView child_view = ChildView::Create(parent_view);
+  ASSERT_DEATH(child_view.GetFieldName(), "validated");
+}
+
+vector<uint8_t> middle_four_bits = {
+    0x95,  // low_two = ONE, next_four = FIVE, straddle = TEN
+    0x8a,  // straddle = TEN, four_more = TWO, high_two = TWO
+};
+
+TEST(GeneratedPacketTest, testMiddleFourBitsPacket) {
+  TwoBits low_two = TwoBits::ONE;
+  FourBits next_four = FourBits::FIVE;
+  FourBits straddle = FourBits::TEN;
+  FourBits four_more = FourBits::TWO;
+  TwoBits high_two = TwoBits::TWO;
+
+  auto packet = MiddleFourBitsBuilder::Create(low_two, next_four, straddle, four_more, high_two);
+
+  ASSERT_EQ(middle_four_bits.size(), packet->size());
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  packet->Serialize(it);
+
+  ASSERT_EQ(packet_bytes->size(), middle_four_bits.size());
+  for (size_t i = 0; i < middle_four_bits.size(); i++) {
+    ASSERT_EQ(packet_bytes->at(i), middle_four_bits[i]);
+  }
+
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  MiddleFourBitsView view = MiddleFourBitsView::Create(packet_bytes_view);
+  ASSERT_TRUE(view.IsValid());
+  ASSERT_EQ(low_two, view.GetLowTwo());
+  ASSERT_EQ(next_four, view.GetNextFour());
+  ASSERT_EQ(straddle, view.GetStraddle());
+  ASSERT_EQ(four_more, view.GetFourMore());
+  ASSERT_EQ(high_two, view.GetHighTwo());
+}
+
+TEST(GeneratedPacketTest, testChildWithSixBytes) {
+  SixBytes six_bytes_a{{0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6}};
+  SixBytes six_bytes_b{{0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6}};
+  auto packet = ChildWithSixBytesBuilder::Create(six_bytes_a, six_bytes_b);
+
+  ASSERT_EQ(child_with_six_bytes.size(), packet->size());
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  packet->Serialize(it);
+
+  ASSERT_EQ(packet_bytes->size(), child_with_six_bytes.size());
+  for (size_t i = 0; i < child_with_six_bytes.size(); i++) {
+    ASSERT_EQ(packet_bytes->at(i), child_with_six_bytes[i]);
+  }
+
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  ParentWithSixBytesView parent_view = ParentWithSixBytesView::Create(packet_bytes_view);
+  ASSERT_TRUE(parent_view.IsValid());
+  ASSERT_EQ(six_bytes_a, parent_view.GetSixBytes());
+
+  ChildWithSixBytesView child_view = ChildWithSixBytesView::Create(parent_view);
+  ASSERT_TRUE(child_view.IsValid());
+
+  ASSERT_EQ(six_bytes_a, child_view.GetSixBytes());
+  ASSERT_EQ(six_bytes_a, ((ParentWithSixBytesView)child_view).GetSixBytes());
+  ASSERT_EQ(six_bytes_b, child_view.GetChildSixBytes());
+}
+
+namespace {
+vector<uint8_t> parent_with_sum = {
+    0x11 /* TwoBytes */, 0x12, 0x21 /* Sum Bytes */, 0x22, 0x43 /* Sum, excluding TwoBytes */, 0x00,
+};
+
+}  // namespace
+
+TEST(GeneratedPacketTest, testParentWithSum) {
+  uint16_t two_bytes = 0x1211;
+  uint16_t sum_bytes = 0x2221;
+  auto packet = ParentWithSumBuilder::Create(two_bytes, sum_bytes, std::make_unique<packet::RawBuilder>());
+
+  ASSERT_EQ(parent_with_sum.size(), packet->size());
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  packet->Serialize(it);
+
+  ASSERT_EQ(packet_bytes->size(), parent_with_sum.size());
+  for (size_t i = 0; i < parent_with_sum.size(); i++) {
+    ASSERT_EQ(packet_bytes->at(i), parent_with_sum[i]);
+  }
+
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  ParentWithSumView parent_view = ParentWithSumView::Create(packet_bytes_view);
+  ASSERT_TRUE(parent_view.IsValid());
+  ASSERT_EQ(two_bytes, parent_view.GetTwoBytes());
+
+  // Corrupt checksum
+  packet_bytes->back()++;
+  PacketView<kLittleEndian> corrupted_bytes_view(packet_bytes);
+  ParentWithSumView corrupted_view = ParentWithSumView::Create(corrupted_bytes_view);
+  ASSERT_FALSE(corrupted_view.IsValid());
+}
+
+namespace {
+vector<uint8_t> child_with_nested_sum = {
+    0x11 /* TwoBytes */,
+    0x12,
+    0x21 /* Sum Bytes */,
+    0x22,
+    0x31 /* More Bytes */,
+    0x32,
+    0x33,
+    0x34,
+    0xca /* Nested Sum */,
+    0x00,
+    0xd7 /* Sum, excluding TwoBytes */,
+    0x01,
+};
+
+}  // namespace
+
+TEST(GeneratedPacketTest, testChildWithNestedSum) {
+  uint16_t two_bytes = 0x1211;
+  uint16_t sum_bytes = 0x2221;
+  uint32_t more_bytes = 0x34333231;
+  auto packet = ChildWithNestedSumBuilder::Create(two_bytes, sum_bytes, more_bytes);
+
+  ASSERT_EQ(child_with_nested_sum.size(), packet->size());
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  packet->Serialize(it);
+
+  ASSERT_EQ(packet_bytes->size(), child_with_nested_sum.size());
+  for (size_t i = 0; i < child_with_nested_sum.size(); i++) {
+    ASSERT_EQ(packet_bytes->at(i), child_with_nested_sum[i]);
+  }
+
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  ParentWithSumView parent_view = ParentWithSumView::Create(packet_bytes_view);
+  ASSERT_TRUE(parent_view.IsValid());
+  ASSERT_EQ(two_bytes, parent_view.GetTwoBytes());
+
+  ChildWithNestedSumView child_view = ChildWithNestedSumView::Create(parent_view);
+  ASSERT_TRUE(child_view.IsValid());
+
+  ASSERT_EQ(more_bytes, child_view.GetMoreBytes());
+}
+
+namespace {
+vector<uint8_t> parent_size_modifier = {
+    0x02 /* Size */,
+    0x11 /* TwoBytes */,
+    0x12,
+};
+
+}  // namespace
+
+TEST(GeneratedPacketTest, testParentSizeModifier) {
+  uint16_t two_bytes = 0x1211;
+  auto packet = ParentSizeModifierBuilder::Create(std::make_unique<RawBuilder>(), two_bytes);
+
+  ASSERT_EQ(parent_size_modifier.size(), packet->size());
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  packet->Serialize(it);
+
+  ASSERT_EQ(parent_size_modifier.size(), packet_bytes->size());
+  for (size_t i = 0; i < parent_size_modifier.size(); i++) {
+    ASSERT_EQ(parent_size_modifier[i], packet_bytes->at(i));
+  }
+
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  ParentSizeModifierView parent_view = ParentSizeModifierView::Create(packet_bytes_view);
+  ASSERT_TRUE(parent_view.IsValid());
+  ASSERT_EQ(two_bytes, parent_view.GetTwoBytes());
+}
+
+namespace {
+vector<uint8_t> child_size_modifier = {
+    0x06 /* PayloadSize (TwoBytes + MoreBytes)*/,
+    0x31 /* MoreBytes */,
+    0x32,
+    0x33,
+    0x34,
+    0x11 /* TwoBytes = 0x1211 */,
+    0x12,
+};
+
+}  // namespace
+
+TEST(GeneratedPacketTest, testChildSizeModifier) {
+  uint16_t two_bytes = 0x1211;
+  uint32_t more_bytes = 0x34333231;
+  auto packet = ChildSizeModifierBuilder::Create(more_bytes);
+
+  ASSERT_EQ(child_size_modifier.size(), packet->size());
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  packet->Serialize(it);
+
+  ASSERT_EQ(child_size_modifier.size(), packet_bytes->size());
+  for (size_t i = 0; i < child_size_modifier.size(); i++) {
+    ASSERT_EQ(child_size_modifier[i], packet_bytes->at(i));
+  }
+
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  ParentSizeModifierView parent_view = ParentSizeModifierView::Create(packet_bytes_view);
+  ASSERT_TRUE(parent_view.IsValid());
+  ASSERT_EQ(two_bytes, parent_view.GetTwoBytes());
+
+  ChildSizeModifierView child_view = ChildSizeModifierView::Create(parent_view);
+  ASSERT_TRUE(child_view.IsValid());
+
+  ASSERT_EQ(more_bytes, child_view.GetMoreBytes());
+}
+
+namespace {
+vector<uint8_t> fixed_array_enum{
+    0x01,  // ONE
+    0x00,
+    0x02,  // TWO
+    0x00,
+    0x01,  // ONE_TWO
+    0x02,
+    0x02,  // TWO_THREE
+    0x03,
+    0xff,  // FFFF
+    0xff,
+};
+}
+
+TEST(GeneratedPacketTest, testFixedArrayEnum) {
+  std::array<ForArrays, 5> fixed_array{
+      {ForArrays::ONE, ForArrays::TWO, ForArrays::ONE_TWO, ForArrays::TWO_THREE, ForArrays::FFFF}};
+  auto packet = FixedArrayEnumBuilder::Create(fixed_array);
+  ASSERT_EQ(fixed_array_enum.size(), packet->size());
+
+  // Verify that the packet is independent from the array.
+  std::array<ForArrays, 5> copy_array(fixed_array);
+  fixed_array[1] = ForArrays::ONE;
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  packet->Serialize(it);
+
+  ASSERT_EQ(fixed_array_enum.size(), packet_bytes->size());
+  for (size_t i = 0; i < fixed_array_enum.size(); i++) {
+    ASSERT_EQ(fixed_array_enum[i], packet_bytes->at(i));
+  }
+
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  auto view = FixedArrayEnumView::Create(packet_bytes_view);
+  ASSERT_TRUE(view.IsValid());
+  auto array = view.GetEnumArray();
+  ASSERT_EQ(copy_array.size(), array.size());
+  for (size_t i = 0; i < copy_array.size(); i++) {
+    ASSERT_EQ(array[i], copy_array[i]);
+  }
+}
+
+namespace {
+vector<uint8_t> sized_array_enum{
+    0x0a,  // _size_
+    0x00,
+    0x01,  // ONE
+    0x00,
+    0x02,  // TWO
+    0x00,
+    0x01,  // ONE_TWO
+    0x02,
+    0x02,  // TWO_THREE
+    0x03,
+    0xff,  // FFFF
+    0xff,
+};
+}
+
+TEST(GeneratedPacketTest, testSizedArrayEnum) {
+  std::vector<ForArrays> sized_array{
+      {ForArrays::ONE, ForArrays::TWO, ForArrays::ONE_TWO, ForArrays::TWO_THREE, ForArrays::FFFF}};
+  auto packet = SizedArrayEnumBuilder::Create(sized_array);
+  ASSERT_EQ(sized_array_enum.size(), packet->size());
+
+  // Copy the original vector and modify it to make sure the packet is independent.
+  std::vector<ForArrays> copy_array(sized_array);
+  sized_array[1] = ForArrays::ONE;
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  packet->Serialize(it);
+
+  ASSERT_EQ(sized_array_enum.size(), packet_bytes->size());
+  for (size_t i = 0; i < sized_array_enum.size(); i++) {
+    ASSERT_EQ(sized_array_enum[i], packet_bytes->at(i));
+  }
+
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  auto view = SizedArrayEnumView::Create(packet_bytes_view);
+  ASSERT_TRUE(view.IsValid());
+  auto array = view.GetEnumArray();
+  ASSERT_EQ(copy_array.size(), array.size());
+  for (size_t i = 0; i < copy_array.size(); i++) {
+    ASSERT_EQ(array[i], copy_array[i]);
+  }
+}
+
+namespace {
+vector<uint8_t> count_array_enum{
+    0x03,  // _count_
+    0x01,  // ONE
+    0x00,
+    0x02,  // TWO_THREE
+    0x03,
+    0xff,  // FFFF
+    0xff,
+};
+}
+
+TEST(GeneratedPacketTest, testCountArrayEnum) {
+  std::vector<ForArrays> count_array{{ForArrays::ONE, ForArrays::TWO_THREE, ForArrays::FFFF}};
+  auto packet = CountArrayEnumBuilder::Create(count_array);
+  ASSERT_EQ(count_array_enum.size(), packet->size());
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  packet->Serialize(it);
+
+  ASSERT_EQ(count_array_enum.size(), packet_bytes->size());
+  for (size_t i = 0; i < count_array_enum.size(); i++) {
+    ASSERT_EQ(count_array_enum[i], packet_bytes->at(i));
+  }
+
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  auto view = CountArrayEnumView::Create(packet_bytes_view);
+  ASSERT_TRUE(view.IsValid());
+  auto array = view.GetEnumArray();
+  ASSERT_EQ(count_array.size(), array.size());
+  for (size_t i = 0; i < count_array.size(); i++) {
+    ASSERT_EQ(array[i], count_array[i]);
+  }
+}
+
+TEST(GeneratedPacketTest, testFixedSizeByteArray) {
+  constexpr std::size_t byte_array_size = 32;
+  std::array<uint8_t, byte_array_size> byte_array;
+  for (uint8_t i = 0; i < byte_array_size; i++) byte_array[i] = i;
+
+  constexpr int word_array_size = 8;
+  std::array<uint32_t, word_array_size> word_array;
+  for (uint32_t i = 0; i < word_array_size; i++) word_array[i] = i;
+
+  auto packet = PacketWithFixedArraysOfBytesBuilder::Create(byte_array, word_array);
+  ASSERT_EQ(2 * (256 / 8), packet->size());
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  packet->Serialize(it);
+
+  ASSERT_EQ(byte_array_size + word_array_size * sizeof(uint32_t), packet_bytes->size());
+
+  for (size_t i = 0; i < byte_array_size; i++) {
+    ASSERT_EQ(byte_array[i], packet_bytes->at(i));
+  }
+
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  auto view = PacketWithFixedArraysOfBytesView::Create(packet_bytes_view);
+  ASSERT_TRUE(view.IsValid());
+  auto array = view.GetFixed256bitInBytes();
+  ASSERT_EQ(byte_array.size(), array.size());
+  for (size_t i = 0; i < array.size(); i++) {
+    ASSERT_EQ(array[i], byte_array[i]);
+  }
+
+  auto decoded_word_array = view.GetFixed256bitInWords();
+  ASSERT_EQ(word_array.size(), decoded_word_array.size());
+  for (size_t i = 0; i < decoded_word_array.size(); i++) {
+    ASSERT_EQ(word_array[i], decoded_word_array[i]);
+  }
+}
+
+vector<uint8_t> one_variable{
+    0x03, 'o', 'n', 'e',  // "one"
+};
+
+TEST(GeneratedPacketTest, testOneVariableField) {
+  Variable variable_one{"one"};
+
+  auto packet = OneVariableBuilder::Create(variable_one);
+  ASSERT_EQ(one_variable.size(), packet->size());
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  packet->Serialize(it);
+
+  ASSERT_EQ(one_variable.size(), packet_bytes->size());
+  for (size_t i = 0; i < one_variable.size(); i++) {
+    ASSERT_EQ(one_variable[i], packet_bytes->at(i));
+  }
+
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  auto view = OneVariableView::Create(packet_bytes_view);
+  ASSERT_TRUE(view.IsValid());
+  auto one = view.GetOne();
+  ASSERT_EQ(one->data, variable_one.data);
+}
+
+vector<uint8_t> fou_variable{
+    0x04, 'f', 'o', 'u',  // too short
+};
+
+TEST(GeneratedPacketTest, testOneVariableFieldTooShort) {
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>(fou_variable);
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  auto view = OneVariableView::Create(packet_bytes_view);
+  ASSERT_TRUE(view.IsValid());
+  auto one = view.GetOne();
+  ASSERT_EQ(one, nullptr);
+}
+
+vector<uint8_t> sized_array_variable{
+    0x0e,                           // _size_
+    0x03, 'o', 'n', 'e',            // "one"
+    0x03, 't', 'w', 'o',            // "two"
+    0x05, 't', 'h', 'r', 'e', 'e',  // "three"
+};
+
+TEST(GeneratedPacketTest, testSizedArrayVariableLength) {
+  std::vector<Variable> sized_array;
+  sized_array.emplace_back("one");
+  sized_array.emplace_back("two");
+  sized_array.emplace_back("three");
+
+  auto packet = SizedArrayVariableBuilder::Create(sized_array);
+  ASSERT_EQ(sized_array_variable.size(), packet->size());
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  packet->Serialize(it);
+
+  ASSERT_EQ(sized_array_variable.size(), packet_bytes->size());
+  for (size_t i = 0; i < sized_array_variable.size(); i++) {
+    ASSERT_EQ(sized_array_variable[i], packet_bytes->at(i));
+  }
+
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  auto view = SizedArrayVariableView::Create(packet_bytes_view);
+  ASSERT_TRUE(view.IsValid());
+  auto array = view.GetVariableArray();
+  ASSERT_EQ(sized_array.size(), array.size());
+  for (size_t i = 0; i < sized_array.size(); i++) {
+    ASSERT_EQ(array[i].data, sized_array[i].data);
+  }
+}
+
+vector<uint8_t> sized_array_variable_too_short{
+    0x0e,                           // _size_
+    0x03, 'o', 'n', 'e',            // "one"
+    0x03, 't', 'w', 'o',            // "two"
+    0x06, 't', 'h', 'r', 'e', 'e',  // "three" needs another letter to be length 6
+};
+
+TEST(GeneratedPacketTest, testSizedArrayVariableLengthLastBad) {
+  std::vector<Variable> sized_array;
+  sized_array.emplace_back("one");
+  sized_array.emplace_back("two");
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes =
+      std::make_shared<std::vector<uint8_t>>(sized_array_variable_too_short);
+
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  auto view = SizedArrayVariableView::Create(packet_bytes_view);
+  ASSERT_TRUE(view.IsValid());
+  auto array = view.GetVariableArray();
+  ASSERT_EQ(sized_array.size(), array.size());
+  for (size_t i = 0; i < sized_array.size(); i++) {
+    ASSERT_EQ(array[i].data, sized_array[i].data);
+  }
+}
+
+vector<uint8_t> sized_array_variable_first_too_short{
+    0x0e,                           // _size_
+    0x02, 'o', 'n', 'e',            // "on"
+    0x03, 't', 'w', 'o',            // "two"
+    0x05, 't', 'h', 'r', 'e', 'e',  // "three"
+};
+
+TEST(GeneratedPacketTest, testSizedArrayVariableLengthFirstBad) {
+  std::vector<Variable> sized_array;
+  sized_array.emplace_back("on");
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes =
+      std::make_shared<std::vector<uint8_t>>(sized_array_variable_first_too_short);
+
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  auto view = SizedArrayVariableView::Create(packet_bytes_view);
+  ASSERT_TRUE(view.IsValid());
+  auto array = view.GetVariableArray();
+  ASSERT_EQ(sized_array.size(), array.size());
+  for (size_t i = 0; i < sized_array.size(); i++) {
+    ASSERT_EQ(array[i].data, sized_array[i].data);
+  }
+}
+
+vector<uint8_t> fixed_array_variable{
+    0x03, 'o', 'n', 'e',            // "one"
+    0x03, 't', 'w', 'o',            // "two"
+    0x05, 't', 'h', 'r', 'e', 'e',  // "three"
+    0x04, 'f', 'o', 'u', 'r',       // "four"
+    0x04, 'f', 'i', 'v', 'e',       // "five"
+};
+
+TEST(GeneratedPacketTest, testFixedArrayVariableLength) {
+  std::array<Variable, 5> fixed_array{std::string("one"), std::string("two"), std::string("three"), std::string("four"),
+                                      std::string("five")};
+
+  auto packet = FixedArrayVariableBuilder::Create(fixed_array);
+  ASSERT_EQ(fixed_array_variable.size(), packet->size());
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  packet->Serialize(it);
+
+  ASSERT_EQ(fixed_array_variable.size(), packet_bytes->size());
+  for (size_t i = 0; i < fixed_array_variable.size(); i++) {
+    ASSERT_EQ(fixed_array_variable[i], packet_bytes->at(i));
+  }
+
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  auto view = FixedArrayVariableView::Create(packet_bytes_view);
+  ASSERT_TRUE(view.IsValid());
+  auto array = view.GetVariableArray();
+  ASSERT_EQ(fixed_array.size(), array.size());
+  for (size_t i = 0; i < fixed_array.size(); i++) {
+    ASSERT_EQ(array[i].data, fixed_array[i].data);
+  }
+}
+
+vector<uint8_t> fixed_array_variable_too_short{
+    0x03, 'o', 'n', 'e',            // "one"
+    0x03, 't', 'w', 'o',            // "two"
+    0x05, 't', 'h', 'r', 'e', 'e',  // "three"
+    0x04, 'f', 'o', 'u', 'r',       // "four"
+    0x05, 'f', 'i', 'v', 'e',       // "five"
+};
+
+TEST(GeneratedPacketTest, testFixedArrayVariableLengthTooShort) {
+  std::array<Variable, 5> fixed_array{std::string("one"), std::string("two"), std::string("three"),
+                                      std::string("four")};
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes =
+      std::make_shared<std::vector<uint8_t>>(fixed_array_variable_too_short);
+
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  auto view = FixedArrayVariableView::Create(packet_bytes_view);
+  ASSERT_TRUE(view.IsValid());
+  auto array = view.GetVariableArray();
+  ASSERT_EQ(fixed_array.size(), array.size());
+  for (size_t i = 0; i < fixed_array.size(); i++) {
+    ASSERT_EQ(array[i].data, fixed_array[i].data);
+  }
+}
+
+vector<uint8_t> count_array_variable{
+    0x04,                           // _count_
+    0x03, 'o', 'n', 'e',            // "one"
+    0x03, 't', 'w', 'o',            // "two"
+    0x05, 't', 'h', 'r', 'e', 'e',  // "three"
+    0x04, 'f', 'o', 'u', 'r',       // "four"
+};
+
+TEST(GeneratedPacketTest, testCountArrayVariableLength) {
+  std::vector<Variable> count_array;
+  count_array.emplace_back("one");
+  count_array.emplace_back("two");
+  count_array.emplace_back("three");
+  count_array.emplace_back("four");
+
+  auto packet = CountArrayVariableBuilder::Create(count_array);
+  ASSERT_EQ(count_array_variable.size(), packet->size());
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  packet->Serialize(it);
+
+  ASSERT_EQ(count_array_variable.size(), packet_bytes->size());
+  for (size_t i = 0; i < count_array_variable.size(); i++) {
+    ASSERT_EQ(count_array_variable[i], packet_bytes->at(i));
+  }
+
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  auto view = CountArrayVariableView::Create(packet_bytes_view);
+  ASSERT_TRUE(view.IsValid());
+  auto array = view.GetVariableArray();
+  ASSERT_EQ(count_array.size(), array.size());
+  for (size_t i = 0; i < count_array.size(); i++) {
+    ASSERT_EQ(array[i].data, count_array[i].data);
+  }
+}
+
+vector<uint8_t> count_array_variable_extra{
+    0x04,                           // _count_
+    0x03, 'o', 'n', 'e',            // "one"
+    0x03, 't', 'w', 'o',            // "two"
+    0x05, 't', 'h', 'r', 'e', 'e',  // "three"
+    0x04, 'f', 'o', 'u', 'r',       // "four"
+    0x04, 'x', 't', 'r', 'a',       // "xtra"
+};
+
+TEST(GeneratedPacketTest, testCountArrayVariableLengthExtraData) {
+  std::vector<Variable> count_array;
+  count_array.emplace_back("one");
+  count_array.emplace_back("two");
+  count_array.emplace_back("three");
+  count_array.emplace_back("four");
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes =
+      std::make_shared<std::vector<uint8_t>>(count_array_variable_extra);
+
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  auto view = CountArrayVariableView::Create(packet_bytes_view);
+  ASSERT_TRUE(view.IsValid());
+  auto array = view.GetVariableArray();
+  ASSERT_EQ(count_array.size(), array.size());
+  for (size_t i = 0; i < count_array.size(); i++) {
+    ASSERT_EQ(array[i].data, count_array[i].data);
+  }
+}
+
+vector<uint8_t> count_array_variable_too_few{
+    0x04,                           // _count_
+    0x03, 'o', 'n', 'e',            // "one"
+    0x03, 't', 'w', 'o',            // "two"
+    0x05, 't', 'h', 'r', 'e', 'e',  // "three"
+};
+
+TEST(GeneratedPacketTest, testCountArrayVariableLengthMissingData) {
+  std::vector<Variable> count_array;
+  count_array.emplace_back("one");
+  count_array.emplace_back("two");
+  count_array.emplace_back("three");
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes =
+      std::make_shared<std::vector<uint8_t>>(count_array_variable_too_few);
+
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  auto view = CountArrayVariableView::Create(packet_bytes_view);
+  ASSERT_TRUE(view.IsValid());
+  auto array = view.GetVariableArray();
+  ASSERT_EQ(count_array.size(), array.size());
+  for (size_t i = 0; i < count_array.size(); i++) {
+    ASSERT_EQ(array[i].data, count_array[i].data);
+  }
+}
+
+vector<uint8_t> one_struct{
+    0x01, 0x02, 0x03,  // id = 0x01, count = 0x0302
+};
+
+TEST(GeneratedPacketTest, testOneStruct) {
+  TwoRelatedNumbers trn;
+  trn.id_ = 1;
+  trn.count_ = 0x0302;
+
+  auto packet = OneStructBuilder::Create(trn);
+  ASSERT_EQ(one_struct.size(), packet->size());
+
+  // Copy the original struct, then modify it to verify independence from the packet.
+  TwoRelatedNumbers copy_trn(trn);
+  trn.id_ = 2;
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  packet->Serialize(it);
+
+  ASSERT_EQ(one_struct.size(), packet_bytes->size());
+  for (size_t i = 0; i < one_struct.size(); i++) {
+    ASSERT_EQ(one_struct[i], packet_bytes->at(i));
+  }
+
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  auto view = OneStructView::Create(packet_bytes_view);
+  ASSERT_TRUE(view.IsValid());
+  auto one = view.GetOne();
+  ASSERT_EQ(one.id_, copy_trn.id_);
+  ASSERT_EQ(one.count_, copy_trn.count_);
+}
+
+vector<uint8_t> two_structs{
+    0x01, 0x01, 0x02,  // id, id * 0x0201
+    0x02, 0x02, 0x04,
+};
+
+TEST(GeneratedPacketTest, testTwoStructs) {
+  std::vector<TwoRelatedNumbers> count_array;
+  for (uint8_t i = 1; i < 3; i++) {
+    TwoRelatedNumbers trn;
+    trn.id_ = i;
+    trn.count_ = 0x0201 * i;
+    count_array.push_back(trn);
+  }
+
+  auto packet = TwoStructsBuilder::Create(count_array[0], count_array[1]);
+  ASSERT_EQ(two_structs.size(), packet->size());
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  packet->Serialize(it);
+
+  ASSERT_EQ(two_structs.size(), packet_bytes->size());
+  for (size_t i = 0; i < two_structs.size(); i++) {
+    ASSERT_EQ(two_structs[i], packet_bytes->at(i));
+  }
+
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  auto view = TwoStructsView::Create(packet_bytes_view);
+  ASSERT_TRUE(view.IsValid());
+  auto one = view.GetOne();
+  ASSERT_EQ(one.id_, count_array[0].id_);
+  ASSERT_EQ(one.count_, count_array[0].count_);
+  auto two = view.GetTwo();
+  ASSERT_EQ(two.id_, count_array[1].id_);
+  ASSERT_EQ(two.count_, count_array[1].count_);
+}
+
+vector<uint8_t> array_or_vector_of_struct{
+    0x04,              // _count_
+    0x01, 0x01, 0x02,  // id, id * 0x0201
+    0x02, 0x02, 0x04, 0x03, 0x03, 0x06, 0x04, 0x04, 0x08,
+};
+
+TEST(GeneratedPacketTest, testVectorOfStruct) {
+  std::vector<TwoRelatedNumbers> count_array;
+  for (uint8_t i = 1; i < 5; i++) {
+    TwoRelatedNumbers trn;
+    trn.id_ = i;
+    trn.count_ = 0x0201 * i;
+    count_array.push_back(trn);
+  }
+
+  // Make a copy
+  std::vector<TwoRelatedNumbers> copy_array(count_array);
+
+  auto packet = VectorOfStructBuilder::Create(count_array);
+
+  // Change the original vector to make sure a copy was made.
+  count_array[0].id_ = count_array[0].id_ + 1;
+
+  ASSERT_EQ(array_or_vector_of_struct.size(), packet->size());
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  packet->Serialize(it);
+
+  ASSERT_EQ(array_or_vector_of_struct.size(), packet_bytes->size());
+  for (size_t i = 0; i < array_or_vector_of_struct.size(); i++) {
+    ASSERT_EQ(array_or_vector_of_struct[i], packet_bytes->at(i));
+  }
+
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  auto view = VectorOfStructView::Create(packet_bytes_view);
+  ASSERT_TRUE(view.IsValid());
+  auto array = view.GetArray();
+  ASSERT_EQ(copy_array.size(), array.size());
+  for (size_t i = 0; i < copy_array.size(); i++) {
+    ASSERT_EQ(array[i].id_, copy_array[i].id_);
+    ASSERT_EQ(array[i].count_, copy_array[i].count_);
+  }
+}
+
+TEST(GeneratedPacketTest, testArrayOfStruct) {
+  std::array<TwoRelatedNumbers, 4> count_array;
+  for (uint8_t i = 1; i < 5; i++) {
+    TwoRelatedNumbers trn;
+    trn.id_ = i;
+    trn.count_ = 0x0201 * i;
+    count_array[i - 1] = trn;
+  }
+
+  // Make a copy
+  std::array<TwoRelatedNumbers, 4> copy_array(count_array);
+
+  auto packet = ArrayOfStructBuilder::Create(4, count_array);
+
+  // Change the original vector to make sure a copy was made.
+  count_array[0].id_ = count_array[0].id_ + 1;
+
+  ASSERT_EQ(array_or_vector_of_struct.size(), packet->size());
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  packet->Serialize(it);
+
+  ASSERT_EQ(array_or_vector_of_struct.size(), packet_bytes->size());
+  for (size_t i = 0; i < array_or_vector_of_struct.size(); i++) {
+    ASSERT_EQ(array_or_vector_of_struct[i], packet_bytes->at(i));
+  }
+
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  auto view = ArrayOfStructView::Create(packet_bytes_view);
+  ASSERT_TRUE(view.IsValid());
+  auto array = view.GetArray();
+  ASSERT_EQ(copy_array.size(), array.size());
+  for (size_t i = 0; i < copy_array.size(); i++) {
+    ASSERT_EQ(array[i].id_, copy_array[i].id_);
+    ASSERT_EQ(array[i].count_, copy_array[i].count_);
+  }
+}
+
+vector<uint8_t> one_fixed_types_struct{
+    0x05,                                // four_bits = FIVE, reserved
+    0xf3,                                // _fixed_
+    0x0d,                                // id = 0x0d
+    0x01, 0x02, 0x03,                    // array = { 1, 2, 3}
+    0x06, 0x01,                          // example_checksum
+    0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6,  // six_bytes
+};
+
+TEST(GeneratedPacketTest, testOneFixedTypesStruct) {
+  StructWithFixedTypes swf;
+  swf.four_bits_ = FourBits::FIVE;
+  swf.id_ = 0x0d;
+  swf.array_ = {{0x01, 0x02, 0x03}};
+  swf.six_bytes_ = SixBytes{{0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6}};
+
+  auto packet = OneFixedTypesStructBuilder::Create(swf);
+  ASSERT_EQ(one_fixed_types_struct.size(), packet->size());
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  packet->Serialize(it);
+
+  ASSERT_EQ(one_fixed_types_struct.size(), packet_bytes->size());
+  for (size_t i = 0; i < one_fixed_types_struct.size(); i++) {
+    ASSERT_EQ(one_fixed_types_struct[i], packet_bytes->at(i));
+  }
+
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  auto view = OneFixedTypesStructView::Create(packet_bytes_view);
+  ASSERT_TRUE(view.IsValid());
+  auto one = view.GetOne();
+  ASSERT_EQ(one.four_bits_, swf.four_bits_);
+  ASSERT_EQ(one.id_, swf.id_);
+  ASSERT_EQ(one.array_, swf.array_);
+  ASSERT_EQ(one.six_bytes_, swf.six_bytes_);
+}
+
+vector<uint8_t> array_of_struct_and_another{
+    0x03,              // _count_
+    0x01, 0x01, 0x02,  // id, id * 0x0201
+    0x02, 0x02, 0x04,  // 2
+    0x03, 0x03, 0x06,  // 3
+    0x04, 0x04, 0x08,  // Another
+};
+
+TEST(GeneratedPacketTest, testArrayOfStructAndAnother) {
+  std::vector<TwoRelatedNumbers> count_array;
+  for (uint8_t i = 1; i < 4; i++) {
+    TwoRelatedNumbers trn;
+    trn.id_ = i;
+    trn.count_ = 0x0201 * i;
+    count_array.push_back(trn);
+  }
+  TwoRelatedNumbers another;
+  another.id_ = 4;
+  another.count_ = 0x0201 * 4;
+
+  auto packet = ArrayOfStructAndAnotherBuilder::Create(count_array, another);
+  ASSERT_EQ(array_or_vector_of_struct.size(), packet->size());
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  packet->Serialize(it);
+
+  ASSERT_EQ(array_of_struct_and_another.size(), packet_bytes->size());
+  for (size_t i = 0; i < array_of_struct_and_another.size(); i++) {
+    ASSERT_EQ(array_of_struct_and_another[i], packet_bytes->at(i));
+  }
+
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  auto view = ArrayOfStructAndAnotherView::Create(packet_bytes_view);
+  ASSERT_TRUE(view.IsValid());
+  auto array = view.GetArray();
+  ASSERT_EQ(count_array.size(), array.size());
+  for (size_t i = 0; i < count_array.size(); i++) {
+    ASSERT_EQ(array[i].id_, count_array[i].id_);
+    ASSERT_EQ(array[i].count_, count_array[i].count_);
+  }
+  auto nother = view.GetAnother();
+  ASSERT_EQ(nother.id_, another.id_);
+  ASSERT_EQ(nother.count_, another.count_);
+}
+
+DEFINE_AND_INSTANTIATE_OneArrayOfStructAndAnotherStructReflectionTest(array_of_struct_and_another);
+
+TEST(GeneratedPacketTest, testOneArrayOfStructAndAnotherStruct) {
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes =
+      std::make_shared<std::vector<uint8_t>>(array_of_struct_and_another);
+
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  auto view = OneArrayOfStructAndAnotherStructView::Create(packet_bytes_view);
+  ASSERT_TRUE(view.IsValid());
+  auto one = view.GetOne();
+  ASSERT_EQ(one.array_.size(), 3);
+  ASSERT_EQ(one.another_.id_, 4);
+  ASSERT_EQ(one.another_.count_, 0x0804);
+}
+
+vector<uint8_t> sized_array_of_struct_and_another{
+    0x09,              // _size_
+    0x01, 0x01, 0x02,  // id, id * 0x0201
+    0x02, 0x02, 0x04,  // 2
+    0x03, 0x03, 0x06,  // 3
+    0x04, 0x04, 0x08,  // Another
+};
+
+DEFINE_AND_INSTANTIATE_OneSizedArrayOfStructAndAnotherStructReflectionTest(sized_array_of_struct_and_another);
+
+vector<uint8_t> bit_field_group_packet{
+    // seven_bits_ = 0x77, straddle_ = 0x5, five_bits_ = 0x15
+    0xf7,  // 0x77 | (0x5 & 0x1) << 7
+    0xaa,  //  0x15 << 3 | (0x5 >> 1)
+};
+
+TEST(GeneratedPacketTest, testBitFieldGroupPacket) {
+  uint8_t seven_bits = 0x77;
+  uint8_t straddle = 0x5;
+  uint8_t five_bits = 0x15;
+
+  auto packet = BitFieldGroupPacketBuilder::Create(seven_bits, straddle, five_bits);
+  ASSERT_EQ(bit_field_group_packet.size(), packet->size());
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  packet->Serialize(it);
+
+  ASSERT_EQ(bit_field_group_packet.size(), packet_bytes->size());
+  for (size_t i = 0; i < bit_field_group_packet.size(); i++) {
+    ASSERT_EQ(bit_field_group_packet[i], packet_bytes->at(i));
+  }
+
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  auto view = BitFieldGroupPacketView::Create(packet_bytes_view);
+  ASSERT_TRUE(view.IsValid());
+  ASSERT_EQ(seven_bits, view.GetSevenBits());
+  ASSERT_EQ(straddle, view.GetStraddle());
+  ASSERT_EQ(five_bits, view.GetFiveBits());
+}
+
+vector<uint8_t> bit_field_packet{
+    // seven_bits_ = 0x77, straddle_ = 0x5, five_bits_ = 0x15
+    0xf7,  // 0x77 | (0x5 & 0x1) << 7
+    0xaa,  //  0x15 << 3 | (0x5 >> 1)
+};
+
+TEST(GeneratedPacketTest, testBitFieldPacket) {
+  BitField bit_field;
+  bit_field.seven_bits_ = 0x77;
+  bit_field.straddle_ = 0x5;
+  bit_field.five_bits_ = 0x15;
+
+  auto packet = BitFieldPacketBuilder::Create(bit_field);
+  ASSERT_EQ(bit_field_packet.size(), packet->size());
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  packet->Serialize(it);
+
+  ASSERT_EQ(bit_field_packet.size(), packet_bytes->size());
+  for (size_t i = 0; i < bit_field_packet.size(); i++) {
+    ASSERT_EQ(bit_field_packet[i], packet_bytes->at(i));
+  }
+
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  auto view = BitFieldPacketView::Create(packet_bytes_view);
+  ASSERT_TRUE(view.IsValid());
+  BitField bf = view.GetBitField();
+  ASSERT_EQ(bf.seven_bits_, bit_field.seven_bits_);
+  ASSERT_EQ(bf.straddle_, bit_field.straddle_);
+  ASSERT_EQ(bf.five_bits_, bit_field.five_bits_);
+}
+
+vector<uint8_t> bit_field_group_after_unsized_array_packet{
+    0x01, 0x02, 0x03, 0x04,  // byte array
+    // seven_bits_ = 0x77, straddle_ = 0x5, five_bits_ = 0x15
+    0xf7,  // 0x77 | (0x5 & 0x1) << 7
+    0xaa,  //  0x15 << 3 | (0x5 >> 1)
+};
+
+TEST(GeneratedPacketTest, testBitFieldGroupAfterUnsizedArrayPacket) {
+  std::vector<uint8_t> count_array;
+  for (uint8_t i = 1; i < 5; i++) {
+    count_array.push_back(i);
+  }
+  uint8_t seven_bits = 0x77;
+  uint8_t straddle = 0x5;
+  uint8_t five_bits = 0x15;
+
+  auto packet = BitFieldGroupAfterUnsizedArrayPacketBuilder::Create(count_array, seven_bits, straddle, five_bits);
+  ASSERT_EQ(bit_field_group_after_unsized_array_packet.size(), packet->size());
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  packet->Serialize(it);
+
+  ASSERT_EQ(bit_field_group_after_unsized_array_packet.size(), packet_bytes->size());
+  for (size_t i = 0; i < bit_field_group_after_unsized_array_packet.size(); i++) {
+    ASSERT_EQ(bit_field_group_after_unsized_array_packet[i], packet_bytes->at(i));
+  }
+
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  auto payload_view = BitFieldGroupAfterPayloadPacketView::Create(packet_bytes_view);
+  ASSERT_TRUE(payload_view.IsValid());
+  EXPECT_EQ(seven_bits, payload_view.GetSevenBits());
+  EXPECT_EQ(straddle, payload_view.GetStraddle());
+  EXPECT_EQ(five_bits, payload_view.GetFiveBits());
+
+  auto view = BitFieldGroupAfterUnsizedArrayPacketView::Create(payload_view);
+  ASSERT_TRUE(view.IsValid());
+  auto array = view.GetArray();
+  ASSERT_EQ(count_array.size(), array.size());
+  for (size_t i = 0; i < count_array.size(); i++) {
+    ASSERT_EQ(array[i], count_array[i]);
+  }
+  ASSERT_EQ(seven_bits, view.GetSevenBits());
+  ASSERT_EQ(straddle, view.GetStraddle());
+  ASSERT_EQ(five_bits, view.GetFiveBits());
+}
+
+vector<uint8_t> bit_field_after_unsized_array_packet{
+    0x01, 0x02, 0x03, 0x04,  // byte array
+    // seven_bits_ = 0x77, straddle_ = 0x5, five_bits_ = 0x15
+    0xf7,  // 0x77 | (0x5 & 0x1) << 7
+    0xaa,  //  0x15 << 3 | (0x5 >> 1)
+};
+
+TEST(GeneratedPacketTest, testBitFieldAfterUnsizedArrayPacket) {
+  std::vector<uint8_t> count_array;
+  for (uint8_t i = 1; i < 5; i++) {
+    count_array.push_back(i);
+  }
+  BitField bit_field;
+  bit_field.seven_bits_ = 0x77;
+  bit_field.straddle_ = 0x5;
+  bit_field.five_bits_ = 0x15;
+
+  auto packet = BitFieldAfterUnsizedArrayPacketBuilder::Create(count_array, bit_field);
+  ASSERT_EQ(bit_field_after_unsized_array_packet.size(), packet->size());
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  packet->Serialize(it);
+
+  ASSERT_EQ(bit_field_after_unsized_array_packet.size(), packet_bytes->size());
+  for (size_t i = 0; i < bit_field_after_unsized_array_packet.size(); i++) {
+    ASSERT_EQ(bit_field_after_unsized_array_packet[i], packet_bytes->at(i));
+  }
+
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  auto payload_view = BitFieldAfterPayloadPacketView::Create(packet_bytes_view);
+  ASSERT_TRUE(payload_view.IsValid());
+  BitField parent_bf = payload_view.GetBitField();
+  ASSERT_EQ(parent_bf.seven_bits_, bit_field.seven_bits_);
+  ASSERT_EQ(parent_bf.straddle_, bit_field.straddle_);
+  ASSERT_EQ(parent_bf.five_bits_, bit_field.five_bits_);
+
+  auto view = BitFieldAfterUnsizedArrayPacketView::Create(payload_view);
+  ASSERT_TRUE(view.IsValid());
+  auto array = view.GetArray();
+  ASSERT_EQ(count_array.size(), array.size());
+  for (size_t i = 0; i < count_array.size(); i++) {
+    ASSERT_EQ(array[i], count_array[i]);
+  }
+  BitField bf = view.GetBitField();
+  ASSERT_EQ(bf.seven_bits_, bit_field.seven_bits_);
+  ASSERT_EQ(bf.straddle_, bit_field.straddle_);
+  ASSERT_EQ(bf.five_bits_, bit_field.five_bits_);
+}
+
+vector<uint8_t> bit_field_array_packet{
+    0x06,  // _size_(array)
+    // seven_bits_ = 0x77, straddle_ = 0x5, five_bits_ = 0x15
+    0xf7,  // 0x77 | (0x5 & 0x1) << 7
+    0xaa,  //  0x15 << 3 | (0x5 >> 1)
+
+    // seven_bits_ = 0x78, straddle_ = 0x6, five_bits_ = 0x16
+    0x78,  // 0x78 | (0x6 & 0x1) << 7
+    0xb3,  //  0x16 << 3 | (0x6 >> 1)
+
+    // seven_bits_ = 0x79, straddle_ = 0x7, five_bits_ = 0x17
+    0xf9,  // 0x79 | (0x7 & 0x1) << 7
+    0xbb,  //  0x17 << 3 | (0x7 >> 1)
+};
+
+TEST(GeneratedPacketTest, testBitFieldArrayPacket) {
+  std::vector<BitField> count_array;
+  for (size_t i = 0; i < 3; i++) {
+    BitField bf;
+    bf.seven_bits_ = 0x77 + i;
+    bf.straddle_ = 0x5 + i;
+    bf.five_bits_ = 0x15 + i;
+    count_array.push_back(bf);
+  }
+
+  auto packet = BitFieldArrayPacketBuilder::Create(count_array);
+  ASSERT_EQ(bit_field_array_packet.size(), packet->size());
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  packet->Serialize(it);
+
+  ASSERT_EQ(bit_field_array_packet.size(), packet_bytes->size());
+  for (size_t i = 0; i < bit_field_array_packet.size(); i++) {
+    ASSERT_EQ(bit_field_array_packet[i], packet_bytes->at(i));
+  }
+
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  auto view = BitFieldArrayPacketView::Create(packet_bytes_view);
+  ASSERT_TRUE(view.IsValid());
+  auto array = view.GetArray();
+  ASSERT_EQ(count_array.size(), array.size());
+  for (size_t i = 0; i < count_array.size(); i++) {
+    ASSERT_EQ(array[i].seven_bits_, count_array[i].seven_bits_);
+    ASSERT_EQ(array[i].straddle_, count_array[i].straddle_);
+    ASSERT_EQ(array[i].five_bits_, count_array[i].five_bits_);
+  }
+}
+
+TEST(GeneratedPacketTest, testNewBitFieldArrayPacket) {
+  PacketView<kLittleEndian> packet_bytes_view(std::make_shared<std::vector<uint8_t>>(bit_field_array_packet));
+  auto view = BitFieldArrayPacketView::Create(packet_bytes_view);
+  ASSERT_TRUE(view.IsValid());
+
+  auto packet = BitFieldArrayPacketBuilder::Create(view.GetArray());
+  ASSERT_EQ(bit_field_array_packet.size(), packet->size());
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  packet->Serialize(it);
+
+  ASSERT_EQ(*packet_bytes, bit_field_array_packet);
+}
+
+std::vector<uint8_t> child_two_two_two_ = {0x20, 0x02};
+std::vector<uint8_t> child_two_two_three_ = {0x20, 0x03};
+std::vector<uint8_t> child_two_two_four_ = {0x20, 0x04};
+
+DEFINE_AND_INSTANTIATE_ParentTwoReflectionTest(child_two_two_two_, child_two_two_three_, child_two_two_four_);
+
+DEFINE_AND_INSTANTIATE_ChildTwoTwoReflectionTest(child_two_two_two_, child_two_two_three_, child_two_two_four_);
+
+DEFINE_AND_INSTANTIATE_ChildTwoTwoThreeReflectionTest(child_two_two_three_);
+
+std::vector<uint8_t> one_versionless_struct_packet = {0x01};
+std::vector<uint8_t> one_versioned_struct_packet = {0x02, 0x03 /* version */, 0x04, 0x05, 0x06};
+std::vector<uint8_t> one_version_one_struct_packet = {0x03, 0x01 /* version */, 0x02};
+std::vector<uint8_t> one_version_two_struct_packet = {0x03, 0x02 /* version */, 0x03, 0x04};
+DEFINE_AND_INSTANTIATE_OneVersionlessStructPacketReflectionTest(one_versionless_struct_packet,
+                                                                one_versioned_struct_packet,
+                                                                one_version_one_struct_packet,
+                                                                one_version_two_struct_packet);
+DEFINE_AND_INSTANTIATE_OneVersionedStructPacketReflectionTest(one_versioned_struct_packet,
+                                                              one_version_one_struct_packet,
+                                                              one_version_two_struct_packet);
+DEFINE_AND_INSTANTIATE_OneVersionOneStructPacketReflectionTest(one_version_one_struct_packet);
+DEFINE_AND_INSTANTIATE_OneVersionTwoStructPacketReflectionTest(one_version_two_struct_packet);
+
+vector<uint8_t> one_struct_be{
+    0x01, 0x02, 0x03,  // id = 0x01, count = 0x0203
+};
+
+TEST(GeneratedPacketTest, testOneStructBe) {
+  TwoRelatedNumbersBe trn;
+  trn.id_ = 1;
+  trn.count_ = 0x0203;
+
+  auto packet = OneStructBeBuilder::Create(trn);
+  ASSERT_EQ(one_struct_be.size(), packet->size());
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  packet->Serialize(it);
+
+  ASSERT_EQ(one_struct_be.size(), packet_bytes->size());
+  for (size_t i = 0; i < one_struct_be.size(); i++) {
+    ASSERT_EQ(one_struct_be[i], packet_bytes->at(i));
+  }
+
+  PacketView<!kLittleEndian> packet_bytes_view(packet_bytes);
+  auto view = OneStructBeView::Create(packet_bytes_view);
+  ASSERT_TRUE(view.IsValid());
+  auto one = view.GetOne();
+  ASSERT_EQ(one.id_, trn.id_);
+  ASSERT_EQ(one.count_, trn.count_);
+}
+
+vector<uint8_t> two_structs_be{
+    0x01, 0x01, 0x02,  // id, id * 0x0102
+    0x02, 0x02, 0x04,
+};
+
+TEST(GeneratedPacketTest, testTwoStructsBe) {
+  std::vector<TwoRelatedNumbersBe> count_array;
+  for (uint8_t i = 1; i < 3; i++) {
+    TwoRelatedNumbersBe trn;
+    trn.id_ = i;
+    trn.count_ = 0x0102 * i;
+    count_array.push_back(trn);
+  }
+
+  auto packet = TwoStructsBeBuilder::Create(count_array[0], count_array[1]);
+  ASSERT_EQ(two_structs_be.size(), packet->size());
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  packet->Serialize(it);
+
+  ASSERT_EQ(two_structs_be.size(), packet_bytes->size());
+  for (size_t i = 0; i < two_structs_be.size(); i++) {
+    ASSERT_EQ(two_structs_be[i], packet_bytes->at(i));
+  }
+
+  PacketView<!kLittleEndian> packet_bytes_view(packet_bytes);
+  auto view = TwoStructsBeView::Create(packet_bytes_view);
+  ASSERT_TRUE(view.IsValid());
+  auto one = view.GetOne();
+  ASSERT_EQ(one.id_, count_array[0].id_);
+  ASSERT_EQ(one.count_, count_array[0].count_);
+  auto two = view.GetTwo();
+  ASSERT_EQ(two.id_, count_array[1].id_);
+  ASSERT_EQ(two.count_, count_array[1].count_);
+}
+
+vector<uint8_t> array_of_struct_be{
+    0x04,              // _count_
+    0x01, 0x01, 0x02,  // id, id * 0x0102
+    0x02, 0x02, 0x04, 0x03, 0x03, 0x06, 0x04, 0x04, 0x08,
+};
+
+TEST(GeneratedPacketTest, testArrayOfStructBe) {
+  std::vector<TwoRelatedNumbersBe> count_array;
+  for (uint8_t i = 1; i < 5; i++) {
+    TwoRelatedNumbersBe trn;
+    trn.id_ = i;
+    trn.count_ = 0x0102 * i;
+    count_array.push_back(trn);
+  }
+
+  auto packet = ArrayOfStructBeBuilder::Create(count_array);
+
+  ASSERT_EQ(array_of_struct_be.size(), packet->size());
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  packet->Serialize(it);
+
+  ASSERT_EQ(array_of_struct_be.size(), packet_bytes->size());
+  for (size_t i = 0; i < array_of_struct_be.size(); i++) {
+    ASSERT_EQ(array_of_struct_be[i], packet_bytes->at(i));
+  }
+
+  PacketView<!kLittleEndian> packet_bytes_view(packet_bytes);
+  auto view = ArrayOfStructBeView::Create(packet_bytes_view);
+  ASSERT_TRUE(view.IsValid());
+  auto array = view.GetArray();
+  ASSERT_EQ(count_array.size(), array.size());
+  for (size_t i = 0; i < count_array.size(); i++) {
+    ASSERT_EQ(array[i].id_, count_array[i].id_);
+    ASSERT_EQ(array[i].count_, count_array[i].count_);
+  }
+}
+
+vector<uint8_t> one_four_byte_struct{
+    0x04,                    // struct_type_ = FourByte
+    0xd1, 0xd2, 0xd3, 0xd4,  // four_bytes_
+};
+
+TEST(GeneratedPacketTest, testOneFourByteStruct) {
+  FourByteStruct four_byte_struct;
+  four_byte_struct.four_bytes_ = 0xd4d3d2d1;
+
+  auto packet = OneFourByteStructBuilder::Create(four_byte_struct);
+  ASSERT_EQ(one_four_byte_struct.size(), packet->size());
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  packet->Serialize(it);
+
+  ASSERT_EQ(one_four_byte_struct.size(), packet_bytes->size());
+  for (size_t i = 0; i < one_four_byte_struct.size(); i++) {
+    ASSERT_EQ(one_four_byte_struct[i], packet_bytes->at(i));
+  }
+
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  auto view = OneFourByteStructView::Create(packet_bytes_view);
+  ASSERT_TRUE(view.IsValid());
+  ASSERT_EQ(StructType::FOUR_BYTE, view.GetOneStruct().struct_type_);
+  ASSERT_EQ(four_byte_struct.four_bytes_, view.GetOneStruct().four_bytes_);
+}
+
+vector<uint8_t> generic_struct_two{
+    0x02,        // struct_type_ = TwoByte
+    0x01, 0x02,  // two_bytes_
+};
+
+TEST(GeneratedPacketTest, testOneGenericStructTwo) {
+  TwoByteStruct two_byte_struct;
+  two_byte_struct.two_bytes_ = 0x0201;
+  std::unique_ptr<TwoByteStruct> two_byte_struct_ptr = std::make_unique<TwoByteStruct>(two_byte_struct);
+
+  auto packet = OneGenericStructBuilder::Create(std::move(two_byte_struct_ptr));
+  ASSERT_EQ(generic_struct_two.size(), packet->size());
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  packet->Serialize(it);
+
+  ASSERT_EQ(generic_struct_two.size(), packet_bytes->size());
+  for (size_t i = 0; i < generic_struct_two.size(); i++) {
+    ASSERT_EQ(generic_struct_two[i], packet_bytes->at(i));
+  }
+
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  auto view = OneGenericStructView::Create(packet_bytes_view);
+  ASSERT_TRUE(view.IsValid());
+  auto base_struct = view.GetBaseStruct();
+  ASSERT_NE(nullptr, base_struct);
+  ASSERT_TRUE(TwoByteStruct::IsInstance(*base_struct));
+  TwoByteStruct* two_byte = static_cast<TwoByteStruct*>(base_struct.get());
+  ASSERT_NE(nullptr, two_byte);
+  ASSERT_TRUE(TwoByteStruct::IsInstance(*two_byte));
+  ASSERT_EQ(two_byte_struct.two_bytes_, 0x0201);
+  uint16_t val = two_byte->two_bytes_;
+  ASSERT_EQ(val, 0x0201);
+  ASSERT_EQ(two_byte_struct.two_bytes_, ((TwoByteStruct*)base_struct.get())->two_bytes_);
+}
+
+vector<uint8_t> generic_struct_four{
+    0x04,                    // struct_type_ = FourByte
+    0x01, 0x02, 0x03, 0x04,  // four_bytes_
+};
+
+TEST(GeneratedPacketTest, testOneGenericStructFour) {
+  FourByteStruct four_byte_struct;
+  four_byte_struct.four_bytes_ = 0x04030201;
+  std::unique_ptr<FourByteStruct> four_byte_struct_p = std::make_unique<FourByteStruct>(four_byte_struct);
+  ASSERT_EQ(four_byte_struct.four_bytes_, four_byte_struct_p->four_bytes_);
+
+  auto packet = OneGenericStructBuilder::Create(std::move(four_byte_struct_p));
+  ASSERT_EQ(generic_struct_four.size(), packet->size());
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  packet->Serialize(it);
+
+  ASSERT_EQ(generic_struct_four.size(), packet_bytes->size());
+  for (size_t i = 0; i < generic_struct_four.size(); i++) {
+    ASSERT_EQ(generic_struct_four[i], packet_bytes->at(i));
+  }
+
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  auto view = OneGenericStructView::Create(packet_bytes_view);
+  ASSERT_TRUE(view.IsValid());
+  auto base_struct = view.GetBaseStruct();
+  ASSERT_NE(nullptr, base_struct);
+  ASSERT_EQ(StructType::FOUR_BYTE, base_struct->struct_type_);
+  ASSERT_EQ(four_byte_struct.four_bytes_, ((FourByteStruct*)base_struct.get())->four_bytes_);
+}
+
+vector<uint8_t> one_struct_array{
+    0x04,                    // struct_type_ = FourByte
+    0xa1, 0xa2, 0xa3, 0xa4,  // four_bytes_
+    0x04,                    // struct_type_ = FourByte
+    0xb2, 0xb2, 0xb3, 0xb4,  // four_bytes_
+    0x02,                    // struct_type_ = TwoByte
+    0xc3, 0xc2,              // two_bytes_
+    0x04,                    // struct_type_ = TwoByte
+    0xd4, 0xd2, 0xd3, 0xd4,  // four_bytes_
+};
+
+TEST(GeneratedPacketTest, testOneGenericStructArray) {
+  std::vector<std::unique_ptr<UnusedParentStruct>> parent_vector;
+  std::unique_ptr<FourByteStruct> fbs;
+  std::unique_ptr<TwoByteStruct> tbs;
+  fbs = std::make_unique<FourByteStruct>();
+  fbs->four_bytes_ = 0xa4a3a2a1;
+  parent_vector.push_back(std::move(fbs));
+  fbs = std::make_unique<FourByteStruct>();
+  fbs->four_bytes_ = 0xb4b3b2b2;
+  parent_vector.push_back(std::move(fbs));
+  tbs = std::make_unique<TwoByteStruct>();
+  tbs->two_bytes_ = 0xc2c3;
+  parent_vector.push_back(std::move(tbs));
+  fbs = std::make_unique<FourByteStruct>();
+  fbs->four_bytes_ = 0xd4d3d2d4;
+  parent_vector.push_back(std::move(fbs));
+
+  std::vector<std::unique_ptr<UnusedParentStruct>> vector_copy;
+  for (auto& s : parent_vector) {
+    if (s->struct_type_ == StructType::TWO_BYTE) {
+      vector_copy.push_back(std::make_unique<TwoByteStruct>(*(TwoByteStruct*)s.get()));
+    }
+    if (s->struct_type_ == StructType::FOUR_BYTE) {
+      vector_copy.push_back(std::make_unique<FourByteStruct>(*(FourByteStruct*)s.get()));
+    }
+  }
+
+  auto packet = OneGenericStructArrayBuilder::Create(std::move(parent_vector));
+  ASSERT_EQ(one_struct_array.size(), packet->size());
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  packet->Serialize(it);
+
+  ASSERT_EQ(one_struct_array.size(), packet_bytes->size());
+  for (size_t i = 0; i < one_struct_array.size(); i++) {
+    ASSERT_EQ(one_struct_array[i], packet_bytes->at(i));
+  }
+
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  auto view = OneGenericStructArrayView::Create(packet_bytes_view);
+  ASSERT_TRUE(view.IsValid());
+  auto an_array = view.GetAnArray();
+  ASSERT_EQ(vector_copy.size(), an_array.size());
+  for (size_t i = 0; i < vector_copy.size(); i++) {
+    ASSERT_NE(nullptr, an_array[i]);
+    ASSERT_EQ(vector_copy[i]->struct_type_, an_array[i]->struct_type_);
+    if (vector_copy[i]->struct_type_ == StructType::FOUR_BYTE) {
+      ASSERT_EQ(FourByteStruct::Specialize(vector_copy[i].get())->four_bytes_,
+                FourByteStruct::Specialize(an_array[i].get())->four_bytes_);
+    } else {
+      ASSERT_EQ(TwoByteStruct::Specialize(vector_copy[i].get())->two_bytes_,
+                TwoByteStruct::Specialize(an_array[i].get())->two_bytes_);
+    }
+  }
+}
+
+TEST(GeneratedPacketTest, testOneGenericStructFourArray) {
+  std::array<std::unique_ptr<UnusedParentStruct>, 4> parent_vector;
+  std::unique_ptr<FourByteStruct> fbs;
+  std::unique_ptr<TwoByteStruct> tbs;
+  fbs = std::make_unique<FourByteStruct>();
+  fbs->four_bytes_ = 0xa4a3a2a1;
+  parent_vector[0] = std::move(fbs);
+  fbs = std::make_unique<FourByteStruct>();
+  fbs->four_bytes_ = 0xb4b3b2b2;
+  parent_vector[1] = std::move(fbs);
+  tbs = std::make_unique<TwoByteStruct>();
+  tbs->two_bytes_ = 0xc2c3;
+  parent_vector[2] = std::move(tbs);
+  fbs = std::make_unique<FourByteStruct>();
+  fbs->four_bytes_ = 0xd4d3d2d4;
+  parent_vector[3] = std::move(fbs);
+
+  std::array<std::unique_ptr<UnusedParentStruct>, 4> vector_copy;
+  size_t index = 0;
+  for (auto& s : parent_vector) {
+    if (s->struct_type_ == StructType::TWO_BYTE) {
+      vector_copy[index] = std::make_unique<TwoByteStruct>(*(TwoByteStruct*)s.get());
+    }
+    if (s->struct_type_ == StructType::FOUR_BYTE) {
+      vector_copy[index] = std::make_unique<FourByteStruct>(*(FourByteStruct*)s.get());
+    }
+    index++;
+  }
+
+  auto packet = OneGenericStructFourArrayBuilder::Create(std::move(parent_vector));
+  ASSERT_EQ(one_struct_array.size(), packet->size());
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  packet->Serialize(it);
+
+  ASSERT_EQ(one_struct_array.size(), packet_bytes->size());
+  for (size_t i = 0; i < one_struct_array.size(); i++) {
+    ASSERT_EQ(one_struct_array[i], packet_bytes->at(i));
+  }
+
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  auto view = OneGenericStructFourArrayView::Create(packet_bytes_view);
+  ASSERT_TRUE(view.IsValid());
+  auto an_array = view.GetAnArray();
+  ASSERT_EQ(vector_copy.size(), an_array.size());
+  for (size_t i = 0; i < vector_copy.size(); i++) {
+    ASSERT_NE(nullptr, an_array[i]);
+    ASSERT_EQ(vector_copy[i]->struct_type_, an_array[i]->struct_type_);
+    if (vector_copy[i]->struct_type_ == StructType::FOUR_BYTE) {
+      ASSERT_EQ(FourByteStruct::Specialize(vector_copy[i].get())->four_bytes_,
+                FourByteStruct::Specialize(an_array[i].get())->four_bytes_);
+    } else {
+      ASSERT_EQ(TwoByteStruct::Specialize(vector_copy[i].get())->two_bytes_,
+                TwoByteStruct::Specialize(an_array[i].get())->two_bytes_);
+    }
+  }
+}
+
+vector<uint8_t> one_struct_array_after_fixed{
+    0x01, 0x02,              // two_bytes = 0x0201
+    0x04,                    // struct_type_ = FourByte
+    0xa1, 0xa2, 0xa3, 0xa4,  // four_bytes_
+    0x04,                    // struct_type_ = FourByte
+    0xb2, 0xb2, 0xb3, 0xb4,  // four_bytes_
+    0x02,                    // struct_type_ = TwoByte
+    0xc3, 0xc2,              // two_bytes_
+    0x04,                    // struct_type_ = TwoByte
+    0xd4, 0xd2, 0xd3, 0xd4,  // four_bytes_
+};
+
+DEFINE_AND_INSTANTIATE_OneGenericStructArrayAfterFixedReflectionTest(one_struct_array_after_fixed);
+
+vector<uint8_t> one_length_type_value_struct{
+    // _size_(value):16 type value
+    0x04, 0x00, 0x01, 'o', 'n', 'e',            // ONE
+    0x04, 0x00, 0x02, 't', 'w', 'o',            // TWO
+    0x06, 0x00, 0x03, 't', 'h', 'r', 'e', 'e',  // THREE
+};
+
+DEFINE_AND_INSTANTIATE_OneLengthTypeValueStructReflectionTest(one_length_type_value_struct);
+
+TEST(GeneratedPacketTest, testOneLengthTypeValueStruct) {
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes =
+      std::make_shared<std::vector<uint8_t>>(one_length_type_value_struct);
+
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  auto view = OneLengthTypeValueStructView::Create(packet_bytes_view);
+  ASSERT_TRUE(view.IsValid());
+  auto one = view.GetOneArray();
+  size_t entry_id = 0;
+  for (const auto& entry : one) {
+    switch (entry_id++) {
+      case 0:
+        ASSERT_EQ(entry.type_, DataType::ONE);
+        ASSERT_EQ(entry.value_, std::vector<uint8_t>({'o', 'n', 'e'}));
+        break;
+      case 1:
+        ASSERT_EQ(entry.type_, DataType::TWO);
+        ASSERT_EQ(entry.value_, std::vector<uint8_t>({'t', 'w', 'o'}));
+        break;
+      case 2:
+        ASSERT_EQ(entry.type_, DataType::THREE);
+        ASSERT_EQ(entry.value_, std::vector<uint8_t>({'t', 'h', 'r', 'e', 'e'}));
+        break;
+      default:
+        ASSERT_EQ(entry.type_, DataType::UNUSED);
+    }
+  }
+}
+
+vector<uint8_t> one_length_type_value_struct_padded_20{
+    0x27,  // _size_(payload),
+    // _size_(value):16 type value
+    0x04, 0x00, 0x01, 'o', 'n', 'e',                             // ONE
+    0x04, 0x00, 0x02, 't', 'w', 'o',                             // TWO
+    0x06, 0x00, 0x03, 't', 'h', 'r', 'e', 'e',                   // THREE
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,        // padding to 30
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  // padding to 40
+};
+
+vector<uint8_t> one_length_type_value_struct_padded_28{
+    0x27,  // _size_(payload),
+    // _size_(value):16 type value
+    0x04, 0x00, 0x01, 'o', 'n', 'e',                             // ONE
+    0x04, 0x00, 0x02, 't', 'w', 'o',                             // TWO
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,                    // padding to 20
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  // padding to 30
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  // padding to 40
+};
+
+// TODO: Revisit LTV parsing.  Right now, the padding bytes are parsed
+// DEFINE_AND_INSTANTIATE_OneLengthTypeValueStructPaddedReflectionTest(one_length_type_value_struct_padded_20,
+// one_length_type_value_struct_padded_28);
+
+TEST(GeneratedPacketTest, testOneLengthTypeValueStructPaddedGeneration) {
+  std::vector<LengthTypeValueStruct> ltv_vector;
+  LengthTypeValueStruct ltv;
+  ltv.type_ = DataType::ONE;
+  ltv.value_ = {
+      'o',
+      'n',
+      'e',
+  };
+  ltv_vector.push_back(ltv);
+  ltv.type_ = DataType::TWO;
+  ltv.value_ = {
+      't',
+      'w',
+      'o',
+  };
+  ltv_vector.push_back(ltv);
+
+  auto packet = OneLengthTypeValueStructPaddedBuilder::Create(ltv_vector);
+  ASSERT_EQ(one_length_type_value_struct_padded_28.size(), packet->size());
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  packet->Serialize(it);
+
+  ASSERT_EQ(one_length_type_value_struct_padded_28.size(), packet_bytes->size());
+  for (size_t i = 0; i < one_length_type_value_struct_padded_28.size(); i++) {
+    ASSERT_EQ(one_length_type_value_struct_padded_28[i], packet_bytes->at(i));
+  }
+
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  auto view = OneLengthTypeValueStructPaddedView::Create(SizedParentView::Create(packet_bytes_view));
+  ASSERT_TRUE(view.IsValid());
+  auto an_array = view.GetOneArray();
+  // TODO: Revisit LTV parsing.  Right now, the padding bytes are parsed
+  // ASSERT_EQ(ltv_vector.size(), an_array.size());
+  for (size_t i = 0; i < ltv_vector.size(); i++) {
+    ASSERT_EQ(ltv_vector[i].type_, an_array[i].type_);
+    ASSERT_EQ(ltv_vector[i].value_, an_array[i].value_);
+  }
+}
+
+vector<uint8_t> byte_sized{
+    0x11,                                            // 1
+    0x21, 0x22,                                      // 2
+    0x31, 0x32, 0x33,                                // 3
+    0x41, 0x42, 0x43, 0x44,                          // 4
+    0x51, 0x52, 0x53, 0x54, 0x55,                    // 5
+    0x61, 0x62, 0x63, 0x64, 0x65, 0x66,              // 6
+    0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,        // 7
+    0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,  // 8
+};
+
+TEST(GeneratedPacketTest, testByteSizedFields) {
+  uint64_t array[9]{
+      0xbadbadbad,
+      0x11,                // 1
+      0x2221,              // 2
+      0x333231,            // 3
+      0x44434241,          // 4
+      0x5554535251,        // 5
+      0x666564636261,      // 6
+      0x77767574737271,    // 7
+      0x8887868584838281,  // 8
+  };
+  auto packet =
+      ByteSizedFieldsBuilder::Create(array[1], array[2], array[3], array[4], array[5], array[6], array[7], array[8]);
+  ASSERT_EQ(byte_sized.size(), packet->size());
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  packet->Serialize(it);
+
+  ASSERT_EQ(byte_sized.size(), packet_bytes->size());
+  for (size_t i = 0; i < byte_sized.size(); i++) {
+    ASSERT_EQ(byte_sized[i], packet_bytes->at(i));
+  }
+
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  auto view = ByteSizedFieldsView::Create(packet_bytes_view);
+  ASSERT_TRUE(view.IsValid());
+  ASSERT_EQ(array[1], view.GetOne());
+  ASSERT_EQ(array[2], view.GetTwo());
+  ASSERT_EQ(array[3], view.GetThree());
+  ASSERT_EQ(array[4], view.GetFour());
+  ASSERT_EQ(array[5], view.GetFive());
+  ASSERT_EQ(array[6], view.GetSix());
+  ASSERT_EQ(array[7], view.GetSeven());
+  ASSERT_EQ(array[8], view.GetEight());
+}
+
+DEFINE_AND_INSTANTIATE_ByteSizedFieldsReflectionTest(byte_sized);
+
+TEST(GeneratedPacketTest, testOneGenericStructArrayNoZeroEmpty) {
+  auto too_few_bytes = std::make_shared<std::vector<uint8_t>>(0);
+  auto view = OneGenericStructArrayNoZeroView::Create(too_few_bytes);
+  for (size_t i = 0; i < 10; i++) {
+    if (view.IsValid()) {
+      view.GetAnArray().size();
+    }
+    too_few_bytes->push_back(0);
+    view = OneGenericStructArrayNoZeroView::Create(too_few_bytes);
+  }
+
+  std::vector<uint8_t> a_two_byte_struct = {
+      static_cast<uint8_t>(StructTypeNoZero::TWO_BYTE),
+      0x01,
+      0x02,
+  };
+  too_few_bytes = std::make_shared<std::vector<uint8_t>>(a_two_byte_struct);
+  view = OneGenericStructArrayNoZeroView::Create(too_few_bytes);
+  ASSERT(view.IsValid());
+  ASSERT_EQ(1, view.GetAnArray().size());
+}
+
+}  // namespace parser
+}  // namespace packet
+}  // namespace bluetooth
diff --git a/gd/packet/parser/test/simple_sum.h b/gd/packet/parser/test/simple_sum.h
new file mode 100644
index 0000000..95c4888
--- /dev/null
+++ b/gd/packet/parser/test/simple_sum.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+
+namespace bluetooth {
+namespace packet {
+namespace parser {
+namespace test {
+
+class SimpleSum {
+ public:
+  void Initialize() {
+    sum = 0;
+  }
+
+  void AddByte(uint8_t byte) {
+    sum += byte;
+  }
+
+  uint16_t GetChecksum() const {
+    return sum;
+  }
+
+ private:
+  uint16_t sum;
+};
+
+}  // namespace test
+}  // namespace parser
+}  // namespace packet
+}  // namespace bluetooth
diff --git a/gd/packet/parser/test/six_bytes.cc b/gd/packet/parser/test/six_bytes.cc
new file mode 100644
index 0000000..614dbaa
--- /dev/null
+++ b/gd/packet/parser/test/six_bytes.cc
@@ -0,0 +1,35 @@
+/******************************************************************************
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#include "six_bytes.h"
+
+namespace bluetooth {
+namespace packet {
+namespace parser {
+namespace test {
+
+static_assert(sizeof(SixBytes) == 6, "SixBytes must be 6 bytes long!");
+
+SixBytes::SixBytes(const uint8_t (&six)[6]) {
+  std::copy(six, six + kLength, six_bytes);
+};
+
+}  // namespace test
+}  // namespace parser
+}  // namespace packet
+}  // namespace bluetooth
diff --git a/gd/packet/parser/test/six_bytes.h b/gd/packet/parser/test/six_bytes.h
new file mode 100644
index 0000000..0f26324
--- /dev/null
+++ b/gd/packet/parser/test/six_bytes.h
@@ -0,0 +1,61 @@
+/******************************************************************************
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#pragma once
+
+#include <stdint.h>
+#include <string>
+
+namespace bluetooth {
+namespace packet {
+namespace parser {
+namespace test {
+
+class SixBytes final {
+ public:
+  static constexpr unsigned int kLength = 6;
+
+  uint8_t six_bytes[kLength];
+
+  SixBytes() = default;
+  SixBytes(const uint8_t (&addr)[6]);
+
+  bool operator<(const SixBytes& rhs) const {
+    return (std::memcmp(six_bytes, rhs.six_bytes, sizeof(six_bytes)) < 0);
+  }
+  bool operator==(const SixBytes& rhs) const {
+    return (std::memcmp(six_bytes, rhs.six_bytes, sizeof(six_bytes)) == 0);
+  }
+  bool operator>(const SixBytes& rhs) const {
+    return (rhs < *this);
+  }
+  bool operator<=(const SixBytes& rhs) const {
+    return !(*this > rhs);
+  }
+  bool operator>=(const SixBytes& rhs) const {
+    return !(*this < rhs);
+  }
+  bool operator!=(const SixBytes& rhs) const {
+    return !(*this == rhs);
+  }
+};
+
+}  // namespace test
+}  // namespace parser
+}  // namespace packet
+}  // namespace bluetooth
diff --git a/gd/packet/parser/test/test_packets.pdl b/gd/packet/parser/test/test_packets.pdl
new file mode 100644
index 0000000..43355ee
--- /dev/null
+++ b/gd/packet/parser/test/test_packets.pdl
@@ -0,0 +1,442 @@
+little_endian_packets
+
+custom_field SixBytes : 48 "packet/parser/test/"
+custom_field Variable "packet/parser/test/"
+
+packet Parent {
+  _fixed_ = 0x12 : 8,
+  _size_(_payload_) : 8,
+  _payload_,
+  footer : 8,
+}
+
+packet Child : Parent {
+  field_name : 16,
+}
+
+enum FourBits : 4 {
+  ONE = 1,
+  TWO = 2,
+  THREE = 3,
+  FIVE = 5,
+  TEN = 10,
+  LAZY_ME = 15,
+}
+
+packet ParentTwo {
+  _reserved_ : 4,
+  four_bits : FourBits,
+  _payload_,
+}
+
+packet ChildTwoThree : ParentTwo (four_bits = THREE) {
+  more_bits : FourBits,
+  _reserved_ : 4,
+  sixteen_bits : 16
+}
+
+packet ChildTwoTwo : ParentTwo (four_bits = TWO) {
+  more_bits : FourBits,
+  _reserved_ : 4,
+}
+
+packet ChildTwoTwoThree :ChildTwoTwo (more_bits = THREE) {
+}
+
+enum TwoBits : 2 {
+  ZERO = 0,
+  ONE = 1,
+  TWO = 2,
+  THREE = 3,
+}
+
+packet MiddleFourBits {
+  low_two : TwoBits,
+  next_four : FourBits,
+  straddle : FourBits,
+  four_more : FourBits,
+  high_two : TwoBits,
+}
+
+packet ParentWithSixBytes {
+  two_bytes : 16,
+  six_bytes : SixBytes,
+  _payload_,
+}
+
+packet ChildWithSixBytes : ParentWithSixBytes (two_bytes = 0x1234) {
+  child_six_bytes : SixBytes,
+}
+
+checksum SimpleSum : 16 "packet/parser/test/"
+
+packet ParentWithSum {
+  two_bytes : 16,
+  _checksum_start_(example_checksum),
+  sum_bytes : 16,
+  _payload_,
+  example_checksum : SimpleSum,
+}
+
+packet ChildWithSum : ParentWithSum {
+  more_bytes : 32,
+  another_byte : 8,
+}
+
+packet ChildWithNestedSum : ParentWithSum {
+  _checksum_start_(nested_checksum),
+  more_bytes : 32,
+  nested_checksum : SimpleSum,
+}
+
+packet ParentSizeModifier {
+  _size_(_payload_) : 8,
+  _payload_ : [+2*8], // Include two_bytes in the size
+  two_bytes : 16,
+}
+
+packet ChildSizeModifier : ParentSizeModifier (two_bytes = 0x1211) {
+  more_bytes : 32,
+}
+
+packet FieldsEndWithNumbers {
+  field_1 : 16,
+  field_2 : 16,
+  field_10 : 16,
+  field_11 : 16,
+}
+
+enum ForArrays : 16 {
+  ONE = 0x0001,
+  TWO = 0x0002,
+  ONE_TWO = 0x0201,
+  TWO_THREE = 0x0302,
+  FFFF = 0xffff,
+}
+
+packet FixedArrayEnum {
+  enum_array : ForArrays[5],
+}
+
+packet SizedArrayEnum {
+  _size_(enum_array) : 16,
+  enum_array : ForArrays[],
+}
+
+packet CountArrayEnum {
+  _count_(enum_array) : 8,
+  enum_array : ForArrays[],
+}
+
+packet SizedArrayCustom {
+  _size_(six_bytes_array) : 8,
+  an_extra_byte : 8,
+  six_bytes_array : SixBytes[+1*8],
+}
+
+packet FixedArrayCustom {
+  six_bytes_array : SixBytes[5],
+}
+
+packet CountArrayCustom {
+  _count_(six_bytes_array) : 8,
+  six_bytes_array : SixBytes[],
+}
+
+packet PacketWithFixedArraysOfBytes {
+  fixed_256bit_in_bytes : 8[32],
+  fixed_256bit_in_words : 32[8],
+}
+
+packet OneVariable {
+  one : Variable,
+}
+
+packet SizedArrayVariable {
+  _size_(variable_array) : 8,
+  variable_array : Variable[],
+}
+
+packet FixedArrayVariable {
+  variable_array : Variable[5],
+}
+
+packet CountArrayVariable {
+  _count_(variable_array) : 8,
+  variable_array : Variable[],
+}
+
+struct TwoRelatedNumbers {
+  id : 8,
+  count : 16,
+}
+
+packet OneStruct {
+  one : TwoRelatedNumbers,
+}
+
+packet TwoStructs {
+  one : TwoRelatedNumbers,
+  two : TwoRelatedNumbers,
+}
+
+packet VectorOfStruct {
+  _count_(array) : 8,
+  array : TwoRelatedNumbers[],
+}
+
+packet ArrayOfStruct {
+  the_count : 8,
+  array : TwoRelatedNumbers[4],
+}
+
+struct StructWithFixedTypes {
+  four_bits : FourBits,
+  _reserved_ : 4,
+  _checksum_start_(example_checksum),
+  _fixed_ = 0xf3 : 8,
+  id : 8,
+  array : 8[3],
+  example_checksum : SimpleSum,
+  six_bytes : SixBytes,
+}
+
+packet OneFixedTypesStruct {
+  one : StructWithFixedTypes,
+}
+
+packet ArrayOfStructAndAnother {
+  _count_(array) : 8,
+  array : TwoRelatedNumbers[],
+  another : TwoRelatedNumbers,
+}
+
+packet SizedArrayOfStructAndAnother {
+  _size_(array) : 8,
+  array : TwoRelatedNumbers[],
+  another : TwoRelatedNumbers,
+}
+
+struct ArrayOfStructAndAnotherStruct {
+  _count_(array) : 8,
+  array : TwoRelatedNumbers[],
+  another : TwoRelatedNumbers,
+}
+
+struct SizedArrayOfStructAndAnotherStruct {
+  _size_(array) : 8,
+  array : TwoRelatedNumbers[],
+  another : TwoRelatedNumbers,
+}
+
+packet OneArrayOfStructAndAnotherStruct {
+  one : ArrayOfStructAndAnotherStruct,
+}
+
+packet OneSizedArrayOfStructAndAnotherStruct {
+  one : SizedArrayOfStructAndAnotherStruct,
+}
+
+group BitFieldGroup {
+  seven_bits : 7,
+  straddle : 4,
+  five_bits : 5,
+}
+
+packet BitFieldGroupPacket {
+  BitFieldGroup,
+}
+
+packet BitFieldGroupAfterPayloadPacket {
+  _payload_,
+  BitFieldGroup,
+}
+
+packet BitFieldGroupAfterUnsizedArrayPacket : BitFieldGroupAfterPayloadPacket {
+  array : 8[],
+}
+
+struct BitField {
+  seven_bits : 7,
+  straddle : 4,
+  five_bits : 5,
+}
+
+packet BitFieldPacket {
+  bit_field : BitField,
+}
+
+packet BitFieldAfterPayloadPacket {
+  _payload_,
+  bit_field : BitField,
+}
+
+packet BitFieldAfterUnsizedArrayPacket : BitFieldAfterPayloadPacket {
+  array : 8[],
+}
+
+packet BitFieldArrayPacket {
+  _size_(array): 8,
+  array : BitField[],
+}
+
+struct VersionlessStruct {
+  one_number : 8,
+}
+
+packet OneVersionlessStructPacket {
+  versionless : VersionlessStruct,
+  _payload_,
+}
+
+packet OneVersionedStructPacket : OneVersionlessStructPacket {
+  version : 8,
+  _payload_,
+}
+
+packet OneVersionOneStructPacket : OneVersionedStructPacket(version = 0x01) {
+  just_one_number : 8,
+}
+
+packet OneVersionTwoStructPacket : OneVersionedStructPacket(version = 0x02) {
+  one_number : 8,
+  another_number : 8,
+}
+
+enum StructType : 8 {
+  ZERO_BYTE = 0x00,
+  TWO_BYTE = 0x02,
+  FOUR_BYTE = 0x04,
+  AT_LEAST_FOUR_BYTE = 0x05,
+  VARIABLE = 0x06,
+}
+
+struct UnusedParentStruct {
+  struct_type : StructType,
+  _body_,
+}
+
+struct TwoByteStruct : UnusedParentStruct (struct_type = TWO_BYTE) {
+  two_bytes : 16,
+}
+
+struct FourByteStruct : UnusedParentStruct (struct_type = FOUR_BYTE) {
+  four_bytes : 32,
+}
+
+struct AtLeastFourByteStruct : UnusedParentStruct (struct_type = AT_LEAST_FOUR_BYTE) {
+  four_bytes : 32,
+  struct_type : StructType,
+  _body_,
+}
+
+struct OnlyFourByteStruct : AtLeastFourByteStruct (struct_type = ZERO_BYTE) {
+}
+
+struct SixByteStruct : AtLeastFourByteStruct (struct_type = TWO_BYTE) {
+  two_bytes : 16,
+}
+
+struct EightByteStruct : AtLeastFourByteStruct (struct_type = FOUR_BYTE) {
+  four_bytes : 32,
+}
+
+packet OneFourByteStruct {
+  one_struct : FourByteStruct,
+}
+
+packet OneGenericStruct {
+  base_struct : UnusedParentStruct,
+}
+
+packet OneGenericStructArray {
+  an_array : UnusedParentStruct[],
+}
+
+packet OneGenericStructFourArray {
+  an_array : UnusedParentStruct[4],
+}
+
+packet ParentWithOnlyFixed {
+  two_bytes : 16,
+  _body_,
+}
+
+packet OneGenericStructArrayAfterFixed : ParentWithOnlyFixed {
+  an_array : UnusedParentStruct[],
+}
+
+enum DataType : 8 {
+  ONE = 0x01,
+  TWO = 0x02,
+  THREE = 0x03,
+  FOUR = 0x04,
+  FIVE = 0x05,
+  UNUSED = 0x06,
+}
+
+struct LengthTypeValueStruct {
+  _size_(value) : 16,
+  type : DataType,
+  value : 8[+1*8],
+}
+
+packet OneLengthTypeValueStruct {
+  one_array : LengthTypeValueStruct[],
+}
+
+packet SizedParent {
+  _size_(payload) : 8,
+  _payload_,
+}
+
+packet OneLengthTypeValueStructPadded : SizedParent {
+  one_array : LengthTypeValueStruct[],
+  _padding_[40],
+}
+
+packet ByteSizedFields {
+  one : 8,
+  two : 16,
+  three : 24,
+  four : 32,
+  five : 40,
+  six : 48,
+  seven : 56,
+  eight : 64,
+}
+
+enum StructTypeNoZero : 4 {
+  TWO_BYTE = 0x02,
+  FOUR_BYTE = 0x04,
+  AT_LEAST_FOUR_BYTE = 0x05,
+}
+
+struct UnusedParentStructNoZero {
+  struct_type : StructTypeNoZero,
+  _reserved_ : 4,
+  length : 8,
+  _body_,
+}
+
+struct TwoByteStructNoZero : UnusedParentStructNoZero (struct_type = TWO_BYTE, length = 2) {
+  two_bytes : 16,
+}
+
+struct FourByteStructNoZero : UnusedParentStructNoZero (struct_type = FOUR_BYTE, length = 4) {
+  four_bytes : 32,
+}
+
+struct AtLeastFourByteStructNoZero : UnusedParentStructNoZero (struct_type = AT_LEAST_FOUR_BYTE) {
+  four_bytes : 32,
+  struct_type : StructTypeNoZero,
+  _body_,
+}
+
+struct EightByteStructNoZero : AtLeastFourByteStructNoZero (struct_type = FOUR_BYTE, length = 9) {
+  four_bytes : 32,
+}
+
+packet OneGenericStructArrayNoZero {
+  an_array : UnusedParentStructNoZero[],
+}
diff --git a/gd/packet/parser/test/variable.cc b/gd/packet/parser/test/variable.cc
new file mode 100644
index 0000000..23443bf
--- /dev/null
+++ b/gd/packet/parser/test/variable.cc
@@ -0,0 +1,48 @@
+/******************************************************************************
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#include "variable.h"
+
+#include <stdio.h>
+#include <sstream>
+
+namespace bluetooth {
+namespace packet {
+namespace parser {
+namespace test {
+
+Variable::Variable(const std::string& str) : data(str) {}
+
+void Variable::Serialize(BitInserter& bi) const {
+  if (data.size() > 255) {
+    fprintf(stderr, "data.size() > 255: (%zu)", data.size());
+    abort();
+  }
+  bi.insert_byte((uint8_t)data.size());
+  for (auto byte : data) {
+    bi.insert_byte(byte);
+  }
+}
+
+size_t Variable::size() const {
+  return data.size() + 1;
+}
+}  // namespace test
+}  // namespace parser
+}  // namespace packet
+}  // namespace bluetooth
diff --git a/gd/packet/parser/test/variable.h b/gd/packet/parser/test/variable.h
new file mode 100644
index 0000000..c452a37
--- /dev/null
+++ b/gd/packet/parser/test/variable.h
@@ -0,0 +1,70 @@
+/******************************************************************************
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#pragma once
+
+#include <stdint.h>
+#include <optional>
+#include <sstream>
+#include <string>
+
+#include "packet/bit_inserter.h"
+#include "packet/iterator.h"
+
+namespace bluetooth {
+namespace packet {
+namespace parser {
+namespace test {
+
+class Variable final {
+ public:
+  std::string data;
+
+  Variable() = default;
+  Variable(const Variable&) = default;
+  Variable(const std::string& str);
+
+  void Serialize(BitInserter& bi) const;
+
+  size_t size() const;
+
+  template <bool little_endian>
+  static std::optional<Iterator<little_endian>> Parse(Variable* instance, Iterator<little_endian> it) {
+    if (it.NumBytesRemaining() < 1) {
+      return {};
+    }
+    size_t data_length = it.template extract<uint8_t>();
+    if (data_length > 255) {
+      return {};
+    }
+    if (it.NumBytesRemaining() < data_length) {
+      return {};
+    }
+    std::stringstream ss;
+    for (size_t i = 0; i < data_length; i++) {
+      ss << it.template extract<char>();
+    }
+    *instance = ss.str();
+    return it;
+  }
+};
+
+}  // namespace test
+}  // namespace parser
+}  // namespace packet
+}  // namespace bluetooth
diff --git a/gd/packet/parser/type_def.h b/gd/packet/parser/type_def.h
new file mode 100644
index 0000000..811dca8
--- /dev/null
+++ b/gd/packet/parser/type_def.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <iostream>
+
+#include "fields/packet_field.h"
+
+class TypeDef {
+ public:
+  TypeDef(std::string name) : name_(name) {}
+
+  TypeDef(std::string name, int size) : name_(name), size_(size) {}
+
+  virtual ~TypeDef() = default;
+
+  std::string GetTypeName() const {
+    return name_;
+  }
+
+  enum class Type {
+    INVALID,
+    ENUM,
+    CHECKSUM,
+    CUSTOM,
+    PACKET,
+    STRUCT,
+  };
+
+  virtual Type GetDefinitionType() const = 0;
+
+  virtual PacketField* GetNewField(const std::string& name, ParseLocation loc) const = 0;
+
+  const std::string name_;
+  const int size_{-1};
+};
diff --git a/gd/packet/parser/util.h b/gd/packet/parser/util.h
new file mode 100644
index 0000000..a8b881d
--- /dev/null
+++ b/gd/packet/parser/util.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <algorithm>
+#include <iostream>
+#include <regex>
+#include <string>
+
+#include "logging.h"
+
+namespace util {
+
+inline std::string GetTypeForSize(int size) {
+  if (size > 64) {
+    ERROR() << __func__ << ": Cannot use a type larger than 64 bits. (" << size << ")\n";
+  }
+
+  if (size <= 8) return "uint8_t";
+
+  if (size <= 16) return "uint16_t";
+
+  if (size <= 32) return "uint32_t";
+
+  return "uint64_t";
+}
+
+inline int RoundSizeUp(int size) {
+  if (size > 64) {
+    ERROR() << __func__ << ": Cannot use a type larger than 64 bits. (" << size << ")\n";
+  }
+
+  if (size <= 8) return 8;
+  if (size <= 16) return 16;
+  if (size <= 32) return 32;
+  return 64;
+}
+
+// Returns the max value that can be contained unsigned in a number of bits.
+inline uint64_t GetMaxValueForBits(int bits) {
+  if (bits > 64) {
+    ERROR() << __func__ << ": Cannot use a type larger than 64 bits. (" << bits << ")\n";
+  }
+
+  // Set all the bits to 1, then shift off extras.
+  return ~(static_cast<uint64_t>(0)) >> (64 - bits);
+}
+
+inline std::string CamelCaseToUnderScore(std::string value) {
+  if (value[0] < 'A' || value[0] > 'Z') {
+    ERROR() << value << " doesn't look like CamelCase";
+  }
+
+  // Use static to avoid compiling the regex more than once.
+  static const std::regex camel_case_regex("[A-Z][a-z0-9]*");
+
+  // Add an underscore to the end of each pattern match.
+  value = std::regex_replace(value, camel_case_regex, "$&_");
+
+  // Remove the last underscore at the end of the string.
+  value.pop_back();
+
+  // Convert all characters to lowercase.
+  std::transform(value.begin(), value.end(), value.begin(), [](unsigned char c) { return std::tolower(c); });
+
+  return value;
+}
+
+inline std::string UnderscoreToCamelCase(std::string value) {
+  if (value[0] < 'a' || value[0] > 'z') {
+    ERROR() << value << " invalid identifier";
+  }
+
+  std::ostringstream camel_case;
+
+  bool capitalize = true;
+  for (unsigned char c : value) {
+    if (c == '_') {
+      capitalize = true;
+    } else {
+      if (capitalize) {
+        c = std::toupper(c);
+        capitalize = false;
+      }
+      camel_case << c;
+    }
+  }
+
+  return camel_case.str();
+}
+
+inline bool IsEnumCase(std::string value) {
+  if (value[0] < 'A' || value[0] > 'Z') {
+    return false;
+  }
+
+  // Use static to avoid compiling the regex more than once.
+  static const std::regex enum_regex("[A-Z][A-Z0-9_]*");
+
+  return std::regex_match(value, enum_regex);
+}
+
+inline std::string StringJoin(const std::string& delimiter, const std::vector<std::string>& vec) {
+  std::stringstream ss;
+  for (size_t i = 0; i < vec.size(); i++) {
+    ss << vec[i];
+    if (i != (vec.size() - 1)) {
+      ss << delimiter;
+    }
+  }
+  return ss.str();
+}
+
+inline std::string StringFindAndReplaceAll(std::string text, const std::string& old, const std::string& replacement) {
+  auto pos = text.find(old);
+  while (pos != std::string::npos) {
+    text.replace(pos, old.size(), replacement);
+    pos = text.find(old, pos + replacement.size());
+  }
+  return text;
+}
+
+}  // namespace util
diff --git a/gd/packet/python3_module.cc b/gd/packet/python3_module.cc
new file mode 100644
index 0000000..64ccb82
--- /dev/null
+++ b/gd/packet/python3_module.cc
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <cstring>
+#include <memory>
+
+#include <pybind11/pybind11.h>
+#include <pybind11/stl.h>
+
+#include "hci/address.h"
+#include "hci/class_of_device.h"
+#include "packet/base_packet_builder.h"
+#include "packet/bit_inserter.h"
+#include "packet/iterator.h"
+#include "packet/packet_builder.h"
+#include "packet/packet_struct.h"
+#include "packet/packet_view.h"
+#include "packet/parser/checksum_type_checker.h"
+#include "packet/parser/custom_type_checker.h"
+
+namespace py = pybind11;
+
+namespace bluetooth {
+
+namespace hci {
+void define_hci_packets_submodule(py::module&);
+}
+namespace l2cap {
+void define_l2cap_packets_submodule(py::module&);
+}
+namespace security {
+void define_smp_packets_submodule(py::module&);
+}
+
+namespace packet {
+
+using ::bluetooth::hci::Address;
+using ::bluetooth::hci::ClassOfDevice;
+using ::bluetooth::packet::BasePacketBuilder;
+using ::bluetooth::packet::BaseStruct;
+using ::bluetooth::packet::BitInserter;
+using ::bluetooth::packet::CustomTypeChecker;
+using ::bluetooth::packet::Iterator;
+using ::bluetooth::packet::kLittleEndian;
+using ::bluetooth::packet::PacketBuilder;
+using ::bluetooth::packet::PacketStruct;
+using ::bluetooth::packet::PacketView;
+using ::bluetooth::packet::parser::ChecksumTypeChecker;
+
+PYBIND11_MODULE(bluetooth_packets_python3, m) {
+  py::class_<BasePacketBuilder, std::shared_ptr<BasePacketBuilder>>(m, "BasePacketBuilder");
+  py::class_<PacketBuilder<kLittleEndian>, BasePacketBuilder, std::shared_ptr<PacketBuilder<kLittleEndian>>>(
+      m, "PacketBuilderLittleEndian");
+  py::class_<PacketBuilder<!kLittleEndian>, BasePacketBuilder, std::shared_ptr<PacketBuilder<!kLittleEndian>>>(
+      m, "PacketBuilderBigEndian");
+  py::class_<BaseStruct, std::shared_ptr<BaseStruct>>(m, "BaseStruct");
+  py::class_<PacketStruct<kLittleEndian>, BaseStruct, std::shared_ptr<PacketStruct<kLittleEndian>>>(
+      m, "PacketStructLittleEndian");
+  py::class_<PacketStruct<!kLittleEndian>, BaseStruct, std::shared_ptr<PacketStruct<!kLittleEndian>>>(
+      m, "PacketStructBigEndian");
+  py::class_<Iterator<kLittleEndian>>(m, "IteratorLittleEndian");
+  py::class_<Iterator<!kLittleEndian>>(m, "IteratorBigEndian");
+  py::class_<PacketView<kLittleEndian>>(m, "PacketViewLittleEndian").def(py::init([](std::vector<uint8_t> bytes) {
+    // Make a copy
+    auto bytes_shared = std::make_shared<std::vector<uint8_t>>(bytes);
+    return std::make_unique<PacketView<kLittleEndian>>(bytes_shared);
+  }));
+  py::class_<PacketView<!kLittleEndian>>(m, "PacketViewBigEndian").def(py::init([](std::vector<uint8_t> bytes) {
+    // Make a copy
+    auto bytes_shared = std::make_shared<std::vector<uint8_t>>(bytes);
+    return std::make_unique<PacketView<!kLittleEndian>>(bytes_shared);
+  }));
+
+  py::module hci_m = m.def_submodule("hci_packets", "A submodule of hci_packets");
+  bluetooth::hci::define_hci_packets_submodule(hci_m);
+
+  py::class_<Address>(hci_m, "Address")
+      .def(py::init<>())
+      .def("__repr__", [](const Address& a) { return a.ToString(); })
+      .def("__str__", [](const Address& a) { return a.ToString(); });
+
+  py::class_<ClassOfDevice>(hci_m, "ClassOfDevice")
+      .def(py::init<>())
+      .def("__repr__", [](const ClassOfDevice& c) { return c.ToString(); })
+      .def("__str__", [](const ClassOfDevice& c) { return c.ToString(); });
+
+  py::module l2cap_m = m.def_submodule("l2cap_packets", "A submodule of l2cap_packets");
+  bluetooth::l2cap::define_l2cap_packets_submodule(l2cap_m);
+  py::module security_m = m.def_submodule("security_packets", "A submodule of security_packets");
+  bluetooth::security::define_smp_packets_submodule(security_m);
+}
+
+}  // namespace packet
+}  // namespace bluetooth
diff --git a/gd/packet/raw_builder.cc b/gd/packet/raw_builder.cc
index fd61417..ec2ff68 100644
--- a/gd/packet/raw_builder.cc
+++ b/gd/packet/raw_builder.cc
@@ -17,16 +17,18 @@
 #include "packet/raw_builder.h"
 
 #include <algorithm>
+#include <utility>
 
 #include "os/log.h"
 
+using bluetooth::hci::Address;
 using std::vector;
-using bluetooth::common::Address;
 
 namespace bluetooth {
 namespace packet {
 
 RawBuilder::RawBuilder(size_t max_bytes) : max_bytes_(max_bytes) {}
+RawBuilder::RawBuilder(std::vector<uint8_t> vec) : payload_(std::move(vec)) {}
 
 bool RawBuilder::AddOctets(size_t octets, const vector<uint8_t>& bytes) {
   if (payload_.size() + octets > max_bytes_) return false;
diff --git a/gd/packet/raw_builder.h b/gd/packet/raw_builder.h
index 8f9edf5..1c9552a 100644
--- a/gd/packet/raw_builder.h
+++ b/gd/packet/raw_builder.h
@@ -19,7 +19,7 @@
 #include <cstdint>
 #include <vector>
 
-#include "common/address.h"
+#include "hci/address.h"
 #include "packet/bit_inserter.h"
 #include "packet/packet_builder.h"
 
@@ -30,6 +30,7 @@
  public:
   RawBuilder() = default;
   RawBuilder(size_t max_bytes);
+  RawBuilder(std::vector<uint8_t> vec);
   virtual ~RawBuilder() = default;
 
   virtual size_t size() const override;
@@ -38,7 +39,7 @@
 
   // Add |address| to the payload.  Return true if:
   // - the new size of the payload is still <= |max_bytes_|
-  bool AddAddress(const common::Address& address);
+  bool AddAddress(const hci::Address& address);
 
   // Return true if |num_bytes| can be added to the payload.
   bool CanAddOctets(size_t num_bytes) const;
@@ -63,7 +64,7 @@
   // - the new size of the payload is still <= |max_bytes_|
   bool AddOctets(size_t octets, uint64_t value);
 
-  size_t max_bytes_{255};
+  size_t max_bytes_{0xffff};
 
   // Underlying containers for storing the actual packet
   std::vector<uint8_t> payload_;
diff --git a/gd/packet/raw_builder_unittest.cc b/gd/packet/raw_builder_unittest.cc
index 27cecd6..64ca0ed 100644
--- a/gd/packet/raw_builder_unittest.cc
+++ b/gd/packet/raw_builder_unittest.cc
@@ -20,9 +20,9 @@
 #include <forward_list>
 #include <memory>
 
-#include "common/address.h"
+#include "hci/address.h"
 
-using bluetooth::common::Address;
+using bluetooth::hci::Address;
 using bluetooth::packet::BitInserter;
 using std::vector;
 
@@ -65,5 +65,48 @@
   ASSERT_EQ(count, packet);
 }
 
+TEST(RawBuilderTest, buildStartingWithVector) {
+  std::vector<uint8_t> count_first(count.begin(), count.begin() + 0x8);
+  std::unique_ptr<RawBuilder> count_builder = std::make_unique<RawBuilder>(count_first);
+  count_builder->AddOctets4(0x0b0a0908);
+  count_builder->AddOctets2(0x0d0c);
+  count_builder->AddOctets1(0x0e);
+  count_builder->AddOctets1(0x0f);
+  count_builder->AddOctets8(0x1716151413121110);
+  std::vector<uint8_t> count_last(count.begin() + 0x18, count.end());
+  count_builder->AddOctets(count_last);
+
+  ASSERT_EQ(count.size(), count_builder->size());
+
+  std::vector<uint8_t> packet;
+  BitInserter it(packet);
+
+  count_builder->Serialize(it);
+
+  ASSERT_EQ(count, packet);
+}
+
+TEST(RawBuilderTest, testMaxBytes) {
+  const size_t kMaxBytes = count.size();
+  std::unique_ptr<RawBuilder> count_builder = std::make_unique<RawBuilder>(kMaxBytes);
+  ASSERT_TRUE(count_builder->AddOctets(count));
+  ASSERT_FALSE(count_builder->AddOctets4(0x0b0a0908));
+  ASSERT_FALSE(count_builder->AddOctets2(0x0d0c));
+  ASSERT_FALSE(count_builder->AddOctets1(0x0e));
+  ASSERT_FALSE(count_builder->AddOctets1(0x0f));
+  ASSERT_FALSE(count_builder->AddOctets8(0x1716151413121110));
+  std::vector<uint8_t> count_last(count.begin() + 0x18, count.end());
+  ASSERT_FALSE(count_builder->AddOctets(count_last));
+
+  ASSERT_EQ(count.size(), count_builder->size());
+
+  std::vector<uint8_t> packet;
+  BitInserter it(packet);
+
+  count_builder->Serialize(it);
+
+  ASSERT_EQ(count, packet);
+}
+
 }  // namespace packet
 }  // namespace bluetooth
diff --git a/gd/packet/view.h b/gd/packet/view.h
index 4f3b508..3b8b679 100644
--- a/gd/packet/view.h
+++ b/gd/packet/view.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <cstdint>
+#include <memory>
 #include <vector>
 
 namespace bluetooth {
diff --git a/gd/security/Android.bp b/gd/security/Android.bp
new file mode 100644
index 0000000..6c373ba
--- /dev/null
+++ b/gd/security/Android.bp
@@ -0,0 +1,36 @@
+filegroup {
+    name: "BluetoothSecuritySources",
+    srcs: [
+        "ecc/multprecision.cc",
+        "ecc/p_256_ecc_pp.cc",
+        "ecdh_keys.cc",
+        "pairing_handler_le.cc",
+        "pairing_handler_le_legacy.cc",
+        "pairing_handler_le_secure_connections.cc",
+        "security_manager.cc",
+        "internal/security_manager_impl.cc",
+        "security_module.cc",
+        ":BluetoothSecurityChannelSources",
+        ":BluetoothSecurityPairingSources",
+    ],
+}
+
+filegroup {
+    name: "BluetoothSecurityTestSources",
+    srcs: [
+        "ecc/multipoint_test.cc",
+        "pairing_handler_le_unittest.cc",
+        "test/ecdh_keys_test.cc",
+        "test/fake_l2cap_test.cc",
+        "test/pairing_handler_le_pair_test.cc",
+        ":BluetoothSecurityChannelTestSources",
+        ":BluetoothSecurityPairingTestSources",
+    ],
+}
+
+filegroup {
+     name: "BluetoothFacade_security_layer",
+     srcs: [
+         "facade.cc",
+     ],
+}
diff --git a/gd/security/cert/simple_security_test.py b/gd/security/cert/simple_security_test.py
new file mode 100644
index 0000000..ff7228e
--- /dev/null
+++ b/gd/security/cert/simple_security_test.py
@@ -0,0 +1,237 @@
+#!/usr/bin/env python3
+#
+#   Copyright 2019 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+from datetime import timedelta
+import os
+import sys
+import logging
+
+from cert.gd_base_test_facade_only import GdFacadeOnlyBaseTestClass
+from cert.event_callback_stream import EventCallbackStream
+from cert.event_asserts import EventAsserts
+from google.protobuf import empty_pb2 as empty_proto
+from facade import common_pb2 as common
+from facade import rootservice_pb2 as facade_rootservice_pb2
+from hci.facade import facade_pb2 as hci_facade
+from hci.facade import acl_manager_facade_pb2 as acl_manager_facade
+from hci.facade import controller_facade_pb2 as controller_facade
+from l2cap.classic import facade_pb2 as l2cap_facade
+from neighbor.facade import facade_pb2 as neighbor_facade
+from security import facade_pb2 as security_facade
+from bluetooth_packets_python3 import hci_packets
+import bluetooth_packets_python3 as bt_packets
+
+
+class SimpleSecurityTest(GdFacadeOnlyBaseTestClass):
+
+    def setup_test(self):
+        self.device_under_test.rootservice.StartStack(
+            facade_rootservice_pb2.StartStackRequest(
+                module_under_test=facade_rootservice_pb2.BluetoothModule.Value(
+                    'SECURITY'),))
+        self.cert_device.rootservice.StartStack(
+            facade_rootservice_pb2.StartStackRequest(
+                module_under_test=facade_rootservice_pb2.BluetoothModule.Value(
+                    'L2CAP'),))
+
+        self.device_under_test.address = self.device_under_test.controller_read_only_property.ReadLocalAddress(
+            empty_proto.Empty()).address
+        self.cert_device.address = self.cert_device.controller_read_only_property.ReadLocalAddress(
+            empty_proto.Empty()).address
+
+        self.device_under_test.neighbor.EnablePageScan(
+            neighbor_facade.EnableMsg(enabled=True))
+        self.cert_device.neighbor.EnablePageScan(
+            neighbor_facade.EnableMsg(enabled=True))
+
+        self.dut_address = common.BluetoothAddress(
+            address=self.device_under_test.address)
+        self.cert_address = common.BluetoothAddress(
+            address=self.cert_device.address)
+
+        self.dut_address_with_type = common.BluetoothAddressWithType()
+        self.dut_address_with_type.address.CopyFrom(self.dut_address)
+        self.dut_address_with_type.type = common.BluetoothPeerAddressTypeEnum.PUBLIC_DEVICE_OR_IDENTITY_ADDRESS
+
+        self.cert_address_with_type = common.BluetoothAddressWithType()
+        self.cert_address_with_type.address.CopyFrom(self.cert_address)
+        self.cert_address_with_type.type = common.BluetoothPeerAddressTypeEnum.PUBLIC_DEVICE_OR_IDENTITY_ADDRESS
+
+        self.device_under_test.wait_channel_ready()
+        self.cert_device.wait_channel_ready()
+
+        self.cert_name = b'ImTheCert'
+        self.cert_device.hci_controller.WriteLocalName(
+            controller_facade.NameMsg(name=self.cert_name))
+        self.dut_name = b'ImTheDUT'
+        self.device_under_test.hci_controller.WriteLocalName(
+            controller_facade.NameMsg(name=self.dut_name))
+
+    def teardown_test(self):
+        self.device_under_test.rootservice.StopStack(
+            facade_rootservice_pb2.StopStackRequest())
+        self.cert_device.rootservice.StopStack(
+            facade_rootservice_pb2.StopStackRequest())
+
+    def tmp_register_for_event(self, event_code):
+        msg = hci_facade.EventCodeMsg(code=int(event_code))
+        self.device_under_test.hci.RegisterEventHandler(msg)
+
+    def tmp_enqueue_hci_command(self, command, expect_complete):
+        cmd_bytes = bytes(command.Serialize())
+        cmd = hci_facade.CommandMsg(command=cmd_bytes)
+        if (expect_complete):
+            self.device_under_test.hci.EnqueueCommandWithComplete(cmd)
+        else:
+            self.device_under_test.hci.EnqueueCommandWithStatus(cmd)
+
+    def register_for_event(self, event_code):
+        msg = hci_facade.EventCodeMsg(code=int(event_code))
+        self.cert_device.hci.RegisterEventHandler(msg)
+
+    def enqueue_hci_command(self, command, expect_complete):
+        cmd_bytes = bytes(command.Serialize())
+        cmd = hci_facade.CommandMsg(command=cmd_bytes)
+        if (expect_complete):
+            self.cert_device.hci.EnqueueCommandWithComplete(cmd)
+        else:
+            self.cert_device.hci.EnqueueCommandWithStatus(cmd)
+
+    def enqueue_acl_data(self, handle, pb_flag, b_flag, acl):
+        acl_msg = hci_facade.AclMsg(
+            handle=int(handle),
+            packet_boundary_flag=int(pb_flag),
+            broadcast_flag=int(b_flag),
+            data=acl)
+        self.cert_device.hci.SendAclData(acl_msg)
+
+    def pair_justworks(self, cert_iocap_reply, expected_ui_event):
+        # Cert event registration
+        self.register_for_event(hci_packets.EventCode.LINK_KEY_REQUEST)
+        self.register_for_event(hci_packets.EventCode.IO_CAPABILITY_REQUEST)
+        self.register_for_event(hci_packets.EventCode.IO_CAPABILITY_RESPONSE)
+        self.register_for_event(hci_packets.EventCode.USER_PASSKEY_NOTIFICATION)
+        self.register_for_event(hci_packets.EventCode.USER_CONFIRMATION_REQUEST)
+        self.register_for_event(
+            hci_packets.EventCode.REMOTE_HOST_SUPPORTED_FEATURES_NOTIFICATION)
+        self.register_for_event(hci_packets.EventCode.LINK_KEY_NOTIFICATION)
+        self.register_for_event(hci_packets.EventCode.SIMPLE_PAIRING_COMPLETE)
+        with EventCallbackStream(self.device_under_test.security.FetchUiEvents(empty_proto.Empty())) as dut_ui_stream, \
+            EventCallbackStream(self.device_under_test.security.FetchBondEvents(empty_proto.Empty())) as dut_bond_stream, \
+            EventCallbackStream(self.device_under_test.neighbor.GetRemoteNameEvents(empty_proto.Empty())) as name_event_stream, \
+            EventCallbackStream(self.cert_device.hci.FetchEvents(empty_proto.Empty())) as cert_hci_event_stream:
+
+            cert_hci_event_asserts = EventAsserts(cert_hci_event_stream)
+            dut_ui_event_asserts = EventAsserts(dut_ui_stream)
+            dut_bond_asserts = EventAsserts(dut_bond_stream)
+            dut_name_asserts = EventAsserts(name_event_stream)
+
+            dut_address = self.device_under_test.hci_controller.GetMacAddress(
+                empty_proto.Empty()).address
+            cert_address = self.cert_device.hci_controller.GetMacAddress(
+                empty_proto.Empty()).address
+
+            # Enable Simple Secure Pairing
+            self.enqueue_hci_command(
+                hci_packets.WriteSimplePairingModeBuilder(
+                    hci_packets.Enable.ENABLED), True)
+
+            cert_hci_event_asserts.assert_event_occurs(
+                lambda msg: b'\x0e\x04\x01\x56\x0c' in msg.event)
+
+            # Get the name
+            self.device_under_test.neighbor.ReadRemoteName(
+                neighbor_facade.RemoteNameRequestMsg(
+                    address=cert_address,
+                    page_scan_repetition_mode=1,
+                    clock_offset=0x6855))
+
+            dut_name_asserts.assert_event_occurs(
+                lambda msg: self.cert_name in msg.name)
+
+            self.device_under_test.security.CreateBond(
+                common.BluetoothAddressWithType(
+                    address=common.BluetoothAddress(address=cert_address),
+                    type=common.BluetoothAddressTypeEnum.PUBLIC_DEVICE_ADDRESS))
+
+            cert_hci_event_asserts.assert_event_occurs(
+                lambda event: logging.debug(event.event) or hci_packets.EventCode.IO_CAPABILITY_REQUEST in event.event
+            )
+
+            self.enqueue_hci_command(cert_iocap_reply, True)
+
+            cert_hci_event_asserts.assert_event_occurs(
+                lambda event: logging.debug(event.event) or hci_packets.EventCode.USER_CONFIRMATION_REQUEST in event.event
+            )
+            self.enqueue_hci_command(
+                hci_packets.UserConfirmationRequestReplyBuilder(
+                    dut_address.decode('utf8')), True)
+
+            logging.info("Waiting for UI event")
+            ui_id = -1
+
+            def get_unique_id(event):
+                if (event.message_type == expected_ui_event):
+                    nonlocal ui_id
+                    ui_id = event.unique_id
+                    return True
+                return False
+
+            dut_ui_event_asserts.assert_event_occurs(get_unique_id)
+
+            logging.info("Sending UI response")
+            self.device_under_test.security.SendUiCallback(
+                security_facade.UiCallbackMsg(
+                    message_type=security_facade.UiCallbackType.YES_NO,
+                    boolean=True,
+                    unique_id=ui_id))
+
+            dut_bond_asserts.assert_event_occurs(
+                lambda bond_event: bond_event.message_type == security_facade.BondMsgType.DEVICE_BONDED
+            )
+
+    def test_display_only(self):
+        dut_address = self.device_under_test.hci_controller.GetMacAddress(
+            empty_proto.Empty()).address
+        self.pair_justworks(
+            hci_packets.IoCapabilityRequestReplyBuilder(
+                dut_address.decode('utf8'),
+                hci_packets.IoCapability.DISPLAY_ONLY,
+                hci_packets.OobDataPresent.NOT_PRESENT, hci_packets.
+                AuthenticationRequirements.DEDICATED_BONDING_MITM_PROTECTION),
+            security_facade.UiMsgType.DISPLAY_YES_NO_WITH_VALUE)
+
+    def test_no_input_no_output(self):
+        dut_address = self.device_under_test.hci_controller.GetMacAddress(
+            empty_proto.Empty()).address
+        self.pair_justworks(
+            hci_packets.IoCapabilityRequestReplyBuilder(
+                dut_address.decode('utf8'),
+                hci_packets.IoCapability.NO_INPUT_NO_OUTPUT,
+                hci_packets.OobDataPresent.NOT_PRESENT, hci_packets.
+                AuthenticationRequirements.DEDICATED_BONDING_MITM_PROTECTION),
+            security_facade.UiMsgType.DISPLAY_YES_NO)
+
+    def test_display_yes_no(self):
+        dut_address = self.device_under_test.hci_controller.GetMacAddress(
+            empty_proto.Empty()).address
+        self.pair_justworks(
+            hci_packets.IoCapabilityRequestReplyBuilder(
+                dut_address.decode('utf8'),
+                hci_packets.IoCapability.DISPLAY_YES_NO,
+                hci_packets.OobDataPresent.NOT_PRESENT, hci_packets.
+                AuthenticationRequirements.DEDICATED_BONDING_MITM_PROTECTION),
+            security_facade.UiMsgType.DISPLAY_YES_NO_WITH_VALUE)
diff --git a/gd/security/channel/Android.bp b/gd/security/channel/Android.bp
new file mode 100644
index 0000000..653902b
--- /dev/null
+++ b/gd/security/channel/Android.bp
@@ -0,0 +1,13 @@
+filegroup {
+    name: "BluetoothSecurityChannelSources",
+    srcs: [
+        "security_manager_channel.cc",
+    ]
+}
+
+filegroup {
+    name: "BluetoothSecurityChannelTestSources",
+    srcs: [
+        "security_manager_channel_unittest.cc",
+    ]
+}
diff --git a/gd/security/channel/security_manager_channel.cc b/gd/security/channel/security_manager_channel.cc
new file mode 100644
index 0000000..fd6e9df
--- /dev/null
+++ b/gd/security/channel/security_manager_channel.cc
@@ -0,0 +1,45 @@
+/*
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+#include "security/channel/security_manager_channel.h"
+
+#include "security/smp_packets.h"
+
+namespace bluetooth {
+namespace security {
+namespace channel {
+
+void SecurityManagerChannel::OnCommandComplete(hci::CommandCompleteView packet) {
+  ASSERT(packet.IsValid());
+  // TODO(optedoblivion): Verify HCI commands
+}
+
+void SecurityManagerChannel::SendCommand(std::unique_ptr<hci::SecurityCommandBuilder> command) {
+  hci_security_interface_->EnqueueCommand(
+      std::move(command), common::BindOnce(&SecurityManagerChannel::OnCommandComplete, common::Unretained(this)),
+      handler_);
+}
+
+void SecurityManagerChannel::OnHciEventReceived(hci::EventPacketView packet) {
+  ASSERT_LOG(listener_ != nullptr, "No listener set!");
+  ASSERT(packet.IsValid());
+  listener_->OnHciEventReceived(packet);
+}
+
+}  // namespace channel
+}  // namespace security
+}  // namespace bluetooth
diff --git a/gd/security/channel/security_manager_channel.h b/gd/security/channel/security_manager_channel.h
new file mode 100644
index 0000000..48bd846
--- /dev/null
+++ b/gd/security/channel/security_manager_channel.h
@@ -0,0 +1,90 @@
+/*
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0;
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+#pragma once
+
+#include <memory>
+#include <vector>
+
+#include "hci/address_with_type.h"
+#include "hci/hci_layer.h"
+#include "hci/hci_packets.h"
+#include "hci/security_interface.h"
+
+namespace bluetooth {
+namespace security {
+namespace channel {
+
+/**
+ * Interface for listening to the channel for SMP commands.
+ */
+class ISecurityManagerChannelListener {
+ public:
+  virtual ~ISecurityManagerChannelListener() = default;
+  virtual void OnHciEventReceived(hci::EventPacketView packet) = 0;
+};
+
+/**
+ * Channel for consolidating traffic and making the transport agnostic.
+ */
+class SecurityManagerChannel {
+ public:
+  explicit SecurityManagerChannel(os::Handler* handler, hci::HciLayer* hci_layer)
+      : listener_(nullptr),
+        hci_security_interface_(hci_layer->GetSecurityInterface(
+            common::Bind(&SecurityManagerChannel::OnHciEventReceived, common::Unretained(this)), handler)),
+        handler_(handler) {}
+
+  /**
+   * Send a given SMP command over the SecurityManagerChannel
+   *
+   * @param command smp command to send
+   */
+  void SendCommand(std::unique_ptr<hci::SecurityCommandBuilder> command);
+
+  /**
+   * Sets the listener to listen for channel events
+   *
+   * @param listener the caller interested in events
+   */
+  void SetChannelListener(ISecurityManagerChannelListener* listener) {
+    listener_ = listener;
+  }
+
+  /**
+   * Called when an incoming HCI event happens
+   *
+   * @param event_packet
+   */
+  void OnHciEventReceived(hci::EventPacketView packet);
+
+  /**
+   * Called when an HCI command is completed
+   *
+   * @param on_complete
+   */
+  void OnCommandComplete(hci::CommandCompleteView packet);
+
+ private:
+  ISecurityManagerChannelListener* listener_;
+  hci::SecurityInterface* hci_security_interface_;
+  os::Handler* handler_;
+};
+
+}  // namespace channel
+}  // namespace security
+}  // namespace bluetooth
diff --git a/gd/security/channel/security_manager_channel_unittest.cc b/gd/security/channel/security_manager_channel_unittest.cc
new file mode 100644
index 0000000..14de43f
--- /dev/null
+++ b/gd/security/channel/security_manager_channel_unittest.cc
@@ -0,0 +1,631 @@
+/*
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+#include "security_manager_channel.h"
+
+#include <gtest/gtest.h>
+
+#include "hci/hci_packets.h"
+#include "packet/raw_builder.h"
+#include "security/smp_packets.h"
+#include "security/test/fake_hci_layer.h"
+
+namespace bluetooth {
+namespace security {
+namespace channel {
+namespace {
+
+using bluetooth::security::channel::SecurityManagerChannel;
+using hci::Address;
+using hci::AuthenticationRequirements;
+using hci::CommandCompleteBuilder;
+using hci::IoCapabilityRequestReplyBuilder;
+using hci::IoCapabilityRequestView;
+using hci::OobDataPresent;
+using hci::OpCode;
+using os::Handler;
+using os::Thread;
+using packet::RawBuilder;
+
+class SecurityManagerChannelCallback : public ISecurityManagerChannelListener {
+ public:
+  // HCI
+  bool receivedChangeConnectionLinkKeyComplete = false;
+  bool receivedMasterLinkKeyComplete = false;
+  bool receivedPinCodeRequest = false;
+  bool receivedLinkKeyRequest = false;
+  bool receivedLinkKeyNotification = false;
+  bool receivedIoCapabilityRequest = false;
+  bool receivedIoCapabilityResponse = false;
+  bool receivedSimplePairingComplete = false;
+  bool receivedReturnLinkKeys = false;
+  bool receivedEncryptionChange = false;
+  bool receivedEncryptionKeyRefreshComplete = false;
+  bool receivedRemoteOobDataRequest = false;
+  bool receivedUserPasskeyNotification = false;
+  bool receivedUserPasskeyRequest = false;
+  bool receivedKeypressNotification = false;
+  bool receivedUserConfirmationRequest = false;
+
+  void OnReceive(hci::AddressWithType device, hci::ChangeConnectionLinkKeyCompleteView packet) {
+    ASSERT_TRUE(packet.IsValid());
+    receivedChangeConnectionLinkKeyComplete = true;
+  }
+  void OnReceive(hci::AddressWithType device, hci::MasterLinkKeyCompleteView packet) {
+    ASSERT_TRUE(packet.IsValid());
+    receivedMasterLinkKeyComplete = true;
+  }
+  void OnReceive(hci::AddressWithType device, hci::PinCodeRequestView packet) {
+    ASSERT_TRUE(packet.IsValid());
+    receivedPinCodeRequest = true;
+  }
+  void OnReceive(hci::AddressWithType device, hci::LinkKeyRequestView packet) {
+    ASSERT_TRUE(packet.IsValid());
+    receivedLinkKeyRequest = true;
+  }
+  void OnReceive(hci::AddressWithType device, hci::LinkKeyNotificationView packet) {
+    ASSERT_TRUE(packet.IsValid());
+    receivedLinkKeyNotification = true;
+  }
+  void OnReceive(hci::AddressWithType device, hci::IoCapabilityRequestView packet) {
+    ASSERT_TRUE(packet.IsValid());
+    receivedIoCapabilityRequest = true;
+  }
+  void OnReceive(hci::AddressWithType device, hci::IoCapabilityResponseView packet) {
+    ASSERT_TRUE(packet.IsValid());
+    receivedIoCapabilityResponse = true;
+  }
+  void OnReceive(hci::AddressWithType device, hci::SimplePairingCompleteView packet) {
+    ASSERT_TRUE(packet.IsValid());
+    receivedSimplePairingComplete = true;
+  }
+  void OnReceive(hci::AddressWithType device, hci::ReturnLinkKeysView packet) {
+    ASSERT_TRUE(packet.IsValid());
+    receivedReturnLinkKeys = true;
+  }
+  void OnReceive(hci::AddressWithType device, hci::EncryptionChangeView packet) {
+    ASSERT_TRUE(packet.IsValid());
+    receivedEncryptionChange = true;
+  }
+  void OnReceive(hci::AddressWithType device, hci::EncryptionKeyRefreshCompleteView packet) {
+    ASSERT_TRUE(packet.IsValid());
+    receivedEncryptionKeyRefreshComplete = true;
+  }
+  void OnReceive(hci::AddressWithType device, hci::RemoteOobDataRequestView packet) {
+    ASSERT_TRUE(packet.IsValid());
+    receivedRemoteOobDataRequest = true;
+  }
+  void OnReceive(hci::AddressWithType device, hci::UserPasskeyNotificationView packet) {
+    ASSERT_TRUE(packet.IsValid());
+    receivedUserPasskeyNotification = true;
+  }
+  void OnReceive(hci::AddressWithType device, hci::KeypressNotificationView packet) {
+    ASSERT_TRUE(packet.IsValid());
+    receivedKeypressNotification = true;
+  }
+  void OnReceive(hci::AddressWithType device, hci::UserConfirmationRequestView packet) {
+    ASSERT_TRUE(packet.IsValid());
+    receivedUserConfirmationRequest = true;
+  }
+  void OnReceive(hci::AddressWithType device, hci::UserPasskeyRequestView packet) {
+    ASSERT_TRUE(packet.IsValid());
+    receivedUserPasskeyRequest = true;
+  }
+
+  void OnHciEventReceived(EventPacketView packet) override {
+    auto event = EventPacketView::Create(packet);
+    ASSERT_LOG(event.IsValid(), "Received invalid packet");
+    const hci::EventCode code = event.GetEventCode();
+    switch (code) {
+      case hci::EventCode::CHANGE_CONNECTION_LINK_KEY_COMPLETE:
+        OnReceive(hci::AddressWithType(), hci::ChangeConnectionLinkKeyCompleteView::Create(event));
+        break;
+      case hci::EventCode::MASTER_LINK_KEY_COMPLETE:
+        OnReceive(hci::AddressWithType(), hci::MasterLinkKeyCompleteView::Create(event));
+        break;
+      case hci::EventCode::PIN_CODE_REQUEST:
+        OnReceive(hci::AddressWithType(), hci::PinCodeRequestView::Create(event));
+        break;
+      case hci::EventCode::LINK_KEY_REQUEST:
+        OnReceive(hci::AddressWithType(), hci::LinkKeyRequestView::Create(event));
+        break;
+      case hci::EventCode::LINK_KEY_NOTIFICATION:
+        OnReceive(hci::AddressWithType(), hci::LinkKeyNotificationView::Create(event));
+        break;
+      case hci::EventCode::IO_CAPABILITY_REQUEST:
+        OnReceive(hci::AddressWithType(), hci::IoCapabilityRequestView::Create(event));
+        break;
+      case hci::EventCode::IO_CAPABILITY_RESPONSE:
+        OnReceive(hci::AddressWithType(), hci::IoCapabilityResponseView::Create(event));
+        break;
+      case hci::EventCode::SIMPLE_PAIRING_COMPLETE:
+        OnReceive(hci::AddressWithType(), hci::SimplePairingCompleteView::Create(event));
+        break;
+      case hci::EventCode::RETURN_LINK_KEYS:
+        OnReceive(hci::AddressWithType(), hci::ReturnLinkKeysView::Create(event));
+        break;
+      case hci::EventCode::ENCRYPTION_CHANGE:
+        OnReceive(hci::AddressWithType(), hci::EncryptionChangeView::Create(event));
+        break;
+      case hci::EventCode::ENCRYPTION_KEY_REFRESH_COMPLETE:
+        OnReceive(hci::AddressWithType(), hci::EncryptionKeyRefreshCompleteView::Create(event));
+        break;
+      case hci::EventCode::REMOTE_OOB_DATA_REQUEST:
+        OnReceive(hci::AddressWithType(), hci::RemoteOobDataRequestView::Create(event));
+        break;
+      case hci::EventCode::USER_PASSKEY_NOTIFICATION:
+        OnReceive(hci::AddressWithType(), hci::UserPasskeyNotificationView::Create(event));
+        break;
+      case hci::EventCode::KEYPRESS_NOTIFICATION:
+        OnReceive(hci::AddressWithType(), hci::KeypressNotificationView::Create(event));
+        break;
+      case hci::EventCode::USER_CONFIRMATION_REQUEST:
+        OnReceive(hci::AddressWithType(), hci::UserConfirmationRequestView::Create(event));
+        break;
+      case hci::EventCode::USER_PASSKEY_REQUEST:
+        OnReceive(hci::AddressWithType(), hci::UserPasskeyRequestView::Create(event));
+        break;
+      default:
+        ASSERT_LOG(false, "Cannot handle received packet: %s", hci::EventCodeText(code).c_str());
+        break;
+    }
+  }
+};
+
+class SecurityManagerChannelTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    handler_ = new Handler(&thread_);
+    callback_ = new SecurityManagerChannelCallback();
+    hci_layer_ = new FakeHciLayer();
+    fake_registry_.InjectTestModule(&FakeHciLayer::Factory, hci_layer_);
+    fake_registry_.Start<FakeHciLayer>(&thread_);
+    channel_ = new SecurityManagerChannel(handler_, hci_layer_);
+    channel_->SetChannelListener(callback_);
+  }
+
+  void TearDown() override {
+    channel_->SetChannelListener(nullptr);
+    handler_->Clear();
+    fake_registry_.SynchronizeModuleHandler(&FakeHciLayer::Factory, std::chrono::milliseconds(20));
+    fake_registry_.StopAll();
+    delete handler_;
+    delete channel_;
+    delete callback_;
+  }
+
+  TestModuleRegistry fake_registry_;
+  Thread& thread_ = fake_registry_.GetTestThread();
+  Handler* handler_ = nullptr;
+  FakeHciLayer* hci_layer_ = nullptr;
+  SecurityManagerChannel* channel_ = nullptr;
+  SecurityManagerChannelCallback* callback_ = nullptr;
+  hci::AddressWithType device_;
+};
+
+TEST_F(SecurityManagerChannelTest, setup_teardown) {}
+
+TEST_F(SecurityManagerChannelTest, recv_io_cap_request) {
+  hci_layer_->IncomingEvent(hci::IoCapabilityRequestBuilder::Create(device_.GetAddress()));
+  ASSERT_TRUE(callback_->receivedIoCapabilityRequest);
+}
+
+TEST_F(SecurityManagerChannelTest, send_io_cap_request_reply) {
+  // Arrange
+  hci::IoCapability io_capability = (hci::IoCapability)0x00;
+  OobDataPresent oob_present = (OobDataPresent)0x00;
+  AuthenticationRequirements authentication_requirements = (AuthenticationRequirements)0x00;
+  auto packet = hci::IoCapabilityRequestReplyBuilder::Create(device_.GetAddress(), io_capability, oob_present,
+                                                             authentication_requirements);
+
+  // Act
+  channel_->SendCommand(std::move(packet));
+  auto last_command = std::move(hci_layer_->GetLastCommand()->command);
+  auto command_packet = GetPacketView(std::move(last_command));
+  hci::CommandPacketView packet_view = hci::CommandPacketView::Create(command_packet);
+
+  // Assert
+  ASSERT_TRUE(packet_view.IsValid());
+  ASSERT_EQ(OpCode::IO_CAPABILITY_REQUEST_REPLY, packet_view.GetOpCode());
+}
+
+TEST_F(SecurityManagerChannelTest, send_io_cap_request_neg_reply) {
+  // Arrange
+  auto packet =
+      hci::IoCapabilityRequestNegativeReplyBuilder::Create(device_.GetAddress(), hci::ErrorCode::COMMAND_DISALLOWED);
+
+  // Act
+  channel_->SendCommand(std::move(packet));
+  auto last_command = std::move(hci_layer_->GetLastCommand()->command);
+  auto command_packet = GetPacketView(std::move(last_command));
+  hci::CommandPacketView packet_view = hci::CommandPacketView::Create(command_packet);
+
+  // Assert
+  ASSERT_TRUE(packet_view.IsValid());
+  ASSERT_EQ(OpCode::IO_CAPABILITY_REQUEST_NEGATIVE_REPLY, packet_view.GetOpCode());
+}
+
+TEST_F(SecurityManagerChannelTest, recv_io_cap_response) {
+  hci::IoCapability io_capability = (hci::IoCapability)0x00;
+  OobDataPresent oob_present = (OobDataPresent)0x00;
+  AuthenticationRequirements authentication_requirements = (AuthenticationRequirements)0x00;
+  hci_layer_->IncomingEvent(hci::IoCapabilityResponseBuilder::Create(device_.GetAddress(), io_capability, oob_present,
+                                                                     authentication_requirements));
+  ASSERT_TRUE(callback_->receivedIoCapabilityResponse);
+}
+
+TEST_F(SecurityManagerChannelTest, recv_pin_code_request) {
+  hci_layer_->IncomingEvent(hci::PinCodeRequestBuilder::Create(device_.GetAddress()));
+  ASSERT_TRUE(callback_->receivedPinCodeRequest);
+}
+
+TEST_F(SecurityManagerChannelTest, send_pin_code_request_reply) {
+  // Arrange
+  uint8_t pin_code_length = 6;
+  std::array<uint8_t, 16> pin_code = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5};
+  auto packet = hci::PinCodeRequestReplyBuilder::Create(device_.GetAddress(), pin_code_length, pin_code);
+
+  // Act
+  channel_->SendCommand(std::move(packet));
+  auto last_command = std::move(hci_layer_->GetLastCommand()->command);
+  auto command_packet = GetPacketView(std::move(last_command));
+  hci::CommandPacketView packet_view = hci::CommandPacketView::Create(command_packet);
+
+  // Assert
+  ASSERT_TRUE(packet_view.IsValid());
+  ASSERT_EQ(OpCode::PIN_CODE_REQUEST_REPLY, packet_view.GetOpCode());
+}
+
+TEST_F(SecurityManagerChannelTest, send_pin_code_request_neg_reply) {
+  // Arrange
+  auto packet = hci::PinCodeRequestNegativeReplyBuilder::Create(device_.GetAddress());
+
+  // Act
+  channel_->SendCommand(std::move(packet));
+  auto last_command = std::move(hci_layer_->GetLastCommand()->command);
+  auto command_packet = GetPacketView(std::move(last_command));
+  hci::CommandPacketView packet_view = hci::CommandPacketView::Create(command_packet);
+
+  // Assert
+  ASSERT_TRUE(packet_view.IsValid());
+  ASSERT_EQ(OpCode::PIN_CODE_REQUEST_NEGATIVE_REPLY, packet_view.GetOpCode());
+}
+
+TEST_F(SecurityManagerChannelTest, recv_user_passkey_notification) {
+  uint32_t passkey = 0x00;
+  hci_layer_->IncomingEvent(hci::UserPasskeyNotificationBuilder::Create(device_.GetAddress(), passkey));
+  ASSERT_TRUE(callback_->receivedUserPasskeyNotification);
+}
+
+TEST_F(SecurityManagerChannelTest, recv_user_confirmation_request) {
+  uint32_t numeric_value = 0x0;
+  hci_layer_->IncomingEvent(hci::UserConfirmationRequestBuilder::Create(device_.GetAddress(), numeric_value));
+  ASSERT_TRUE(callback_->receivedUserConfirmationRequest);
+}
+
+TEST_F(SecurityManagerChannelTest, send_user_confirmation_request_reply) {
+  // Arrange
+  auto packet = hci::UserConfirmationRequestReplyBuilder::Create(device_.GetAddress());
+
+  // Act
+  channel_->SendCommand(std::move(packet));
+  auto last_command = std::move(hci_layer_->GetLastCommand()->command);
+  auto command_packet = GetPacketView(std::move(last_command));
+  hci::CommandPacketView packet_view = hci::CommandPacketView::Create(command_packet);
+
+  // Assert
+  ASSERT_TRUE(packet_view.IsValid());
+  ASSERT_EQ(OpCode::USER_CONFIRMATION_REQUEST_REPLY, packet_view.GetOpCode());
+}
+
+TEST_F(SecurityManagerChannelTest, send_user_confirmation_request_negative_reply) {
+  // Arrange
+  auto packet = hci::UserConfirmationRequestNegativeReplyBuilder::Create(device_.GetAddress());
+
+  // Act
+  channel_->SendCommand(std::move(packet));
+  auto last_command = std::move(hci_layer_->GetLastCommand()->command);
+  auto command_packet = GetPacketView(std::move(last_command));
+  hci::CommandPacketView packet_view = hci::CommandPacketView::Create(command_packet);
+
+  // Assert
+  ASSERT_TRUE(packet_view.IsValid());
+  ASSERT_EQ(OpCode::USER_CONFIRMATION_REQUEST_NEGATIVE_REPLY, packet_view.GetOpCode());
+}
+
+TEST_F(SecurityManagerChannelTest, recv_remote_oob_data_request) {
+  hci_layer_->IncomingEvent(hci::RemoteOobDataRequestBuilder::Create(device_.GetAddress()));
+  ASSERT_TRUE(callback_->receivedRemoteOobDataRequest);
+}
+
+TEST_F(SecurityManagerChannelTest, send_remote_oob_data_request_reply) {
+  // Arrange
+  std::array<uint8_t, 16> c = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5};
+  std::array<uint8_t, 16> r = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5};
+  auto packet = hci::RemoteOobDataRequestReplyBuilder::Create(device_.GetAddress(), c, r);
+
+  // Act
+  channel_->SendCommand(std::move(packet));
+  auto last_command = std::move(hci_layer_->GetLastCommand()->command);
+  auto command_packet = GetPacketView(std::move(last_command));
+  hci::CommandPacketView packet_view = hci::CommandPacketView::Create(command_packet);
+
+  // Assert
+  ASSERT_TRUE(packet_view.IsValid());
+  ASSERT_EQ(OpCode::REMOTE_OOB_DATA_REQUEST_REPLY, packet_view.GetOpCode());
+}
+
+TEST_F(SecurityManagerChannelTest, send_remote_oob_data_request_neg_reply) {
+  // Arrange
+  auto packet = hci::RemoteOobDataRequestNegativeReplyBuilder::Create(device_.GetAddress());
+
+  // Act
+  channel_->SendCommand(std::move(packet));
+  auto last_command = std::move(hci_layer_->GetLastCommand()->command);
+  auto command_packet = GetPacketView(std::move(last_command));
+  hci::CommandPacketView packet_view = hci::CommandPacketView::Create(command_packet);
+
+  // Assert
+  ASSERT_TRUE(packet_view.IsValid());
+  ASSERT_EQ(OpCode::REMOTE_OOB_DATA_REQUEST_NEGATIVE_REPLY, packet_view.GetOpCode());
+}
+
+TEST_F(SecurityManagerChannelTest, send_read_local_oob_data) {
+  // Arrange
+  auto packet = hci::ReadLocalOobDataBuilder::Create();
+
+  // Act
+  channel_->SendCommand(std::move(packet));
+  auto last_command = std::move(hci_layer_->GetLastCommand()->command);
+  auto command_packet = GetPacketView(std::move(last_command));
+  hci::CommandPacketView packet_view = hci::CommandPacketView::Create(command_packet);
+
+  // Assert
+  ASSERT_TRUE(packet_view.IsValid());
+  ASSERT_EQ(OpCode::READ_LOCAL_OOB_DATA, packet_view.GetOpCode());
+}
+
+TEST_F(SecurityManagerChannelTest, send_read_local_oob_extended_data) {
+  // Arrange
+  auto packet = hci::ReadLocalOobExtendedDataBuilder::Create();
+
+  // Act
+  channel_->SendCommand(std::move(packet));
+  auto last_command = std::move(hci_layer_->GetLastCommand()->command);
+  auto command_packet = GetPacketView(std::move(last_command));
+  hci::CommandPacketView packet_view = hci::CommandPacketView::Create(command_packet);
+
+  // Assert
+  ASSERT_TRUE(packet_view.IsValid());
+  ASSERT_EQ(OpCode::READ_LOCAL_OOB_EXTENDED_DATA, packet_view.GetOpCode());
+}
+
+TEST_F(SecurityManagerChannelTest, recv_link_key_request) {
+  hci_layer_->IncomingEvent(hci::LinkKeyRequestBuilder::Create(device_.GetAddress()));
+  ASSERT_TRUE(callback_->receivedLinkKeyRequest);
+}
+
+TEST_F(SecurityManagerChannelTest, recv_link_key_notification) {
+  std::array<uint8_t, 16> link_key = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5};
+  hci_layer_->IncomingEvent(
+      hci::LinkKeyNotificationBuilder::Create(device_.GetAddress(), link_key, hci::KeyType::DEBUG_COMBINATION));
+  ASSERT_TRUE(callback_->receivedLinkKeyNotification);
+}
+
+TEST_F(SecurityManagerChannelTest, recv_master_link_key_complete) {
+  uint16_t connection_handle = 0x0;
+  hci_layer_->IncomingEvent(
+      hci::MasterLinkKeyCompleteBuilder::Create(hci::ErrorCode::SUCCESS, connection_handle, hci::KeyFlag::TEMPORARY));
+  ASSERT_TRUE(callback_->receivedMasterLinkKeyComplete);
+}
+
+TEST_F(SecurityManagerChannelTest, recv_change_connection_link_key_complete) {
+  uint16_t connection_handle = 0x0;
+  hci_layer_->IncomingEvent(
+      hci::ChangeConnectionLinkKeyCompleteBuilder::Create(hci::ErrorCode::SUCCESS, connection_handle));
+  ASSERT_TRUE(callback_->receivedChangeConnectionLinkKeyComplete);
+}
+
+TEST_F(SecurityManagerChannelTest, recv_return_link_keys) {
+  std::vector<hci::ZeroKeyAndAddress> keys;
+  hci_layer_->IncomingEvent(hci::ReturnLinkKeysBuilder::Create(keys));
+  ASSERT_TRUE(callback_->receivedReturnLinkKeys);
+}
+
+TEST_F(SecurityManagerChannelTest, send_link_key_request_reply) {
+  // Arrange
+  std::array<uint8_t, 16> link_key = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5};
+  auto packet = hci::LinkKeyRequestReplyBuilder::Create(device_.GetAddress(), link_key);
+
+  // Act
+  channel_->SendCommand(std::move(packet));
+  auto last_command = std::move(hci_layer_->GetLastCommand()->command);
+  auto command_packet = GetPacketView(std::move(last_command));
+  hci::CommandPacketView packet_view = hci::CommandPacketView::Create(command_packet);
+
+  // Assert
+  ASSERT_TRUE(packet_view.IsValid());
+  ASSERT_EQ(OpCode::LINK_KEY_REQUEST_REPLY, packet_view.GetOpCode());
+}
+
+TEST_F(SecurityManagerChannelTest, send_link_key_request_neg_reply) {
+  // Arrange
+  auto packet = hci::LinkKeyRequestNegativeReplyBuilder::Create(device_.GetAddress());
+
+  // Act
+  channel_->SendCommand(std::move(packet));
+  auto last_command = std::move(hci_layer_->GetLastCommand()->command);
+  auto command_packet = GetPacketView(std::move(last_command));
+  hci::CommandPacketView packet_view = hci::CommandPacketView::Create(command_packet);
+
+  // Assert
+  ASSERT_TRUE(packet_view.IsValid());
+  ASSERT_EQ(OpCode::LINK_KEY_REQUEST_NEGATIVE_REPLY, packet_view.GetOpCode());
+}
+
+TEST_F(SecurityManagerChannelTest, send_read_stored_link_key) {
+  // Arrange
+  auto packet = hci::ReadStoredLinkKeyBuilder::Create(device_.GetAddress(), hci::ReadStoredLinkKeyReadAllFlag::ALL);
+
+  // Act
+  channel_->SendCommand(std::move(packet));
+  auto last_command = std::move(hci_layer_->GetLastCommand()->command);
+  auto command_packet = GetPacketView(std::move(last_command));
+  hci::CommandPacketView packet_view = hci::CommandPacketView::Create(command_packet);
+
+  // Assert
+  ASSERT_TRUE(packet_view.IsValid());
+  ASSERT_EQ(OpCode::READ_STORED_LINK_KEY, packet_view.GetOpCode());
+}
+
+TEST_F(SecurityManagerChannelTest, send_write_stored_link_key) {
+  // Arrange
+  std::vector<hci::KeyAndAddress> keys_to_write;
+  auto packet = hci::WriteStoredLinkKeyBuilder::Create(keys_to_write);
+
+  // Act
+  channel_->SendCommand(std::move(packet));
+  auto last_command = std::move(hci_layer_->GetLastCommand()->command);
+  auto command_packet = GetPacketView(std::move(last_command));
+  hci::CommandPacketView packet_view = hci::CommandPacketView::Create(command_packet);
+
+  // Assert
+  ASSERT_TRUE(packet_view.IsValid());
+  ASSERT_EQ(OpCode::WRITE_STORED_LINK_KEY, packet_view.GetOpCode());
+}
+
+TEST_F(SecurityManagerChannelTest, send_delete_stored_link_key) {
+  // Arrange
+  auto packet =
+      hci::DeleteStoredLinkKeyBuilder::Create(device_.GetAddress(), hci::DeleteStoredLinkKeyDeleteAllFlag::ALL);
+
+  // Act
+  channel_->SendCommand(std::move(packet));
+  auto last_command = std::move(hci_layer_->GetLastCommand()->command);
+  auto command_packet = GetPacketView(std::move(last_command));
+  hci::CommandPacketView packet_view = hci::CommandPacketView::Create(command_packet);
+
+  // Assert
+  ASSERT_TRUE(packet_view.IsValid());
+  ASSERT_EQ(OpCode::DELETE_STORED_LINK_KEY, packet_view.GetOpCode());
+}
+
+TEST_F(SecurityManagerChannelTest, recv_encryption_key_refresh) {
+  uint16_t connection_handle = 0x0;
+  hci_layer_->IncomingEvent(
+      hci::EncryptionKeyRefreshCompleteBuilder::Create(hci::ErrorCode::SUCCESS, connection_handle));
+  ASSERT_TRUE(callback_->receivedEncryptionKeyRefreshComplete);
+}
+
+TEST_F(SecurityManagerChannelTest, send_refresh_encryption_key) {
+  // Arrange
+  uint16_t connection_handle = 0x0;
+  auto packet = hci::RefreshEncryptionKeyBuilder::Create(connection_handle);
+
+  // Act
+  channel_->SendCommand(std::move(packet));
+  auto last_command = std::move(hci_layer_->GetLastCommand()->command);
+  auto command_packet = GetPacketView(std::move(last_command));
+  hci::CommandPacketView packet_view = hci::CommandPacketView::Create(command_packet);
+
+  // Assert
+  ASSERT_TRUE(packet_view.IsValid());
+  ASSERT_EQ(OpCode::REFRESH_ENCRYPTION_KEY, packet_view.GetOpCode());
+}
+
+TEST_F(SecurityManagerChannelTest, send_read_encryption_key_size) {
+  // Arrange
+  uint16_t connection_handle = 0x0;
+  auto packet = hci::ReadEncryptionKeySizeBuilder::Create(connection_handle);
+
+  // Act
+  channel_->SendCommand(std::move(packet));
+  auto last_command = std::move(hci_layer_->GetLastCommand()->command);
+  auto command_packet = GetPacketView(std::move(last_command));
+  hci::CommandPacketView packet_view = hci::CommandPacketView::Create(command_packet);
+
+  // Assert
+  ASSERT_TRUE(packet_view.IsValid());
+  ASSERT_EQ(OpCode::READ_ENCRYPTION_KEY_SIZE, packet_view.GetOpCode());
+}
+
+TEST_F(SecurityManagerChannelTest, recv_simple_pairing_complete) {
+  hci_layer_->IncomingEvent(hci::SimplePairingCompleteBuilder::Create(hci::ErrorCode::SUCCESS, device_.GetAddress()));
+  ASSERT_TRUE(callback_->receivedSimplePairingComplete);
+}
+
+TEST_F(SecurityManagerChannelTest, send_read_simple_pairing_mode) {
+  // Arrange
+  auto packet = hci::ReadSimplePairingModeBuilder::Create();
+
+  // Act
+  channel_->SendCommand(std::move(packet));
+  auto last_command = std::move(hci_layer_->GetLastCommand()->command);
+  auto command_packet = GetPacketView(std::move(last_command));
+  hci::CommandPacketView packet_view = hci::CommandPacketView::Create(command_packet);
+
+  // Assert
+  ASSERT_TRUE(packet_view.IsValid());
+  ASSERT_EQ(OpCode::READ_SIMPLE_PAIRING_MODE, packet_view.GetOpCode());
+}
+
+TEST_F(SecurityManagerChannelTest, send_write_simple_pairing_mode) {
+  // Arrange
+  auto packet = hci::WriteSimplePairingModeBuilder::Create(hci::Enable::ENABLED);
+
+  // Act
+  channel_->SendCommand(std::move(packet));
+  auto last_command = std::move(hci_layer_->GetLastCommand()->command);
+  auto command_packet = GetPacketView(std::move(last_command));
+  hci::CommandPacketView packet_view = hci::CommandPacketView::Create(command_packet);
+
+  // Assert
+  ASSERT_TRUE(packet_view.IsValid());
+  ASSERT_EQ(OpCode::WRITE_SIMPLE_PAIRING_MODE, packet_view.GetOpCode());
+}
+
+TEST_F(SecurityManagerChannelTest, recv_keypress_notification) {
+  hci_layer_->IncomingEvent(
+      hci::KeypressNotificationBuilder::Create(device_.GetAddress(), hci::KeypressNotificationType::ENTRY_COMPLETED));
+  ASSERT_TRUE(callback_->receivedKeypressNotification);
+}
+
+TEST_F(SecurityManagerChannelTest, send_keypress_notification) {
+  // Arrange
+  auto packet =
+      hci::SendKeypressNotificationBuilder::Create(device_.GetAddress(), hci::KeypressNotificationType::ENTRY_STARTED);
+
+  // Act
+  channel_->SendCommand(std::move(packet));
+  auto last_command = std::move(hci_layer_->GetLastCommand()->command);
+  auto command_packet = GetPacketView(std::move(last_command));
+  hci::CommandPacketView packet_view = hci::CommandPacketView::Create(command_packet);
+
+  // Assert
+  ASSERT_TRUE(packet_view.IsValid());
+  ASSERT_EQ(OpCode::SEND_KEYPRESS_NOTIFICATION, packet_view.GetOpCode());
+}
+
+TEST_F(SecurityManagerChannelTest, recv_user_passkey_request) {
+  hci_layer_->IncomingEvent(hci::UserPasskeyRequestBuilder::Create(device_.GetAddress()));
+  ASSERT_TRUE(callback_->receivedUserPasskeyRequest);
+}
+
+}  // namespace
+}  // namespace channel
+}  // namespace security
+}  // namespace bluetooth
diff --git a/gd/security/ecc/multipoint_test.cc b/gd/security/ecc/multipoint_test.cc
new file mode 100644
index 0000000..95e43d4
--- /dev/null
+++ b/gd/security/ecc/multipoint_test.cc
@@ -0,0 +1,112 @@
+/******************************************************************************
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#include <gtest/gtest.h>
+
+#include "security/ecc/p_256_ecc_pp.h"
+
+namespace bluetooth {
+namespace security {
+namespace ecc {
+
+// Test ECC point validation
+TEST(SmpEccValidationTest, test_valid_points) {
+  Point p;
+
+  // Test data from Bluetooth Core Specification
+  // Version 5.0 | Vol 2, Part G | 7.1.2
+
+  // Sample 1
+  p.x[7] = 0x20b003d2;
+  p.x[6] = 0xf297be2c;
+  p.x[5] = 0x5e2c83a7;
+  p.x[4] = 0xe9f9a5b9;
+  p.x[3] = 0xeff49111;
+  p.x[2] = 0xacf4fddb;
+  p.x[1] = 0xcc030148;
+  p.x[0] = 0x0e359de6;
+
+  p.y[7] = 0xdc809c49;
+  p.y[6] = 0x652aeb6d;
+  p.y[5] = 0x63329abf;
+  p.y[4] = 0x5a52155c;
+  p.y[3] = 0x766345c2;
+  p.y[2] = 0x8fed3024;
+  p.y[1] = 0x741c8ed0;
+  p.y[0] = 0x1589d28b;
+
+  EXPECT_TRUE(ECC_ValidatePoint(p));
+
+  // Sample 2
+  p.x[7] = 0x2c31a47b;
+  p.x[6] = 0x5779809e;
+  p.x[5] = 0xf44cb5ea;
+  p.x[4] = 0xaf5c3e43;
+  p.x[3] = 0xd5f8faad;
+  p.x[2] = 0x4a8794cb;
+  p.x[1] = 0x987e9b03;
+  p.x[0] = 0x745c78dd;
+
+  p.y[7] = 0x91951218;
+  p.y[6] = 0x3898dfbe;
+  p.y[5] = 0xcd52e240;
+  p.y[4] = 0x8e43871f;
+  p.y[3] = 0xd0211091;
+  p.y[2] = 0x17bd3ed4;
+  p.y[1] = 0xeaf84377;
+  p.y[0] = 0x43715d4f;
+
+  EXPECT_TRUE(ECC_ValidatePoint(p));
+}
+
+TEST(SmpEccValidationTest, test_invalid_points) {
+  Point p;
+  multiprecision_init(p.x);
+  multiprecision_init(p.y);
+
+  EXPECT_FALSE(ECC_ValidatePoint(p));
+
+  // Sample 1
+  p.x[7] = 0x20b003d2;
+  p.x[6] = 0xf297be2c;
+  p.x[5] = 0x5e2c83a7;
+  p.x[4] = 0xe9f9a5b9;
+  p.x[3] = 0xeff49111;
+  p.x[2] = 0xacf4fddb;
+  p.x[1] = 0xcc030148;
+  p.x[0] = 0x0e359de6;
+
+  EXPECT_FALSE(ECC_ValidatePoint(p));
+
+  p.y[7] = 0xdc809c49;
+  p.y[6] = 0x652aeb6d;
+  p.y[5] = 0x63329abf;
+  p.y[4] = 0x5a52155c;
+  p.y[3] = 0x766345c2;
+  p.y[2] = 0x8fed3024;
+  p.y[1] = 0x741c8ed0;
+  p.y[0] = 0x1589d28b;
+
+  p.y[0]--;
+
+  EXPECT_FALSE(ECC_ValidatePoint(p));
+}
+
+}  // namespace ecc
+}  // namespace security
+}  // namespace bluetooth
diff --git a/gd/security/ecc/multprecision.cc b/gd/security/ecc/multprecision.cc
new file mode 100644
index 0000000..c2583ed
--- /dev/null
+++ b/gd/security/ecc/multprecision.cc
@@ -0,0 +1,504 @@
+/******************************************************************************
+ *
+ *  Copyright 2006-2015 Broadcom Corporation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+/*******************************************************************************
+ *
+ *  This file contains simple pairing algorithms
+ *
+ ******************************************************************************/
+
+#include "security/ecc/multprecision.h"
+#include <string.h>
+
+namespace bluetooth {
+namespace security {
+namespace ecc {
+
+#define DWORD_BITS 32
+#define DWORD_BYTES 4
+#define DWORD_BITS_SHIFT 5
+
+void multiprecision_init(uint32_t* c) {
+  for (uint32_t i = 0; i < KEY_LENGTH_DWORDS_P256; i++) c[i] = 0;
+}
+
+void multiprecision_copy(uint32_t* c, const uint32_t* a) {
+  for (uint32_t i = 0; i < KEY_LENGTH_DWORDS_P256; i++) c[i] = a[i];
+}
+
+int multiprecision_compare(const uint32_t* a, const uint32_t* b) {
+  for (int i = KEY_LENGTH_DWORDS_P256 - 1; i >= 0; i--) {
+    if (a[i] > b[i]) return 1;
+    if (a[i] < b[i]) return -1;
+  }
+  return 0;
+}
+
+int multiprecision_iszero(const uint32_t* a) {
+  for (uint32_t i = 0; i < KEY_LENGTH_DWORDS_P256; i++)
+    if (a[i]) return 0;
+
+  return 1;
+}
+
+uint32_t multiprecision_dword_bits(uint32_t a) {
+  uint32_t i;
+  for (i = 0; i < DWORD_BITS; i++, a >>= 1)
+    if (a == 0) break;
+
+  return i;
+}
+
+uint32_t multiprecision_most_signdwords(const uint32_t* a) {
+  int i;
+  for (i = KEY_LENGTH_DWORDS_P256 - 1; i >= 0; i--)
+    if (a[i]) break;
+  return (i + 1);
+}
+
+uint32_t multiprecision_most_signbits(const uint32_t* a) {
+  int aMostSignDWORDs;
+
+  aMostSignDWORDs = multiprecision_most_signdwords(a);
+  if (aMostSignDWORDs == 0) return 0;
+
+  return (((aMostSignDWORDs - 1) << DWORD_BITS_SHIFT) + multiprecision_dword_bits(a[aMostSignDWORDs - 1]));
+}
+
+uint32_t multiprecision_add(uint32_t* c, const uint32_t* a, const uint32_t* b) {
+  uint32_t carrier;
+  uint32_t temp;
+
+  carrier = 0;
+  for (uint32_t i = 0; i < KEY_LENGTH_DWORDS_P256; i++) {
+    temp = a[i] + carrier;
+    carrier = (temp < carrier);
+    temp += b[i];
+    carrier |= (temp < b[i]);
+    c[i] = temp;
+  }
+
+  return carrier;
+}
+
+// c=a-b
+uint32_t multiprecision_sub(uint32_t* c, const uint32_t* a, const uint32_t* b) {
+  uint32_t borrow;
+  uint32_t temp;
+
+  borrow = 0;
+  for (uint32_t i = 0; i < KEY_LENGTH_DWORDS_P256; i++) {
+    temp = a[i] - borrow;
+    borrow = (temp > a[i]);
+    c[i] = temp - b[i];
+    borrow |= (c[i] > temp);
+  }
+
+  return borrow;
+}
+
+// c = a << 1
+void multiprecision_lshift_mod(uint32_t* c, const uint32_t* a, const uint32_t* modp) {
+  uint32_t carrier = multiprecision_lshift(c, a);
+  if (carrier) {
+    multiprecision_sub(c, c, modp);
+  } else if (multiprecision_compare(c, modp) >= 0) {
+    multiprecision_sub(c, c, modp);
+  }
+}
+
+// c=a>>1
+void multiprecision_rshift(uint32_t* c, const uint32_t* a) {
+  int j;
+  uint32_t b = 1;
+
+  j = DWORD_BITS - b;
+
+  uint32_t carrier = 0;
+  uint32_t temp;
+  for (int i = KEY_LENGTH_DWORDS_P256 - 1; i >= 0; i--) {
+    temp = a[i];  // in case of c==a
+    c[i] = (temp >> b) | carrier;
+    carrier = temp << j;
+  }
+}
+
+// Curve specific optimization when p is a pseudo-Mersenns prime,
+// p=2^(KEY_LENGTH_BITS)-omega
+void multiprecision_mersenns_mult_mod(uint32_t* c, const uint32_t* a, const uint32_t* b, const uint32_t* modp) {
+  uint32_t cc[2 * KEY_LENGTH_DWORDS_P256];
+
+  multiprecision_mult(cc, a, b);
+  multiprecision_fast_mod_P256(c, cc, modp);
+}
+
+// Curve specific optimization when p is a pseudo-Mersenns prime
+void multiprecision_mersenns_squa_mod(uint32_t* c, const uint32_t* a, const uint32_t* modp) {
+  multiprecision_mersenns_mult_mod(c, a, a, modp);
+}
+
+// c=(a+b) mod p, b<p, a<p
+void multiprecision_add_mod(uint32_t* c, const uint32_t* a, const uint32_t* b, const uint32_t* modp) {
+  uint32_t carrier = multiprecision_add(c, a, b);
+  if (carrier) {
+    multiprecision_sub(c, c, modp);
+  } else if (multiprecision_compare(c, modp) >= 0) {
+    multiprecision_sub(c, c, modp);
+  }
+}
+
+// c=(a-b) mod p, a<p, b<p
+void multiprecision_sub_mod(uint32_t* c, const uint32_t* a, const uint32_t* b, const uint32_t* modp) {
+  uint32_t borrow;
+
+  borrow = multiprecision_sub(c, a, b);
+  if (borrow) multiprecision_add(c, c, modp);
+}
+
+// c=a<<b, b<DWORD_BITS, c has a buffer size of Numuint32_ts+1
+uint32_t multiprecision_lshift(uint32_t* c, const uint32_t* a) {
+  int j;
+  uint32_t b = 1;
+  j = DWORD_BITS - b;
+
+  uint32_t carrier = 0;
+  uint32_t temp;
+
+  for (uint32_t i = 0; i < KEY_LENGTH_DWORDS_P256; i++) {
+    temp = a[i];  // in case c==a
+    c[i] = (temp << b) | carrier;
+    carrier = temp >> j;
+  }
+
+  return carrier;
+}
+
+// c=a*b; c must have a buffer of 2*Key_LENGTH_uint32_tS, c != a != b
+void multiprecision_mult(uint32_t* c, const uint32_t* a, const uint32_t* b) {
+  uint32_t W;
+  uint32_t U;
+  uint32_t V;
+
+  U = V = W = 0;
+  multiprecision_init(c);
+
+  // assume little endian right now
+  for (uint32_t i = 0; i < KEY_LENGTH_DWORDS_P256; i++) {
+    U = 0;
+    for (uint32_t j = 0; j < KEY_LENGTH_DWORDS_P256; j++) {
+      uint64_t result;
+      result = ((uint64_t)a[i]) * ((uint64_t)b[j]);
+      W = result >> 32;
+      V = a[i] * b[j];
+      V = V + U;
+      U = (V < U);
+      U += W;
+      V = V + c[i + j];
+      U += (V < c[i + j]);
+      c[i + j] = V;
+    }
+    c[i + KEY_LENGTH_DWORDS_P256] = U;
+  }
+}
+
+void multiprecision_fast_mod_P256(uint32_t* c, const uint32_t* a, const uint32_t* modp) {
+  uint32_t A;
+  uint32_t B;
+  uint32_t C;
+  uint32_t D;
+  uint32_t E;
+  uint32_t F;
+  uint32_t G;
+  uint8_t UA;
+  uint8_t UB;
+  uint8_t UC;
+  uint8_t UD;
+  uint8_t UE;
+  uint8_t UF;
+  uint8_t UG;
+  uint32_t U;
+
+  // C = a[13] + a[14] + a[15];
+  C = a[13];
+  C += a[14];
+  UC = (C < a[14]);
+  C += a[15];
+  UC += (C < a[15]);
+
+  // E = a[8] + a[9];
+  E = a[8];
+  E += a[9];
+  UE = (E < a[9]);
+
+  // F = a[9] + a[10];
+  F = a[9];
+  F += a[10];
+  UF = (F < a[10]);
+
+  // G = a[10] + a[11]
+  G = a[10];
+  G += a[11];
+  UG = (G < a[11]);
+
+  // B = a[12] + a[13] + a[14] + a[15] == C + a[12]
+  B = C;
+  UB = UC;
+  B += a[12];
+  UB += (B < a[12]);
+
+  // A = a[11] + a[12] + a[13] + a[14] == B + a[11] - a[15]
+  A = B;
+  UA = UB;
+  A += a[11];
+  UA += (A < a[11]);
+  UA -= (A < a[15]);
+  A -= a[15];
+
+  // D = a[10] + a[11] + a[12] + a[13] == A + a[10] - a[14]
+  D = A;
+  UD = UA;
+  D += a[10];
+  UD += (D < a[10]);
+  UD -= (D < a[14]);
+  D -= a[14];
+
+  c[0] = a[0];
+  c[0] += E;
+  U = (c[0] < E);
+  U += UE;
+  U -= (c[0] < A);
+  U -= UA;
+  c[0] -= A;
+
+  if (U & 0x80000000) {
+    uint32_t UU;
+    UU = 0 - U;
+    U = (a[1] < UU);
+    c[1] = a[1] - UU;
+  } else {
+    c[1] = a[1] + U;
+    U = (c[1] < a[1]);
+  }
+
+  c[1] += F;
+  U += (c[1] < F);
+  U += UF;
+  U -= (c[1] < B);
+  U -= UB;
+  c[1] -= B;
+
+  if (U & 0x80000000) {
+    uint32_t UU;
+    UU = 0 - U;
+    U = (a[2] < UU);
+    c[2] = a[2] - UU;
+  } else {
+    c[2] = a[2] + U;
+    U = (c[2] < a[2]);
+  }
+
+  c[2] += G;
+  U += (c[2] < G);
+  U += UG;
+  U -= (c[2] < C);
+  U -= UC;
+  c[2] -= C;
+
+  if (U & 0x80000000) {
+    uint32_t UU;
+    UU = 0 - U;
+    U = (a[3] < UU);
+    c[3] = a[3] - UU;
+  } else {
+    c[3] = a[3] + U;
+    U = (c[3] < a[3]);
+  }
+
+  c[3] += A;
+  U += (c[3] < A);
+  U += UA;
+  c[3] += a[11];
+  U += (c[3] < a[11]);
+  c[3] += a[12];
+  U += (c[3] < a[12]);
+  U -= (c[3] < a[14]);
+  c[3] -= a[14];
+  U -= (c[3] < a[15]);
+  c[3] -= a[15];
+  U -= (c[3] < E);
+  U -= UE;
+  c[3] -= E;
+
+  if (U & 0x80000000) {
+    uint32_t UU;
+    UU = 0 - U;
+    U = (a[4] < UU);
+    c[4] = a[4] - UU;
+  } else {
+    c[4] = a[4] + U;
+    U = (c[4] < a[4]);
+  }
+
+  c[4] += B;
+  U += (c[4] < B);
+  U += UB;
+  U -= (c[4] < a[15]);
+  c[4] -= a[15];
+  c[4] += a[12];
+  U += (c[4] < a[12]);
+  c[4] += a[13];
+  U += (c[4] < a[13]);
+  U -= (c[4] < F);
+  U -= UF;
+  c[4] -= F;
+
+  if (U & 0x80000000) {
+    uint32_t UU;
+    UU = 0 - U;
+    U = (a[5] < UU);
+    c[5] = a[5] - UU;
+  } else {
+    c[5] = a[5] + U;
+    U = (c[5] < a[5]);
+  }
+
+  c[5] += C;
+  U += (c[5] < C);
+  U += UC;
+  c[5] += a[13];
+  U += (c[5] < a[13]);
+  c[5] += a[14];
+  U += (c[5] < a[14]);
+  U -= (c[5] < G);
+  U -= UG;
+  c[5] -= G;
+
+  if (U & 0x80000000) {
+    uint32_t UU;
+    UU = 0 - U;
+    U = (a[6] < UU);
+    c[6] = a[6] - UU;
+  } else {
+    c[6] = a[6] + U;
+    U = (c[6] < a[6]);
+  }
+
+  c[6] += C;
+  U += (c[6] < C);
+  U += UC;
+  c[6] += a[14];
+  U += (c[6] < a[14]);
+  c[6] += a[14];
+  U += (c[6] < a[14]);
+  c[6] += a[15];
+  U += (c[6] < a[15]);
+  U -= (c[6] < E);
+  U -= UE;
+  c[6] -= E;
+
+  if (U & 0x80000000) {
+    uint32_t UU;
+    UU = 0 - U;
+    U = (a[7] < UU);
+    c[7] = a[7] - UU;
+  } else {
+    c[7] = a[7] + U;
+    U = (c[7] < a[7]);
+  }
+
+  c[7] += a[15];
+  U += (c[7] < a[15]);
+  c[7] += a[15];
+  U += (c[7] < a[15]);
+  c[7] += a[15];
+  U += (c[7] < a[15]);
+  c[7] += a[8];
+  U += (c[7] < a[8]);
+  U -= (c[7] < D);
+  U -= UD;
+  c[7] -= D;
+
+  if (U & 0x80000000) {
+    while (U) {
+      multiprecision_add(c, c, modp);
+      U++;
+    }
+  } else if (U) {
+    while (U) {
+      multiprecision_sub(c, c, modp);
+      U--;
+    }
+  }
+
+  if (multiprecision_compare(c, modp) >= 0) multiprecision_sub(c, c, modp);
+}
+
+void multiprecision_inv_mod(uint32_t* aminus, uint32_t* u, const uint32_t* modp) {
+  uint32_t v[KEY_LENGTH_DWORDS_P256];
+  uint32_t A[KEY_LENGTH_DWORDS_P256 + 1];
+  uint32_t C[KEY_LENGTH_DWORDS_P256 + 1];
+
+  multiprecision_copy(v, modp);
+  multiprecision_init(A);
+  multiprecision_init(C);
+  A[0] = 1;
+
+  while (!multiprecision_iszero(u)) {
+    while (!(u[0] & 0x01))  // u is even
+    {
+      multiprecision_rshift(u, u);
+      if (!(A[0] & 0x01))  // A is even
+        multiprecision_rshift(A, A);
+      else {
+        A[KEY_LENGTH_DWORDS_P256] = multiprecision_add(A, A, modp);  // A =A+p
+        multiprecision_rshift(A, A);
+        A[KEY_LENGTH_DWORDS_P256 - 1] |= (A[KEY_LENGTH_DWORDS_P256] << 31);
+      }
+    }
+
+    while (!(v[0] & 0x01))  // v is even
+    {
+      multiprecision_rshift(v, v);
+      if (!(C[0] & 0x01))  // C is even
+      {
+        multiprecision_rshift(C, C);
+      } else {
+        C[KEY_LENGTH_DWORDS_P256] = multiprecision_add(C, C, modp);  // C =C+p
+        multiprecision_rshift(C, C);
+        C[KEY_LENGTH_DWORDS_P256 - 1] |= (C[KEY_LENGTH_DWORDS_P256] << 31);
+      }
+    }
+
+    if (multiprecision_compare(u, v) >= 0) {
+      multiprecision_sub(u, u, v);
+      multiprecision_sub_mod(A, A, C, modp);
+    } else {
+      multiprecision_sub(v, v, u);
+      multiprecision_sub_mod(C, C, A, modp);
+    }
+  }
+
+  if (multiprecision_compare(C, modp) >= 0)
+    multiprecision_sub(aminus, C, modp);
+  else
+    multiprecision_copy(aminus, C);
+}
+
+}  // namespace ecc
+}  // namespace security
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/security/ecc/multprecision.h b/gd/security/ecc/multprecision.h
new file mode 100644
index 0000000..ad149cd
--- /dev/null
+++ b/gd/security/ecc/multprecision.h
@@ -0,0 +1,56 @@
+/******************************************************************************
+ *
+ *  Copyright 2006-2015 Broadcom Corporation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+/*******************************************************************************
+ *
+ *  This file contains simple pairing algorithms
+ *
+ ******************************************************************************/
+#pragma once
+
+#include <cstdint>
+
+namespace bluetooth {
+namespace security {
+namespace ecc {
+
+#define KEY_LENGTH_DWORDS_P256 8
+/* Arithmetic Operations*/
+int multiprecision_compare(const uint32_t* a, const uint32_t* b);
+int multiprecision_iszero(const uint32_t* a);
+void multiprecision_init(uint32_t* c);
+void multiprecision_copy(uint32_t* c, const uint32_t* a);
+uint32_t multiprecision_dword_bits(uint32_t a);
+uint32_t multiprecision_most_signdwords(const uint32_t* a);
+uint32_t multiprecision_most_signbits(const uint32_t* a);
+void multiprecision_inv_mod(uint32_t* aminus, uint32_t* a, const uint32_t* modp);
+uint32_t multiprecision_add(uint32_t* c, const uint32_t* a, const uint32_t* b);  // c=a+b
+void multiprecision_add_mod(uint32_t* c, const uint32_t* a, const uint32_t* b, const uint32_t* modp);
+uint32_t multiprecision_sub(uint32_t* c, const uint32_t* a, const uint32_t* b);  // c=a-b
+void multiprecision_sub_mod(uint32_t* c, const uint32_t* a, const uint32_t* b, const uint32_t* modp);
+void multiprecision_rshift(uint32_t* c, const uint32_t* a);                            // c=a>>1, return carrier
+void multiprecision_lshift_mod(uint32_t* c, const uint32_t* a, const uint32_t* modp);  // c=a<<b, return carrier
+uint32_t multiprecision_lshift(uint32_t* c, const uint32_t* a);
+void multiprecision_mult(uint32_t* c, const uint32_t* a, const uint32_t* b);  // c=a*b
+void multiprecision_mersenns_mult_mod(uint32_t* c, const uint32_t* a, const uint32_t* b, const uint32_t* modp);
+void multiprecision_mersenns_squa_mod(uint32_t* c, const uint32_t* a, const uint32_t* modp);
+void multiprecision_fast_mod_P256(uint32_t* c, const uint32_t* a, const uint32_t* modp);
+
+}  // namespace ecc
+}  // namespace security
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/security/ecc/p_256_ecc_pp.cc b/gd/security/ecc/p_256_ecc_pp.cc
new file mode 100644
index 0000000..ecb805c
--- /dev/null
+++ b/gd/security/ecc/p_256_ecc_pp.cc
@@ -0,0 +1,264 @@
+/******************************************************************************
+ *
+ *  Copyright 2006-2015 Broadcom Corporation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+/*******************************************************************************
+ *
+ *  This file contains simple pairing algorithms using Elliptic Curve
+ *  Cryptography for private public key
+ *
+ ******************************************************************************/
+#include "security/ecc/p_256_ecc_pp.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "security/ecc/multprecision.h"
+
+namespace bluetooth {
+namespace security {
+namespace ecc {
+
+const uint32_t* modp = curve_p256.p;
+
+static void p_256_init_point(Point* q) {
+  memset(q, 0, sizeof(Point));
+}
+
+static void p_256_copy_point(Point* q, const Point* p) {
+  memcpy(q, p, sizeof(Point));
+}
+
+// q=2q
+static void ECC_Double(Point* q, const Point* p) {
+  uint32_t t1[KEY_LENGTH_DWORDS_P256];
+  uint32_t t2[KEY_LENGTH_DWORDS_P256];
+  uint32_t t3[KEY_LENGTH_DWORDS_P256];
+  const uint32_t* x1;
+  uint32_t* x3;
+  const uint32_t* y1;
+  uint32_t* y3;
+  const uint32_t* z1;
+  uint32_t* z3;
+
+  if (multiprecision_iszero(p->z)) {
+    multiprecision_init(q->z);
+    return;  // return infinity
+  }
+
+  x1 = p->x;
+  y1 = p->y;
+  z1 = p->z;
+  x3 = q->x;
+  y3 = q->y;
+  z3 = q->z;
+
+  multiprecision_mersenns_squa_mod(t1, z1, modp);      // t1=z1^2
+  multiprecision_sub_mod(t2, x1, t1, modp);            // t2=x1-t1
+  multiprecision_add_mod(t1, x1, t1, modp);            // t1=x1+t1
+  multiprecision_mersenns_mult_mod(t2, t1, t2, modp);  // t2=t2*t1
+  multiprecision_lshift_mod(t3, t2, modp);
+  multiprecision_add_mod(t2, t3, t2, modp);  // t2=3t2
+
+  multiprecision_mersenns_mult_mod(z3, y1, z1, modp);  // z3=y1*z1
+  multiprecision_lshift_mod(z3, z3, modp);
+
+  multiprecision_mersenns_squa_mod(y3, y1, modp);  // y3=y1^2
+  multiprecision_lshift_mod(y3, y3, modp);
+  multiprecision_mersenns_mult_mod(t3, y3, x1, modp);  // t3=y3*x1=x1*y1^2
+  multiprecision_lshift_mod(t3, t3, modp);
+  multiprecision_mersenns_squa_mod(y3, y3, modp);  // y3=y3^2=y1^4
+  multiprecision_lshift_mod(y3, y3, modp);
+
+  multiprecision_mersenns_squa_mod(x3, t2, modp);      // x3=t2^2
+  multiprecision_lshift_mod(t1, t3, modp);             // t1=2t3
+  multiprecision_sub_mod(x3, x3, t1, modp);            // x3=x3-t1
+  multiprecision_sub_mod(t1, t3, x3, modp);            // t1=t3-x3
+  multiprecision_mersenns_mult_mod(t1, t1, t2, modp);  // t1=t1*t2
+  multiprecision_sub_mod(y3, t1, y3, modp);            // y3=t1-y3
+}
+
+// q=q+p,     zp must be 1
+static void ECC_Add(Point* r, Point* p, const Point* q) {
+  uint32_t t1[KEY_LENGTH_DWORDS_P256];
+  uint32_t t2[KEY_LENGTH_DWORDS_P256];
+  uint32_t* x1;
+  const uint32_t* x2;
+  uint32_t* x3;
+  uint32_t* y1;
+  const uint32_t* y2;
+  uint32_t* y3;
+  uint32_t* z1;
+  const uint32_t* z2;
+  uint32_t* z3;
+
+  x1 = p->x;
+  y1 = p->y;
+  z1 = p->z;
+  x2 = q->x;
+  y2 = q->y;
+  z2 = q->z;
+  x3 = r->x;
+  y3 = r->y;
+  z3 = r->z;
+
+  // if Q=infinity, return p
+  if (multiprecision_iszero(z2)) {
+    p_256_copy_point(r, p);
+    return;
+  }
+
+  // if P=infinity, return q
+  if (multiprecision_iszero(z1)) {
+    p_256_copy_point(r, q);
+    return;
+  }
+
+  multiprecision_mersenns_squa_mod(t1, z1, modp);      // t1=z1^2
+  multiprecision_mersenns_mult_mod(t2, z1, t1, modp);  // t2=t1*z1
+  multiprecision_mersenns_mult_mod(t1, x2, t1, modp);  // t1=t1*x2
+  multiprecision_mersenns_mult_mod(t2, y2, t2, modp);  // t2=t2*y2
+
+  multiprecision_sub_mod(t1, t1, x1, modp);  // t1=t1-x1
+  multiprecision_sub_mod(t2, t2, y1, modp);  // t2=t2-y1
+
+  if (multiprecision_iszero(t1)) {
+    if (multiprecision_iszero(t2)) {
+      ECC_Double(r, q);
+      return;
+    } else {
+      multiprecision_init(z3);
+      return;  // return infinity
+    }
+  }
+
+  multiprecision_mersenns_mult_mod(z3, z1, t1, modp);  // z3=z1*t1
+  multiprecision_mersenns_squa_mod(y3, t1, modp);      // t3=t1^2
+  multiprecision_mersenns_mult_mod(z1, y3, t1, modp);  // t4=t3*t1
+  multiprecision_mersenns_mult_mod(y3, y3, x1, modp);  // t3=t3*x1
+  multiprecision_lshift_mod(t1, y3, modp);             // t1=2*t3
+  multiprecision_mersenns_squa_mod(x3, t2, modp);      // x3=t2^2
+  multiprecision_sub_mod(x3, x3, t1, modp);            // x3=x3-t1
+  multiprecision_sub_mod(x3, x3, z1, modp);            // x3=x3-t4
+  multiprecision_sub_mod(y3, y3, x3, modp);            // t3=t3-x3
+  multiprecision_mersenns_mult_mod(y3, y3, t2, modp);  // t3=t3*t2
+  multiprecision_mersenns_mult_mod(z1, z1, y1, modp);  // t4=t4*t1
+  multiprecision_sub_mod(y3, y3, z1, modp);
+}
+
+// Computing the Non-Adjacent Form of a positive integer
+static void ECC_NAF(uint8_t* naf, uint32_t* NumNAF, uint32_t* k) {
+  uint32_t sign;
+  int i = 0;
+  int j;
+  uint32_t var;
+
+  while ((var = multiprecision_most_signbits(k)) >= 1) {
+    if (k[0] & 0x01)  // k is odd
+    {
+      sign = (k[0] & 0x03);  // 1 or 3
+
+      // k = k-naf[i]
+      if (sign == 1)
+        k[0] = k[0] & 0xFFFFFFFE;
+      else {
+        k[0] = k[0] + 1;
+        if (k[0] == 0)  // overflow
+        {
+          j = 1;
+          do {
+            k[j]++;
+          } while (k[j++] == 0);  // overflow
+        }
+      }
+    } else
+      sign = 0;
+
+    multiprecision_rshift(k, k);
+    naf[i / 4] |= (sign) << ((i % 4) * 2);
+    i++;
+  }
+
+  *NumNAF = i;
+}
+
+// Binary Non-Adjacent Form for point multiplication
+void ECC_PointMult_Bin_NAF(Point* q, const Point* p, uint32_t* n) {
+  uint32_t sign;
+  uint8_t naf[256 / 4 + 1];
+  uint32_t NumNaf;
+  Point minus_p;
+  Point r;
+
+  p_256_init_point(&r);
+
+  // initialization
+  p_256_init_point(q);
+
+  // -p
+  multiprecision_copy(minus_p.x, p->x);
+  multiprecision_sub(minus_p.y, modp, p->y);
+
+  multiprecision_init(minus_p.z);
+  minus_p.z[0] = 1;
+
+  // NAF
+  memset(naf, 0, sizeof(naf));
+  ECC_NAF(naf, &NumNaf, n);
+
+  for (int i = NumNaf - 1; i >= 0; i--) {
+    p_256_copy_point(&r, q);
+    ECC_Double(q, &r);
+    sign = (naf[i / 4] >> ((i % 4) * 2)) & 0x03;
+
+    if (sign == 1) {
+      p_256_copy_point(&r, q);
+      ECC_Add(q, &r, p);
+    } else if (sign == 3) {
+      p_256_copy_point(&r, q);
+      ECC_Add(q, &r, &minus_p);
+    }
+  }
+
+  multiprecision_inv_mod(minus_p.x, q->z, modp);
+  multiprecision_mersenns_squa_mod(q->z, minus_p.x, modp);
+  multiprecision_mersenns_mult_mod(q->x, q->x, q->z, modp);
+  multiprecision_mersenns_mult_mod(q->z, q->z, minus_p.x, modp);
+  multiprecision_mersenns_mult_mod(q->y, q->y, q->z, modp);
+}
+
+bool ECC_ValidatePoint(const Point& pt) {
+  // Ensure y^2 = x^3 + a*x + b (mod p); a = -3
+
+  // y^2 mod p
+  uint32_t y2_mod[KEY_LENGTH_DWORDS_P256] = {0};
+  multiprecision_mersenns_squa_mod(y2_mod, (uint32_t*)pt.y, modp);
+
+  // Right hand side calculation
+  uint32_t rhs[KEY_LENGTH_DWORDS_P256] = {0};
+  multiprecision_mersenns_squa_mod(rhs, (uint32_t*)pt.x, modp);
+  uint32_t three[KEY_LENGTH_DWORDS_P256] = {0};
+  three[0] = 3;
+  multiprecision_sub_mod(rhs, rhs, three, modp);
+  multiprecision_mersenns_mult_mod(rhs, rhs, (uint32_t*)pt.x, modp);
+  multiprecision_add_mod(rhs, rhs, curve_p256.b, modp);
+
+  return multiprecision_compare(rhs, y2_mod) == 0;
+}
+
+}  // namespace ecc
+}  // namespace security
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/security/ecc/p_256_ecc_pp.h b/gd/security/ecc/p_256_ecc_pp.h
new file mode 100644
index 0000000..54c7245
--- /dev/null
+++ b/gd/security/ecc/p_256_ecc_pp.h
@@ -0,0 +1,76 @@
+/******************************************************************************
+ *
+ *  Copyright 2006-2015 Broadcom Corporation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+/******************************************************************************
+ *
+ *  This file contains simple pairing algorithms using Elliptic Curve
+ *Cryptography for private public key
+ *
+ ******************************************************************************/
+
+#pragma once
+
+#include "security/ecc/multprecision.h"
+
+namespace bluetooth {
+namespace security {
+namespace ecc {
+
+struct Point {
+  uint32_t x[KEY_LENGTH_DWORDS_P256];
+  uint32_t y[KEY_LENGTH_DWORDS_P256];
+  uint32_t z[KEY_LENGTH_DWORDS_P256];
+};
+
+struct elliptic_curve_t {
+  // curve's coefficients
+  uint32_t a[KEY_LENGTH_DWORDS_P256];
+  uint32_t b[KEY_LENGTH_DWORDS_P256];
+
+  // prime modulus
+  uint32_t p[KEY_LENGTH_DWORDS_P256];
+
+  // Omega, p = 2^m -omega
+  uint32_t omega[KEY_LENGTH_DWORDS_P256];
+
+  // base point, a point on E of order r
+  Point G;
+};
+
+// P-256 elliptic curve, as per BT Spec 5.1 Vol 2, Part H 7.6
+static constexpr elliptic_curve_t curve_p256{
+    .a = {0},
+    .b = {0x27d2604b, 0x3bce3c3e, 0xcc53b0f6, 0x651d06b0, 0x769886bc, 0xb3ebbd55, 0xaa3a93e7, 0x5ac635d8},
+    .p = {0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x0, 0x0, 0x0, 0x00000001, 0xFFFFFFFF},
+    .omega = {0},
+
+    .G = {.x = {0xd898c296, 0xf4a13945, 0x2deb33a0, 0x77037d81, 0x63a440f2, 0xf8bce6e5, 0xe12c4247, 0x6b17d1f2},
+          .y = {0x37bf51f5, 0xcbb64068, 0x6b315ece, 0x2bce3357, 0x7c0f9e16, 0x8ee7eb4a, 0xfe1a7f9b, 0x4fe342e2},
+          .z = {0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}},
+};
+
+/* This function checks that point is on the elliptic curve*/
+bool ECC_ValidatePoint(const Point& point);
+
+void ECC_PointMult_Bin_NAF(Point* q, const Point* p, uint32_t* n);
+
+#define ECC_PointMult(q, p, n) ECC_PointMult_Bin_NAF(q, p, n)
+
+}  // namespace ecc
+}  // namespace security
+}  // namespace bluetooth
diff --git a/gd/security/ecdh_keys.cc b/gd/security/ecdh_keys.cc
new file mode 100644
index 0000000..951654c
--- /dev/null
+++ b/gd/security/ecdh_keys.cc
@@ -0,0 +1,93 @@
+/******************************************************************************
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#include "security/ecdh_keys.h"
+
+/**********************************************************************************************************************
+ TODO: We should have random number management in separate file, and we
+       should honour all the random number requirements from the spec!!
+**********************************************************************************************************************/
+#include <chrono>
+#include <cstdlib>
+
+#include "security/ecc/p_256_ecc_pp.h"
+
+namespace {
+
+static bool srand_initiated = false;
+
+template <size_t SIZE>
+static std::array<uint8_t, SIZE> GenerateRandom() {
+  if (!srand_initiated) {
+    srand_initiated = true;
+    // TODO:  We need a proper  random number generator here.
+    // use current time as seed for random generator
+    std::srand(std::time(nullptr));
+  }
+
+  std::array<uint8_t, SIZE> r;
+  for (size_t i = 0; i < SIZE; i++) r[i] = std::rand();
+  return r;
+}
+}  // namespace
+/*********************************************************************************************************************/
+
+namespace bluetooth {
+namespace security {
+
+std::pair<std::array<uint8_t, 32>, EcdhPublicKey> GenerateECDHKeyPair() {
+  std::array<uint8_t, 32> private_key = GenerateRandom<32>();
+  std::array<uint8_t, 32> private_key_copy = private_key;
+  ecc::Point public_key;
+
+  ECC_PointMult(&public_key, &(ecc::curve_p256.G), (uint32_t*)private_key_copy.data());
+
+  EcdhPublicKey pk;
+  memcpy(pk.x.data(), public_key.x, 32);
+  memcpy(pk.y.data(), public_key.y, 32);
+
+  /* private_key, public key pair */
+  return std::make_pair<std::array<uint8_t, 32>, EcdhPublicKey>(std::move(private_key), std::move(pk));
+}
+
+bool ValidateECDHPoint(EcdhPublicKey pk) {
+  ecc::Point public_key;
+  memcpy(public_key.x, pk.x.data(), 32);
+  memcpy(public_key.y, pk.y.data(), 32);
+  memset(public_key.z, 0, 32);
+  return ECC_ValidatePoint(public_key);
+}
+
+std::array<uint8_t, 32> ComputeDHKey(std::array<uint8_t, 32> my_private_key, EcdhPublicKey remote_public_key) {
+  ecc::Point peer_publ_key, new_publ_key;
+  uint32_t private_key[8];
+  memcpy(private_key, my_private_key.data(), 32);
+  memcpy(peer_publ_key.x, remote_public_key.x.data(), 32);
+  memcpy(peer_publ_key.y, remote_public_key.y.data(), 32);
+  memset(peer_publ_key.z, 0, 32);
+  peer_publ_key.z[0] = 1;
+
+  ECC_PointMult(&new_publ_key, &peer_publ_key, (uint32_t*)private_key);
+
+  std::array<uint8_t, 32> dhkey;
+  memcpy(dhkey.data(), new_publ_key.x, 32);
+  return dhkey;
+}
+
+}  // namespace security
+}  // namespace bluetooth
diff --git a/gd/security/ecdh_keys.h b/gd/security/ecdh_keys.h
new file mode 100644
index 0000000..8ec25a8
--- /dev/null
+++ b/gd/security/ecdh_keys.h
@@ -0,0 +1,40 @@
+/******************************************************************************
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+#pragma once
+
+#include <array>
+
+namespace bluetooth {
+namespace security {
+
+struct EcdhPublicKey {
+  std::array<uint8_t, 32> x;
+  std::array<uint8_t, 32> y;
+};
+
+/* this generates private and public Eliptic Curve Diffie Helman keys */
+std::pair<std::array<uint8_t, 32>, EcdhPublicKey> GenerateECDHKeyPair();
+
+/* This function validates that the given public key (point) lays on the special
+ * Bluetooth curve */
+bool ValidateECDHPoint(EcdhPublicKey pk);
+
+std::array<uint8_t, 32> ComputeDHKey(std::array<uint8_t, 32> my_private_key, EcdhPublicKey remote_public_key);
+
+}  // namespace security
+}  // namespace bluetooth
diff --git a/gd/security/facade.cc b/gd/security/facade.cc
new file mode 100644
index 0000000..c9d4ee6
--- /dev/null
+++ b/gd/security/facade.cc
@@ -0,0 +1,209 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "security/facade.h"
+
+#include "grpc/grpc_event_queue.h"
+#include "hci/address_with_type.h"
+#include "os/handler.h"
+#include "security/facade.grpc.pb.h"
+#include "security/security_manager_listener.h"
+#include "security/security_module.h"
+
+namespace bluetooth {
+namespace security {
+
+class SecurityModuleFacadeService : public SecurityModuleFacade::Service, public ISecurityManagerListener, public UI {
+ public:
+  SecurityModuleFacadeService(SecurityModule* security_module, ::bluetooth::os::Handler* security_handler)
+      : security_module_(security_module), security_handler_(security_handler) {
+    security_module_->GetSecurityManager()->RegisterCallbackListener(this, security_handler_);
+  }
+
+  ::grpc::Status CreateBond(::grpc::ServerContext* context, const facade::BluetoothAddressWithType* request,
+                            ::google::protobuf::Empty* response) override {
+    hci::Address peer;
+    ASSERT(hci::Address::FromString(request->address().address(), peer));
+    hci::AddressType peer_type = hci::AddressType::PUBLIC_DEVICE_ADDRESS;
+    security_module_->GetSecurityManager()->CreateBond(hci::AddressWithType(peer, peer_type));
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status CancelBond(::grpc::ServerContext* context, const facade::BluetoothAddressWithType* request,
+                            ::google::protobuf::Empty* response) override {
+    hci::Address peer;
+    ASSERT(hci::Address::FromString(request->address().address(), peer));
+    hci::AddressType peer_type = hci::AddressType::PUBLIC_DEVICE_ADDRESS;
+    security_module_->GetSecurityManager()->CancelBond(hci::AddressWithType(peer, peer_type));
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status RemoveBond(::grpc::ServerContext* context, const facade::BluetoothAddressWithType* request,
+                            ::google::protobuf::Empty* response) override {
+    hci::Address peer;
+    ASSERT(hci::Address::FromString(request->address().address(), peer));
+    hci::AddressType peer_type = hci::AddressType::PUBLIC_DEVICE_ADDRESS;
+    security_module_->GetSecurityManager()->RemoveBond(hci::AddressWithType(peer, peer_type));
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status FetchUiEvents(::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
+                               ::grpc::ServerWriter<UiMsg>* writer) override {
+    return ui_events_.RunLoop(context, writer);
+  }
+
+  ::grpc::Status SendUiCallback(::grpc::ServerContext* context, const UiCallbackMsg* request,
+                                ::google::protobuf::Empty* response) override {
+    switch (request->message_type()) {
+      case UiCallbackType::PASSKEY:
+        // TODO: security_module_->GetSecurityManager()->OnPasskeyEntry();
+        break;
+      case UiCallbackType::YES_NO:
+        // TODO: security_module_->GetSecurityManager()->OnConfirmYesNo(request->boolean());
+        break;
+      default:
+        LOG_ERROR("Unknown UiCallbackType %d", static_cast<int>(request->message_type()));
+        return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Unknown UiCallbackType");
+    }
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status FetchBondEvents(::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
+                                 ::grpc::ServerWriter<BondMsg>* writer) override {
+    return bond_events_.RunLoop(context, writer);
+  }
+
+  void DisplayPairingPrompt(const bluetooth::hci::AddressWithType& peer, std::string name) {
+    LOG_INFO("%s", peer.ToString().c_str());
+    UiMsg display_yes_no;
+    display_yes_no.mutable_peer()->mutable_address()->set_address(peer.ToString());
+    display_yes_no.mutable_peer()->set_type(facade::BluetoothAddressTypeEnum::PUBLIC_DEVICE_ADDRESS);
+    display_yes_no.set_message_type(UiMsgType::DISPLAY_YES_NO);
+    display_yes_no.set_unique_id(unique_id++);
+  }
+
+  virtual void DisplayConfirmValue(const bluetooth::hci::AddressWithType& peer, std::string name,
+                                   uint32_t numeric_value) {
+    LOG_INFO("%s value = 0x%x", peer.ToString().c_str(), numeric_value);
+    UiMsg display_with_value;
+    display_with_value.mutable_peer()->mutable_address()->set_address(peer.ToString());
+    display_with_value.mutable_peer()->set_type(facade::BluetoothAddressTypeEnum::PUBLIC_DEVICE_ADDRESS);
+    display_with_value.set_message_type(UiMsgType::DISPLAY_YES_NO_WITH_VALUE);
+    display_with_value.set_numeric_value(numeric_value);
+    display_with_value.set_unique_id(unique_id++);
+    ui_events_.OnIncomingEvent(display_with_value);
+  }
+
+  void DisplayYesNoDialog(const bluetooth::hci::AddressWithType& peer, std::string name) override {
+    LOG_INFO("%s", peer.ToString().c_str());
+    UiMsg display_yes_no;
+    display_yes_no.mutable_peer()->mutable_address()->set_address(peer.ToString());
+    display_yes_no.mutable_peer()->set_type(facade::BluetoothAddressTypeEnum::PUBLIC_DEVICE_ADDRESS);
+    display_yes_no.set_message_type(UiMsgType::DISPLAY_YES_NO);
+    display_yes_no.set_unique_id(unique_id++);
+  }
+
+  void DisplayPasskey(const bluetooth::hci::AddressWithType& peer, std::string name, uint32_t passkey) override {
+    LOG_INFO("%s value = 0x%x", peer.ToString().c_str(), passkey);
+    UiMsg display_passkey;
+    display_passkey.mutable_peer()->mutable_address()->set_address(peer.ToString());
+    display_passkey.mutable_peer()->set_type(facade::BluetoothAddressTypeEnum::PUBLIC_DEVICE_ADDRESS);
+    display_passkey.set_message_type(UiMsgType::DISPLAY_PASSKEY);
+    display_passkey.set_numeric_value(passkey);
+    display_passkey.set_unique_id(unique_id++);
+    ui_events_.OnIncomingEvent(display_passkey);
+  }
+
+  void DisplayEnterPasskeyDialog(const bluetooth::hci::AddressWithType& peer, std::string name) override {
+    LOG_INFO("%s", peer.ToString().c_str());
+    UiMsg display_passkey_input;
+    display_passkey_input.mutable_peer()->mutable_address()->set_address(peer.ToString());
+    display_passkey_input.mutable_peer()->set_type(facade::BluetoothAddressTypeEnum::PUBLIC_DEVICE_ADDRESS);
+    display_passkey_input.set_message_type(UiMsgType::DISPLAY_PASSKEY_ENTRY);
+    display_passkey_input.set_unique_id(unique_id++);
+    ui_events_.OnIncomingEvent(display_passkey_input);
+  }
+
+  void Cancel(const bluetooth::hci::AddressWithType& peer) override {
+    LOG_INFO("%s", peer.ToString().c_str());
+    UiMsg display_cancel;
+    display_cancel.mutable_peer()->mutable_address()->set_address(peer.ToString());
+    display_cancel.mutable_peer()->set_type(facade::BluetoothAddressTypeEnum::PUBLIC_DEVICE_ADDRESS);
+    display_cancel.set_message_type(UiMsgType::DISPLAY_CANCEL);
+    display_cancel.set_unique_id(unique_id++);
+    ui_events_.OnIncomingEvent(display_cancel);
+  }
+
+  void OnDeviceBonded(hci::AddressWithType peer) override {
+    LOG_INFO("%s", peer.ToString().c_str());
+    BondMsg bonded;
+    bonded.mutable_peer()->mutable_address()->set_address(peer.ToString());
+    bonded.mutable_peer()->set_type(facade::BluetoothAddressTypeEnum::PUBLIC_DEVICE_ADDRESS);
+    bonded.set_message_type(BondMsgType::DEVICE_BONDED);
+    bond_events_.OnIncomingEvent(bonded);
+  }
+
+  void OnDeviceUnbonded(hci::AddressWithType peer) override {
+    LOG_INFO("%s", peer.ToString().c_str());
+    BondMsg unbonded;
+    unbonded.mutable_peer()->mutable_address()->set_address(peer.ToString());
+    unbonded.mutable_peer()->set_type(facade::BluetoothAddressTypeEnum::PUBLIC_DEVICE_ADDRESS);
+    unbonded.set_message_type(BondMsgType::DEVICE_UNBONDED);
+    bond_events_.OnIncomingEvent(unbonded);
+  }
+
+  void OnDeviceBondFailed(hci::AddressWithType peer) override {
+    LOG_INFO("%s", peer.ToString().c_str());
+    BondMsg bond_failed;
+    bond_failed.mutable_peer()->mutable_address()->set_address(peer.ToString());
+    bond_failed.mutable_peer()->set_type(facade::BluetoothAddressTypeEnum::PUBLIC_DEVICE_ADDRESS);
+    bond_failed.set_message_type(BondMsgType::DEVICE_BOND_FAILED);
+    bond_events_.OnIncomingEvent(bond_failed);
+  }
+
+ private:
+  SecurityModule* security_module_;
+  ::bluetooth::os::Handler* security_handler_;
+  ::bluetooth::grpc::GrpcEventQueue<UiMsg> ui_events_{"UI events"};
+  ::bluetooth::grpc::GrpcEventQueue<BondMsg> bond_events_{"Bond events"};
+  uint32_t unique_id{1};
+  std::map<uint32_t, common::OnceCallback<void(bool)>> user_yes_no_callbacks_;
+  std::map<uint32_t, common::OnceCallback<void(uint32_t)>> user_passkey_callbacks_;
+};
+
+void SecurityModuleFacadeModule::ListDependencies(ModuleList* list) {
+  ::bluetooth::grpc::GrpcFacadeModule::ListDependencies(list);
+  list->add<SecurityModule>();
+}
+
+void SecurityModuleFacadeModule::Start() {
+  ::bluetooth::grpc::GrpcFacadeModule::Start();
+  service_ = new SecurityModuleFacadeService(GetDependency<SecurityModule>(), GetHandler());
+}
+
+void SecurityModuleFacadeModule::Stop() {
+  delete service_;
+  ::bluetooth::grpc::GrpcFacadeModule::Stop();
+}
+
+::grpc::Service* SecurityModuleFacadeModule::GetService() const {
+  return service_;
+}
+
+const ModuleFactory SecurityModuleFacadeModule::Factory =
+    ::bluetooth::ModuleFactory([]() { return new SecurityModuleFacadeModule(); });
+
+}  // namespace security
+}  // namespace bluetooth
diff --git a/gd/security/facade.h b/gd/security/facade.h
new file mode 100644
index 0000000..8f060c2
--- /dev/null
+++ b/gd/security/facade.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <grpc++/grpc++.h>
+
+#include "grpc/grpc_module.h"
+
+namespace bluetooth {
+namespace security {
+
+class SecurityModuleFacadeService;
+
+class SecurityModuleFacadeModule : public ::bluetooth::grpc::GrpcFacadeModule {
+ public:
+  static const ModuleFactory Factory;
+
+  void ListDependencies(ModuleList* list) override;
+  void Start() override;
+  void Stop() override;
+  ::grpc::Service* GetService() const override;
+
+ private:
+  SecurityModuleFacadeService* service_;
+};
+
+}  // namespace security
+}  // namespace bluetooth
diff --git a/gd/security/facade.proto b/gd/security/facade.proto
new file mode 100644
index 0000000..e1362ae
--- /dev/null
+++ b/gd/security/facade.proto
@@ -0,0 +1,53 @@
+syntax = "proto3";
+
+package bluetooth.security;
+
+import "google/protobuf/empty.proto";
+import "facade/common.proto";
+
+service SecurityModuleFacade {
+  rpc CreateBond(facade.BluetoothAddressWithType) returns (google.protobuf.Empty) {}
+  rpc CancelBond(facade.BluetoothAddressWithType) returns (google.protobuf.Empty) {}
+  rpc RemoveBond(facade.BluetoothAddressWithType) returns (google.protobuf.Empty) {}
+  rpc SendUiCallback(UiCallbackMsg) returns (google.protobuf.Empty) {}
+  rpc FetchUiEvents(google.protobuf.Empty) returns (stream UiMsg) {}
+  rpc FetchBondEvents(google.protobuf.Empty) returns (stream BondMsg) {}
+}
+
+enum UiMsgType {
+  DISPLAY_YES_NO_WITH_VALUE = 0;
+  DISPLAY_YES_NO = 1;
+  DISPLAY_PASSKEY = 2;
+  DISPLAY_PASSKEY_ENTRY = 3;
+  DISPLAY_CANCEL = 4;
+}
+
+message UiMsg {
+  UiMsgType message_type = 1;
+  facade.BluetoothAddressWithType peer = 2;
+  uint32 numeric_value = 3;
+  uint32 unique_id = 4;
+}
+
+enum UiCallbackType {
+  YES_NO = 0;
+  PASSKEY = 1;
+}
+
+message UiCallbackMsg {
+  UiCallbackType message_type = 1;
+  bool boolean = 2;
+  uint32 numeric_value = 3;
+  uint32 unique_id = 4;
+}
+
+enum BondMsgType {
+  DEVICE_BONDED = 0;
+  DEVICE_UNBONDED = 1;
+  DEVICE_BOND_FAILED = 2;
+}
+
+message BondMsg {
+  BondMsgType message_type = 1;
+  facade.BluetoothAddressWithType peer = 2;
+}
diff --git a/gd/security/initial_informations.h b/gd/security/initial_informations.h
new file mode 100644
index 0000000..5721931
--- /dev/null
+++ b/gd/security/initial_informations.h
@@ -0,0 +1,115 @@
+/******************************************************************************
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#pragma once
+
+#include <optional>
+#include <variant>
+
+#include "common/bidi_queue.h"
+#include "common/callback.h"
+#include "crypto_toolbox/crypto_toolbox.h"
+#include "hci/address_with_type.h"
+#include "hci/le_security_interface.h"
+#include "os/handler.h"
+#include "packet/base_packet_builder.h"
+#include "packet/packet_view.h"
+#include "security/ecdh_keys.h"
+#include "security/pairing_failure.h"
+#include "security/smp_packets.h"
+#include "security/ui.h"
+
+namespace bluetooth {
+namespace security {
+
+using DistributedKeys =
+    std::tuple<std::optional<crypto_toolbox::Octet16> /* ltk */, std::optional<uint16_t> /*ediv*/,
+               std::optional<std::array<uint8_t, 8>> /* rand */, std::optional<Address> /* Identity address */,
+               AddrType, std::optional<crypto_toolbox::Octet16> /* IRK */,
+               std::optional<crypto_toolbox::Octet16>> /* Signature Key */;
+
+/* This class represents the result of pairing, as returned from Pairing Handler */
+struct PairingResult {
+  hci::AddressWithType connection_address;
+  DistributedKeys distributed_keys;
+};
+
+using PairingResultOrFailure = std::variant<PairingResult, PairingFailure>;
+
+/* Data we use for Out Of Band Pairing */
+struct MyOobData {
+  /*  private key is just for this single pairing only, so it might be safe to
+   * expose it to other parts of stack. It should not be exposed to upper
+   * layers though */
+  std::array<uint8_t, 32> private_key;
+  EcdhPublicKey public_key;
+  crypto_toolbox::Octet16 c;
+  crypto_toolbox::Octet16 r;
+};
+
+/* This structure is filled and send to PairingHandlerLe to initiate the Pairing process with remote device */
+struct InitialInformations {
+  hci::Role my_role;
+  hci::AddressWithType my_connection_address;
+
+  /* My capabilities, as in pairing request/response */
+  struct {
+    IoCapability io_capability;
+    OobDataFlag oob_data_flag;
+    uint8_t auth_req;
+    uint8_t maximum_encryption_key_size;
+    uint8_t initiator_key_distribution;
+    uint8_t responder_key_distribution;
+  } myPairingCapabilities;
+
+  /* was it remote device that initiated the Pairing ? */
+  bool remotely_initiated;
+  uint16_t connection_handle;
+  hci::AddressWithType remote_connection_address;
+  std::string remote_name;
+
+  /* contains pairing request, if the pairing was remotely initiated */
+  std::optional<PairingRequestView> pairing_request;
+
+  struct out_of_band_data {
+    crypto_toolbox::Octet16 le_sc_c; /* LE Secure Connections Confirmation Value */
+    crypto_toolbox::Octet16 le_sc_r; /* LE Secure Connections Random Value */
+
+    crypto_toolbox::Octet16 security_manager_tk_value; /* OOB data for LE Legacy Pairing */
+  };
+
+  // If we received OOB data from remote device, this field contains it.
+  std::optional<out_of_band_data> remote_oob_data;
+  std::optional<MyOobData> my_oob_data;
+
+  /* Used by Pairing Handler to present user with requests*/
+  UI* user_interface;
+  os::Handler* user_interface_handler;
+
+  /* HCI interface to use */
+  hci::LeSecurityInterface* le_security_interface;
+
+  os::EnqueueBuffer<packet::BasePacketBuilder>* proper_l2cap_interface;
+  os::Handler* l2cap_handler;
+
+  /* Callback to execute once the Pairing process is finished */
+  std::function<void(PairingResultOrFailure)> OnPairingFinished;
+};
+
+}  // namespace security
+}  // namespace bluetooth
diff --git a/gd/security/internal/security_manager_impl.cc b/gd/security/internal/security_manager_impl.cc
new file mode 100644
index 0000000..5ca3436
--- /dev/null
+++ b/gd/security/internal/security_manager_impl.cc
@@ -0,0 +1,416 @@
+/*
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+#include "security_manager_impl.h"
+
+#include <iostream>
+
+#include "common/bind.h"
+#include "crypto_toolbox/crypto_toolbox.h"
+#include "hci/address_with_type.h"
+#include "os/log.h"
+#include "security/initial_informations.h"
+#include "security/internal/security_manager_impl.h"
+#include "security/pairing_handler_le.h"
+#include "security/security_manager.h"
+#include "security/ui.h"
+
+namespace bluetooth {
+namespace security {
+namespace internal {
+
+void SecurityManagerImpl::DispatchPairingHandler(record::SecurityRecord& record, bool locally_initiated,
+                                                 hci::AuthenticationRequirements authentication_requirements) {
+  common::OnceCallback<void(hci::Address, PairingResultOrFailure)> callback =
+      common::BindOnce(&SecurityManagerImpl::OnPairingHandlerComplete, common::Unretained(this));
+  auto entry = pairing_handler_map_.find(record.GetPseudoAddress().GetAddress());
+  if (entry != pairing_handler_map_.end()) {
+    LOG_WARN("Device already has a pairing handler, and is in the middle of pairing!");
+    return;
+  }
+  std::shared_ptr<pairing::PairingHandler> pairing_handler = nullptr;
+  switch (record.GetPseudoAddress().GetAddressType()) {
+    case hci::AddressType::PUBLIC_DEVICE_ADDRESS: {
+      std::shared_ptr<record::SecurityRecord> record_copy =
+          std::make_shared<record::SecurityRecord>(record.GetPseudoAddress());
+      pairing_handler = std::make_shared<security::pairing::ClassicPairingHandler>(
+          l2cap_classic_module_->GetFixedChannelManager(), security_manager_channel_, record_copy, security_handler_,
+          std::move(callback), user_interface_, user_interface_handler_, "TODO: grab device name properly");
+      break;
+    }
+    default:
+      ASSERT_LOG(false, "Pairing type %hhu not implemented!", record.GetPseudoAddress().GetAddressType());
+  }
+  auto new_entry = std::pair<hci::Address, std::shared_ptr<pairing::PairingHandler>>(
+      record.GetPseudoAddress().GetAddress(), pairing_handler);
+  pairing_handler_map_.insert(std::move(new_entry));
+  pairing_handler->Initiate(locally_initiated, pairing::kDefaultIoCapability, pairing::kDefaultOobDataPresent,
+                            authentication_requirements);
+}
+
+void SecurityManagerImpl::Init() {
+  security_manager_channel_->SetChannelListener(this);
+  security_manager_channel_->SendCommand(hci::WriteSimplePairingModeBuilder::Create(hci::Enable::ENABLED));
+  security_manager_channel_->SendCommand(hci::WriteSecureConnectionsHostSupportBuilder::Create(hci::Enable::ENABLED));
+  // TODO(optedoblivion): Populate security record memory map from disk
+}
+
+void SecurityManagerImpl::CreateBond(hci::AddressWithType device) {
+  record::SecurityRecord& record = security_database_.FindOrCreate(device);
+  if (record.IsBonded()) {
+    NotifyDeviceBonded(device);
+  } else {
+    // Dispatch pairing handler, if we are calling create we are the initiator
+    DispatchPairingHandler(record, true, pairing::kDefaultAuthenticationRequirements);
+  }
+}
+
+void SecurityManagerImpl::CreateBondLe(hci::AddressWithType address) {
+  record::SecurityRecord& record = security_database_.FindOrCreate(address);
+  if (record.IsBonded()) {
+    NotifyDeviceBondFailed(address, PairingFailure("Already bonded"));
+    return;
+  }
+
+  pending_le_pairing_.address_ = address;
+
+  l2cap_manager_le_->ConnectServices(
+      address, common::BindOnce(&SecurityManagerImpl::OnConnectionFailureLe, common::Unretained(this)),
+      security_handler_);
+}
+
+void SecurityManagerImpl::CancelBond(hci::AddressWithType device) {
+  auto entry = pairing_handler_map_.find(device.GetAddress());
+  if (entry != pairing_handler_map_.end()) {
+    auto cancel_me = entry->second;
+    pairing_handler_map_.erase(entry);
+    cancel_me->Cancel();
+  }
+}
+
+void SecurityManagerImpl::RemoveBond(hci::AddressWithType device) {
+  CancelBond(device);
+  security_database_.Remove(device);
+  // Signal disconnect
+  // Remove security record
+  // Signal Remove from database
+}
+
+void SecurityManagerImpl::SetUserInterfaceHandler(UI* user_interface, os::Handler* handler) {
+  if (user_interface_ != nullptr || user_interface_handler_ != nullptr) {
+    LOG_ALWAYS_FATAL("Listener has already been registered!");
+  }
+  user_interface_ = user_interface;
+  user_interface_handler_ = handler;
+}
+
+void SecurityManagerImpl::RegisterCallbackListener(ISecurityManagerListener* listener, os::Handler* handler) {
+  for (auto it = listeners_.begin(); it != listeners_.end(); ++it) {
+    if (it->first == listener) {
+      LOG_ALWAYS_FATAL("Listener has already been registered!");
+    }
+  }
+
+  listeners_.push_back({listener, handler});
+}
+
+void SecurityManagerImpl::UnregisterCallbackListener(ISecurityManagerListener* listener) {
+  for (auto it = listeners_.begin(); it != listeners_.end(); ++it) {
+    if (it->first == listener) {
+      listeners_.erase(it);
+      return;
+    }
+  }
+
+  LOG_ALWAYS_FATAL("Listener has not been registered!");
+}
+
+void SecurityManagerImpl::NotifyDeviceBonded(hci::AddressWithType device) {
+  for (auto& iter : listeners_) {
+    iter.second->Post(common::Bind(&ISecurityManagerListener::OnDeviceBonded, common::Unretained(iter.first), device));
+  }
+}
+
+void SecurityManagerImpl::NotifyDeviceBondFailed(hci::AddressWithType device, PairingResultOrFailure status) {
+  for (auto& iter : listeners_) {
+    iter.second->Post(common::Bind(&ISecurityManagerListener::OnDeviceBondFailed, common::Unretained(iter.first),
+                                   device /*, status */));
+  }
+}
+
+void SecurityManagerImpl::NotifyDeviceUnbonded(hci::AddressWithType device) {
+  for (auto& iter : listeners_) {
+    iter.second->Post(
+        common::Bind(&ISecurityManagerListener::OnDeviceUnbonded, common::Unretained(iter.first), device));
+  }
+}
+
+template <class T>
+void SecurityManagerImpl::HandleEvent(T packet) {
+  ASSERT(packet.IsValid());
+  auto entry = pairing_handler_map_.find(packet.GetBdAddr());
+
+  if (entry == pairing_handler_map_.end()) {
+    auto bd_addr = packet.GetBdAddr();
+    auto event_code = packet.GetEventCode();
+    auto event = hci::EventPacketView::Create(std::move(packet));
+    ASSERT_LOG(event.IsValid(), "Received invalid packet");
+
+    const hci::EventCode code = event.GetEventCode();
+    if (code != hci::EventCode::LINK_KEY_REQUEST) {
+      LOG_ERROR("No classic pairing handler for device '%s' ready for command %s ", bd_addr.ToString().c_str(),
+                hci::EventCodeText(event_code).c_str());
+      return;
+    }
+
+    auto record =
+        security_database_.FindOrCreate(hci::AddressWithType{bd_addr, hci::AddressType::PUBLIC_DEVICE_ADDRESS});
+    auto authentication_requirements = hci::AuthenticationRequirements::NO_BONDING;
+    DispatchPairingHandler(record, true, authentication_requirements);
+    entry = pairing_handler_map_.find(bd_addr);
+  }
+  entry->second->OnReceive(packet);
+}
+
+void SecurityManagerImpl::OnHciEventReceived(hci::EventPacketView packet) {
+  auto event = hci::EventPacketView::Create(packet);
+  ASSERT_LOG(event.IsValid(), "Received invalid packet");
+  const hci::EventCode code = event.GetEventCode();
+  switch (code) {
+    case hci::EventCode::PIN_CODE_REQUEST:
+      HandleEvent<hci::PinCodeRequestView>(hci::PinCodeRequestView::Create(event));
+      break;
+    case hci::EventCode::LINK_KEY_REQUEST:
+      HandleEvent(hci::LinkKeyRequestView::Create(event));
+      break;
+    case hci::EventCode::LINK_KEY_NOTIFICATION:
+      HandleEvent(hci::LinkKeyNotificationView::Create(event));
+      break;
+    case hci::EventCode::IO_CAPABILITY_REQUEST:
+      HandleEvent(hci::IoCapabilityRequestView::Create(event));
+      break;
+    case hci::EventCode::IO_CAPABILITY_RESPONSE:
+      HandleEvent(hci::IoCapabilityResponseView::Create(event));
+      break;
+    case hci::EventCode::SIMPLE_PAIRING_COMPLETE:
+      HandleEvent(hci::SimplePairingCompleteView::Create(event));
+      break;
+    case hci::EventCode::REMOTE_OOB_DATA_REQUEST:
+      HandleEvent(hci::RemoteOobDataRequestView::Create(event));
+      break;
+    case hci::EventCode::USER_PASSKEY_NOTIFICATION:
+      HandleEvent(hci::UserPasskeyNotificationView::Create(event));
+      break;
+    case hci::EventCode::KEYPRESS_NOTIFICATION:
+      HandleEvent(hci::KeypressNotificationView::Create(event));
+      break;
+    case hci::EventCode::USER_CONFIRMATION_REQUEST:
+      HandleEvent(hci::UserConfirmationRequestView::Create(event));
+      break;
+    case hci::EventCode::USER_PASSKEY_REQUEST:
+      HandleEvent(hci::UserPasskeyRequestView::Create(event));
+      break;
+    case hci::EventCode::REMOTE_HOST_SUPPORTED_FEATURES_NOTIFICATION:
+      LOG_INFO("Unhandled event: %s", hci::EventCodeText(code).c_str());
+      break;
+
+    case hci::EventCode::ENCRYPTION_CHANGE: {
+      EncryptionChangeView enc_chg_packet = EncryptionChangeView::Create(event);
+      if (!enc_chg_packet.IsValid()) {
+        LOG_ERROR("Invalid EncryptionChange packet received");
+        return;
+      }
+      if (enc_chg_packet.GetConnectionHandle() == pending_le_pairing_.connection_handle_) {
+        pending_le_pairing_.handler_->OnHciEvent(event);
+        return;
+      }
+      break;
+    }
+
+    default:
+      ASSERT_LOG(false, "Cannot handle received packet: %s", hci::EventCodeText(code).c_str());
+      break;
+  }
+}
+
+void SecurityManagerImpl::OnHciLeEvent(hci::LeMetaEventView event) {
+  // hci::SubeventCode::LONG_TERM_KEY_REQUEST,
+  // hci::SubeventCode::READ_LOCAL_P256_PUBLIC_KEY_COMPLETE,
+  // hci::SubeventCode::GENERATE_DHKEY_COMPLETE,
+  LOG_ERROR("Unhandled HCI LE security event");
+}
+
+void SecurityManagerImpl::OnPairingPromptAccepted(const bluetooth::hci::AddressWithType& address, bool confirmed) {
+  auto entry = pairing_handler_map_.find(address.GetAddress());
+  if (entry != pairing_handler_map_.end()) {
+    entry->second->OnPairingPromptAccepted(address, confirmed);
+  } else {
+    pending_le_pairing_.handler_->OnUiAction(PairingEvent::UI_ACTION_TYPE::PAIRING_ACCEPTED, confirmed);
+  }
+}
+
+void SecurityManagerImpl::OnConfirmYesNo(const bluetooth::hci::AddressWithType& address, bool confirmed) {
+  auto entry = pairing_handler_map_.find(address.GetAddress());
+  if (entry != pairing_handler_map_.end()) {
+    entry->second->OnConfirmYesNo(address, confirmed);
+  } else {
+    if (pending_le_pairing_.address_ == address) {
+      pending_le_pairing_.handler_->OnUiAction(PairingEvent::UI_ACTION_TYPE::CONFIRM_YESNO, confirmed);
+    }
+  }
+}
+
+void SecurityManagerImpl::OnPasskeyEntry(const bluetooth::hci::AddressWithType& address, uint32_t passkey) {
+  auto entry = pairing_handler_map_.find(address.GetAddress());
+  if (entry != pairing_handler_map_.end()) {
+    entry->second->OnPasskeyEntry(address, passkey);
+  } else {
+    if (pending_le_pairing_.address_ == address) {
+      pending_le_pairing_.handler_->OnUiAction(PairingEvent::UI_ACTION_TYPE::PASSKEY, passkey);
+    }
+  }
+}
+
+void SecurityManagerImpl::OnPairingHandlerComplete(hci::Address address, PairingResultOrFailure status) {
+  auto entry = pairing_handler_map_.find(address);
+  if (entry != pairing_handler_map_.end()) {
+    pairing_handler_map_.erase(entry);
+  }
+  if (!std::holds_alternative<PairingFailure>(status)) {
+    NotifyDeviceBonded(hci::AddressWithType(address, hci::AddressType::PUBLIC_DEVICE_ADDRESS));
+  } else {
+    NotifyDeviceBondFailed(hci::AddressWithType(address, hci::AddressType::PUBLIC_DEVICE_ADDRESS), status);
+  }
+}
+
+void SecurityManagerImpl::OnL2capRegistrationCompleteLe(
+    l2cap::le::FixedChannelManager::RegistrationResult result,
+    std::unique_ptr<l2cap::le::FixedChannelService> le_smp_service) {
+  ASSERT_LOG(result == bluetooth::l2cap::le::FixedChannelManager::RegistrationResult::SUCCESS,
+             "Failed to register to LE SMP Fixed Channel Service");
+}
+
+void SecurityManagerImpl::OnSmpCommandLe() {
+  auto packet = pending_le_pairing_.channel_->GetQueueUpEnd()->TryDequeue();
+  if (!packet) LOG_ERROR("Received dequeue, but no data ready...");
+
+  auto temp_cmd_view = CommandView::Create(*packet);
+  pending_le_pairing_.handler_->OnCommandView(temp_cmd_view);
+}
+
+void SecurityManagerImpl::OnConnectionOpenLe(std::unique_ptr<l2cap::le::FixedChannel> channel) {
+  if (pending_le_pairing_.address_ != channel->GetDevice()) {
+    return;
+  }
+  pending_le_pairing_.channel_ = std::move(channel);
+  pending_le_pairing_.channel_->RegisterOnCloseCallback(
+      security_handler_, common::BindOnce(&SecurityManagerImpl::OnConnectionClosedLe, common::Unretained(this),
+                                          pending_le_pairing_.channel_->GetDevice()));
+  // TODO: this enqueue buffer must be stored together with pairing_handler, and we must make sure it doesn't go out of
+  // scope while the pairing happens
+  pending_le_pairing_.enqueue_buffer_ =
+      std::make_unique<os::EnqueueBuffer<packet::BasePacketBuilder>>(pending_le_pairing_.channel_->GetQueueUpEnd());
+  pending_le_pairing_.channel_->GetQueueUpEnd()->RegisterDequeue(
+      security_handler_, common::Bind(&SecurityManagerImpl::OnSmpCommandLe, common::Unretained(this)));
+
+  // TODO: this doesn't have to be a unique ptr, if there is a way to properly std::move it into place where it's stored
+  pending_le_pairing_.connection_handle_ = pending_le_pairing_.channel_->GetAclConnection()->GetHandle();
+  InitialInformations initial_informations{
+      .my_role = pending_le_pairing_.channel_->GetAclConnection()->GetRole(),
+      .my_connection_address = {hci::Address{{0x00, 0x11, 0xFF, 0xFF, 0x33, 0x22}} /*TODO: obtain my address*/,
+                                hci::AddressType::RANDOM_DEVICE_ADDRESS},
+      /*TODO: properly obtain capabilities from device-specific storage*/
+      .myPairingCapabilities = {.io_capability = IoCapability::KEYBOARD_DISPLAY,
+                                .oob_data_flag = OobDataFlag::NOT_PRESENT,
+                                .auth_req = AuthReqMaskBondingFlag | AuthReqMaskMitm | AuthReqMaskSc,
+                                .maximum_encryption_key_size = 16,
+                                .initiator_key_distribution = 0x07,
+                                .responder_key_distribution = 0x07},
+      .remotely_initiated = false,
+      .connection_handle = pending_le_pairing_.channel_->GetAclConnection()->GetHandle(),
+      .remote_connection_address = pending_le_pairing_.channel_->GetDevice(),
+      .remote_name = "TODO: grab proper device name in sec mgr",
+      /* contains pairing request, if the pairing was remotely initiated */
+      .pairing_request = std::nullopt,  // TODO: handle remotely initiated pairing in SecurityManager properly
+      .remote_oob_data = std::nullopt,  // TODO:
+      .my_oob_data = std::nullopt,      // TODO:
+      /* Used by Pairing Handler to present user with requests*/
+      .user_interface = user_interface_,
+      .user_interface_handler = user_interface_handler_,
+
+      /* HCI interface to use */
+      .le_security_interface = hci_security_interface_le_,
+      .proper_l2cap_interface = pending_le_pairing_.enqueue_buffer_.get(),
+      .l2cap_handler = security_handler_,
+      /* Callback to execute once the Pairing process is finished */
+      // TODO: make it an common::OnceCallback ?
+      .OnPairingFinished = std::bind(&SecurityManagerImpl::OnPairingFinished, this, std::placeholders::_1),
+  };
+  pending_le_pairing_.handler_ = std::make_unique<PairingHandlerLe>(PairingHandlerLe::PHASE1, initial_informations);
+}
+
+void SecurityManagerImpl::OnConnectionClosedLe(hci::AddressWithType address, hci::ErrorCode error_code) {
+  if (pending_le_pairing_.address_ != address) {
+    return;
+  }
+  pending_le_pairing_.handler_->SendExitSignal();
+  NotifyDeviceBondFailed(address, PairingFailure("Connection closed"));
+}
+
+void SecurityManagerImpl::OnConnectionFailureLe(bluetooth::l2cap::le::FixedChannelManager::ConnectionResult result) {
+  if (result.connection_result_code ==
+      bluetooth::l2cap::le::FixedChannelManager::ConnectionResultCode::FAIL_ALL_SERVICES_HAVE_CHANNEL) {
+    // TODO: already connected
+  }
+
+  // This callback is invoked only for devices we attempted to connect to.
+  NotifyDeviceBondFailed(pending_le_pairing_.address_, PairingFailure("Connection establishment failed"));
+}
+
+SecurityManagerImpl::SecurityManagerImpl(os::Handler* security_handler, l2cap::le::L2capLeModule* l2cap_le_module,
+                                         l2cap::classic::L2capClassicModule* l2cap_classic_module,
+                                         channel::SecurityManagerChannel* security_manager_channel,
+                                         hci::HciLayer* hci_layer)
+    : security_handler_(security_handler), l2cap_le_module_(l2cap_le_module),
+      l2cap_classic_module_(l2cap_classic_module), l2cap_manager_le_(l2cap_le_module_->GetFixedChannelManager()),
+      hci_security_interface_le_(hci_layer->GetLeSecurityInterface(
+          common::Bind(&SecurityManagerImpl::OnHciLeEvent, common::Unretained(this)), security_handler)),
+      security_manager_channel_(security_manager_channel) {
+  Init();
+  l2cap_manager_le_->RegisterService(
+      bluetooth::l2cap::kSmpCid, {},
+      common::BindOnce(&SecurityManagerImpl::OnL2capRegistrationCompleteLe, common::Unretained(this)),
+      common::Bind(&SecurityManagerImpl::OnConnectionOpenLe, common::Unretained(this)), security_handler_);
+}
+
+void SecurityManagerImpl::OnPairingFinished(security::PairingResultOrFailure pairing_result) {
+  LOG_INFO(" â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â–  Received pairing result");
+
+  if (std::holds_alternative<PairingFailure>(pairing_result)) {
+    PairingFailure failure = std::get<PairingFailure>(pairing_result);
+    LOG_INFO(" â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â– â–  failure message: %s",
+             failure.message.c_str());
+    return;
+  }
+
+  LOG_INFO("Pairing with %s was successfull",
+           std::get<PairingResult>(pairing_result).connection_address.ToString().c_str());
+}
+
+}  // namespace internal
+}  // namespace security
+}  // namespace bluetooth
diff --git a/gd/security/internal/security_manager_impl.h b/gd/security/internal/security_manager_impl.h
new file mode 100644
index 0000000..fdc601a
--- /dev/null
+++ b/gd/security/internal/security_manager_impl.h
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <unordered_map>
+#include <utility>
+
+#include "hci/classic_device.h"
+#include "l2cap/classic/l2cap_classic_module.h"
+#include "l2cap/le/l2cap_le_module.h"
+#include "os/handler.h"
+#include "security/channel/security_manager_channel.h"
+#include "security/initial_informations.h"
+#include "security/pairing/classic_pairing_handler.h"
+#include "security/pairing_handler_le.h"
+#include "security/record/security_record.h"
+#include "security/security_record_database.h"
+
+namespace bluetooth {
+namespace security {
+
+class ISecurityManagerListener;
+
+namespace internal {
+
+class SecurityManagerImpl : public channel::ISecurityManagerChannelListener, public UICallbacks {
+ public:
+  explicit SecurityManagerImpl(os::Handler* security_handler, l2cap::le::L2capLeModule* l2cap_le_module,
+                               l2cap::classic::L2capClassicModule* l2cap_classic_module,
+                               channel::SecurityManagerChannel* security_manager_channel, hci::HciLayer* hci_layer);
+  ~SecurityManagerImpl() = default;
+
+  // All APIs must be invoked in SM layer handler
+
+  /**
+   * Initialize the security record map from an internal device database.
+   */
+  void Init();
+
+  /**
+   * Initiates bond over Classic transport with device, if not bonded yet.
+   *
+   * @param address device address we want to bond with
+   */
+  void CreateBond(hci::AddressWithType address);
+
+  /**
+   * Initiates bond over Low Energy transport with device, if not bonded yet.
+   *
+   * @param address device address we want to bond with
+   */
+  void CreateBondLe(hci::AddressWithType address);
+
+  /* void CreateBond(std::shared_ptr<hci::LeDevice> device); */
+
+  /**
+   * Cancels the pairing process for this device.
+   *
+   * @param device pointer to device with which we want to cancel our bond
+   */
+  void CancelBond(hci::AddressWithType device);
+
+  /* void CancelBond(std::shared_ptr<hci::LeDevice> device); */
+
+  /**
+   * Disassociates the device and removes the persistent LTK
+   *
+   * @param device pointer to device we want to forget
+   * @return true if removed
+   */
+  void RemoveBond(hci::AddressWithType device);
+
+  /* void RemoveBond(std::shared_ptr<hci::LeDevice> device); */
+
+  /**
+   * Register Security UI handler, for handling prompts around the Pairing process.
+   */
+  void SetUserInterfaceHandler(UI* user_interface, os::Handler* handler);
+
+  /**
+   * Register to listen for callback events from SecurityManager
+   *
+   * @param listener ISecurityManagerListener instance to handle callbacks
+   */
+  void RegisterCallbackListener(ISecurityManagerListener* listener, os::Handler* handler);
+
+  /**
+   * Unregister listener for callback events from SecurityManager
+   *
+   * @param listener ISecurityManagerListener instance to unregister
+   */
+  void UnregisterCallbackListener(ISecurityManagerListener* listener);
+
+  /**
+   * Handle the events sent back from HCI that we care about
+   *
+   * @param packet data received from HCI
+   */
+  void OnHciEventReceived(hci::EventPacketView packet) override;
+
+  /**
+   * Pairing handler has finished or cancelled
+   *
+   * @param address address for pairing handler
+   * @param status status from SimplePairingComplete or other error code
+   */
+  void OnPairingHandlerComplete(hci::Address address, PairingResultOrFailure status);
+
+  // UICallbacks implementation
+  void OnPairingPromptAccepted(const bluetooth::hci::AddressWithType& address, bool confirmed) override;
+  void OnConfirmYesNo(const bluetooth::hci::AddressWithType& address, bool confirmed) override;
+  void OnPasskeyEntry(const bluetooth::hci::AddressWithType& address, uint32_t passkey) override;
+
+ protected:
+  std::vector<std::pair<ISecurityManagerListener*, os::Handler*>> listeners_;
+  UI* user_interface_ = nullptr;
+  os::Handler* user_interface_handler_ = nullptr;
+
+  void NotifyDeviceBonded(hci::AddressWithType device);
+  void NotifyDeviceBondFailed(hci::AddressWithType device, PairingResultOrFailure status);
+  void NotifyDeviceUnbonded(hci::AddressWithType device);
+
+ private:
+  template <class T>
+  void HandleEvent(T packet);
+
+  void DispatchPairingHandler(record::SecurityRecord& record, bool locally_initiated,
+                              hci::AuthenticationRequirements authentication_requirements);
+  void OnL2capRegistrationCompleteLe(l2cap::le::FixedChannelManager::RegistrationResult result,
+                                     std::unique_ptr<l2cap::le::FixedChannelService> le_smp_service);
+  void OnSmpCommandLe();
+  void OnConnectionOpenLe(std::unique_ptr<l2cap::le::FixedChannel> channel);
+  void OnConnectionClosedLe(hci::AddressWithType address, hci::ErrorCode error_code);
+  void OnConnectionFailureLe(bluetooth::l2cap::le::FixedChannelManager::ConnectionResult result);
+  void OnPairingFinished(bluetooth::security::PairingResultOrFailure pairing_result);
+  void OnHciLeEvent(hci::LeMetaEventView event);
+
+  os::Handler* security_handler_ __attribute__((unused));
+  l2cap::le::L2capLeModule* l2cap_le_module_ __attribute__((unused));
+  l2cap::classic::L2capClassicModule* l2cap_classic_module_ __attribute__((unused));
+  std::unique_ptr<l2cap::le::FixedChannelManager> l2cap_manager_le_;
+  hci::LeSecurityInterface* hci_security_interface_le_ __attribute__((unused));
+  channel::SecurityManagerChannel* security_manager_channel_;
+  SecurityRecordDatabase security_database_;
+  std::unordered_map<hci::Address, std::shared_ptr<pairing::PairingHandler>> pairing_handler_map_;
+
+  struct {
+    hci::AddressWithType address_;
+    std::unique_ptr<l2cap::le::FixedChannel> channel_;
+    uint8_t connection_handle_;
+    std::unique_ptr<PairingHandlerLe> handler_;
+    std::unique_ptr<os::EnqueueBuffer<packet::BasePacketBuilder>> enqueue_buffer_;
+  } pending_le_pairing_;
+};
+}  // namespace internal
+}  // namespace security
+}  // namespace bluetooth
diff --git a/gd/security/pairing/Android.bp b/gd/security/pairing/Android.bp
new file mode 100644
index 0000000..350c10e
--- /dev/null
+++ b/gd/security/pairing/Android.bp
@@ -0,0 +1,13 @@
+filegroup {
+    name: "BluetoothSecurityPairingSources",
+    srcs: [
+        "classic_pairing_handler.cc",
+    ]
+}
+
+filegroup {
+    name: "BluetoothSecurityPairingTestSources",
+    srcs: [
+        "classic_pairing_handler_unittest.cc",
+    ]
+}
diff --git a/gd/security/pairing/classic_pairing_handler.cc b/gd/security/pairing/classic_pairing_handler.cc
new file mode 100644
index 0000000..93c0865
--- /dev/null
+++ b/gd/security/pairing/classic_pairing_handler.cc
@@ -0,0 +1,421 @@
+/*
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+#include "security/pairing/classic_pairing_handler.h"
+
+#include "common/bind.h"
+
+namespace bluetooth {
+namespace security {
+namespace pairing {
+
+void ClassicPairingHandler::NotifyUiDisplayYesNo(uint32_t numeric_value) {
+  user_interface_handler_->Post(common::BindOnce(&UI::DisplayConfirmValue, common::Unretained(user_interface_),
+                                                 GetRecord()->GetPseudoAddress(), device_name_, numeric_value));
+}
+
+void ClassicPairingHandler::NotifyUiDisplayYesNo() {
+  user_interface_handler_->Post(common::BindOnce(&UI::DisplayYesNoDialog, common::Unretained(user_interface_),
+                                                 GetRecord()->GetPseudoAddress(), device_name_));
+}
+
+void ClassicPairingHandler::NotifyUiDisplayPasskey(uint32_t passkey) {
+  user_interface_handler_->Post(common::BindOnce(&UI::DisplayPasskey, common::Unretained(user_interface_),
+                                                 GetRecord()->GetPseudoAddress(), device_name_, passkey));
+}
+
+void ClassicPairingHandler::NotifyUiDisplayPasskeyInput() {
+  user_interface_handler_->Post(common::BindOnce(&UI::DisplayEnterPasskeyDialog, common::Unretained(user_interface_),
+                                                 GetRecord()->GetPseudoAddress(), device_name_));
+}
+
+void ClassicPairingHandler::NotifyUiDisplayCancel() {
+  user_interface_handler_->Post(
+      common::BindOnce(&UI::Cancel, common::Unretained(user_interface_), GetRecord()->GetPseudoAddress()));
+}
+
+void ClassicPairingHandler::OnRegistrationComplete(
+    l2cap::classic::FixedChannelManager::RegistrationResult result,
+    std::unique_ptr<l2cap::classic::FixedChannelService> fixed_channel_service) {
+  if (result != l2cap::classic::FixedChannelManager::RegistrationResult::SUCCESS) {
+    LOG_ERROR("Failed service registration!");
+    return;
+  }
+  fixed_channel_service_ = std::move(fixed_channel_service);
+  fixed_channel_manager_->ConnectServices(
+      GetRecord()->GetPseudoAddress().GetAddress(),
+      common::Bind(&ClassicPairingHandler::OnConnectionFail, common::Unretained(this)), security_handler_);
+}
+
+void ClassicPairingHandler::OnUnregistered() {
+  PairingResultOrFailure result = PairingResult();
+  if (last_status_ != hci::ErrorCode::SUCCESS) {
+    result = PairingFailure(hci::ErrorCodeText(last_status_));
+  }
+  std::move(complete_callback_).Run(GetRecord()->GetPseudoAddress().GetAddress(), result);
+}
+
+void ClassicPairingHandler::OnConnectionOpen(std::unique_ptr<l2cap::classic::FixedChannel> fixed_channel) {
+  ASSERT(fixed_channel_ == nullptr);
+  fixed_channel_ = std::move(fixed_channel);
+  ASSERT(fixed_channel_->GetDevice() == GetRecord()->GetPseudoAddress().GetAddress());
+  fixed_channel_->RegisterOnCloseCallback(
+      security_handler_, common::BindOnce(&ClassicPairingHandler::OnConnectionClose, common::Unretained(this)));
+  fixed_channel_->Acquire();
+}
+
+void ClassicPairingHandler::OnConnectionFail(l2cap::classic::FixedChannelManager::ConnectionResult result) {
+  Cancel();
+}
+
+void ClassicPairingHandler::OnConnectionClose(hci::ErrorCode error_code) {
+  // Called when the connection gets closed
+  LOG_ERROR("Connection closed due to: %s", hci::ErrorCodeText(error_code).c_str());
+  ASSERT(fixed_channel_ != nullptr);
+  fixed_channel_.reset();
+  Cancel();
+}
+
+void ClassicPairingHandler::OnPairingPromptAccepted(const bluetooth::hci::AddressWithType& address, bool confirmed) {
+  LOG_WARN("TODO Not Implemented!");
+}
+
+void ClassicPairingHandler::OnConfirmYesNo(const bluetooth::hci::AddressWithType& address, bool confirmed) {
+  LOG_WARN("TODO Not Implemented!");
+  GetChannel()->SendCommand(
+      hci::UserConfirmationRequestReplyBuilder::Create(GetRecord()->GetPseudoAddress().GetAddress()));
+}
+
+void ClassicPairingHandler::OnPasskeyEntry(const bluetooth::hci::AddressWithType& address, uint32_t passkey) {
+  LOG_WARN("TODO Not Implemented!");
+}
+
+void ClassicPairingHandler::Initiate(bool locally_initiated, hci::IoCapability io_capability,
+                                     hci::OobDataPresent oob_present,
+                                     hci::AuthenticationRequirements auth_requirements) {
+  locally_initiated_ = locally_initiated;
+  local_io_capability_ = io_capability;
+  local_oob_present_ = oob_present;
+  local_authentication_requirements_ = auth_requirements;
+
+  // TODO(optedoblivion): Read OOB data
+  // if host and controller support secure connections used HCIREADLOCALOOBEXTENDEDDATA vs HCIREADLOCALOOBDATA
+
+  fixed_channel_manager_->RegisterService(
+      l2cap::kClassicPairingTriggerCid, security_policy_,
+      common::Bind(&ClassicPairingHandler::OnRegistrationComplete, common::Unretained(this)),
+      common::Bind(&ClassicPairingHandler::OnConnectionOpen, common::Unretained(this)), security_handler_);
+}
+
+void ClassicPairingHandler::Cancel() {
+  if (fixed_channel_ != nullptr) {
+    fixed_channel_->Release();
+  }
+  if (fixed_channel_service_ != nullptr) {
+    fixed_channel_service_->Unregister(common::Bind(&ClassicPairingHandler::OnUnregistered, common::Unretained(this)),
+                                       security_handler_);
+  }
+}
+
+void ClassicPairingHandler::OnReceive(hci::ChangeConnectionLinkKeyCompleteView packet) {
+  ASSERT(packet.IsValid());
+  LOG_INFO("Received unsupported event: %s", hci::EventCodeText(packet.GetEventCode()).c_str());
+}
+
+void ClassicPairingHandler::OnReceive(hci::MasterLinkKeyCompleteView packet) {
+  ASSERT(packet.IsValid());
+  LOG_INFO("Received unsupported event: %s", hci::EventCodeText(packet.GetEventCode()).c_str());
+}
+
+void ClassicPairingHandler::OnReceive(hci::PinCodeRequestView packet) {
+  ASSERT(packet.IsValid());
+  LOG_INFO("Received: %s", hci::EventCodeText(packet.GetEventCode()).c_str());
+  ASSERT_LOG(GetRecord()->GetPseudoAddress().GetAddress() == packet.GetBdAddr(), "Address mismatch");
+}
+
+void ClassicPairingHandler::OnReceive(hci::LinkKeyRequestView packet) {
+  ASSERT(packet.IsValid());
+  // TODO(optedoblivion): Add collision detection here
+  LOG_INFO("Received: %s", hci::EventCodeText(packet.GetEventCode()).c_str());
+  ASSERT_LOG(GetRecord()->GetPseudoAddress().GetAddress() == packet.GetBdAddr(), "Address mismatch");
+  if (GetRecord()->IsBonded() || GetRecord()->IsPaired()) {
+    auto packet = hci::LinkKeyRequestReplyBuilder::Create(GetRecord()->GetPseudoAddress().GetAddress(),
+                                                          GetRecord()->GetLinkKey());
+    this->GetChannel()->SendCommand(std::move(packet));
+  } else {
+    auto packet = hci::LinkKeyRequestNegativeReplyBuilder::Create(GetRecord()->GetPseudoAddress().GetAddress());
+    this->GetChannel()->SendCommand(std::move(packet));
+  }
+}
+
+void ClassicPairingHandler::OnReceive(hci::LinkKeyNotificationView packet) {
+  ASSERT(packet.IsValid());
+  LOG_INFO("Received: %s", hci::EventCodeText(packet.GetEventCode()).c_str());
+  ASSERT_LOG(GetRecord()->GetPseudoAddress().GetAddress() == packet.GetBdAddr(), "Address mismatch");
+  GetRecord()->SetLinkKey(packet.GetLinkKey(), packet.GetKeyType());
+  Cancel();
+}
+
+void ClassicPairingHandler::OnReceive(hci::IoCapabilityRequestView packet) {
+  ASSERT(packet.IsValid());
+  LOG_INFO("Received: %s", hci::EventCodeText(packet.GetEventCode()).c_str());
+  ASSERT_LOG(GetRecord()->GetPseudoAddress().GetAddress() == packet.GetBdAddr(), "Address mismatch");
+  hci::IoCapability io_capability = local_io_capability_;
+  hci::OobDataPresent oob_present = hci::OobDataPresent::NOT_PRESENT;
+  hci::AuthenticationRequirements authentication_requirements = local_authentication_requirements_;
+  auto reply_packet = hci::IoCapabilityRequestReplyBuilder::Create(
+      GetRecord()->GetPseudoAddress().GetAddress(), io_capability, oob_present, authentication_requirements);
+  this->GetChannel()->SendCommand(std::move(reply_packet));
+}
+
+void ClassicPairingHandler::OnReceive(hci::IoCapabilityResponseView packet) {
+  ASSERT(packet.IsValid());
+  LOG_INFO("Received: %s", hci::EventCodeText(packet.GetEventCode()).c_str());
+  ASSERT_LOG(GetRecord()->GetPseudoAddress().GetAddress() == packet.GetBdAddr(), "Address mismatch");
+
+  // Using local variable until device database pointer is ready
+  remote_io_capability_ = packet.GetIoCapability();
+}
+
+void ClassicPairingHandler::OnReceive(hci::SimplePairingCompleteView packet) {
+  ASSERT(packet.IsValid());
+  LOG_INFO("Received: %s", hci::EventCodeText(packet.GetEventCode()).c_str());
+  ASSERT_LOG(GetRecord()->GetPseudoAddress().GetAddress() == packet.GetBdAddr(), "Address mismatch");
+  last_status_ = packet.GetStatus();
+  if (last_status_ != hci::ErrorCode::SUCCESS) {
+    LOG_INFO("Failed SimplePairingComplete: %s", hci::ErrorCodeText(last_status_).c_str());
+    Cancel();
+  }
+}
+
+void ClassicPairingHandler::OnReceive(hci::ReturnLinkKeysView packet) {
+  ASSERT(packet.IsValid());
+  LOG_INFO("Received: %s", hci::EventCodeText(packet.GetEventCode()).c_str());
+}
+
+void ClassicPairingHandler::OnReceive(hci::EncryptionChangeView packet) {
+  ASSERT(packet.IsValid());
+  LOG_INFO("Received: %s", hci::EventCodeText(packet.GetEventCode()).c_str());
+}
+
+void ClassicPairingHandler::OnReceive(hci::EncryptionKeyRefreshCompleteView packet) {
+  ASSERT(packet.IsValid());
+  LOG_INFO("Received: %s", hci::EventCodeText(packet.GetEventCode()).c_str());
+}
+
+void ClassicPairingHandler::OnReceive(hci::RemoteOobDataRequestView packet) {
+  ASSERT(packet.IsValid());
+  LOG_INFO("Received: %s", hci::EventCodeText(packet.GetEventCode()).c_str());
+  ASSERT_LOG(GetRecord()->GetPseudoAddress().GetAddress() == packet.GetBdAddr(), "Address mismatch");
+}
+
+void ClassicPairingHandler::OnReceive(hci::UserPasskeyNotificationView packet) {
+  ASSERT(packet.IsValid());
+  LOG_INFO("Received: %s", hci::EventCodeText(packet.GetEventCode()).c_str());
+  ASSERT_LOG(GetRecord()->GetPseudoAddress().GetAddress() == packet.GetBdAddr(), "Address mismatch");
+}
+
+void ClassicPairingHandler::OnReceive(hci::KeypressNotificationView packet) {
+  ASSERT(packet.IsValid());
+  LOG_INFO("Received: %s", hci::EventCodeText(packet.GetEventCode()).c_str());
+  LOG_INFO("Notification Type: %s", hci::KeypressNotificationTypeText(packet.GetNotificationType()).c_str());
+  switch (packet.GetNotificationType()) {
+    case hci::KeypressNotificationType::ENTRY_STARTED:
+      // Get ready to keep track of key input
+      break;
+    case hci::KeypressNotificationType::DIGIT_ENTERED:
+      // Append digit to key
+      break;
+    case hci::KeypressNotificationType::DIGIT_ERASED:
+      // erase last digit from key
+      break;
+    case hci::KeypressNotificationType::CLEARED:
+      // erase all digits from key
+      break;
+    case hci::KeypressNotificationType::ENTRY_COMPLETED:
+      // set full key to security record
+      break;
+  }
+}
+
+/**
+ * Here we decide what type of pairing authentication method we will use
+ *
+ * The table is on pg 2133 of the Core v5.1 spec.
+ */
+void ClassicPairingHandler::OnReceive(hci::UserConfirmationRequestView packet) {
+  ASSERT(packet.IsValid());
+  LOG_INFO("Received: %s", hci::EventCodeText(packet.GetEventCode()).c_str());
+  ASSERT_LOG(GetRecord()->GetPseudoAddress().GetAddress() == packet.GetBdAddr(), "Address mismatch");
+  // if locally_initialized, use default, otherwise us remote io caps
+  hci::IoCapability initiator_io_capability = (locally_initiated_) ? local_io_capability_ : remote_io_capability_;
+  hci::IoCapability responder_io_capability = (!locally_initiated_) ? local_io_capability_ : remote_io_capability_;
+  // TODO(optedoblivion): Check for TEMPORARY pairing case
+  switch (initiator_io_capability) {
+    case hci::IoCapability::DISPLAY_ONLY:
+      switch (responder_io_capability) {
+        case hci::IoCapability::DISPLAY_ONLY:
+          // NumericComparison, Both auto confirm
+          LOG_INFO("Numeric Comparison: A and B auto confirm");
+          GetChannel()->SendCommand(
+              hci::UserConfirmationRequestReplyBuilder::Create(GetRecord()->GetPseudoAddress().GetAddress()));
+          // Unauthenticated
+          break;
+        case hci::IoCapability::DISPLAY_YES_NO:
+          // NumericComparison, Initiator auto confirm, Responder display
+          GetChannel()->SendCommand(
+              hci::UserConfirmationRequestReplyBuilder::Create(GetRecord()->GetPseudoAddress().GetAddress()));
+          LOG_INFO("Numeric Comparison: A auto confirm");
+          // Unauthenticated
+          break;
+        case hci::IoCapability::KEYBOARD_ONLY:
+          // PassKey Entry, Initiator display, Responder input
+          NotifyUiDisplayPasskey(packet.GetNumericValue());
+          LOG_INFO("Passkey Entry: A display, B input");
+          // Authenticated
+          break;
+        case hci::IoCapability::NO_INPUT_NO_OUTPUT:
+          // NumericComparison, Both auto confirm
+          LOG_INFO("Numeric Comparison: A and B auto confirm");
+          GetChannel()->SendCommand(
+              hci::UserConfirmationRequestReplyBuilder::Create(GetRecord()->GetPseudoAddress().GetAddress()));
+          // Unauthenticated
+          break;
+      }
+      break;
+    case hci::IoCapability::DISPLAY_YES_NO:
+      switch (responder_io_capability) {
+        case hci::IoCapability::DISPLAY_ONLY:
+          // NumericComparison, Initiator display, Responder auto confirm
+          LOG_INFO("Numeric Comparison: A DisplayYesNo, B auto confirm");
+          NotifyUiDisplayYesNo(packet.GetNumericValue());
+          // Unauthenticated
+          break;
+        case hci::IoCapability::DISPLAY_YES_NO:
+          // NumericComparison Both Display, Both confirm
+          LOG_INFO("Numeric Comparison: A and B DisplayYesNo");
+          NotifyUiDisplayYesNo(packet.GetNumericValue());
+          // Authenticated
+          break;
+        case hci::IoCapability::KEYBOARD_ONLY:
+          // PassKey Entry, Initiator display, Responder input
+          NotifyUiDisplayPasskey(packet.GetNumericValue());
+          LOG_INFO("Passkey Entry: A display, B input");
+          // Authenticated
+          break;
+        case hci::IoCapability::NO_INPUT_NO_OUTPUT:
+          // NumericComparison, auto confirm Responder, Yes/No confirm Initiator. Don't show confirmation value
+          NotifyUiDisplayYesNo();
+          LOG_INFO("Numeric Comparison: A DisplayYesNo, B auto confirm, no show value");
+          // Unauthenticated
+          break;
+      }
+      break;
+    case hci::IoCapability::KEYBOARD_ONLY:
+      switch (responder_io_capability) {
+        case hci::IoCapability::DISPLAY_ONLY:
+          // PassKey Entry, Responder display, Initiator input
+          NotifyUiDisplayPasskeyInput();
+          LOG_INFO("Passkey Entry: A input, B display");
+          // Authenticated
+          break;
+        case hci::IoCapability::DISPLAY_YES_NO:
+          // PassKey Entry, Responder display, Initiator input
+          NotifyUiDisplayPasskeyInput();
+          LOG_INFO("Passkey Entry: A input, B display");
+          // Authenticated
+          break;
+        case hci::IoCapability::KEYBOARD_ONLY:
+          // PassKey Entry, both input
+          NotifyUiDisplayPasskeyInput();
+          LOG_INFO("Passkey Entry: A input, B input");
+          // Authenticated
+          break;
+        case hci::IoCapability::NO_INPUT_NO_OUTPUT:
+          // NumericComparison, both auto confirm
+          LOG_INFO("Numeric Comparison: A and B auto confirm");
+          GetChannel()->SendCommand(
+              hci::UserConfirmationRequestReplyBuilder::Create(GetRecord()->GetPseudoAddress().GetAddress()));
+          // Unauthenticated
+          break;
+      }
+      break;
+    case hci::IoCapability::NO_INPUT_NO_OUTPUT:
+      switch (responder_io_capability) {
+        case hci::IoCapability::DISPLAY_ONLY:
+          // NumericComparison, both auto confirm
+          LOG_INFO("Numeric Comparison: A and B auto confirm");
+          GetChannel()->SendCommand(
+              hci::UserConfirmationRequestReplyBuilder::Create(GetRecord()->GetPseudoAddress().GetAddress()));
+          // Unauthenticated
+          break;
+        case hci::IoCapability::DISPLAY_YES_NO:
+          // NumericComparison, Initiator auto confirm, Responder Yes/No confirm, no show conf val
+          LOG_INFO("Numeric Comparison: A auto confirm");
+          GetChannel()->SendCommand(
+              hci::UserConfirmationRequestReplyBuilder::Create(GetRecord()->GetPseudoAddress().GetAddress()));
+          // Unauthenticated
+          break;
+        case hci::IoCapability::KEYBOARD_ONLY:
+          // NumericComparison, both auto confirm
+          LOG_INFO("Numeric Comparison: A and B auto confirm");
+          GetChannel()->SendCommand(
+              hci::UserConfirmationRequestReplyBuilder::Create(GetRecord()->GetPseudoAddress().GetAddress()));
+          // Unauthenticated
+          break;
+        case hci::IoCapability::NO_INPUT_NO_OUTPUT:
+          // NumericComparison, both auto confirm
+          LOG_INFO("Numeric Comparison: A and B auto confirm");
+          GetChannel()->SendCommand(
+              hci::UserConfirmationRequestReplyBuilder::Create(GetRecord()->GetPseudoAddress().GetAddress()));
+          // Unauthenticated
+          break;
+      }
+      break;
+  }
+}
+
+void ClassicPairingHandler::OnReceive(hci::UserPasskeyRequestView packet) {
+  ASSERT(packet.IsValid());
+  ASSERT_LOG(GetRecord()->GetPseudoAddress().GetAddress() == packet.GetBdAddr(), "Address mismatch");
+}
+
+void ClassicPairingHandler::OnUserInput(bool user_input) {
+  if (user_input) {
+    UserClickedYes();
+  } else {
+    UserClickedNo();
+  }
+}
+
+void ClassicPairingHandler::UserClickedYes() {
+  GetChannel()->SendCommand(
+      hci::UserConfirmationRequestReplyBuilder::Create(GetRecord()->GetPseudoAddress().GetAddress()));
+}
+
+void ClassicPairingHandler::UserClickedNo() {
+  GetChannel()->SendCommand(
+      hci::UserConfirmationRequestNegativeReplyBuilder::Create(GetRecord()->GetPseudoAddress().GetAddress()));
+}
+
+void ClassicPairingHandler::OnPasskeyInput(uint32_t passkey) {
+  passkey_ = passkey;
+}
+
+}  // namespace pairing
+}  // namespace security
+}  // namespace bluetooth
diff --git a/gd/security/pairing/classic_pairing_handler.h b/gd/security/pairing/classic_pairing_handler.h
new file mode 100644
index 0000000..86cb358
--- /dev/null
+++ b/gd/security/pairing/classic_pairing_handler.h
@@ -0,0 +1,121 @@
+/*
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License") override;
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+#pragma once
+
+#include "security/pairing/pairing_handler.h"
+
+#include <utility>
+
+#include "common/callback.h"
+#include "l2cap/classic/l2cap_classic_module.h"
+#include "security/initial_informations.h"
+#include "security/security_manager_listener.h"
+
+namespace bluetooth {
+namespace security {
+
+class ISecurityManagerListener;
+
+namespace pairing {
+
+static constexpr hci::IoCapability kDefaultIoCapability = hci::IoCapability::DISPLAY_YES_NO;
+static constexpr hci::OobDataPresent kDefaultOobDataPresent = hci::OobDataPresent::NOT_PRESENT;
+static constexpr hci::AuthenticationRequirements kDefaultAuthenticationRequirements =
+    hci::AuthenticationRequirements::DEDICATED_BONDING_MITM_PROTECTION;
+
+class ClassicPairingHandler : public PairingHandler {
+ public:
+  ClassicPairingHandler(std::shared_ptr<l2cap::classic::FixedChannelManager> fixed_channel_manager,
+                        channel::SecurityManagerChannel* security_manager_channel,
+                        std::shared_ptr<record::SecurityRecord> record, os::Handler* security_handler,
+                        common::OnceCallback<void(hci::Address, PairingResultOrFailure)> complete_callback,
+                        UI* user_interface, os::Handler* user_interface_handler, std::string device_name)
+      : PairingHandler(security_manager_channel, std::move(record)),
+        fixed_channel_manager_(std::move(fixed_channel_manager)), security_policy_(),
+        security_handler_(security_handler), remote_io_capability_(kDefaultIoCapability),
+        local_io_capability_(kDefaultIoCapability), local_oob_present_(kDefaultOobDataPresent),
+        local_authentication_requirements_(kDefaultAuthenticationRequirements),
+        complete_callback_(std::move(complete_callback)), user_interface_(user_interface),
+        user_interface_handler_(user_interface_handler), device_name_(device_name) {}
+
+  ~ClassicPairingHandler() override = default;
+
+  void Initiate(bool locally_initiated, hci::IoCapability io_capability, hci::OobDataPresent oob_present,
+                hci::AuthenticationRequirements auth_requirements) override;
+  void Cancel() override;
+
+  void OnReceive(hci::ChangeConnectionLinkKeyCompleteView packet) override;
+  void OnReceive(hci::MasterLinkKeyCompleteView packet) override;
+  void OnReceive(hci::PinCodeRequestView packet) override;
+  void OnReceive(hci::LinkKeyRequestView packet) override;
+  void OnReceive(hci::LinkKeyNotificationView packet) override;
+  void OnReceive(hci::IoCapabilityRequestView packet) override;
+  void OnReceive(hci::IoCapabilityResponseView packet) override;
+  void OnReceive(hci::SimplePairingCompleteView packet) override;
+  void OnReceive(hci::ReturnLinkKeysView packet) override;
+  void OnReceive(hci::EncryptionChangeView packet) override;
+  void OnReceive(hci::EncryptionKeyRefreshCompleteView packet) override;
+  void OnReceive(hci::RemoteOobDataRequestView packet) override;
+  void OnReceive(hci::UserPasskeyNotificationView packet) override;
+  void OnReceive(hci::KeypressNotificationView packet) override;
+  void OnReceive(hci::UserConfirmationRequestView packet) override;
+  void OnReceive(hci::UserPasskeyRequestView packet) override;
+
+  void OnPairingPromptAccepted(const bluetooth::hci::AddressWithType& address, bool confirmed) override;
+  void OnConfirmYesNo(const bluetooth::hci::AddressWithType& address, bool confirmed) override;
+  void OnPasskeyEntry(const bluetooth::hci::AddressWithType& address, uint32_t passkey) override;
+
+ private:
+  void OnRegistrationComplete(l2cap::classic::FixedChannelManager::RegistrationResult result,
+                              std::unique_ptr<l2cap::classic::FixedChannelService> fixed_channel_service);
+  void OnUnregistered();
+  void OnConnectionOpen(std::unique_ptr<l2cap::classic::FixedChannel> fixed_channel);
+  void OnConnectionFail(l2cap::classic::FixedChannelManager::ConnectionResult result);
+  void OnConnectionClose(hci::ErrorCode error_code);
+  void OnUserInput(bool user_input);
+  void OnPasskeyInput(uint32_t passkey);
+  void NotifyUiDisplayYesNo(uint32_t numeric_value);
+  void NotifyUiDisplayYesNo();
+  void NotifyUiDisplayPasskey(uint32_t passkey);
+  void NotifyUiDisplayPasskeyInput();
+  void NotifyUiDisplayCancel();
+  void UserClickedYes();
+  void UserClickedNo();
+
+  std::shared_ptr<l2cap::classic::FixedChannelManager> fixed_channel_manager_;
+  std::unique_ptr<l2cap::classic::FixedChannelService> fixed_channel_service_{nullptr};
+  l2cap::SecurityPolicy security_policy_ __attribute__((unused));
+  os::Handler* security_handler_ __attribute__((unused));
+  hci::IoCapability remote_io_capability_ __attribute__((unused));
+  hci::IoCapability local_io_capability_ __attribute__((unused));
+  hci::OobDataPresent local_oob_present_ __attribute__((unused));
+  hci::AuthenticationRequirements local_authentication_requirements_ __attribute__((unused));
+  std::unique_ptr<l2cap::classic::FixedChannel> fixed_channel_{nullptr};
+  common::OnceCallback<void(hci::Address, PairingResultOrFailure)> complete_callback_;
+  UI* user_interface_;
+  os::Handler* user_interface_handler_;
+  std::string device_name_;
+
+  hci::ErrorCode last_status_;
+  bool locally_initiated_ = false;
+  uint32_t passkey_ = 0;
+};
+
+}  // namespace pairing
+}  // namespace security
+}  // namespace bluetooth
diff --git a/gd/security/pairing/classic_pairing_handler_unittest.cc b/gd/security/pairing/classic_pairing_handler_unittest.cc
new file mode 100644
index 0000000..d134ff8
--- /dev/null
+++ b/gd/security/pairing/classic_pairing_handler_unittest.cc
@@ -0,0 +1,529 @@
+/*
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+#include "security/pairing/classic_pairing_handler.h"
+
+#include <gtest/gtest.h>
+#include <memory>
+
+#include "hci/hci_packets.h"
+#include "l2cap/classic/fixed_channel_manager_mock.h"
+#include "packet/raw_builder.h"
+#include "security/channel/security_manager_channel.h"
+#include "security/initial_informations.h"
+#include "security/smp_packets.h"
+#include "security/test/fake_hci_layer.h"
+
+namespace bluetooth {
+namespace security {
+namespace channel {
+namespace {
+
+using bluetooth::security::channel::SecurityManagerChannel;
+using hci::Address;
+using hci::AuthenticationRequirements;
+using hci::CommandCompleteBuilder;
+using hci::IoCapabilityRequestReplyBuilder;
+using hci::IoCapabilityRequestView;
+using hci::OobDataPresent;
+using hci::OpCode;
+using os::Handler;
+using os::Thread;
+using packet::RawBuilder;
+
+class SecurityManagerChannelCallback : public ISecurityManagerChannelListener {
+ public:
+  explicit SecurityManagerChannelCallback(pairing::ClassicPairingHandler* pairing_handler)
+      : pairing_handler_(pairing_handler) {}
+  void OnHciEventReceived(hci::EventPacketView packet) override {
+    auto event = hci::EventPacketView::Create(packet);
+    ASSERT_LOG(event.IsValid(), "Received invalid packet");
+    const hci::EventCode code = event.GetEventCode();
+    switch (code) {
+      case hci::EventCode::PIN_CODE_REQUEST:
+        pairing_handler_->OnReceive(hci::PinCodeRequestView::Create(event));
+        break;
+      case hci::EventCode::LINK_KEY_REQUEST:
+        pairing_handler_->OnReceive(hci::LinkKeyRequestView::Create(event));
+        break;
+      case hci::EventCode::LINK_KEY_NOTIFICATION:
+        pairing_handler_->OnReceive(hci::LinkKeyNotificationView::Create(event));
+        break;
+      case hci::EventCode::IO_CAPABILITY_REQUEST:
+        pairing_handler_->OnReceive(hci::IoCapabilityRequestView::Create(event));
+        break;
+      case hci::EventCode::IO_CAPABILITY_RESPONSE:
+        pairing_handler_->OnReceive(hci::IoCapabilityResponseView::Create(event));
+        break;
+      case hci::EventCode::SIMPLE_PAIRING_COMPLETE:
+        pairing_handler_->OnReceive(hci::SimplePairingCompleteView::Create(event));
+        break;
+      case hci::EventCode::RETURN_LINK_KEYS:
+        pairing_handler_->OnReceive(hci::ReturnLinkKeysView::Create(event));
+        break;
+      case hci::EventCode::REMOTE_OOB_DATA_REQUEST:
+        pairing_handler_->OnReceive(hci::RemoteOobDataRequestView::Create(event));
+        break;
+      case hci::EventCode::USER_PASSKEY_NOTIFICATION:
+        pairing_handler_->OnReceive(hci::UserPasskeyNotificationView::Create(event));
+        break;
+      case hci::EventCode::KEYPRESS_NOTIFICATION:
+        pairing_handler_->OnReceive(hci::KeypressNotificationView::Create(event));
+        break;
+      case hci::EventCode::USER_CONFIRMATION_REQUEST:
+        pairing_handler_->OnReceive(hci::UserConfirmationRequestView::Create(event));
+        break;
+      case hci::EventCode::USER_PASSKEY_REQUEST:
+        pairing_handler_->OnReceive(hci::UserPasskeyRequestView::Create(event));
+        break;
+      default:
+        ASSERT_LOG(false, "Cannot handle received packet: %s", hci::EventCodeText(code).c_str());
+        break;
+    }
+  }
+
+ private:
+  pairing::ClassicPairingHandler* pairing_handler_ = nullptr;
+};
+
+static void pairing_complete_callback(bluetooth::hci::Address address, PairingResultOrFailure status) {
+  ASSERT(std::holds_alternative<PairingResult>(status));
+  // auto result = std::get<PairingResult>(status);
+  //  if (std::holds_alternative<PairingResult>(status)) {
+  //    auto result = status::get<PairingResult>(status);
+  //  }
+  //  if (std::holds_alternative<PairingFailure>(status)) {
+  //    auto failure = status::get<PairingFailure>(status);
+  //  }
+}
+
+class ClassicPairingHandlerTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    handler_ = new Handler(&thread_);
+    hci_layer_ = new FakeHciLayer();
+    channel_ = new channel::SecurityManagerChannel(handler_, hci_layer_);
+    fake_registry_.InjectTestModule(&FakeHciLayer::Factory, hci_layer_);
+    fake_registry_.Start<FakeHciLayer>(&thread_);
+    security_record_ = std::make_shared<record::SecurityRecord>(device_);
+    std::shared_ptr<l2cap::classic::testing::MockFixedChannelManager> sptr =
+        std::shared_ptr<l2cap::classic::testing::MockFixedChannelManager>(
+            new l2cap::classic::testing::MockFixedChannelManager());
+    EXPECT_CALL(*sptr, RegisterService(::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_))
+        .Times(::testing::AnyNumber());
+    pairing_handler_ = new pairing::ClassicPairingHandler(sptr, channel_, security_record_, handler_,
+                                                          common::Bind(&pairing_complete_callback), user_interface_,
+                                                          user_interface_handler_, "Fake name");
+    channel_callback_ = new SecurityManagerChannelCallback(pairing_handler_);
+    channel_->SetChannelListener(channel_callback_);
+  }
+
+  void TearDown() override {
+    channel_->SetChannelListener(nullptr);
+    handler_->Clear();
+    fake_registry_.SynchronizeModuleHandler(&FakeHciLayer::Factory, std::chrono::milliseconds(20));
+    fake_registry_.StopAll();
+    delete pairing_handler_;
+    delete handler_;
+    delete channel_;
+    delete channel_callback_;
+  }
+
+  TestModuleRegistry fake_registry_;
+  Thread& thread_ = fake_registry_.GetTestThread();
+  Handler* handler_ = nullptr;
+  FakeHciLayer* hci_layer_ = nullptr;
+  hci::AddressWithType device_;
+  SecurityManagerChannelCallback* channel_callback_ = nullptr;
+  channel::SecurityManagerChannel* channel_ = nullptr;
+  pairing::ClassicPairingHandler* pairing_handler_ = nullptr;
+  std::shared_ptr<record::SecurityRecord> security_record_ = nullptr;
+  UI* user_interface_;
+  os::Handler* user_interface_handler_;
+};
+
+// Security Manager Boot Sequence (Required for SSP, these are already set at boot time)
+//  - WriteSimplePairingMode
+//  - WriteSecureConnectionsHostSupport
+//  - WriteAuthenticatedPayloadTimeout
+
+/*** Locally initiated ***/
+// Security Pairing Sequence (JustWorks)
+//  -> *Establish L2CAP connection*
+//  -> AuthenticationRequested (L2CAP handles this)
+//  <- LinkKeyRequest   // This is entry point for remote initiated
+//  -> LinkKeyRequestNegativeReply
+//  <- IoCapabilityRequest
+//  -> IoCapabilityRequestReply
+//  <- IoCapabilityResponse
+//  <- UserConfirmationRequest
+//  -> UserConfirmationRequestReply (auto)
+//  <- SimplePairingComplete
+//  <- LinkKeyNotification
+//  <- AuthenticationComplete
+//  -> SetConnectionEncryption
+//  <- EncryptionChange
+//  -> L2capConnectionResponse (if triggered by L2cap connection request)
+
+void ReceiveLinkKeyRequest(FakeHciLayer* hci_layer, hci::AddressWithType device) {
+  hci_layer->IncomingEvent(hci::LinkKeyRequestBuilder::Create(device.GetAddress()));
+}
+
+void ReceiveIoCapabilityRequest(FakeHciLayer* hci_layer, hci::AddressWithType device) {
+  hci_layer->IncomingEvent(hci::IoCapabilityRequestBuilder::Create(device.GetAddress()));
+}
+
+void ReceiveIoCapabilityResponse(FakeHciLayer* hci_layer, hci::AddressWithType device, hci::IoCapability io_cap,
+                                 hci::OobDataPresent oob_present, hci::AuthenticationRequirements auth_reqs) {
+  hci_layer->IncomingEvent(
+      hci::IoCapabilityResponseBuilder::Create(device.GetAddress(), io_cap, oob_present, auth_reqs));
+}
+
+void ReceiveUserConfirmationRequest(FakeHciLayer* hci_layer, hci::AddressWithType device, uint32_t numeric_value) {
+  hci_layer->IncomingEvent(hci::UserConfirmationRequestBuilder::Create(device.GetAddress(), numeric_value));
+}
+
+void ReceiveSimplePairingComplete(FakeHciLayer* hci_layer, hci::ErrorCode status, hci::AddressWithType device) {
+  hci_layer->IncomingEvent(hci::SimplePairingCompleteBuilder::Create(status, device.GetAddress()));
+}
+
+void ReceiveLinkKeyNotification(FakeHciLayer* hci_layer, hci::AddressWithType device, std::array<uint8_t, 16> link_key,
+                                hci::KeyType key_type) {
+  hci_layer->IncomingEvent(hci::LinkKeyNotificationBuilder::Create(device.GetAddress(), link_key, key_type));
+}
+
+hci::SecurityCommandView GetLastCommand(FakeHciLayer* hci_layer) {
+  auto last_command = std::move(hci_layer->GetLastCommand()->command);
+  auto command_packet = GetPacketView(std::move(last_command));
+  auto command_packet_view = hci::CommandPacketView::Create(command_packet);
+  ASSERT(command_packet_view.IsValid());
+  auto security_command_view = hci::SecurityCommandView::Create(command_packet_view);
+  ASSERT(security_command_view.IsValid());
+  return security_command_view;
+}
+
+TEST_F(ClassicPairingHandlerTest, setup_teardown) {}
+
+/*** JustWorks (Numeric Comparison w/ no UI) ***/
+// display_only + display_only is JustWorks no confirmation
+// Needs dialog as per security a bug unless pairing is temporary
+TEST_F(ClassicPairingHandlerTest, locally_initiatied_display_only_display_only_temp) {
+  hci::IoCapability injected_io_capability = hci::IoCapability::DISPLAY_ONLY;
+  hci::AuthenticationRequirements injected_authentication_requirements = hci::AuthenticationRequirements::NO_BONDING;
+  pairing_handler_->Initiate(true, injected_io_capability, hci::OobDataPresent::NOT_PRESENT,
+                             injected_authentication_requirements);
+  ReceiveLinkKeyRequest(hci_layer_, device_);
+  auto security_command_view = GetLastCommand(hci_layer_);
+  auto link_key_neg_reply = hci::LinkKeyRequestNegativeReplyView::Create(security_command_view);
+  ASSERT_TRUE(link_key_neg_reply.IsValid());
+  ASSERT_EQ(OpCode::LINK_KEY_REQUEST_NEGATIVE_REPLY, link_key_neg_reply.GetOpCode());
+  ReceiveIoCapabilityRequest(hci_layer_, device_);
+  security_command_view = GetLastCommand(hci_layer_);
+  ASSERT_EQ(OpCode::IO_CAPABILITY_REQUEST_REPLY, security_command_view.GetOpCode());
+  auto io_cap_request_reply = hci::IoCapabilityRequestReplyView::Create(security_command_view);
+  ASSERT_TRUE(io_cap_request_reply.IsValid());
+  ASSERT_EQ(injected_io_capability, io_cap_request_reply.GetIoCapability());
+  ASSERT_EQ(hci::OobDataPresent::NOT_PRESENT, io_cap_request_reply.GetOobPresent());
+  ASSERT_EQ(injected_authentication_requirements, io_cap_request_reply.GetAuthenticationRequirements());
+  ReceiveIoCapabilityResponse(hci_layer_, device_, hci::IoCapability::DISPLAY_ONLY, hci::OobDataPresent::NOT_PRESENT,
+                              hci::AuthenticationRequirements::NO_BONDING);
+  uint32_t numeric_value = 0x123;
+  ReceiveUserConfirmationRequest(hci_layer_, device_, numeric_value);
+  security_command_view = GetLastCommand(hci_layer_);
+  ASSERT_EQ(OpCode::USER_CONFIRMATION_REQUEST_REPLY, security_command_view.GetOpCode());
+  auto user_conf_request_reply = hci::UserConfirmationRequestReplyView::Create(security_command_view);
+  ASSERT_TRUE(user_conf_request_reply.IsValid());
+  ReceiveSimplePairingComplete(hci_layer_, hci::ErrorCode::SUCCESS, device_);
+  std::array<uint8_t, 16> link_key = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5};
+  hci::KeyType key_type = hci::KeyType::DEBUG_COMBINATION;
+  ReceiveLinkKeyNotification(hci_layer_, device_, link_key, key_type);
+  ASSERT_EQ(link_key, security_record_->GetLinkKey());
+  ASSERT_EQ(key_type, security_record_->GetKeyType());
+}
+
+// display_only + display_yes_no is JustWorks no confirmation
+// Needs dialog as per security a bug unless pairing is temporary
+TEST_F(ClassicPairingHandlerTest, locally_initiatied_display_only_display_yes_no_temp) {
+  hci::IoCapability injected_io_capability = hci::IoCapability::DISPLAY_ONLY;
+  hci::AuthenticationRequirements injected_authentication_requirements = hci::AuthenticationRequirements::NO_BONDING;
+  pairing_handler_->Initiate(true, injected_io_capability, hci::OobDataPresent::NOT_PRESENT,
+                             injected_authentication_requirements);
+  ReceiveLinkKeyRequest(hci_layer_, device_);
+  auto security_command_view = GetLastCommand(hci_layer_);
+  auto link_key_neg_reply = hci::LinkKeyRequestNegativeReplyView::Create(security_command_view);
+  ASSERT_TRUE(link_key_neg_reply.IsValid());
+  ASSERT_EQ(OpCode::LINK_KEY_REQUEST_NEGATIVE_REPLY, link_key_neg_reply.GetOpCode());
+  ReceiveIoCapabilityRequest(hci_layer_, device_);
+  security_command_view = GetLastCommand(hci_layer_);
+  ASSERT_EQ(OpCode::IO_CAPABILITY_REQUEST_REPLY, security_command_view.GetOpCode());
+  auto io_cap_request_reply = hci::IoCapabilityRequestReplyView::Create(security_command_view);
+  ASSERT_TRUE(io_cap_request_reply.IsValid());
+  ASSERT_EQ(injected_io_capability, io_cap_request_reply.GetIoCapability());
+  ASSERT_EQ(hci::OobDataPresent::NOT_PRESENT, io_cap_request_reply.GetOobPresent());
+  ASSERT_EQ(injected_authentication_requirements, io_cap_request_reply.GetAuthenticationRequirements());
+  ReceiveIoCapabilityResponse(hci_layer_, device_, hci::IoCapability::DISPLAY_YES_NO, hci::OobDataPresent::NOT_PRESENT,
+                              hci::AuthenticationRequirements::NO_BONDING);
+  uint32_t numeric_value = 0x123;
+  ReceiveUserConfirmationRequest(hci_layer_, device_, numeric_value);
+  security_command_view = GetLastCommand(hci_layer_);
+  ASSERT_EQ(OpCode::USER_CONFIRMATION_REQUEST_REPLY, security_command_view.GetOpCode());
+  auto user_conf_request_reply = hci::UserConfirmationRequestReplyView::Create(security_command_view);
+  ASSERT_TRUE(user_conf_request_reply.IsValid());
+  ReceiveSimplePairingComplete(hci_layer_, hci::ErrorCode::SUCCESS, device_);
+  std::array<uint8_t, 16> link_key = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5};
+  hci::KeyType key_type = hci::KeyType::DEBUG_COMBINATION;
+  ReceiveLinkKeyNotification(hci_layer_, device_, link_key, key_type);
+  ASSERT_EQ(link_key, security_record_->GetLinkKey());
+  ASSERT_EQ(key_type, security_record_->GetKeyType());
+}
+
+// display_only + no_input_no_output is JustWorks no confirmation
+// Needs dialog as per security a bug unless pairing is temporary
+TEST_F(ClassicPairingHandlerTest, locally_initiatied_display_only_no_input_no_output_temp) {
+  hci::IoCapability injected_io_capability = hci::IoCapability::DISPLAY_ONLY;
+  hci::AuthenticationRequirements injected_authentication_requirements = hci::AuthenticationRequirements::NO_BONDING;
+  pairing_handler_->Initiate(true, injected_io_capability, hci::OobDataPresent::NOT_PRESENT,
+                             injected_authentication_requirements);
+  ReceiveLinkKeyRequest(hci_layer_, device_);
+  auto security_command_view = GetLastCommand(hci_layer_);
+  auto link_key_neg_reply = hci::LinkKeyRequestNegativeReplyView::Create(security_command_view);
+  ASSERT_TRUE(link_key_neg_reply.IsValid());
+  ASSERT_EQ(OpCode::LINK_KEY_REQUEST_NEGATIVE_REPLY, link_key_neg_reply.GetOpCode());
+  ReceiveIoCapabilityRequest(hci_layer_, device_);
+  security_command_view = GetLastCommand(hci_layer_);
+  ASSERT_EQ(OpCode::IO_CAPABILITY_REQUEST_REPLY, security_command_view.GetOpCode());
+  auto io_cap_request_reply = hci::IoCapabilityRequestReplyView::Create(security_command_view);
+  ASSERT_TRUE(io_cap_request_reply.IsValid());
+  ASSERT_EQ(injected_io_capability, io_cap_request_reply.GetIoCapability());
+  ASSERT_EQ(hci::OobDataPresent::NOT_PRESENT, io_cap_request_reply.GetOobPresent());
+  ASSERT_EQ(injected_authentication_requirements, io_cap_request_reply.GetAuthenticationRequirements());
+  ReceiveIoCapabilityResponse(hci_layer_, device_, hci::IoCapability::NO_INPUT_NO_OUTPUT,
+                              hci::OobDataPresent::NOT_PRESENT, hci::AuthenticationRequirements::NO_BONDING);
+  uint32_t numeric_value = 0x123;
+  ReceiveUserConfirmationRequest(hci_layer_, device_, numeric_value);
+  security_command_view = GetLastCommand(hci_layer_);
+  ASSERT_EQ(OpCode::USER_CONFIRMATION_REQUEST_REPLY, security_command_view.GetOpCode());
+  auto user_conf_request_reply = hci::UserConfirmationRequestReplyView::Create(security_command_view);
+  ASSERT_TRUE(user_conf_request_reply.IsValid());
+  ReceiveSimplePairingComplete(hci_layer_, hci::ErrorCode::SUCCESS, device_);
+  std::array<uint8_t, 16> link_key = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5};
+  hci::KeyType key_type = hci::KeyType::DEBUG_COMBINATION;
+  ReceiveLinkKeyNotification(hci_layer_, device_, link_key, key_type);
+  ASSERT_EQ(link_key, security_record_->GetLinkKey());
+  ASSERT_EQ(key_type, security_record_->GetKeyType());
+}
+
+// keyboard_only + no_input_no_output is JustWorks no confirmation
+// Needs dialog as per security a bug unless pairing is temporary
+TEST_F(ClassicPairingHandlerTest, locally_initiatied_keyboard_only_no_input_no_output_temp) {
+  hci::IoCapability injected_io_capability = hci::IoCapability::KEYBOARD_ONLY;
+  hci::AuthenticationRequirements injected_authentication_requirements = hci::AuthenticationRequirements::NO_BONDING;
+  pairing_handler_->Initiate(true, injected_io_capability, hci::OobDataPresent::NOT_PRESENT,
+                             injected_authentication_requirements);
+  ReceiveLinkKeyRequest(hci_layer_, device_);
+  auto security_command_view = GetLastCommand(hci_layer_);
+  auto link_key_neg_reply = hci::LinkKeyRequestNegativeReplyView::Create(security_command_view);
+  ASSERT_TRUE(link_key_neg_reply.IsValid());
+  ASSERT_EQ(OpCode::LINK_KEY_REQUEST_NEGATIVE_REPLY, link_key_neg_reply.GetOpCode());
+  ReceiveIoCapabilityRequest(hci_layer_, device_);
+  security_command_view = GetLastCommand(hci_layer_);
+  ASSERT_EQ(OpCode::IO_CAPABILITY_REQUEST_REPLY, security_command_view.GetOpCode());
+  auto io_cap_request_reply = hci::IoCapabilityRequestReplyView::Create(security_command_view);
+  ASSERT_TRUE(io_cap_request_reply.IsValid());
+  ASSERT_EQ(injected_io_capability, io_cap_request_reply.GetIoCapability());
+  ASSERT_EQ(hci::OobDataPresent::NOT_PRESENT, io_cap_request_reply.GetOobPresent());
+  ASSERT_EQ(injected_authentication_requirements, io_cap_request_reply.GetAuthenticationRequirements());
+  ReceiveIoCapabilityResponse(hci_layer_, device_, hci::IoCapability::NO_INPUT_NO_OUTPUT,
+                              hci::OobDataPresent::NOT_PRESENT, hci::AuthenticationRequirements::NO_BONDING);
+  uint32_t numeric_value = 0x123;
+  ReceiveUserConfirmationRequest(hci_layer_, device_, numeric_value);
+  security_command_view = GetLastCommand(hci_layer_);
+  ASSERT_EQ(OpCode::USER_CONFIRMATION_REQUEST_REPLY, security_command_view.GetOpCode());
+  auto user_conf_request_reply = hci::UserConfirmationRequestReplyView::Create(security_command_view);
+  ASSERT_TRUE(user_conf_request_reply.IsValid());
+  ReceiveSimplePairingComplete(hci_layer_, hci::ErrorCode::SUCCESS, device_);
+  std::array<uint8_t, 16> link_key = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5};
+  hci::KeyType key_type = hci::KeyType::DEBUG_COMBINATION;
+  ReceiveLinkKeyNotification(hci_layer_, device_, link_key, key_type);
+  ASSERT_EQ(link_key, security_record_->GetLinkKey());
+  ASSERT_EQ(key_type, security_record_->GetKeyType());
+}
+
+// no_input_no_output + display_only is JustWorks no confirmation
+// Needs dialog as per security a bug unless pairing is temporary
+TEST_F(ClassicPairingHandlerTest, locally_initiatied_no_input_no_output_display_only_temp) {
+  hci::IoCapability injected_io_capability = hci::IoCapability::NO_INPUT_NO_OUTPUT;
+  hci::AuthenticationRequirements injected_authentication_requirements = hci::AuthenticationRequirements::NO_BONDING;
+  pairing_handler_->Initiate(true, injected_io_capability, hci::OobDataPresent::NOT_PRESENT,
+                             injected_authentication_requirements);
+  ReceiveLinkKeyRequest(hci_layer_, device_);
+  auto security_command_view = GetLastCommand(hci_layer_);
+  auto link_key_neg_reply = hci::LinkKeyRequestNegativeReplyView::Create(security_command_view);
+  ASSERT_TRUE(link_key_neg_reply.IsValid());
+  ASSERT_EQ(OpCode::LINK_KEY_REQUEST_NEGATIVE_REPLY, link_key_neg_reply.GetOpCode());
+  ReceiveIoCapabilityRequest(hci_layer_, device_);
+  security_command_view = GetLastCommand(hci_layer_);
+  ASSERT_EQ(OpCode::IO_CAPABILITY_REQUEST_REPLY, security_command_view.GetOpCode());
+  auto io_cap_request_reply = hci::IoCapabilityRequestReplyView::Create(security_command_view);
+  ASSERT_TRUE(io_cap_request_reply.IsValid());
+  ASSERT_EQ(injected_io_capability, io_cap_request_reply.GetIoCapability());
+  ASSERT_EQ(hci::OobDataPresent::NOT_PRESENT, io_cap_request_reply.GetOobPresent());
+  ASSERT_EQ(injected_authentication_requirements, io_cap_request_reply.GetAuthenticationRequirements());
+  ReceiveIoCapabilityResponse(hci_layer_, device_, hci::IoCapability::DISPLAY_ONLY, hci::OobDataPresent::NOT_PRESENT,
+                              hci::AuthenticationRequirements::NO_BONDING);
+  uint32_t numeric_value = 0x123;
+  ReceiveUserConfirmationRequest(hci_layer_, device_, numeric_value);
+  security_command_view = GetLastCommand(hci_layer_);
+  ASSERT_EQ(OpCode::USER_CONFIRMATION_REQUEST_REPLY, security_command_view.GetOpCode());
+  auto user_conf_request_reply = hci::UserConfirmationRequestReplyView::Create(security_command_view);
+  ASSERT_TRUE(user_conf_request_reply.IsValid());
+  ReceiveSimplePairingComplete(hci_layer_, hci::ErrorCode::SUCCESS, device_);
+  std::array<uint8_t, 16> link_key = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5};
+  hci::KeyType key_type = hci::KeyType::DEBUG_COMBINATION;
+  ReceiveLinkKeyNotification(hci_layer_, device_, link_key, key_type);
+  ASSERT_EQ(link_key, security_record_->GetLinkKey());
+  ASSERT_EQ(key_type, security_record_->GetKeyType());
+}
+
+// no_input_no_output + display_yes_no is JustWorks no confirmation
+// Needs dialog as per security a bug unless pairing is temporary
+TEST_F(ClassicPairingHandlerTest, locally_initiatied_no_input_no_output_display_yes_no_temp) {
+  hci::IoCapability injected_io_capability = hci::IoCapability::NO_INPUT_NO_OUTPUT;
+  hci::AuthenticationRequirements injected_authentication_requirements = hci::AuthenticationRequirements::NO_BONDING;
+  pairing_handler_->Initiate(true, injected_io_capability, hci::OobDataPresent::NOT_PRESENT,
+                             injected_authentication_requirements);
+  ReceiveLinkKeyRequest(hci_layer_, device_);
+  auto security_command_view = GetLastCommand(hci_layer_);
+  auto link_key_neg_reply = hci::LinkKeyRequestNegativeReplyView::Create(security_command_view);
+  ASSERT_TRUE(link_key_neg_reply.IsValid());
+  ASSERT_EQ(OpCode::LINK_KEY_REQUEST_NEGATIVE_REPLY, link_key_neg_reply.GetOpCode());
+  ReceiveIoCapabilityRequest(hci_layer_, device_);
+  security_command_view = GetLastCommand(hci_layer_);
+  ASSERT_EQ(OpCode::IO_CAPABILITY_REQUEST_REPLY, security_command_view.GetOpCode());
+  auto io_cap_request_reply = hci::IoCapabilityRequestReplyView::Create(security_command_view);
+  ASSERT_TRUE(io_cap_request_reply.IsValid());
+  ASSERT_EQ(injected_io_capability, io_cap_request_reply.GetIoCapability());
+  ASSERT_EQ(hci::OobDataPresent::NOT_PRESENT, io_cap_request_reply.GetOobPresent());
+  ASSERT_EQ(injected_authentication_requirements, io_cap_request_reply.GetAuthenticationRequirements());
+  ReceiveIoCapabilityResponse(hci_layer_, device_, hci::IoCapability::DISPLAY_YES_NO, hci::OobDataPresent::NOT_PRESENT,
+                              hci::AuthenticationRequirements::NO_BONDING);
+  uint32_t numeric_value = 0x123;
+  ReceiveUserConfirmationRequest(hci_layer_, device_, numeric_value);
+  security_command_view = GetLastCommand(hci_layer_);
+  ASSERT_EQ(OpCode::USER_CONFIRMATION_REQUEST_REPLY, security_command_view.GetOpCode());
+  auto user_conf_request_reply = hci::UserConfirmationRequestReplyView::Create(security_command_view);
+  ASSERT_TRUE(user_conf_request_reply.IsValid());
+  ReceiveSimplePairingComplete(hci_layer_, hci::ErrorCode::SUCCESS, device_);
+  std::array<uint8_t, 16> link_key = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5};
+  hci::KeyType key_type = hci::KeyType::DEBUG_COMBINATION;
+  ReceiveLinkKeyNotification(hci_layer_, device_, link_key, key_type);
+  ASSERT_EQ(link_key, security_record_->GetLinkKey());
+  ASSERT_EQ(key_type, security_record_->GetKeyType());
+}
+
+// no_input_no_output + keyboard_only is JustWorks no confirmation
+// Needs dialog as per security a bug unless pairing is temporary
+TEST_F(ClassicPairingHandlerTest, locally_initiatied_no_input_no_output_keyboard_only_temp) {
+  hci::IoCapability injected_io_capability = hci::IoCapability::NO_INPUT_NO_OUTPUT;
+  hci::AuthenticationRequirements injected_authentication_requirements = hci::AuthenticationRequirements::NO_BONDING;
+  pairing_handler_->Initiate(true, injected_io_capability, hci::OobDataPresent::NOT_PRESENT,
+                             injected_authentication_requirements);
+  ReceiveLinkKeyRequest(hci_layer_, device_);
+  auto security_command_view = GetLastCommand(hci_layer_);
+  auto link_key_neg_reply = hci::LinkKeyRequestNegativeReplyView::Create(security_command_view);
+  ASSERT_TRUE(link_key_neg_reply.IsValid());
+  ASSERT_EQ(OpCode::LINK_KEY_REQUEST_NEGATIVE_REPLY, link_key_neg_reply.GetOpCode());
+  ReceiveIoCapabilityRequest(hci_layer_, device_);
+  security_command_view = GetLastCommand(hci_layer_);
+  ASSERT_EQ(OpCode::IO_CAPABILITY_REQUEST_REPLY, security_command_view.GetOpCode());
+  auto io_cap_request_reply = hci::IoCapabilityRequestReplyView::Create(security_command_view);
+  ASSERT_TRUE(io_cap_request_reply.IsValid());
+  ASSERT_EQ(injected_io_capability, io_cap_request_reply.GetIoCapability());
+  ASSERT_EQ(hci::OobDataPresent::NOT_PRESENT, io_cap_request_reply.GetOobPresent());
+  ASSERT_EQ(injected_authentication_requirements, io_cap_request_reply.GetAuthenticationRequirements());
+  ReceiveIoCapabilityResponse(hci_layer_, device_, hci::IoCapability::KEYBOARD_ONLY, hci::OobDataPresent::NOT_PRESENT,
+                              hci::AuthenticationRequirements::NO_BONDING);
+  uint32_t numeric_value = 0x123;
+  ReceiveUserConfirmationRequest(hci_layer_, device_, numeric_value);
+  security_command_view = GetLastCommand(hci_layer_);
+  ASSERT_EQ(OpCode::USER_CONFIRMATION_REQUEST_REPLY, security_command_view.GetOpCode());
+  auto user_conf_request_reply = hci::UserConfirmationRequestReplyView::Create(security_command_view);
+  ASSERT_TRUE(user_conf_request_reply.IsValid());
+  ReceiveSimplePairingComplete(hci_layer_, hci::ErrorCode::SUCCESS, device_);
+  std::array<uint8_t, 16> link_key = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5};
+  hci::KeyType key_type = hci::KeyType::DEBUG_COMBINATION;
+  ReceiveLinkKeyNotification(hci_layer_, device_, link_key, key_type);
+  ASSERT_EQ(link_key, security_record_->GetLinkKey());
+  ASSERT_EQ(key_type, security_record_->GetKeyType());
+}
+
+// no_input_no_output + no_input_no_output is JustWorks no confirmation
+// Needs dialog as per security a bug unless pairing is temporary
+TEST_F(ClassicPairingHandlerTest, locally_initiatied_no_input_no_output_no_input_no_output_temp) {
+  hci::IoCapability injected_io_capability = hci::IoCapability::NO_INPUT_NO_OUTPUT;
+  hci::AuthenticationRequirements injected_authentication_requirements = hci::AuthenticationRequirements::NO_BONDING;
+  pairing_handler_->Initiate(true, injected_io_capability, hci::OobDataPresent::NOT_PRESENT,
+                             injected_authentication_requirements);
+  ReceiveLinkKeyRequest(hci_layer_, device_);
+  auto security_command_view = GetLastCommand(hci_layer_);
+  auto link_key_neg_reply = hci::LinkKeyRequestNegativeReplyView::Create(security_command_view);
+  ASSERT_TRUE(link_key_neg_reply.IsValid());
+  ASSERT_EQ(OpCode::LINK_KEY_REQUEST_NEGATIVE_REPLY, link_key_neg_reply.GetOpCode());
+  ReceiveIoCapabilityRequest(hci_layer_, device_);
+  security_command_view = GetLastCommand(hci_layer_);
+  ASSERT_EQ(OpCode::IO_CAPABILITY_REQUEST_REPLY, security_command_view.GetOpCode());
+  auto io_cap_request_reply = hci::IoCapabilityRequestReplyView::Create(security_command_view);
+  ASSERT_TRUE(io_cap_request_reply.IsValid());
+  ASSERT_EQ(injected_io_capability, io_cap_request_reply.GetIoCapability());
+  ASSERT_EQ(hci::OobDataPresent::NOT_PRESENT, io_cap_request_reply.GetOobPresent());
+  ASSERT_EQ(injected_authentication_requirements, io_cap_request_reply.GetAuthenticationRequirements());
+  ReceiveIoCapabilityResponse(hci_layer_, device_, hci::IoCapability::NO_INPUT_NO_OUTPUT,
+                              hci::OobDataPresent::NOT_PRESENT, hci::AuthenticationRequirements::NO_BONDING);
+  uint32_t numeric_value = 0x123;
+  ReceiveUserConfirmationRequest(hci_layer_, device_, numeric_value);
+  security_command_view = GetLastCommand(hci_layer_);
+  ASSERT_EQ(OpCode::USER_CONFIRMATION_REQUEST_REPLY, security_command_view.GetOpCode());
+  auto user_conf_request_reply = hci::UserConfirmationRequestReplyView::Create(security_command_view);
+  ASSERT_TRUE(user_conf_request_reply.IsValid());
+  ReceiveSimplePairingComplete(hci_layer_, hci::ErrorCode::SUCCESS, device_);
+  std::array<uint8_t, 16> link_key = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5};
+  hci::KeyType key_type = hci::KeyType::DEBUG_COMBINATION;
+  ReceiveLinkKeyNotification(hci_layer_, device_, link_key, key_type);
+  ASSERT_EQ(link_key, security_record_->GetLinkKey());
+  ASSERT_EQ(key_type, security_record_->GetKeyType());
+}
+
+/*** Numeric Comparison ***/
+// display_yes_no + display_only
+
+// display_yes_no + display_yes_no
+// display_yes_no + keyboard_only
+// display_yes_no + no_input_no_output
+
+// keyboard_only + display_only
+// keyboard_only + display_yes_no
+
+// keyboard_only + keyboard_only  (a just works I missed)
+
+// Remotely initiated
+
+// Collisions
+
+}  // namespace
+}  // namespace channel
+}  // namespace security
+}  // namespace bluetooth
diff --git a/gd/security/pairing/pairing_handler.h b/gd/security/pairing/pairing_handler.h
new file mode 100644
index 0000000..8047147
--- /dev/null
+++ b/gd/security/pairing/pairing_handler.h
@@ -0,0 +1,85 @@
+/*
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+#pragma once
+
+#include <utility>
+
+#include "hci/address_with_type.h"
+#include "hci/hci_packets.h"
+#include "security/channel/security_manager_channel.h"
+#include "security/record/security_record.h"
+#include "security/smp_packets.h"
+#include "security/ui.h"
+
+namespace bluetooth {
+namespace security {
+namespace pairing {
+
+/**
+ * Base structure for handling pairing events.
+ *
+ * <p>Extend this class in order to implement a new style of pairing.
+ */
+class PairingHandler : public UICallbacks {
+ public:
+  PairingHandler(channel::SecurityManagerChannel* security_manager_channel,
+                 std::shared_ptr<record::SecurityRecord> record)
+      : security_manager_channel_(security_manager_channel), record_(std::move(record)) {}
+  virtual ~PairingHandler() = default;
+
+  // Classic
+  virtual void Initiate(bool locally_initiated, hci::IoCapability io_capability, hci::OobDataPresent oob_present,
+                        hci::AuthenticationRequirements auth_requirements) = 0;  // This is for local initiated only
+  virtual void Cancel() = 0;
+  virtual void OnReceive(hci::ChangeConnectionLinkKeyCompleteView packet) = 0;
+  virtual void OnReceive(hci::MasterLinkKeyCompleteView packet) = 0;
+  virtual void OnReceive(hci::PinCodeRequestView packet) = 0;
+  virtual void OnReceive(hci::LinkKeyRequestView packet) = 0;
+  virtual void OnReceive(hci::LinkKeyNotificationView packet) = 0;
+  virtual void OnReceive(hci::IoCapabilityRequestView packet) = 0;
+  virtual void OnReceive(hci::IoCapabilityResponseView packet) = 0;
+  virtual void OnReceive(hci::SimplePairingCompleteView packet) = 0;
+  virtual void OnReceive(hci::ReturnLinkKeysView packet) = 0;
+  virtual void OnReceive(hci::EncryptionChangeView packet) = 0;
+  virtual void OnReceive(hci::EncryptionKeyRefreshCompleteView packet) = 0;
+  virtual void OnReceive(hci::RemoteOobDataRequestView packet) = 0;
+  virtual void OnReceive(hci::UserPasskeyNotificationView packet) = 0;
+  virtual void OnReceive(hci::KeypressNotificationView packet) = 0;
+  virtual void OnReceive(hci::UserConfirmationRequestView packet) = 0;
+  virtual void OnReceive(hci::UserPasskeyRequestView packet) = 0;
+
+  virtual void OnPairingPromptAccepted(const bluetooth::hci::AddressWithType& address, bool confirmed) = 0;
+  virtual void OnConfirmYesNo(const bluetooth::hci::AddressWithType& address, bool confirmed) = 0;
+  virtual void OnPasskeyEntry(const bluetooth::hci::AddressWithType& address, uint32_t passkey) = 0;
+
+ protected:
+  std::shared_ptr<record::SecurityRecord> GetRecord() {
+    return record_;
+  }
+  channel::SecurityManagerChannel* GetChannel() {
+    return security_manager_channel_;
+  }
+
+ private:
+  channel::SecurityManagerChannel* security_manager_channel_ __attribute__((unused));
+  std::shared_ptr<record::SecurityRecord> record_ __attribute__((unused));
+};
+
+}  // namespace pairing
+}  // namespace security
+}  // namespace bluetooth
diff --git a/gd/security/pairing_failure.h b/gd/security/pairing_failure.h
new file mode 100644
index 0000000..f7bbbae
--- /dev/null
+++ b/gd/security/pairing_failure.h
@@ -0,0 +1,57 @@
+/******************************************************************************
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#pragma once
+
+#include <string>
+
+#include "security/smp_packets.h"
+
+namespace bluetooth {
+namespace security {
+
+/* This structure holds the information about the failure in case of airing failure */
+struct PairingFailure {
+  /* A place in code that triggered this failure. It can be modified by functions that pass the error to a location that
+   * better reflect the current state of flow. i.e. instead of generic location responsible for waiting for packet,
+   * replace it with location of receiving specific packet in a specific flow */
+  // base::Location location;
+
+  /* This is the failure message, that will be passed, either into upper layers,
+   * or to the metrics in future */
+  std::string message;
+
+  /* If failure is due to mismatch of received code, this contains the received opcode */
+  Code received_code_;
+
+  /* if the failure is due to "SMP failure", this field contains the reson code
+   */
+  PairingFailedReason reason;
+
+  PairingFailure(/*const base::Location& location, */ const std::string& message)
+      : /*location(location), */ message(message) {}
+
+  PairingFailure(/*const base::Location& location, */ const std::string& message, Code received_code)
+      : /*location(location), */ message(message), received_code_(received_code) {}
+
+  PairingFailure(/*const base::Location& location, */ const std::string& message, PairingFailedReason reason)
+      : /*location(location),*/ message(message), reason(reason) {}
+};
+
+}  // namespace security
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/security/pairing_handler_le.cc b/gd/security/pairing_handler_le.cc
new file mode 100644
index 0000000..15acea9
--- /dev/null
+++ b/gd/security/pairing_handler_le.cc
@@ -0,0 +1,412 @@
+/******************************************************************************
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#include "security/pairing_handler_le.h"
+
+namespace bluetooth {
+namespace security {
+
+void PairingHandlerLe::PairingMain(InitialInformations i) {
+  LOG_INFO("Pairing Started");
+
+  if (i.remotely_initiated) {
+    LOG_INFO("Was remotely initiated, presenting user with the accept prompt");
+    i.user_interface_handler->Post(common::BindOnce(&UI::DisplayPairingPrompt, common::Unretained(i.user_interface),
+                                                    i.remote_connection_address, i.remote_name));
+
+    // If pairing was initiated by remote device, wait for the user to accept
+    // the request from the UI.
+    LOG_INFO("Waiting for the prompt response");
+    std::optional<PairingEvent> pairingAccepted = WaitUiPairingAccept();
+    if (!pairingAccepted || pairingAccepted->ui_value == 0) {
+      LOG_INFO("User either did not accept the remote pairing, or the prompt timed out");
+      SendL2capPacket(i, PairingFailedBuilder::Create(PairingFailedReason::UNSPECIFIED_REASON));
+      i.OnPairingFinished(PairingFailure("User either did not accept the remote pairing, or the prompt timed out"));
+      return;
+    }
+
+    LOG_INFO("Pairing prompt accepted");
+  }
+
+  /************************************************ PHASE 1 *********************************************************/
+  Phase1ResultOrFailure phase_1_result = ExchangePairingFeature(i);
+  if (std::holds_alternative<PairingFailure>(phase_1_result)) {
+    LOG_WARN("Pairing failed in phase 1");
+    // We already send pairing fialed in lower layer. Which one should do that ? how about disconneciton?
+    // SendL2capPacket(i, PairingFailedBuilder::Create(PairingFailedReason::UNSPECIFIED_REASON));
+    // TODO: disconnect?
+    i.OnPairingFinished(std::get<PairingFailure>(phase_1_result));
+    return;
+  }
+
+  auto [pairing_request, pairing_response] = std::get<Phase1Result>(phase_1_result);
+
+  /************************************************ PHASE 2 *********************************************************/
+  bool isSecureConnections = pairing_request.GetAuthReq() & pairing_response.GetAuthReq() & AuthReqMaskSc;
+  if (isSecureConnections) {
+    // 2.3.5.6 LE Secure Connections pairing phase 2
+    LOG_INFO("Pairing Phase 2 LE Secure connections Started");
+
+    /*
+    TODO: what to do with this piece of spec ?
+    If Secure Connections pairing has been initiated over BR/EDR, the
+    following fields of the SM Pairing Request PDU are reserved for future use:
+     • the IO Capability field,
+     • the OOB data flag field, and
+     • all bits in the Auth Req field except the CT2 bit.
+    */
+
+    OobDataFlag remote_have_oob_data =
+        IAmMaster(i) ? pairing_response.GetOobDataFlag() : pairing_request.GetOobDataFlag();
+
+    auto key_exchange_result = ExchangePublicKeys(i, remote_have_oob_data);
+    if (std::holds_alternative<PairingFailure>(key_exchange_result)) {
+      LOG_ERROR("Public key exchange failed");
+      i.OnPairingFinished(std::get<PairingFailure>(key_exchange_result));
+      return;
+    }
+    auto [PKa, PKb, dhkey] = std::get<KeyExchangeResult>(key_exchange_result);
+
+    // Public key exchange finished, Diffie-Hellman key computed.
+
+    Stage1ResultOrFailure stage1result = DoSecureConnectionsStage1(i, PKa, PKb, pairing_request, pairing_response);
+    if (std::holds_alternative<PairingFailure>(stage1result)) {
+      i.OnPairingFinished(std::get<PairingFailure>(stage1result));
+      return;
+    }
+
+    Stage2ResultOrFailure stage_2_result = DoSecureConnectionsStage2(i, PKa, PKb, pairing_request, pairing_response,
+                                                                     std::get<Stage1Result>(stage1result), dhkey);
+    if (std::holds_alternative<PairingFailure>(stage_2_result)) {
+      i.OnPairingFinished(std::get<PairingFailure>(stage_2_result));
+      return;
+    }
+
+    Octet16 ltk = std::get<Octet16>(stage_2_result);
+    if (IAmMaster(i)) {
+      LOG_INFO("Sending start encryption request");
+      SendHciLeStartEncryption(i, i.connection_handle, {0}, {0}, ltk);
+    }
+
+  } else {
+    // 2.3.5.5 LE legacy pairing phase 2
+    LOG_INFO("Pairing Phase 2 LE legacy pairing Started");
+
+    LegacyStage1ResultOrFailure stage1result = DoLegacyStage1(i, pairing_request, pairing_response);
+    if (std::holds_alternative<PairingFailure>(stage1result)) {
+      LOG_ERROR("Phase 1 failed");
+      i.OnPairingFinished(std::get<PairingFailure>(stage1result));
+      return;
+    }
+
+    Octet16 tk = std::get<Octet16>(stage1result);
+    StkOrFailure stage2result = DoLegacyStage2(i, pairing_request, pairing_response, tk);
+    if (std::holds_alternative<PairingFailure>(stage2result)) {
+      LOG_ERROR("stage 2 failed");
+      i.OnPairingFinished(std::get<PairingFailure>(stage2result));
+      return;
+    }
+
+    Octet16 stk = std::get<Octet16>(stage2result);
+    if (IAmMaster(i)) {
+      SendHciLeStartEncryption(i, i.connection_handle, {0}, {0}, stk);
+    }
+  }
+
+  /************************************************ PHASE 3 *********************************************************/
+  LOG_INFO("Waiting for encryption changed");
+  auto encryption_change_result = WaitEncryptionChanged();
+  if (std::holds_alternative<PairingFailure>(encryption_change_result)) {
+    i.OnPairingFinished(std::get<PairingFailure>(encryption_change_result));
+    return;
+  } else if (std::holds_alternative<EncryptionChangeView>(encryption_change_result)) {
+    EncryptionChangeView encryption_changed = std::get<EncryptionChangeView>(encryption_change_result);
+    if (encryption_changed.GetStatus() != hci::ErrorCode::SUCCESS ||
+        encryption_changed.GetEncryptionEnabled() != hci::EncryptionEnabled::ON) {
+      i.OnPairingFinished(PairingFailure("Encryption change failed"));
+      return;
+    }
+  } else if (std::holds_alternative<EncryptionKeyRefreshCompleteView>(encryption_change_result)) {
+    EncryptionKeyRefreshCompleteView encryption_changed =
+        std::get<EncryptionKeyRefreshCompleteView>(encryption_change_result);
+    if (encryption_changed.GetStatus() != hci::ErrorCode::SUCCESS) {
+      i.OnPairingFinished(PairingFailure("Encryption key refresh failed"));
+      return;
+    }
+  } else {
+    i.OnPairingFinished(PairingFailure("Unknown case of encryption change result"));
+    return;
+  }
+  LOG_INFO("Encryption change finished successfully");
+
+  DistributedKeysOrFailure keyExchangeStatus = DistributeKeys(i, pairing_response, isSecureConnections);
+  if (std::holds_alternative<PairingFailure>(keyExchangeStatus)) {
+    i.OnPairingFinished(std::get<PairingFailure>(keyExchangeStatus));
+    LOG_ERROR("Key exchange failed");
+    return;
+  }
+
+  // bool bonding = pairing_request.GetAuthReq() & pairing_response.GetAuthReq() & AuthReqMaskBondingFlag;
+
+  i.OnPairingFinished(PairingResult{
+      .connection_address = i.remote_connection_address,
+      .distributed_keys = std::get<DistributedKeys>(keyExchangeStatus),
+  });
+
+  LOG_INFO("Pairing finished successfully.");
+}
+
+Phase1ResultOrFailure PairingHandlerLe::ExchangePairingFeature(const InitialInformations& i) {
+  LOG_INFO("Phase 1 start");
+
+  if (IAmMaster(i)) {
+    // Send Pairing Request
+    const auto& x = i.myPairingCapabilities;
+    auto pairing_request_builder =
+        PairingRequestBuilder::Create(x.io_capability, x.oob_data_flag, x.auth_req, x.maximum_encryption_key_size,
+                                      x.initiator_key_distribution, x.responder_key_distribution);
+    // basically pairing_request = myPairingCapabilities;
+
+    // Convert builder to view
+    std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+    BitInserter it(*packet_bytes);
+    pairing_request_builder->Serialize(it);
+    PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+    auto temp_cmd_view = CommandView::Create(packet_bytes_view);
+    auto pairing_request = PairingRequestView::Create(temp_cmd_view);
+    ASSERT(pairing_request.IsValid());
+
+    LOG_INFO("Sending Pairing Request");
+    SendL2capPacket(i, std::move(pairing_request_builder));
+
+    LOG_INFO("Waiting for Pairing Response");
+    auto response = WaitPairingResponse();
+
+    /* There is a potential collision where the slave initiates the pairing at the same time we initiate it, by sending
+     * security request. */
+    if (std::holds_alternative<PairingFailure>(response) &&
+        std::get<PairingFailure>(response).received_code_ == Code::SECURITY_REQUEST) {
+      LOG_INFO("Received security request, waiting for Pairing Response again...");
+      response = WaitPairingResponse();
+    }
+
+    if (std::holds_alternative<PairingFailure>(response)) {
+      // TODO: should the failure reason be different in some cases ? How about
+      // when we lost connection ? Don't send anything at all, or have L2CAP
+      // layer ignore it?
+      SendL2capPacket(i, PairingFailedBuilder::Create(PairingFailedReason::UNSPECIFIED_REASON));
+      return std::get<PairingFailure>(response);
+    }
+
+    auto pairing_response = std::get<PairingResponseView>(response);
+
+    LOG_INFO("Phase 1 finish");
+    return Phase1Result{pairing_request, pairing_response};
+  } else {
+    std::optional<PairingRequestView> pairing_request;
+
+    if (i.remotely_initiated) {
+      if (!i.pairing_request.has_value()) {
+        return PairingFailure("You must pass PairingRequest as a initial information to slave!");
+      }
+
+      pairing_request = i.pairing_request.value();
+
+      if (!pairing_request->IsValid()) return PairingFailure("Malformed PairingRequest");
+    } else {
+      SendL2capPacket(i, SecurityRequestBuilder::Create(i.myPairingCapabilities.auth_req));
+
+      LOG_INFO("Waiting for Pairing Request");
+      auto request = WaitPairingRequest();
+      if (std::holds_alternative<PairingFailure>(request)) {
+        LOG_INFO("%s", std::get<PairingFailure>(request).message.c_str());
+        SendL2capPacket(i, PairingFailedBuilder::Create(PairingFailedReason::UNSPECIFIED_REASON));
+        return std::get<PairingFailure>(request);
+      }
+
+      pairing_request = std::get<PairingRequestView>(request);
+    }
+
+    // Send Pairing Request
+    const auto& x = i.myPairingCapabilities;
+    // basically pairing_response_builder = my_first_packet;
+    // We are not allowed to enable bits that the remote did not allow us to set in initiator_key_dist and
+    // responder_key_distribution
+    auto pairing_response_builder =
+        PairingResponseBuilder::Create(x.io_capability, x.oob_data_flag, x.auth_req, x.maximum_encryption_key_size,
+                                       x.initiator_key_distribution & pairing_request->GetInitiatorKeyDistribution(),
+                                       x.responder_key_distribution & pairing_request->GetResponderKeyDistribution());
+
+    // Convert builder to view
+    std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+    BitInserter it(*packet_bytes);
+    pairing_response_builder->Serialize(it);
+    PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+    auto temp_cmd_view = CommandView::Create(packet_bytes_view);
+    auto pairing_response = PairingResponseView::Create(temp_cmd_view);
+    ASSERT(pairing_response.IsValid());
+
+    LOG_INFO("Sending Pairing Response");
+    SendL2capPacket(i, std::move(pairing_response_builder));
+
+    LOG_INFO("Phase 1 finish");
+    return Phase1Result{pairing_request.value(), pairing_response};
+  }
+}
+
+DistributedKeysOrFailure PairingHandlerLe::DistributeKeys(const InitialInformations& i,
+                                                          const PairingResponseView& pairing_response,
+                                                          bool isSecureConnections) {
+  uint8_t keys_i_receive =
+      IAmMaster(i) ? pairing_response.GetResponderKeyDistribution() : pairing_response.GetInitiatorKeyDistribution();
+  uint8_t keys_i_send =
+      IAmMaster(i) ? pairing_response.GetInitiatorKeyDistribution() : pairing_response.GetResponderKeyDistribution();
+
+  // In Secure Connections on the LE Transport, the EncKey field shall be ignored
+  if (isSecureConnections) {
+    keys_i_send = (~KeyMaskEnc) & keys_i_send;
+    keys_i_receive = (~KeyMaskEnc) & keys_i_receive;
+  }
+
+  LOG_INFO("Key distribution start, keys_i_send=%02x, keys_i_receive=%02x", keys_i_send, keys_i_receive);
+
+  // TODO: obtain actual values!
+  Octet16 my_ltk = {0};
+  uint16_t my_ediv{0};
+  std::array<uint8_t, 8> my_rand = {0};
+
+  Octet16 my_irk = {0x01};
+  Address my_identity_address;
+  AddrType my_identity_address_type = AddrType::PUBLIC;
+  Octet16 my_signature_key{0};
+
+  if (IAmMaster(i)) {
+    // EncKey is unused for LE Secure Connections
+    DistributedKeysOrFailure keys = ReceiveKeys(keys_i_receive);
+    if (std::holds_alternative<PairingFailure>(keys)) {
+      return keys;
+    }
+
+    SendKeys(i, keys_i_send, my_ltk, my_ediv, my_rand, my_irk, my_identity_address, my_identity_address_type,
+             my_signature_key);
+    LOG_INFO("Key distribution finish");
+    return keys;
+  } else {
+    SendKeys(i, keys_i_send, my_ltk, my_ediv, my_rand, my_irk, my_identity_address, my_identity_address_type,
+             my_signature_key);
+
+    DistributedKeysOrFailure keys = ReceiveKeys(keys_i_receive);
+    if (std::holds_alternative<PairingFailure>(keys)) {
+      return keys;
+    }
+    LOG_INFO("Key distribution finish");
+    return keys;
+  }
+}
+
+DistributedKeysOrFailure PairingHandlerLe::ReceiveKeys(const uint8_t& keys_i_receive) {
+  std::optional<Octet16> ltk;                 /* Legacy only */
+  std::optional<uint16_t> ediv;               /* Legacy only */
+  std::optional<std::array<uint8_t, 8>> rand; /* Legacy only */
+  std::optional<Address> identity_address;
+  AddrType identity_address_type;
+  std::optional<Octet16> irk;
+  std::optional<Octet16> signature_key;
+
+  if (keys_i_receive & KeyMaskEnc) {
+    {
+      auto packet = WaitEncryptionInformation();
+      if (std::holds_alternative<PairingFailure>(packet)) {
+        LOG_ERROR(" Was expecting Encryption Information but did not receive!");
+        return std::get<PairingFailure>(packet);
+      }
+      ltk = std::get<EncryptionInformationView>(packet).GetLongTermKey();
+    }
+
+    {
+      auto packet = WaitMasterIdentification();
+      if (std::holds_alternative<PairingFailure>(packet)) {
+        LOG_ERROR(" Was expecting Master Identification but did not receive!");
+        return std::get<PairingFailure>(packet);
+      }
+      ediv = std::get<MasterIdentificationView>(packet).GetEdiv();
+      rand = std::get<MasterIdentificationView>(packet).GetRand();
+    }
+  }
+
+  if (keys_i_receive & KeyMaskId) {
+    auto packet = WaitIdentityInformation();
+    if (std::holds_alternative<PairingFailure>(packet)) {
+      LOG_ERROR(" Was expecting Identity Information but did not receive!");
+      return std::get<PairingFailure>(packet);
+    }
+
+    LOG_INFO("Received Identity Information");
+    irk = std::get<IdentityInformationView>(packet).GetIdentityResolvingKey();
+
+    auto iapacket = WaitIdentityAddressInformation();
+    if (std::holds_alternative<PairingFailure>(iapacket)) {
+      LOG_ERROR(
+          "Was expecting Identity Address Information but did "
+          "not receive!");
+      return std::get<PairingFailure>(iapacket);
+    }
+    LOG_INFO("Received Identity Address Information");
+    identity_address = std::get<IdentityAddressInformationView>(iapacket).GetBdAddr();
+    identity_address_type = std::get<IdentityAddressInformationView>(iapacket).GetAddrType();
+  }
+
+  if (keys_i_receive & KeyMaskSign) {
+    auto packet = WaitSigningInformation();
+    if (std::holds_alternative<PairingFailure>(packet)) {
+      LOG_ERROR(" Was expecting Signing Information but did not receive!");
+      return std::get<PairingFailure>(packet);
+    }
+
+    LOG_INFO("Received Signing Information");
+    signature_key = std::get<SigningInformationView>(packet).GetSignatureKey();
+  }
+
+  return DistributedKeys{ltk, ediv, rand, identity_address, identity_address_type, irk, signature_key};
+}
+
+void PairingHandlerLe::SendKeys(const InitialInformations& i, const uint8_t& keys_i_send, Octet16 ltk, uint16_t ediv,
+                                std::array<uint8_t, 8> rand, Octet16 irk, Address identity_address,
+                                AddrType identity_addres_type, Octet16 signature_key) {
+  if (keys_i_send & KeyMaskEnc) {
+    LOG_INFO("Sending Encryption Information");
+    SendL2capPacket(i, EncryptionInformationBuilder::Create(ltk));
+    LOG_INFO("Sending Master Identification");
+    SendL2capPacket(i, MasterIdentificationBuilder::Create(ediv, rand));
+  }
+
+  if (keys_i_send & KeyMaskId) {
+    LOG_INFO("Sending Identity Information");
+    SendL2capPacket(i, IdentityInformationBuilder::Create(irk));
+    LOG_INFO("Sending Identity Address Information");
+    SendL2capPacket(i, IdentityAddressInformationBuilder::Create(identity_addres_type, identity_address));
+  }
+
+  if (keys_i_send & KeyMaskSign) {
+    LOG_INFO("Sending Signing Information");
+    SendL2capPacket(i, SigningInformationBuilder::Create(signature_key));
+  }
+}
+
+}  // namespace security
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/security/pairing_handler_le.h b/gd/security/pairing_handler_le.h
new file mode 100644
index 0000000..cbc87fa
--- /dev/null
+++ b/gd/security/pairing_handler_le.h
@@ -0,0 +1,497 @@
+/******************************************************************************
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#pragma once
+
+#include <array>
+#include <chrono>
+#include <condition_variable>
+#include <mutex>
+#include <optional>
+#include <queue>
+#include <thread>
+#include <variant>
+
+#include "common/bind.h"
+#include "crypto_toolbox/crypto_toolbox.h"
+#include "hci/hci_packets.h"
+#include "hci/le_security_interface.h"
+#include "packet/packet_view.h"
+#include "security/ecdh_keys.h"
+#include "security/initial_informations.h"
+#include "security/pairing_failure.h"
+#include "security/smp_packets.h"
+#include "security/ui.h"
+
+// Code generated by PDL does not allow us ot do || and && operations on bits
+// efficiently. Use those masks on fields requiring them until this is solved
+constexpr uint8_t AuthReqMaskBondingFlag = 0x01;
+constexpr uint8_t AuthReqMaskMitm = 0x04;
+constexpr uint8_t AuthReqMaskSc = 0x08;
+constexpr uint8_t AuthReqMaskKeypress = 0x10;
+constexpr uint8_t AuthReqMaskCt2 = 0x20;
+
+constexpr uint8_t KeyMaskEnc = 0x01;
+constexpr uint8_t KeyMaskId = 0x02;
+constexpr uint8_t KeyMaskSign = 0x04;
+constexpr uint8_t KeyMaskLink = 0x08;
+
+using bluetooth::hci::EncryptionChangeView;
+using bluetooth::hci::EncryptionKeyRefreshCompleteView;
+
+namespace bluetooth {
+namespace security {
+
+using crypto_toolbox::Octet16;
+
+/* This class represents an event send from other subsystems into SMP Pairing Handler,
+ * i.e. user request from the UI, L2CAP or HCI interaction */
+class PairingEvent {
+ public:
+  enum TYPE { EXIT, L2CAP, HCI_EVENT, UI };
+  TYPE type;
+
+  std::optional<CommandView> l2cap_packet;
+
+  std::optional<hci::EventPacketView> hci_event;
+
+  enum UI_ACTION_TYPE { PAIRING_ACCEPTED, CONFIRM_YESNO, PASSKEY };
+  UI_ACTION_TYPE ui_action;
+  uint32_t ui_value;
+
+  PairingEvent(TYPE type) : type(type) {}
+  PairingEvent(CommandView l2cap_packet) : type(L2CAP), l2cap_packet(l2cap_packet) {}
+  PairingEvent(UI_ACTION_TYPE ui_action, uint32_t ui_value) : type(UI), ui_action(ui_action), ui_value(ui_value) {}
+  PairingEvent(hci::EventPacketView hci_event) : type(HCI_EVENT), hci_event(hci_event) {}
+};
+
+constexpr int SMP_TIMEOUT = 30;
+
+using CommandViewOrFailure = std::variant<CommandView, PairingFailure>;
+using Phase1Result = std::pair<PairingRequestView /* pairning_request*/, PairingResponseView /* pairing_response */>;
+using Phase1ResultOrFailure = std::variant<PairingFailure, Phase1Result>;
+using KeyExchangeResult =
+    std::tuple<EcdhPublicKey /* PKa */, EcdhPublicKey /* PKb */, std::array<uint8_t, 32> /*dhkey*/>;
+using Stage1Result = std::tuple<Octet16, Octet16, Octet16, Octet16>;
+using Stage1ResultOrFailure = std::variant<PairingFailure, Stage1Result>;
+using Stage2ResultOrFailure = std::variant<PairingFailure, Octet16 /* LTK */>;
+using DistributedKeysOrFailure = std::variant<PairingFailure, DistributedKeys, std::monostate>;
+
+using LegacyStage1Result = Octet16 /*TK*/;
+using LegacyStage1ResultOrFailure = std::variant<PairingFailure, LegacyStage1Result>;
+using StkOrFailure = std::variant<PairingFailure, Octet16 /* STK */>;
+
+/* PairingHandlerLe takes care of the Pairing process. Pairing is strictly defined
+ * exchange of messages and UI interactions, divided into PHASES.
+ *
+ * Each PairingHandlerLe have a thread executing |PairingMain| method. Thread is
+ * blocked when waiting for UI/L2CAP/HCI interactions, and moves through all the
+ * phases.
+ */
+class PairingHandlerLe {
+ public:
+  // This is the phase of pairing as defined in BT Spec (with exception of
+  // accept prompt)
+  // * ACCEPT_PROMPT - we're waiting for the user to accept remotely initiated pairing
+  // * PHASE1 - feature exchange
+  // * PHASE2 - authentication
+  // * PHASE3 - key exchange
+  enum PAIRING_PHASE { ACCEPT_PROMPT, PHASE1, PHASE2, PHASE3 };
+  PAIRING_PHASE phase;
+
+  // All the knowledge to initiate the pairing process must be passed into this function
+  PairingHandlerLe(PAIRING_PHASE phase, InitialInformations informations)
+      : phase(phase), queue_guard(), thread_(&PairingHandlerLe::PairingMain, this, informations) {}
+
+  ~PairingHandlerLe() {
+    SendExitSignal();
+    // we need ot check if thread is joinable, because tests call join form
+    // within WaitUntilPairingFinished
+    if (thread_.joinable()) thread_.join();
+  }
+
+  void PairingMain(InitialInformations i);
+
+  Phase1ResultOrFailure ExchangePairingFeature(const InitialInformations& i);
+
+  void SendL2capPacket(const InitialInformations& i, std::unique_ptr<bluetooth::security::CommandBuilder> command) {
+    i.proper_l2cap_interface->Enqueue(std::move(command), i.l2cap_handler);
+  }
+
+  void SendHciLeStartEncryption(const InitialInformations& i, uint16_t conn_handle, const std::array<uint8_t, 8>& rand,
+                                const uint16_t& ediv, const Octet16& ltk) {
+    i.le_security_interface->EnqueueCommand(hci::LeStartEncryptionBuilder::Create(conn_handle, rand, ediv, ltk),
+                                            common::BindOnce([](hci::CommandStatusView) {
+                                              // TODO: handle command status. It's important - can show we are not
+                                              // connected any more.
+
+                                              // TODO: if anything useful must be done there, use some sort of proper
+                                              // handler, wait/notify, and execute on the handler thread
+                                            }),
+                                            i.l2cap_handler);
+  }
+
+  std::variant<PairingFailure, EncryptionChangeView, EncryptionKeyRefreshCompleteView> WaitEncryptionChanged() {
+    PairingEvent e = WaitForEvent();
+    if (e.type != PairingEvent::HCI_EVENT) return PairingFailure("Was expecting HCI event but received something else");
+
+    if (!e.hci_event->IsValid()) return PairingFailure("Received invalid HCI event");
+
+    if (e.hci_event->GetEventCode() == hci::EventCode::ENCRYPTION_CHANGE) {
+      EncryptionChangeView enc_chg_packet = EncryptionChangeView::Create(*e.hci_event);
+      if (!enc_chg_packet.IsValid()) {
+        return PairingFailure("Invalid Encryption Change packet received");
+      }
+      return enc_chg_packet;
+    }
+
+    if (e.hci_event->GetEventCode() == hci::EventCode::ENCRYPTION_KEY_REFRESH_COMPLETE) {
+      hci::EncryptionKeyRefreshCompleteView enc_packet = EncryptionKeyRefreshCompleteView::Create(*e.hci_event);
+      if (!enc_packet.IsValid()) {
+        return PairingFailure("Invalid Key Refresh packet received");
+      }
+      return enc_packet;
+    }
+
+    return PairingFailure("Was expecting Encryption Change or Key Refresh Complete but received something else");
+  }
+
+  inline bool IAmMaster(const InitialInformations& i) {
+    return i.my_role == hci::Role::MASTER;
+  }
+
+  /* This function generates data that should be passed to remote device, except
+     the private key. */
+  static MyOobData GenerateOobData() {
+    MyOobData data;
+    std::tie(data.private_key, data.public_key) = GenerateECDHKeyPair();
+
+    data.r = GenerateRandom<16>();
+    data.c = crypto_toolbox::f4(data.public_key.x.data(), data.public_key.x.data(), data.r, 0);
+    return data;
+  }
+
+  std::variant<PairingFailure, KeyExchangeResult> ExchangePublicKeys(const InitialInformations& i,
+                                                                     OobDataFlag remote_have_oob_data);
+
+  Stage1ResultOrFailure DoSecureConnectionsStage1(const InitialInformations& i, const EcdhPublicKey& PKa,
+                                                  const EcdhPublicKey& PKb, const PairingRequestView& pairing_request,
+                                                  const PairingResponseView& pairing_response);
+
+  Stage1ResultOrFailure SecureConnectionsNumericComparison(const InitialInformations& i, const EcdhPublicKey& PKa,
+                                                           const EcdhPublicKey& PKb);
+
+  Stage1ResultOrFailure SecureConnectionsJustWorks(const InitialInformations& i, const EcdhPublicKey& PKa,
+                                                   const EcdhPublicKey& PKb);
+
+  Stage1ResultOrFailure SecureConnectionsPasskeyEntry(const InitialInformations& i, const EcdhPublicKey& PKa,
+                                                      const EcdhPublicKey& PKb, IoCapability my_iocaps,
+                                                      IoCapability remote_iocaps);
+
+  Stage1ResultOrFailure SecureConnectionsOutOfBand(const InitialInformations& i, const EcdhPublicKey& Pka,
+                                                   const EcdhPublicKey& Pkb, OobDataFlag my_oob_flag,
+                                                   OobDataFlag remote_oob_flag);
+
+  Stage2ResultOrFailure DoSecureConnectionsStage2(const InitialInformations& i, const EcdhPublicKey& PKa,
+                                                  const EcdhPublicKey& PKb, const PairingRequestView& pairing_request,
+                                                  const PairingResponseView& pairing_response,
+                                                  const Stage1Result stage1result,
+                                                  const std::array<uint8_t, 32>& dhkey);
+
+  DistributedKeysOrFailure DistributeKeys(const InitialInformations& i, const PairingResponseView& pairing_response,
+                                          bool isSecureConnections);
+
+  DistributedKeysOrFailure ReceiveKeys(const uint8_t& keys_i_receive);
+
+  LegacyStage1ResultOrFailure DoLegacyStage1(const InitialInformations& i, const PairingRequestView& pairing_request,
+                                             const PairingResponseView& pairing_response);
+  LegacyStage1ResultOrFailure LegacyOutOfBand(const InitialInformations& i);
+  LegacyStage1ResultOrFailure LegacyJustWorks();
+  LegacyStage1ResultOrFailure LegacyPasskeyEntry(const InitialInformations& i, const IoCapability& my_iocaps,
+                                                 const IoCapability& remote_iocaps);
+  StkOrFailure DoLegacyStage2(const InitialInformations& i, const PairingRequestView& pairing_request,
+                              const PairingResponseView& pairing_response, const Octet16& tk);
+
+  void SendKeys(const InitialInformations& i, const uint8_t& keys_i_send, Octet16 ltk, uint16_t ediv,
+                std::array<uint8_t, 8> rand, Octet16 irk, Address identity_address, AddrType identity_addres_type,
+                Octet16 signature_key);
+
+  /* This can be called from any thread to immediately finish the pairing in progress. */
+  void SendExitSignal() {
+    {
+      std::unique_lock<std::mutex> lock(queue_guard);
+      queue.push(PairingEvent(PairingEvent::EXIT));
+    }
+    pairing_thread_blocker_.notify_one();
+  }
+
+  /* SMP Command received from remote device */
+  void OnCommandView(CommandView packet) {
+    {
+      std::unique_lock<std::mutex> lock(queue_guard);
+      queue.push(PairingEvent(std::move(packet)));
+    }
+    pairing_thread_blocker_.notify_one();
+  }
+
+  /* SMP Command received from remote device */
+  void OnHciEvent(hci::EventPacketView hci_event) {
+    {
+      std::unique_lock<std::mutex> lock(queue_guard);
+      queue.push(PairingEvent(std::move(hci_event)));
+    }
+    pairing_thread_blocker_.notify_one();
+  }
+
+  /* Interaction from user */
+  void OnUiAction(PairingEvent::UI_ACTION_TYPE ui_action, uint32_t ui_value) {
+    {
+      std::unique_lock<std::mutex> lock(queue_guard);
+      queue.push(PairingEvent(ui_action, ui_value));
+    }
+    pairing_thread_blocker_.notify_one();
+  }
+
+  /* Blocks the pairing process until some external interaction, or timeout happens */
+  PairingEvent WaitForEvent() {
+    std::unique_lock<std::mutex> lock(queue_guard);
+    do {
+      if (!queue.empty()) {
+        PairingEvent e = queue.front();
+        queue.pop();
+        return e;
+      }
+      // This releases the lock while blocking.
+      if (pairing_thread_blocker_.wait_for(lock, std::chrono::seconds(SMP_TIMEOUT)) == std::cv_status::timeout) {
+        return PairingEvent(PairingEvent::EXIT);
+      }
+
+    } while (true);
+  }
+
+  std::optional<PairingEvent> WaitUiPairingAccept() {
+    PairingEvent e = WaitForEvent();
+    if (e.type == PairingEvent::UI & e.ui_action == PairingEvent::PAIRING_ACCEPTED) {
+      return e;
+    } else {
+      return std::nullopt;
+    }
+  }
+
+  std::optional<PairingEvent> WaitUiConfirmYesNo() {
+    PairingEvent e = WaitForEvent();
+    if (e.type == PairingEvent::UI & e.ui_action == PairingEvent::CONFIRM_YESNO) {
+      return e;
+    } else {
+      return std::nullopt;
+    }
+  }
+
+  std::optional<PairingEvent> WaitUiPasskey() {
+    PairingEvent e = WaitForEvent();
+    if (e.type == PairingEvent::UI & e.ui_action == PairingEvent::PASSKEY) {
+      return e;
+    } else {
+      return std::nullopt;
+    }
+  }
+
+  template <Code C>
+  struct CodeToPacketView;
+  template <>
+  struct CodeToPacketView<Code::PAIRING_REQUEST> {
+    typedef PairingRequestView type;
+  };
+  template <>
+  struct CodeToPacketView<Code::PAIRING_RESPONSE> {
+    typedef PairingResponseView type;
+  };
+  template <>
+  struct CodeToPacketView<Code::PAIRING_CONFIRM> {
+    typedef PairingConfirmView type;
+  };
+  template <>
+  struct CodeToPacketView<Code::PAIRING_RANDOM> {
+    typedef PairingRandomView type;
+  };
+  template <>
+  struct CodeToPacketView<Code::PAIRING_FAILED> {
+    typedef PairingFailedView type;
+  };
+  template <>
+  struct CodeToPacketView<Code::ENCRYPTION_INFORMATION> {
+    typedef EncryptionInformationView type;
+  };
+  template <>
+  struct CodeToPacketView<Code::MASTER_IDENTIFICATION> {
+    typedef MasterIdentificationView type;
+  };
+  template <>
+  struct CodeToPacketView<Code::IDENTITY_INFORMATION> {
+    typedef IdentityInformationView type;
+  };
+  template <>
+  struct CodeToPacketView<Code::IDENTITY_ADDRESS_INFORMATION> {
+    typedef IdentityAddressInformationView type;
+  };
+  template <>
+  struct CodeToPacketView<Code::SIGNING_INFORMATION> {
+    typedef SigningInformationView type;
+  };
+  template <>
+  struct CodeToPacketView<Code::SECURITY_REQUEST> {
+    typedef SecurityRequestView type;
+  };
+  template <>
+  struct CodeToPacketView<Code::PAIRING_PUBLIC_KEY> {
+    typedef PairingPublicKeyView type;
+  };
+  template <>
+  struct CodeToPacketView<Code::PAIRING_DH_KEY_CHECK> {
+    typedef PairingDhKeyCheckView type;
+  };
+  template <>
+  struct CodeToPacketView<Code::PAIRING_KEYPRESS_NOTIFICATION> {
+    typedef PairingKeypressNotificationView type;
+  };
+
+  template <Code CODE>
+  std::variant<typename CodeToPacketView<CODE>::type, PairingFailure> WaitPacket() {
+    PairingEvent e = WaitForEvent();
+    switch (e.type) {
+      case PairingEvent::EXIT:
+        return PairingFailure(
+            /*FROM_HERE,*/ "Was expecting L2CAP Packet " + CodeText(CODE) + ", but received EXIT instead");
+
+      case PairingEvent::HCI_EVENT:
+        return PairingFailure(
+            /*FROM_HERE,*/ "Was expecting L2CAP Packet " + CodeText(CODE) + ", but received HCI_EVENT instead");
+
+      case PairingEvent::UI:
+        return PairingFailure(
+            /*FROM_HERE,*/ "Was expecting L2CAP Packet " + CodeText(CODE) + ", but received UI instead");
+
+      case PairingEvent::L2CAP: {
+        auto l2cap_packet = e.l2cap_packet.value();
+        if (!l2cap_packet.IsValid()) {
+          return PairingFailure("Malformed L2CAP packet received!");
+        }
+
+        const auto& received_code = l2cap_packet.GetCode();
+        if (received_code != CODE) {
+          if (received_code == Code::PAIRING_FAILED) {
+            auto pkt = PairingFailedView::Create(l2cap_packet);
+            if (!pkt.IsValid()) return PairingFailure("Malformed " + CodeText(CODE) + " packet");
+            return PairingFailure(/*FROM_HERE,*/
+                                  "Was expecting " + CodeText(CODE) + ", but received PAIRING_FAILED instead",
+                                  pkt.GetReason());
+          }
+
+          return PairingFailure(/*FROM_HERE,*/
+                                "Was expecting " + CodeText(CODE) + ", but received " + CodeText(received_code) +
+                                    " instead",
+                                received_code);
+        }
+
+        auto pkt = CodeToPacketView<CODE>::type::Create(l2cap_packet);
+        if (!pkt.IsValid()) return PairingFailure("Malformed " + CodeText(CODE) + " packet");
+        return pkt;
+      }
+    }
+  }
+
+  auto WaitPairingRequest() {
+    return WaitPacket<Code::PAIRING_REQUEST>();
+  }
+
+  auto WaitPairingResponse() {
+    return WaitPacket<Code::PAIRING_RESPONSE>();
+  }
+
+  auto WaitPairingConfirm() {
+    return WaitPacket<Code::PAIRING_CONFIRM>();
+  }
+
+  auto WaitPairingRandom() {
+    return WaitPacket<Code::PAIRING_RANDOM>();
+  }
+
+  auto WaitPairingPublicKey() {
+    return WaitPacket<Code::PAIRING_PUBLIC_KEY>();
+  }
+
+  auto WaitPairingDHKeyCheck() {
+    return WaitPacket<Code::PAIRING_DH_KEY_CHECK>();
+  }
+
+  auto WaitEncryptionInformationRequest() {
+    return WaitPacket<Code::ENCRYPTION_INFORMATION>();
+  }
+
+  auto WaitEncryptionInformation() {
+    return WaitPacket<Code::ENCRYPTION_INFORMATION>();
+  }
+
+  auto WaitMasterIdentification() {
+    return WaitPacket<Code::MASTER_IDENTIFICATION>();
+  }
+
+  auto WaitIdentityInformation() {
+    return WaitPacket<Code::IDENTITY_INFORMATION>();
+  }
+
+  auto WaitIdentityAddressInformation() {
+    return WaitPacket<Code::IDENTITY_ADDRESS_INFORMATION>();
+  }
+
+  auto WaitSigningInformation() {
+    return WaitPacket<Code::SIGNING_INFORMATION>();
+  }
+
+  template <size_t SIZE>
+  static std::array<uint8_t, SIZE> GenerateRandom() {
+    // TODO:  We need a proper  random number generator here.
+    // use current time as seed for random generator
+    std::srand(std::time(nullptr));
+    std::array<uint8_t, SIZE> r;
+    for (size_t i = 0; i < SIZE; i++) r[i] = std::rand();
+    return r;
+  }
+
+  uint32_t GenerateRandom() {
+    // TODO:  We need a proper  random number generator here.
+    // use current time as seed for random generator
+    std::srand(std::time(nullptr));
+    return std::rand();
+  }
+
+  /* This is just for test, never use in production code! */
+  void WaitUntilPairingFinished() {
+    thread_.join();
+  }
+
+ private:
+  std::condition_variable pairing_thread_blocker_;
+
+  std::mutex queue_guard;
+  std::queue<PairingEvent> queue;
+
+  std::thread thread_;
+};
+}  // namespace security
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/security/pairing_handler_le_legacy.cc b/gd/security/pairing_handler_le_legacy.cc
new file mode 100644
index 0000000..93a1a8a
--- /dev/null
+++ b/gd/security/pairing_handler_le_legacy.cc
@@ -0,0 +1,222 @@
+/******************************************************************************
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#include "security/pairing_handler_le.h"
+
+namespace bluetooth {
+namespace security {
+
+LegacyStage1ResultOrFailure PairingHandlerLe::DoLegacyStage1(const InitialInformations& i,
+                                                             const PairingRequestView& pairing_request,
+                                                             const PairingResponseView& pairing_response) {
+  if (((pairing_request.GetAuthReq() | pairing_response.GetAuthReq()) & AuthReqMaskMitm) == 0) {
+    // If both devices have not set MITM option, Just Works shall be used
+    return LegacyJustWorks();
+  }
+
+  if (pairing_request.GetOobDataFlag() == OobDataFlag::PRESENT &&
+      pairing_response.GetOobDataFlag() == OobDataFlag::PRESENT) {
+    // OobDataFlag remote_oob_flag = IAmMaster(i) ? pairing_response.GetOobDataFlag() :
+    // pairing_request.GetOobDataFlag(); OobDataFlag my_oob_flag = IAmMaster(i) ? pairing_request.GetOobDataFlag() :
+    // pairing_response.GetOobDataFlag();
+    return LegacyOutOfBand(i);
+  }
+
+  const auto& iom = pairing_request.GetIoCapability();
+  const auto& ios = pairing_response.GetIoCapability();
+
+  if (iom == IoCapability::NO_INPUT_NO_OUTPUT || ios == IoCapability::NO_INPUT_NO_OUTPUT) {
+    return LegacyJustWorks();
+  }
+
+  if ((iom == IoCapability::DISPLAY_ONLY || iom == IoCapability::DISPLAY_YES_NO) &&
+      (ios == IoCapability::DISPLAY_ONLY || ios == IoCapability::DISPLAY_YES_NO)) {
+    return LegacyJustWorks();
+  }
+
+  // This if() should not be needed, these are only combinations left.
+  if (iom == IoCapability::KEYBOARD_DISPLAY || iom == IoCapability::KEYBOARD_ONLY ||
+      ios == IoCapability::KEYBOARD_DISPLAY || ios == IoCapability::KEYBOARD_ONLY) {
+    IoCapability my_iocaps = IAmMaster(i) ? iom : ios;
+    IoCapability remote_iocaps = IAmMaster(i) ? ios : iom;
+    return LegacyPasskeyEntry(i, my_iocaps, remote_iocaps);
+  }
+
+  // We went through all possble combinations.
+  LOG_ALWAYS_FATAL("This should never happen");
+}
+
+LegacyStage1ResultOrFailure PairingHandlerLe::LegacyJustWorks() {
+  LOG_INFO("Legacy Just Works start");
+  return Octet16{0};
+}
+
+LegacyStage1ResultOrFailure PairingHandlerLe::LegacyPasskeyEntry(const InitialInformations& i,
+                                                                 const IoCapability& my_iocaps,
+                                                                 const IoCapability& remote_iocaps) {
+  bool i_am_displaying = false;
+  if (my_iocaps == IoCapability::DISPLAY_ONLY || my_iocaps == IoCapability::DISPLAY_YES_NO) {
+    i_am_displaying = true;
+  } else if (IAmMaster(i) && remote_iocaps == IoCapability::KEYBOARD_DISPLAY &&
+             my_iocaps == IoCapability::KEYBOARD_DISPLAY) {
+    i_am_displaying = true;
+  } else if (my_iocaps == IoCapability::KEYBOARD_DISPLAY && remote_iocaps == IoCapability::KEYBOARD_ONLY) {
+    i_am_displaying = true;
+  }
+
+  LOG_INFO("Passkey Entry start %s", i_am_displaying ? "displaying" : "accepting");
+
+  uint32_t passkey;
+  if (i_am_displaying) {
+    // generate passkey in a valid range
+    passkey = GenerateRandom();
+    passkey &= 0x0fffff; /* maximum 20 significant bits */
+    constexpr uint32_t PASSKEY_MAX = 999999;
+    if (passkey > PASSKEY_MAX) passkey >>= 1;
+
+    i.user_interface_handler->Post(common::BindOnce(&UI::DisplayConfirmValue, common::Unretained(i.user_interface),
+                                                    i.remote_connection_address, i.remote_name, passkey));
+  } else {
+    i.user_interface_handler->Post(common::BindOnce(&UI::DisplayEnterPasskeyDialog,
+                                                    common::Unretained(i.user_interface), i.remote_connection_address,
+                                                    i.remote_name));
+    std::optional<PairingEvent> response = WaitUiPasskey();
+    if (!response) return PairingFailure("Passkey did not arrive!");
+
+    passkey = response->ui_value;
+  }
+
+  Octet16 tk{0};
+  tk[0] = (uint8_t)(passkey);
+  tk[1] = (uint8_t)(passkey >> 8);
+  tk[2] = (uint8_t)(passkey >> 16);
+  tk[3] = (uint8_t)(passkey >> 24);
+
+  LOG_INFO("Passkey Entry finish");
+  return tk;
+}
+
+LegacyStage1ResultOrFailure PairingHandlerLe::LegacyOutOfBand(const InitialInformations& i) {
+  return i.remote_oob_data->security_manager_tk_value;
+}
+
+StkOrFailure PairingHandlerLe::DoLegacyStage2(const InitialInformations& i, const PairingRequestView& pairing_request,
+                                              const PairingResponseView& pairing_response, const Octet16& tk) {
+  LOG_INFO("Legacy Step 2 start");
+  std::vector<uint8_t> preq(pairing_request.begin(), pairing_request.end());
+  std::vector<uint8_t> pres(pairing_response.begin(), pairing_response.end());
+
+  Octet16 mrand, srand;
+  if (IAmMaster(i)) {
+    mrand = GenerateRandom<16>();
+
+    // LOG(INFO) << +(IAmMaster(i)) << " tk = " << base::HexEncode(tk.data(), tk.size());
+    // LOG(INFO) << +(IAmMaster(i)) << " mrand = " << base::HexEncode(mrand.data(), mrand.size());
+    // LOG(INFO) << +(IAmMaster(i)) << " pres = " << base::HexEncode(pres.data(), pres.size());
+    // LOG(INFO) << +(IAmMaster(i)) << " preq = " << base::HexEncode(preq.data(), preq.size());
+
+    Octet16 mconfirm = crypto_toolbox::c1(
+        tk, mrand, preq.data(), pres.data(), (uint8_t)i.my_connection_address.GetAddressType(),
+        i.my_connection_address.GetAddress().address, (uint8_t)i.remote_connection_address.GetAddressType(),
+        i.remote_connection_address.GetAddress().address);
+
+    // LOG(INFO) << +(IAmMaster(i)) << " mconfirm = " << base::HexEncode(mconfirm.data(), mconfirm.size());
+
+    LOG_INFO("Master sends Mconfirm");
+    SendL2capPacket(i, PairingConfirmBuilder::Create(mconfirm));
+
+    LOG_INFO("Master waits for the Sconfirm");
+    auto sconfirm_pkt = WaitPairingConfirm();
+    if (std::holds_alternative<PairingFailure>(sconfirm_pkt)) {
+      return std::get<PairingFailure>(sconfirm_pkt);
+    }
+    Octet16 sconfirm = std::get<PairingConfirmView>(sconfirm_pkt).GetConfirmValue();
+
+    LOG_INFO("Master sends Mrand");
+    SendL2capPacket(i, PairingRandomBuilder::Create(mrand));
+
+    LOG_INFO("Master waits for Srand");
+    auto random_pkt = WaitPairingRandom();
+    if (std::holds_alternative<PairingFailure>(random_pkt)) {
+      return std::get<PairingFailure>(random_pkt);
+    }
+    srand = std::get<PairingRandomView>(random_pkt).GetRandomValue();
+
+    // LOG(INFO) << +(IAmMaster(i)) << " srand = " << base::HexEncode(srand.data(), srand.size());
+
+    Octet16 sconfirm_generated = crypto_toolbox::c1(
+        tk, srand, preq.data(), pres.data(), (uint8_t)i.my_connection_address.GetAddressType(),
+        i.my_connection_address.GetAddress().address, (uint8_t)i.remote_connection_address.GetAddressType(),
+        i.remote_connection_address.GetAddress().address);
+
+    if (sconfirm != sconfirm_generated) {
+      LOG_INFO("sconfirm does not match generated value");
+
+      SendL2capPacket(i, PairingFailedBuilder::Create(PairingFailedReason::CONFIRM_VALUE_FAILED));
+      return PairingFailure("sconfirm does not match generated value");
+    }
+  } else {
+    srand = GenerateRandom<16>();
+
+    std::vector<uint8_t> preq(pairing_request.begin(), pairing_request.end());
+    std::vector<uint8_t> pres(pairing_response.begin(), pairing_response.end());
+
+    Octet16 sconfirm = crypto_toolbox::c1(
+        tk, srand, preq.data(), pres.data(), (uint8_t)i.remote_connection_address.GetAddressType(),
+        i.remote_connection_address.GetAddress().address, (uint8_t)i.my_connection_address.GetAddressType(),
+        i.my_connection_address.GetAddress().address);
+
+    LOG_INFO("Slave waits for the Mconfirm");
+    auto mconfirm_pkt = WaitPairingConfirm();
+    if (std::holds_alternative<PairingFailure>(mconfirm_pkt)) {
+      return std::get<PairingFailure>(mconfirm_pkt);
+    }
+    Octet16 mconfirm = std::get<PairingConfirmView>(mconfirm_pkt).GetConfirmValue();
+
+    LOG_INFO("Slave sends Sconfirm");
+    SendL2capPacket(i, PairingConfirmBuilder::Create(sconfirm));
+
+    LOG_INFO("Slave waits for Mrand");
+    auto random_pkt = WaitPairingRandom();
+    if (std::holds_alternative<PairingFailure>(random_pkt)) {
+      return std::get<PairingFailure>(random_pkt);
+    }
+    mrand = std::get<PairingRandomView>(random_pkt).GetRandomValue();
+
+    Octet16 mconfirm_generated = crypto_toolbox::c1(
+        tk, mrand, preq.data(), pres.data(), (uint8_t)i.remote_connection_address.GetAddressType(),
+        i.remote_connection_address.GetAddress().address, (uint8_t)i.my_connection_address.GetAddressType(),
+        i.my_connection_address.GetAddress().address);
+
+    if (mconfirm != mconfirm_generated) {
+      LOG_INFO("mconfirm does not match generated value");
+      SendL2capPacket(i, PairingFailedBuilder::Create(PairingFailedReason::CONFIRM_VALUE_FAILED));
+      return PairingFailure("mconfirm does not match generated value");
+    }
+
+    LOG_INFO("Slave sends Srand");
+    SendL2capPacket(i, PairingRandomBuilder::Create(srand));
+  }
+
+  LOG_INFO("Legacy stage 2 finish");
+
+  /* STK */
+  return crypto_toolbox::s1(tk, mrand, srand);
+}
+}  // namespace security
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/security/pairing_handler_le_secure_connections.cc b/gd/security/pairing_handler_le_secure_connections.cc
new file mode 100644
index 0000000..1aa7fae
--- /dev/null
+++ b/gd/security/pairing_handler_le_secure_connections.cc
@@ -0,0 +1,476 @@
+/******************************************************************************
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#include "security/pairing_handler_le.h"
+
+namespace bluetooth {
+namespace security {
+
+std::variant<PairingFailure, KeyExchangeResult> PairingHandlerLe::ExchangePublicKeys(const InitialInformations& i,
+                                                                                     OobDataFlag remote_have_oob_data) {
+  // Generate ECDH, or use one that was used for OOB data
+  const auto [private_key, public_key] = (remote_have_oob_data == OobDataFlag::NOT_PRESENT || !i.my_oob_data)
+                                             ? GenerateECDHKeyPair()
+                                             : std::make_pair(i.my_oob_data->private_key, i.my_oob_data->public_key);
+
+  LOG_INFO("Public key exchange start");
+  std::unique_ptr<PairingPublicKeyBuilder> myPublicKey = PairingPublicKeyBuilder::Create(public_key.x, public_key.y);
+
+  if (!ValidateECDHPoint(public_key)) {
+    LOG_ERROR("Can't validate my own public key!!!");
+    return PairingFailure("Can't validate my own public key");
+  }
+
+  if (IAmMaster(i)) {
+    // Send pairing public key
+    LOG_INFO("Master sends out public key");
+    SendL2capPacket(i, std::move(myPublicKey));
+  }
+
+  LOG_INFO(" Waiting for Public key...");
+  auto response = WaitPairingPublicKey();
+  LOG_INFO(" Received public key");
+  if (std::holds_alternative<PairingFailure>(response)) {
+    return std::get<PairingFailure>(response);
+  }
+
+  EcdhPublicKey remote_public_key;
+  auto ppkv = std::get<PairingPublicKeyView>(response);
+  remote_public_key.x = ppkv.GetPublicKeyX();
+  remote_public_key.y = ppkv.GetPublicKeyY();
+  LOG_INFO("Received Public key from remote");
+
+  // validate received public key
+  if (!ValidateECDHPoint(remote_public_key)) {
+    // TODO: Spec is unclear what should happend when the point is not on
+    // the correct curve: A device that detects an invalid public key from
+    // the peer at any point during the LE Secure Connections pairing
+    // process shall not use the resulting LTK, if any.
+    LOG_INFO("Can't validate remote public key");
+    return PairingFailure("Can't validate remote public key");
+  }
+
+  if (!IAmMaster(i)) {
+    LOG_INFO("Slave sends out public key");
+    // Send pairing public key
+    SendL2capPacket(i, std::move(myPublicKey));
+  }
+
+  LOG_INFO("Public key exchange finish");
+
+  std::array<uint8_t, 32> dhkey = ComputeDHKey(private_key, remote_public_key);
+
+  const EcdhPublicKey& PKa = IAmMaster(i) ? public_key : remote_public_key;
+  const EcdhPublicKey& PKb = IAmMaster(i) ? remote_public_key : public_key;
+
+  return KeyExchangeResult{PKa, PKb, dhkey};
+}
+
+Stage1ResultOrFailure PairingHandlerLe::DoSecureConnectionsStage1(const InitialInformations& i,
+                                                                  const EcdhPublicKey& PKa, const EcdhPublicKey& PKb,
+                                                                  const PairingRequestView& pairing_request,
+                                                                  const PairingResponseView& pairing_response) {
+  if ((pairing_request.GetAuthReq() & pairing_response.GetAuthReq() & AuthReqMaskMitm) == 0) {
+    // If both devices have not set MITM option, Just Works shall be used
+    return SecureConnectionsJustWorks(i, PKa, PKb);
+  }
+
+  if (pairing_request.GetOobDataFlag() == OobDataFlag::PRESENT ||
+      pairing_response.GetOobDataFlag() == OobDataFlag::PRESENT) {
+    OobDataFlag remote_oob_flag = IAmMaster(i) ? pairing_response.GetOobDataFlag() : pairing_request.GetOobDataFlag();
+    OobDataFlag my_oob_flag = IAmMaster(i) ? pairing_request.GetOobDataFlag() : pairing_response.GetOobDataFlag();
+    return SecureConnectionsOutOfBand(i, PKa, PKb, my_oob_flag, remote_oob_flag);
+  }
+
+  const auto& iom = pairing_request.GetIoCapability();
+  const auto& ios = pairing_response.GetIoCapability();
+
+  if ((iom == IoCapability::KEYBOARD_DISPLAY || iom == IoCapability::DISPLAY_YES_NO) &&
+      (ios == IoCapability::KEYBOARD_DISPLAY || ios == IoCapability::DISPLAY_YES_NO)) {
+    return SecureConnectionsNumericComparison(i, PKa, PKb);
+  }
+
+  if (iom == IoCapability::NO_INPUT_NO_OUTPUT || ios == IoCapability::NO_INPUT_NO_OUTPUT) {
+    return SecureConnectionsJustWorks(i, PKa, PKb);
+  }
+
+  if ((iom == IoCapability::DISPLAY_ONLY || iom == IoCapability::DISPLAY_YES_NO) &&
+      (ios == IoCapability::DISPLAY_ONLY || ios == IoCapability::DISPLAY_YES_NO)) {
+    return SecureConnectionsJustWorks(i, PKa, PKb);
+  }
+
+  IoCapability my_iocaps = IAmMaster(i) ? iom : ios;
+  IoCapability remote_iocaps = IAmMaster(i) ? ios : iom;
+  return SecureConnectionsPasskeyEntry(i, PKa, PKb, my_iocaps, remote_iocaps);
+}
+
+Stage2ResultOrFailure PairingHandlerLe::DoSecureConnectionsStage2(const InitialInformations& i,
+                                                                  const EcdhPublicKey& PKa, const EcdhPublicKey& PKb,
+                                                                  const PairingRequestView& pairing_request,
+                                                                  const PairingResponseView& pairing_response,
+                                                                  const Stage1Result stage1result,
+                                                                  const std::array<uint8_t, 32>& dhkey) {
+  LOG_INFO("Authentication stage 2 started");
+
+  auto [Na, Nb, ra, rb] = stage1result;
+
+  // 2.3.5.6.5 Authentication stage 2 long term key calculation
+  uint8_t a[7];
+  uint8_t b[7];
+
+  if (IAmMaster(i)) {
+    memcpy(a, i.my_connection_address.GetAddress().address, 6);
+    a[6] = (uint8_t)i.my_connection_address.GetAddressType();
+    memcpy(b, i.remote_connection_address.GetAddress().address, 6);
+    b[6] = (uint8_t)i.remote_connection_address.GetAddressType();
+  } else {
+    memcpy(a, i.remote_connection_address.GetAddress().address, 6);
+    a[6] = (uint8_t)i.remote_connection_address.GetAddressType();
+    memcpy(b, i.my_connection_address.GetAddress().address, 6);
+    b[6] = (uint8_t)i.my_connection_address.GetAddressType();
+  }
+
+  Octet16 ltk, mac_key;
+  crypto_toolbox::f5((uint8_t*)dhkey.data(), Na, Nb, a, b, &mac_key, &ltk);
+
+  // DHKey exchange and check
+
+  std::array<uint8_t, 3> iocapA{static_cast<uint8_t>(pairing_request.GetIoCapability()),
+                                static_cast<uint8_t>(pairing_request.GetOobDataFlag()), pairing_request.GetAuthReq()};
+  std::array<uint8_t, 3> iocapB{static_cast<uint8_t>(pairing_response.GetIoCapability()),
+                                static_cast<uint8_t>(pairing_response.GetOobDataFlag()), pairing_response.GetAuthReq()};
+
+  // LOG(INFO) << +(IAmMaster(i)) << " LTK = " << base::HexEncode(ltk.data(), ltk.size());
+  // LOG(INFO) << +(IAmMaster(i)) << " MAC_KEY = " << base::HexEncode(mac_key.data(), mac_key.size());
+  // LOG(INFO) << +(IAmMaster(i)) << " Na = " << base::HexEncode(Na.data(), Na.size());
+  // LOG(INFO) << +(IAmMaster(i)) << " Nb = " << base::HexEncode(Nb.data(), Nb.size());
+  // LOG(INFO) << +(IAmMaster(i)) << " ra = " << base::HexEncode(ra.data(), ra.size());
+  // LOG(INFO) << +(IAmMaster(i)) << " rb = " << base::HexEncode(rb.data(), rb.size());
+  // LOG(INFO) << +(IAmMaster(i)) << " iocapA = " << base::HexEncode(iocapA.data(), iocapA.size());
+  // LOG(INFO) << +(IAmMaster(i)) << " iocapB = " << base::HexEncode(iocapB.data(), iocapB.size());
+  // LOG(INFO) << +(IAmMaster(i)) << " a = " << base::HexEncode(a, 7);
+  // LOG(INFO) << +(IAmMaster(i)) << " b = " << base::HexEncode(b, 7);
+
+  Octet16 Ea = crypto_toolbox::f6(mac_key, Na, Nb, rb, iocapA.data(), a, b);
+
+  Octet16 Eb = crypto_toolbox::f6(mac_key, Nb, Na, ra, iocapB.data(), b, a);
+
+  if (IAmMaster(i)) {
+    // send Pairing DHKey Check
+    SendL2capPacket(i, PairingDhKeyCheckBuilder::Create(Ea));
+
+    auto response = WaitPairingDHKeyCheck();
+    if (std::holds_alternative<PairingFailure>(response)) {
+      return std::get<PairingFailure>(response);
+    }
+
+    if (std::get<PairingDhKeyCheckView>(response).GetDhKeyCheck() != Eb) {
+      LOG_INFO("Ea != Eb, aborting!");
+      SendL2capPacket(i, PairingFailedBuilder::Create(PairingFailedReason::DHKEY_CHECK_FAILED));
+      return PairingFailure("Ea != Eb");
+    }
+  } else {
+    auto response = WaitPairingDHKeyCheck();
+    if (std::holds_alternative<PairingFailure>(response)) {
+      return std::get<PairingFailure>(response);
+    }
+
+    if (std::get<PairingDhKeyCheckView>(response).GetDhKeyCheck() != Ea) {
+      LOG_INFO("Ea != Eb, aborting!");
+      SendL2capPacket(i, PairingFailedBuilder::Create(PairingFailedReason::DHKEY_CHECK_FAILED));
+      return PairingFailure("Ea != Eb");
+    }
+
+    // send Pairing DHKey Check
+    SendL2capPacket(i, PairingDhKeyCheckBuilder::Create(Eb));
+  }
+
+  LOG_INFO("Authentication stage 2 (DHKey checks) finished");
+  return ltk;
+}
+
+Stage1ResultOrFailure PairingHandlerLe::SecureConnectionsOutOfBand(const InitialInformations& i,
+                                                                   const EcdhPublicKey& Pka, const EcdhPublicKey& Pkb,
+                                                                   OobDataFlag my_oob_flag,
+                                                                   OobDataFlag remote_oob_flag) {
+  LOG_INFO("Out Of Band start");
+
+  Octet16 zeros{0};
+  Octet16 localR = (remote_oob_flag == OobDataFlag::PRESENT && i.my_oob_data) ? i.my_oob_data->r : zeros;
+  Octet16 remoteR;
+
+  if (my_oob_flag == OobDataFlag::NOT_PRESENT || (my_oob_flag == OobDataFlag::PRESENT && !i.remote_oob_data)) {
+    /* we have send the OOB data, but not received them. remote will check if
+     * C value is correct */
+    remoteR = zeros;
+  } else {
+    remoteR = i.remote_oob_data->le_sc_r;
+    Octet16 remoteC = i.remote_oob_data->le_sc_c;
+
+    Octet16 remoteC2;
+    if (IAmMaster(i)) {
+      remoteC2 = crypto_toolbox::f4((uint8_t*)Pkb.x.data(), (uint8_t*)Pkb.x.data(), remoteR, 0);
+    } else {
+      remoteC2 = crypto_toolbox::f4((uint8_t*)Pka.x.data(), (uint8_t*)Pka.x.data(), remoteR, 0);
+    }
+
+    if (remoteC2 != remoteC) {
+      LOG_ERROR("C_computed != C_from_remote, aborting!");
+      return PairingFailure("C_computed != C_from_remote, aborting");
+    }
+  }
+
+  Octet16 Na, Nb, ra, rb;
+  if (IAmMaster(i)) {
+    ra = localR;
+    rb = remoteR;
+    Na = GenerateRandom<16>();
+    // Send Pairing Random
+    SendL2capPacket(i, PairingRandomBuilder::Create(Na));
+
+    LOG_INFO("Master waits for Nb");
+    auto random = WaitPairingRandom();
+    if (std::holds_alternative<PairingFailure>(random)) {
+      return std::get<PairingFailure>(random);
+    }
+    Nb = std::get<PairingRandomView>(random).GetRandomValue();
+  } else {
+    ra = remoteR;
+    rb = localR;
+    Nb = GenerateRandom<16>();
+
+    LOG_INFO("Slave waits for random");
+    auto random = WaitPairingRandom();
+    if (std::holds_alternative<PairingFailure>(random)) {
+      return std::get<PairingFailure>(random);
+    }
+    Na = std::get<PairingRandomView>(random).GetRandomValue();
+
+    SendL2capPacket(i, PairingRandomBuilder::Create(Nb));
+  }
+
+  return Stage1Result{Na, Nb, ra, rb};
+}
+
+Stage1ResultOrFailure PairingHandlerLe::SecureConnectionsPasskeyEntry(const InitialInformations& i,
+                                                                      const EcdhPublicKey& PKa,
+                                                                      const EcdhPublicKey& PKb, IoCapability my_iocaps,
+                                                                      IoCapability remote_iocaps) {
+  LOG_INFO("Passkey Entry start");
+  Octet16 Na, Nb, ra{0}, rb{0};
+
+  uint32_t passkey;
+
+  if (my_iocaps == IoCapability::DISPLAY_ONLY || remote_iocaps == IoCapability::KEYBOARD_ONLY) {
+    // I display
+    passkey = GenerateRandom();
+    passkey &= 0x0fffff; /* maximum 20 significant bytes */
+    constexpr uint32_t PASSKEY_MAX = 999999;
+    while (passkey > PASSKEY_MAX) passkey >>= 1;
+
+    i.user_interface_handler->Post(common::BindOnce(&UI::DisplayPasskey, common::Unretained(i.user_interface),
+                                                    i.remote_connection_address, i.remote_name, passkey));
+
+  } else if (my_iocaps == IoCapability::KEYBOARD_ONLY || remote_iocaps == IoCapability::DISPLAY_ONLY) {
+    i.user_interface_handler->Post(common::BindOnce(&UI::DisplayEnterPasskeyDialog,
+                                                    common::Unretained(i.user_interface), i.remote_connection_address,
+                                                    i.remote_name));
+    std::optional<PairingEvent> response = WaitUiPasskey();
+    if (!response) return PairingFailure("Passkey did not arrive!");
+
+    passkey = response->ui_value;
+
+    /*TODO: shall we send "Keypress Notification" after each key ? This would
+     * have impact on the SMP timeout*/
+
+  } else {
+    LOG(FATAL) << "THIS SHOULD NEVER HAPPEN";
+    return PairingFailure("FATAL!");
+  }
+
+  uint32_t bitmask = 0x01;
+  for (int loop = 0; loop < 20; loop++, bitmask <<= 1) {
+    LOG_INFO("Iteration no %d", loop);
+    bool bit_set = ((bitmask & passkey) != 0);
+    uint8_t ri = bit_set ? 0x81 : 0x80;
+
+    Octet16 Cai, Cbi, Nai, Nbi;
+    if (IAmMaster(i)) {
+      Nai = GenerateRandom<16>();
+
+      Cai = crypto_toolbox::f4((uint8_t*)PKa.x.data(), (uint8_t*)PKb.x.data(), Nai, ri);
+
+      // Send Pairing Confirm
+      LOG_INFO("Master sends Cai");
+      SendL2capPacket(i, PairingConfirmBuilder::Create(Cai));
+
+      LOG_INFO("Master waits for the Cbi");
+      auto confirm = WaitPairingConfirm();
+      if (std::holds_alternative<PairingFailure>(confirm)) {
+        return std::get<PairingFailure>(confirm);
+      }
+      Cbi = std::get<PairingConfirmView>(confirm).GetConfirmValue();
+
+      // Send Pairing Random
+      SendL2capPacket(i, PairingRandomBuilder::Create(Nai));
+
+      LOG_INFO("Master waits for Nbi");
+      auto random = WaitPairingRandom();
+      if (std::holds_alternative<PairingFailure>(random)) {
+        return std::get<PairingFailure>(random);
+      }
+      Nbi = std::get<PairingRandomView>(random).GetRandomValue();
+
+      Octet16 Cbi2 = crypto_toolbox::f4((uint8_t*)PKb.x.data(), (uint8_t*)PKa.x.data(), Nbi, ri);
+      if (Cbi != Cbi2) {
+        LOG_INFO("Cai != Cbi, aborting!");
+        SendL2capPacket(i, PairingFailedBuilder::Create(PairingFailedReason::CONFIRM_VALUE_FAILED));
+        return PairingFailure("Cai != Cbi");
+      }
+    } else {
+      Nbi = GenerateRandom<16>();
+      // Compute confirm
+      Cbi = crypto_toolbox::f4((uint8_t*)PKb.x.data(), (uint8_t*)PKa.x.data(), Nbi, ri);
+
+      LOG_INFO("Slave waits for the Cai");
+      auto confirm = WaitPairingConfirm();
+      if (std::holds_alternative<PairingFailure>(confirm)) {
+        return std::get<PairingFailure>(confirm);
+      }
+      Cai = std::get<PairingConfirmView>(confirm).GetConfirmValue();
+
+      // Send Pairing Confirm
+      LOG_INFO("Slave sends confirmation");
+      SendL2capPacket(i, PairingConfirmBuilder::Create(Cbi));
+
+      LOG_INFO("Slave waits for random");
+      auto random = WaitPairingRandom();
+      if (std::holds_alternative<PairingFailure>(random)) {
+        return std::get<PairingFailure>(random);
+      }
+      Nai = std::get<PairingRandomView>(random).GetRandomValue();
+
+      Octet16 Cai2 = crypto_toolbox::f4((uint8_t*)PKa.x.data(), (uint8_t*)PKb.x.data(), Nai, ri);
+      if (Cai != Cai2) {
+        LOG_INFO("Cai != Cai2, aborting!");
+        SendL2capPacket(i, PairingFailedBuilder::Create(PairingFailedReason::CONFIRM_VALUE_FAILED));
+        return PairingFailure("Cai != Cai2");
+      }
+
+      // Send Pairing Random
+      SendL2capPacket(i, PairingRandomBuilder::Create(Nbi));
+    }
+
+    if (loop == 19) {
+      Na = Nai;
+      Nb = Nbi;
+    }
+  }
+
+  ra[0] = (uint8_t)(passkey);
+  ra[1] = (uint8_t)(passkey >> 8);
+  ra[2] = (uint8_t)(passkey >> 16);
+  ra[3] = (uint8_t)(passkey >> 24);
+  rb = ra;
+
+  return Stage1Result{Na, Nb, ra, rb};
+}
+
+Stage1ResultOrFailure PairingHandlerLe::SecureConnectionsNumericComparison(const InitialInformations& i,
+                                                                           const EcdhPublicKey& PKa,
+                                                                           const EcdhPublicKey& PKb) {
+  LOG_INFO("Numeric Comparison start");
+  Stage1ResultOrFailure result = SecureConnectionsJustWorks(i, PKa, PKb);
+  if (std::holds_alternative<PairingFailure>(result)) {
+    return std::get<PairingFailure>(result);
+  }
+
+  const auto [Na, Nb, ra, rb] = std::get<Stage1Result>(result);
+
+  uint32_t number_to_display = crypto_toolbox::g2((uint8_t*)PKa.x.data(), (uint8_t*)PKb.x.data(), Na, Nb);
+
+  i.user_interface_handler->Post(common::BindOnce(&UI::DisplayConfirmValue, common::Unretained(i.user_interface),
+                                                  i.remote_connection_address, i.remote_name, number_to_display));
+
+  std::optional<PairingEvent> confirmyesno = WaitUiConfirmYesNo();
+  if (!confirmyesno || confirmyesno->ui_value == 0) {
+    LOG_INFO("Was expecting the user value confirm");
+    return PairingFailure("Was expecting the user value confirm");
+  }
+
+  return result;
+}
+
+Stage1ResultOrFailure PairingHandlerLe::SecureConnectionsJustWorks(const InitialInformations& i,
+                                                                   const EcdhPublicKey& PKa, const EcdhPublicKey& PKb) {
+  Octet16 Cb, Na, Nb, ra, rb;
+
+  ra = rb = {0};
+
+  if (IAmMaster(i)) {
+    Na = GenerateRandom<16>();
+    LOG_INFO("Master waits for confirmation");
+    auto confirm = WaitPairingConfirm();
+    if (std::holds_alternative<PairingFailure>(confirm)) {
+      return std::get<PairingFailure>(confirm);
+    }
+    Cb = std::get<PairingConfirmView>(confirm).GetConfirmValue();
+
+    // Send Pairing Random
+    SendL2capPacket(i, PairingRandomBuilder::Create(Na));
+
+    LOG_INFO("Master waits for Random");
+    auto random = WaitPairingRandom();
+    if (std::holds_alternative<PairingFailure>(random)) {
+      return std::get<PairingFailure>(random);
+    }
+    Nb = std::get<PairingRandomView>(random).GetRandomValue();
+
+    // Compute Cb locally
+    Octet16 Cb_local = crypto_toolbox::f4((uint8_t*)PKb.x.data(), (uint8_t*)PKa.x.data(), Nb, 0);
+
+    if (Cb_local != Cb) {
+      LOG_INFO("Cb_local != Cb, aborting!");
+      SendL2capPacket(i, PairingFailedBuilder::Create(PairingFailedReason::CONFIRM_VALUE_FAILED));
+      return PairingFailure("Cb_local != Cb");
+    }
+  } else {
+    Nb = GenerateRandom<16>();
+    // Compute confirm
+    Cb = crypto_toolbox::f4((uint8_t*)PKb.x.data(), (uint8_t*)PKa.x.data(), Nb, 0);
+
+    // Send Pairing Confirm
+    LOG_INFO("Slave sends confirmation");
+    SendL2capPacket(i, PairingConfirmBuilder::Create(Cb));
+
+    LOG_INFO("Slave waits for random");
+    auto random = WaitPairingRandom();
+    if (std::holds_alternative<PairingFailure>(random)) {
+      return std::get<PairingFailure>(random);
+    }
+    Na = std::get<PairingRandomView>(random).GetRandomValue();
+
+    // Send Pairing Random
+    SendL2capPacket(i, PairingRandomBuilder::Create(Nb));
+  }
+
+  return Stage1Result{Na, Nb, ra, rb};
+}
+
+}  // namespace security
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/security/pairing_handler_le_unittest.cc b/gd/security/pairing_handler_le_unittest.cc
new file mode 100644
index 0000000..1f585fe
--- /dev/null
+++ b/gd/security/pairing_handler_le_unittest.cc
@@ -0,0 +1,334 @@
+/******************************************************************************
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <memory>
+
+#include "os/log.h"
+#include "security/pairing_handler_le.h"
+#include "security/test/mocks.h"
+
+using ::testing::_;
+using ::testing::Eq;
+using ::testing::Field;
+using ::testing::VariantWith;
+
+using bluetooth::security::CommandView;
+
+namespace bluetooth {
+namespace security {
+
+namespace {
+
+CommandView BuilderToView(std::unique_ptr<BasePacketBuilder> builder) {
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  builder->Serialize(it);
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  auto temp_cmd_view = CommandView::Create(packet_bytes_view);
+  return CommandView::Create(temp_cmd_view);
+}
+
+std::condition_variable outgoing_l2cap_blocker_;
+std::optional<bluetooth::security::CommandView> outgoing_l2cap_packet_;
+
+bool WaitForOutgoingL2capPacket() {
+  std::mutex mutex;
+  std::unique_lock<std::mutex> lock(mutex);
+  if (outgoing_l2cap_blocker_.wait_for(lock, std::chrono::seconds(5)) == std::cv_status::timeout) {
+    return false;
+  }
+  return true;
+}
+
+class PairingResultHandlerMock {
+ public:
+  MOCK_CONST_METHOD1(OnPairingFinished, void(PairingResultOrFailure));
+};
+
+std::unique_ptr<PairingResultHandlerMock> pairingResult;
+LeSecurityInterfaceMock leSecurityMock;
+UIMock uiMock;
+
+void OnPairingFinished(PairingResultOrFailure r) {
+  if (std::holds_alternative<PairingResult>(r)) {
+    LOG(INFO) << "pairing with " << std::get<PairingResult>(r).connection_address << " finished successfully!";
+  } else {
+    LOG(INFO) << "pairing with ... failed!";
+  }
+  pairingResult->OnPairingFinished(r);
+}
+}  // namespace
+
+class PairingHandlerUnitTest : public testing::Test {
+ protected:
+  void SetUp() {
+    thread_ = new os::Thread("test_thread", os::Thread::Priority::NORMAL);
+    handler_ = new os::Handler(thread_);
+
+    bidi_queue_ =
+        std::make_unique<common::BidiQueue<packet::PacketView<packet::kLittleEndian>, packet::BasePacketBuilder>>(10);
+    up_buffer_ = std::make_unique<os::EnqueueBuffer<packet::BasePacketBuilder>>(bidi_queue_->GetUpEnd());
+
+    bidi_queue_->GetDownEnd()->RegisterDequeue(
+        handler_, common::Bind(&PairingHandlerUnitTest::L2CAP_SendSmp, common::Unretained(this)));
+
+    pairingResult.reset(new PairingResultHandlerMock);
+  }
+  void TearDown() {
+    pairingResult.reset();
+    bidi_queue_->GetDownEnd()->UnregisterDequeue();
+    handler_->Clear();
+    delete handler_;
+    delete thread_;
+
+    ::testing::Mock::VerifyAndClearExpectations(&leSecurityMock);
+    ::testing::Mock::VerifyAndClearExpectations(&uiMock);
+  }
+
+  void L2CAP_SendSmp() {
+    std::unique_ptr<packet::BasePacketBuilder> builder = bidi_queue_->GetDownEnd()->TryDequeue();
+
+    outgoing_l2cap_packet_ = BuilderToView(std::move(builder));
+    outgoing_l2cap_packet_->IsValid();
+
+    outgoing_l2cap_blocker_.notify_one();
+  }
+
+ public:
+  os::Thread* thread_;
+  os::Handler* handler_;
+  std::unique_ptr<common::BidiQueue<packet::PacketView<packet::kLittleEndian>, packet::BasePacketBuilder>> bidi_queue_;
+  std::unique_ptr<os::EnqueueBuffer<packet::BasePacketBuilder>> up_buffer_;
+};
+
+InitialInformations initial_informations{
+    .my_role = hci::Role::MASTER,
+    .my_connection_address = {{}, hci::AddressType::PUBLIC_DEVICE_ADDRESS},
+
+    .myPairingCapabilities = {.io_capability = IoCapability::NO_INPUT_NO_OUTPUT,
+                              .oob_data_flag = OobDataFlag::NOT_PRESENT,
+                              .auth_req = AuthReqMaskBondingFlag | AuthReqMaskMitm | AuthReqMaskSc,
+                              .maximum_encryption_key_size = 16,
+                              .initiator_key_distribution = 0x03,
+                              .responder_key_distribution = 0x03},
+
+    .remotely_initiated = false,
+    .remote_connection_address = {{}, hci::AddressType::RANDOM_DEVICE_ADDRESS},
+    .user_interface = &uiMock,
+    .le_security_interface = &leSecurityMock,
+    .OnPairingFinished = OnPairingFinished,
+};
+
+TEST_F(PairingHandlerUnitTest, test_phase_1_failure) {
+  initial_informations.proper_l2cap_interface = up_buffer_.get();
+  initial_informations.l2cap_handler = handler_;
+  initial_informations.user_interface_handler = handler_;
+
+  std::unique_ptr<PairingHandlerLe> pairing_handler =
+      std::make_unique<PairingHandlerLe>(PairingHandlerLe::PHASE1, initial_informations);
+
+  EXPECT_TRUE(WaitForOutgoingL2capPacket());
+  EXPECT_EQ(outgoing_l2cap_packet_->GetCode(), Code::PAIRING_REQUEST);
+
+  EXPECT_CALL(*pairingResult, OnPairingFinished(VariantWith<PairingFailure>(_))).Times(1);
+
+  // SMP will waith for Pairing Response, once bad packet is received, it should stop the Pairing
+  CommandView bad_pairing_response = BuilderToView(PairingRandomBuilder::Create({}));
+  bad_pairing_response.IsValid();
+  pairing_handler->OnCommandView(bad_pairing_response);
+
+  EXPECT_TRUE(WaitForOutgoingL2capPacket());
+  EXPECT_EQ(outgoing_l2cap_packet_->GetCode(), Code::PAIRING_FAILED);
+}
+
+TEST_F(PairingHandlerUnitTest, test_secure_connections_just_works) {
+  initial_informations.proper_l2cap_interface = up_buffer_.get();
+  initial_informations.l2cap_handler = handler_;
+  initial_informations.user_interface_handler = handler_;
+
+  // we keep the pairing_handler as unique_ptr to better mimick how it's used
+  // in the real world
+  std::unique_ptr<PairingHandlerLe> pairing_handler =
+      std::make_unique<PairingHandlerLe>(PairingHandlerLe::PHASE1, initial_informations);
+
+  EXPECT_TRUE(WaitForOutgoingL2capPacket());
+  EXPECT_EQ(outgoing_l2cap_packet_->GetCode(), Code::PAIRING_REQUEST);
+  CommandView pairing_request = outgoing_l2cap_packet_.value();
+
+  auto pairing_response = BuilderToView(
+      PairingResponseBuilder::Create(IoCapability::KEYBOARD_DISPLAY, OobDataFlag::NOT_PRESENT,
+                                     AuthReqMaskBondingFlag | AuthReqMaskMitm | AuthReqMaskSc, 16, 0x03, 0x03));
+  pairing_handler->OnCommandView(pairing_response);
+  // Phase 1 finished.
+
+  // pairing public key
+  EXPECT_TRUE(WaitForOutgoingL2capPacket());
+  EXPECT_EQ(Code::PAIRING_PUBLIC_KEY, outgoing_l2cap_packet_->GetCode());
+  EcdhPublicKey my_public_key;
+  auto ppkv = PairingPublicKeyView::Create(outgoing_l2cap_packet_.value());
+  ppkv.IsValid();
+  my_public_key.x = ppkv.GetPublicKeyX();
+  my_public_key.y = ppkv.GetPublicKeyY();
+
+  const auto [private_key, public_key] = GenerateECDHKeyPair();
+
+  pairing_handler->OnCommandView(BuilderToView(PairingPublicKeyBuilder::Create(public_key.x, public_key.y)));
+  // DHKey exchange finished
+  std::array<uint8_t, 32> dhkey = ComputeDHKey(private_key, my_public_key);
+
+  // Phasae 2 Stage 1 start
+  Octet16 ra, rb;
+  ra = rb = {0};
+
+  Octet16 Nb = PairingHandlerLe::GenerateRandom<16>();
+
+  // Compute confirm
+  Octet16 Cb = crypto_toolbox::f4((uint8_t*)public_key.x.data(), (uint8_t*)my_public_key.x.data(), Nb, 0);
+
+  pairing_handler->OnCommandView(BuilderToView(PairingConfirmBuilder::Create(Cb)));
+
+  // random
+  EXPECT_TRUE(WaitForOutgoingL2capPacket());
+  EXPECT_EQ(Code::PAIRING_RANDOM, outgoing_l2cap_packet_->GetCode());
+  auto prv = PairingRandomView::Create(outgoing_l2cap_packet_.value());
+  prv.IsValid();
+  Octet16 Na = prv.GetRandomValue();
+
+  pairing_handler->OnCommandView(BuilderToView(PairingRandomBuilder::Create(Nb)));
+
+  // Start of authentication stage 2
+  uint8_t a[7];
+  uint8_t b[7];
+  memcpy(b, initial_informations.remote_connection_address.GetAddress().address, 6);
+  b[6] = (uint8_t)initial_informations.remote_connection_address.GetAddressType();
+  memcpy(a, initial_informations.my_connection_address.GetAddress().address, 6);
+  a[6] = (uint8_t)initial_informations.my_connection_address.GetAddressType();
+
+  Octet16 ltk, mac_key;
+  crypto_toolbox::f5(dhkey.data(), Na, Nb, a, b, &mac_key, &ltk);
+
+  PairingRequestView preqv = PairingRequestView::Create(pairing_request);
+  PairingResponseView prspv = PairingResponseView::Create(pairing_response);
+
+  preqv.IsValid();
+  prspv.IsValid();
+  std::array<uint8_t, 3> iocapA{static_cast<uint8_t>(preqv.GetIoCapability()),
+                                static_cast<uint8_t>(preqv.GetOobDataFlag()), preqv.GetAuthReq()};
+  std::array<uint8_t, 3> iocapB{static_cast<uint8_t>(prspv.GetIoCapability()),
+                                static_cast<uint8_t>(prspv.GetOobDataFlag()), prspv.GetAuthReq()};
+
+  Octet16 Ea = crypto_toolbox::f6(mac_key, Na, Nb, rb, iocapA.data(), a, b);
+  Octet16 Eb = crypto_toolbox::f6(mac_key, Nb, Na, ra, iocapB.data(), b, a);
+
+  EXPECT_TRUE(WaitForOutgoingL2capPacket());
+  EXPECT_EQ(Code::PAIRING_DH_KEY_CHECK, outgoing_l2cap_packet_->GetCode());
+  auto pdhkcv = PairingDhKeyCheckView::Create(outgoing_l2cap_packet_.value());
+  pdhkcv.IsValid();
+  EXPECT_EQ(pdhkcv.GetDhKeyCheck(), Ea);
+
+  pairing_handler->OnCommandView(BuilderToView(PairingDhKeyCheckBuilder::Create(Eb)));
+
+  // Phase 2 finished
+  // We don't care for the rest of the flow, let it die.
+}
+
+InitialInformations initial_informations_trsi{
+    .my_role = hci::Role::MASTER,
+    .my_connection_address = hci::AddressWithType(),
+
+    .myPairingCapabilities = {.io_capability = IoCapability::NO_INPUT_NO_OUTPUT,
+                              .oob_data_flag = OobDataFlag::NOT_PRESENT,
+                              .auth_req = AuthReqMaskBondingFlag | AuthReqMaskMitm | AuthReqMaskSc,
+                              .maximum_encryption_key_size = 16,
+                              .initiator_key_distribution = 0x03,
+                              .responder_key_distribution = 0x03},
+
+    .remotely_initiated = true,
+    .remote_connection_address = hci::AddressWithType(),
+    .user_interface = &uiMock,
+    .le_security_interface = &leSecurityMock,
+    .OnPairingFinished = OnPairingFinished,
+};
+
+/* This test verifies that when remote slave device sends security request , and user
+ * does accept the prompt, we do send pairing request */
+TEST_F(PairingHandlerUnitTest, test_remote_slave_initiating) {
+  initial_informations_trsi.proper_l2cap_interface = up_buffer_.get();
+  initial_informations_trsi.l2cap_handler = handler_;
+  initial_informations_trsi.user_interface_handler = handler_;
+
+  std::unique_ptr<PairingHandlerLe> pairing_handler =
+      std::make_unique<PairingHandlerLe>(PairingHandlerLe::ACCEPT_PROMPT, initial_informations_trsi);
+
+  // Simulate user accepting the pairing in UI
+  pairing_handler->OnUiAction(PairingEvent::PAIRING_ACCEPTED, 0x01 /* Non-zero value means success */);
+
+  EXPECT_TRUE(WaitForOutgoingL2capPacket());
+  EXPECT_EQ(Code::PAIRING_REQUEST, outgoing_l2cap_packet_->GetCode());
+
+  // We don't care for the rest of the flow, let it die.
+  pairing_handler.reset();
+}
+
+InitialInformations initial_informations_trmi{
+    .my_role = hci::Role::SLAVE,
+    .my_connection_address = hci::AddressWithType(),
+
+    .myPairingCapabilities = {.io_capability = IoCapability::NO_INPUT_NO_OUTPUT,
+                              .oob_data_flag = OobDataFlag::NOT_PRESENT,
+                              .auth_req = AuthReqMaskBondingFlag | AuthReqMaskMitm | AuthReqMaskSc,
+                              .maximum_encryption_key_size = 16,
+                              .initiator_key_distribution = 0x03,
+                              .responder_key_distribution = 0x03},
+
+    .remotely_initiated = true,
+    .remote_connection_address = hci::AddressWithType(),
+    .pairing_request = PairingRequestView::Create(BuilderToView(
+        PairingRequestBuilder::Create(IoCapability::NO_INPUT_NO_OUTPUT, OobDataFlag::NOT_PRESENT,
+                                      AuthReqMaskBondingFlag | AuthReqMaskMitm | AuthReqMaskSc, 16, 0x03, 0x03))),
+    .user_interface = &uiMock,
+    .le_security_interface = &leSecurityMock,
+
+    .OnPairingFinished = OnPairingFinished,
+};
+
+/* This test verifies that when remote device sends pairing request, and user does accept the prompt, we do send proper
+ * reply back */
+TEST_F(PairingHandlerUnitTest, test_remote_master_initiating) {
+  initial_informations_trmi.proper_l2cap_interface = up_buffer_.get();
+  initial_informations_trmi.l2cap_handler = handler_;
+  initial_informations_trmi.user_interface_handler = handler_;
+
+  std::unique_ptr<PairingHandlerLe> pairing_handler =
+      std::make_unique<PairingHandlerLe>(PairingHandlerLe::ACCEPT_PROMPT, initial_informations_trmi);
+
+  // Simulate user accepting the pairing in UI
+  pairing_handler->OnUiAction(PairingEvent::PAIRING_ACCEPTED, 0x01 /* Non-zero value means success */);
+
+  EXPECT_TRUE(WaitForOutgoingL2capPacket());
+  EXPECT_EQ(Code::PAIRING_RESPONSE, outgoing_l2cap_packet_->GetCode());
+  // Phase 1 finished.
+
+  // We don't care for the rest of the flow, it's handled in in other tests. let it die.
+  pairing_handler.reset();
+}
+
+}  // namespace security
+}  // namespace bluetooth
diff --git a/gd/security/record/security_record.h b/gd/security/record/security_record.h
new file mode 100644
index 0000000..79f7154
--- /dev/null
+++ b/gd/security/record/security_record.h
@@ -0,0 +1,113 @@
+/*
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+#pragma once
+
+#include <memory>
+#include <utility>
+
+#include "crypto_toolbox/crypto_toolbox.h"
+#include "hci/address_with_type.h"
+
+namespace bluetooth {
+namespace security {
+namespace record {
+
+class SecurityRecord {
+ public:
+  explicit SecurityRecord(hci::AddressWithType address) : pseudo_address_(address), pairing_(true) {}
+
+  SecurityRecord& operator=(const SecurityRecord& other) = default;
+
+  /**
+   * Returns true if a device is currently pairing to another device
+   */
+  bool IsPairing() const {
+    return pairing_;
+  }
+
+  /* Link key has been exchanged, but not stored */
+  bool IsPaired() const {
+    return IsClassicLinkKeyValid();
+  }
+
+  /**
+   * Returns true if Link Keys are stored persistently
+   */
+  bool IsBonded() const {
+    return IsPaired() && persisted_;
+  }
+
+  /**
+   * Called by storage manager once record has persisted
+   */
+  void SetPersisted(bool persisted) {
+    persisted_ = persisted;
+  }
+
+  void SetLinkKey(std::array<uint8_t, 16> link_key, hci::KeyType key_type) {
+    link_key_ = link_key;
+    key_type_ = key_type;
+    CancelPairing();
+  }
+
+  void CancelPairing() {
+    pairing_ = false;
+  }
+
+  std::array<uint8_t, 16> GetLinkKey() {
+    ASSERT(IsClassicLinkKeyValid());
+    return link_key_;
+  }
+
+  hci::KeyType GetKeyType() {
+    ASSERT(IsClassicLinkKeyValid());
+    return key_type_;
+  }
+
+  hci::AddressWithType GetPseudoAddress() {
+    return pseudo_address_;
+  }
+
+ private:
+  /* First address we have ever seen this device with, that we used to create bond */
+  hci::AddressWithType pseudo_address_;
+
+  std::array<uint8_t, 16> link_key_ = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+  hci::KeyType key_type_ = hci::KeyType::DEBUG_COMBINATION;
+
+  bool IsClassicLinkKeyValid() const {
+    return !std::all_of(link_key_.begin(), link_key_.end(), [](uint8_t b) { return b == 0; });
+  }
+  bool persisted_ = false;
+  bool pairing_ = false;
+
+ public:
+  /* Identity Address */
+  std::optional<hci::AddressWithType> identity_address_;
+
+  std::optional<crypto_toolbox::Octet16> ltk;
+  std::optional<uint16_t> ediv;
+  std::optional<std::array<uint8_t, 8>> rand;
+  std::optional<crypto_toolbox::Octet16> irk;
+  std::optional<crypto_toolbox::Octet16> signature_key;
+};
+
+}  // namespace record
+}  // namespace security
+}  // namespace bluetooth
diff --git a/gd/security/security_manager.cc b/gd/security/security_manager.cc
new file mode 100644
index 0000000..34efbe2
--- /dev/null
+++ b/gd/security/security_manager.cc
@@ -0,0 +1,86 @@
+/*
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+#include "security_manager.h"
+
+#include "os/log.h"
+
+namespace bluetooth {
+namespace security {
+
+// Definition of Pure Virtual Destructor
+ISecurityManagerListener::~ISecurityManagerListener() {}
+
+void SecurityManager::Init() {
+  security_handler_->Post(
+      common::BindOnce(&internal::SecurityManagerImpl::Init, common::Unretained(security_manager_impl_)));
+}
+
+void SecurityManager::CreateBond(hci::AddressWithType device) {
+  security_handler_->Post(common::BindOnce(&internal::SecurityManagerImpl::CreateBond,
+                                           common::Unretained(security_manager_impl_),
+                                           std::forward<hci::AddressWithType>(device)));
+}
+
+void SecurityManager::CreateBondLe(hci::AddressWithType device) {
+  security_handler_->Post(common::BindOnce(&internal::SecurityManagerImpl::CreateBondLe,
+                                           common::Unretained(security_manager_impl_),
+                                           std::forward<hci::AddressWithType>(device)));
+}
+
+void SecurityManager::CancelBond(hci::AddressWithType device) {
+  security_handler_->Post(common::BindOnce(&internal::SecurityManagerImpl::CancelBond,
+                                           common::Unretained(security_manager_impl_),
+                                           std::forward<hci::AddressWithType>(device)));
+}
+
+void SecurityManager::RemoveBond(hci::AddressWithType device) {
+  security_handler_->Post(common::BindOnce(&internal::SecurityManagerImpl::RemoveBond,
+                                           common::Unretained(security_manager_impl_),
+                                           std::forward<hci::AddressWithType>(device)));
+}
+
+void SecurityManager::SetUserInterfaceHandler(UI* user_interface, os::Handler* handler) {
+  security_handler_->Post(common::BindOnce(&internal::SecurityManagerImpl::SetUserInterfaceHandler,
+                                           common::Unretained(security_manager_impl_), user_interface, handler));
+}
+
+void SecurityManager::RegisterCallbackListener(ISecurityManagerListener* listener, os::Handler* handler) {
+  security_handler_->Post(common::BindOnce(&internal::SecurityManagerImpl::RegisterCallbackListener,
+                                           common::Unretained(security_manager_impl_), listener, handler));
+}
+
+void SecurityManager::UnregisterCallbackListener(ISecurityManagerListener* listener) {
+  security_handler_->Post(common::BindOnce(&internal::SecurityManagerImpl::UnregisterCallbackListener,
+                                           common::Unretained(security_manager_impl_), listener));
+}
+
+void SecurityManager::OnPairingPromptAccepted(const bluetooth::hci::AddressWithType& address, bool confirmed) {
+  security_handler_->Post(common::BindOnce(&internal::SecurityManagerImpl::OnPairingPromptAccepted,
+                                           common::Unretained(security_manager_impl_), address, confirmed));
+}
+void SecurityManager::OnConfirmYesNo(const bluetooth::hci::AddressWithType& address, bool confirmed) {
+  security_handler_->Post(common::BindOnce(&internal::SecurityManagerImpl::OnConfirmYesNo,
+                                           common::Unretained(security_manager_impl_), address, confirmed));
+}
+void SecurityManager::OnPasskeyEntry(const bluetooth::hci::AddressWithType& address, uint32_t passkey) {
+  security_handler_->Post(common::BindOnce(&internal::SecurityManagerImpl::OnPasskeyEntry,
+                                           common::Unretained(security_manager_impl_), address, passkey));
+}
+
+}  // namespace security
+}  // namespace bluetooth
diff --git a/gd/security/security_manager.h b/gd/security/security_manager.h
new file mode 100644
index 0000000..fc9dbcd
--- /dev/null
+++ b/gd/security/security_manager.h
@@ -0,0 +1,106 @@
+/*
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+#pragma once
+
+#include <memory>
+#include <vector>
+
+#include "hci/address_with_type.h"
+#include "security/internal/security_manager_impl.h"
+#include "security/security_manager_listener.h"
+
+namespace bluetooth {
+namespace security {
+
+/**
+ * Manages the security attributes, pairing, bonding of devices, and the
+ * encryption/decryption of communications.
+ */
+class SecurityManager : public UICallbacks {
+ public:
+  friend class SecurityModule;
+
+  /**
+   * Initialize the security record map from an internal device database.
+   */
+  void Init();
+
+  /**
+   * Initiates bond over Classic transport with device, if not bonded yet.
+   *
+   * @param address device address we want to bond with
+   */
+  void CreateBond(hci::AddressWithType address);
+
+  /**
+   * Initiates bond over Low Energy transport with device, if not bonded yet.
+   *
+   * @param address device address we want to bond with
+   */
+  void CreateBondLe(hci::AddressWithType address);
+
+  /**
+   * Cancels the pairing process for this device.
+   *
+   * @param device pointer to device with which we want to cancel our bond
+   */
+  void CancelBond(hci::AddressWithType device);
+
+  /**
+   * Disassociates the device and removes the persistent LTK
+   *
+   * @param device pointer to device we want to forget
+   */
+  void RemoveBond(hci::AddressWithType device);
+
+  /**
+   * Register Security UI handler, for handling prompts around the Pairing process.
+   */
+  void SetUserInterfaceHandler(UI* user_interface, os::Handler* handler);
+
+  /**
+   * Register to listen for callback events from SecurityManager
+   *
+   * @param listener ISecurityManagerListener instance to handle callbacks
+   */
+  void RegisterCallbackListener(ISecurityManagerListener* listener, os::Handler* handler);
+
+  /**
+   * Unregister listener for callback events from SecurityManager
+   *
+   * @param listener ISecurityManagerListener instance to unregister
+   */
+  void UnregisterCallbackListener(ISecurityManagerListener* listener);
+
+  void OnPairingPromptAccepted(const bluetooth::hci::AddressWithType& address, bool confirmed) override;
+  void OnConfirmYesNo(const bluetooth::hci::AddressWithType& address, bool confirmed) override;
+  void OnPasskeyEntry(const bluetooth::hci::AddressWithType& address, uint32_t passkey) override;
+
+ protected:
+  SecurityManager(os::Handler* security_handler, internal::SecurityManagerImpl* security_manager_impl)
+      : security_handler_(security_handler), security_manager_impl_(security_manager_impl) {}
+
+ private:
+  os::Handler* security_handler_ = nullptr;
+  internal::SecurityManagerImpl* security_manager_impl_;
+  DISALLOW_COPY_AND_ASSIGN(SecurityManager);
+};
+
+}  // namespace security
+}  // namespace bluetooth
diff --git a/gd/security/security_manager_listener.h b/gd/security/security_manager_listener.h
new file mode 100644
index 0000000..edc4e6b
--- /dev/null
+++ b/gd/security/security_manager_listener.h
@@ -0,0 +1,56 @@
+/*
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+#pragma once
+
+#include "common/callback.h"
+
+namespace bluetooth {
+namespace security {
+
+/**
+ * Callback interface from SecurityManager.
+ */
+class ISecurityManagerListener {
+ public:
+  virtual ~ISecurityManagerListener() = 0;
+
+  /**
+   * Called when a device is successfully bonded.
+   *
+   * @param address of the newly bonded device
+   */
+  virtual void OnDeviceBonded(bluetooth::hci::AddressWithType device) = 0;
+
+  /**
+   * Called when a device is successfully un-bonded.
+   *
+   * @param address of device that is no longer bonded
+   */
+  virtual void OnDeviceUnbonded(bluetooth::hci::AddressWithType device) = 0;
+
+  /**
+   * Called as a result of a failure during the bonding process.
+   *
+   * @param address of the device that failed to bond
+   */
+  virtual void OnDeviceBondFailed(bluetooth::hci::AddressWithType device) = 0;
+};
+
+}  // namespace security
+}  // namespace bluetooth
diff --git a/gd/security/security_module.cc b/gd/security/security_module.cc
new file mode 100644
index 0000000..eb0a543
--- /dev/null
+++ b/gd/security/security_module.cc
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "security"
+
+#include <memory>
+#include "module.h"
+#include "os/handler.h"
+#include "os/log.h"
+
+#include "hci/hci_layer.h"
+#include "l2cap/le/l2cap_le_module.h"
+#include "security/channel/security_manager_channel.h"
+#include "security/internal/security_manager_impl.h"
+#include "security/security_module.h"
+
+namespace bluetooth {
+namespace security {
+
+const ModuleFactory SecurityModule::Factory = ModuleFactory([]() { return new SecurityModule(); });
+
+struct SecurityModule::impl {
+  impl(os::Handler* security_handler, l2cap::le::L2capLeModule* l2cap_le_module,
+       l2cap::classic::L2capClassicModule* l2cap_classic_module, hci::HciLayer* hci_layer)
+      : security_handler_(security_handler), l2cap_le_module_(l2cap_le_module),
+        l2cap_classic_module_(l2cap_classic_module),
+        security_manager_channel_(new channel::SecurityManagerChannel(security_handler_, hci_layer)),
+        hci_layer_(hci_layer) {}
+
+  os::Handler* security_handler_;
+  l2cap::le::L2capLeModule* l2cap_le_module_;
+  l2cap::classic::L2capClassicModule* l2cap_classic_module_;
+  channel::SecurityManagerChannel* security_manager_channel_;
+  hci::HciLayer* hci_layer_;
+  internal::SecurityManagerImpl security_manager_impl{security_handler_, l2cap_le_module_, l2cap_classic_module_,
+                                                      security_manager_channel_, hci_layer_};
+  ~impl() {
+    delete security_manager_channel_;
+  }
+};
+
+void SecurityModule::ListDependencies(ModuleList* list) {
+  list->add<l2cap::le::L2capLeModule>();
+  list->add<l2cap::classic::L2capClassicModule>();
+  list->add<hci::HciLayer>();
+}
+
+void SecurityModule::Start() {
+  pimpl_ = std::make_unique<impl>(GetHandler(), GetDependency<l2cap::le::L2capLeModule>(),
+                                  GetDependency<l2cap::classic::L2capClassicModule>(), GetDependency<hci::HciLayer>());
+}
+
+void SecurityModule::Stop() {
+  pimpl_.reset();
+}
+
+std::string SecurityModule::ToString() const {
+  return "Security Module";
+}
+
+std::unique_ptr<SecurityManager> SecurityModule::GetSecurityManager() {
+  return std::unique_ptr<SecurityManager>(
+      new SecurityManager(pimpl_->security_handler_, &pimpl_->security_manager_impl));
+}
+
+}  // namespace security
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/security/security_module.h b/gd/security/security_module.h
new file mode 100644
index 0000000..2ec456d
--- /dev/null
+++ b/gd/security/security_module.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <memory>
+
+#include "module.h"
+#include "security/security_manager.h"
+
+namespace bluetooth {
+namespace security {
+
+class SecurityModule : public bluetooth::Module {
+ public:
+  SecurityModule() = default;
+  ~SecurityModule() = default;
+
+  /**
+   * Get the api to the SecurityManager
+   */
+  std::unique_ptr<SecurityManager> GetSecurityManager();
+
+  static const ModuleFactory Factory;
+
+ protected:
+  void ListDependencies(ModuleList* list) override;
+
+  void Start() override;
+
+  void Stop() override;
+
+  std::string ToString() const override;
+
+ private:
+  struct impl;
+  std::unique_ptr<impl> pimpl_;
+  DISALLOW_COPY_AND_ASSIGN(SecurityModule);
+};
+
+}  // namespace security
+}  // namespace bluetooth
diff --git a/gd/security/security_record_database.h b/gd/security/security_record_database.h
new file mode 100644
index 0000000..2360475
--- /dev/null
+++ b/gd/security/security_record_database.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "security/record/security_record.h"
+
+namespace bluetooth {
+namespace security {
+
+class SecurityRecordDatabase {
+ public:
+  using iterator = std::vector<record::SecurityRecord>::iterator;
+
+  record::SecurityRecord& FindOrCreate(hci::AddressWithType address) {
+    auto it = Find(address);
+    // Security record check
+    if (it != records_.end()) return *it;
+
+    // No security record, create one
+    records_.emplace_back(address);
+    return records_.back();
+  }
+
+  void Remove(const hci::AddressWithType& address) {
+    auto it = Find(address);
+
+    // No record exists
+    if (it == records_.end()) return;
+
+    record::SecurityRecord& last = records_.back();
+    *it = std::move(last);
+    records_.pop_back();
+  }
+
+  iterator Find(hci::AddressWithType address) {
+    for (auto it = records_.begin(); it != records_.end(); ++it) {
+      record::SecurityRecord& record = *it;
+      if (record.identity_address_.has_value() && record.identity_address_.value() == address) return it;
+      if (record.GetPseudoAddress() == address) return it;
+      if (record.irk.has_value() && address.IsRpaThatMatchesIrk(record.irk.value())) return it;
+    }
+    return records_.end();
+  }
+
+  std::vector<record::SecurityRecord> records_;
+};
+
+}  // namespace security
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/security/smp_packets.pdl b/gd/security/smp_packets.pdl
new file mode 100644
index 0000000..69269dd
--- /dev/null
+++ b/gd/security/smp_packets.pdl
@@ -0,0 +1,142 @@
+little_endian_packets
+
+custom_field Address : 48 "hci/"
+
+enum Code : 8 {
+  PAIRING_REQUEST = 0x01,
+  PAIRING_RESPONSE = 0x02,
+  PAIRING_CONFIRM = 0x03,
+  PAIRING_RANDOM = 0x04,
+  PAIRING_FAILED = 0x05,
+  ENCRYPTION_INFORMATION = 0x06,
+  MASTER_IDENTIFICATION = 0x07,
+  IDENTITY_INFORMATION = 0x08,
+  IDENTITY_ADDRESS_INFORMATION = 0x09,
+  SIGNING_INFORMATION = 0x0A,
+  SECURITY_REQUEST = 0x0B,
+  PAIRING_PUBLIC_KEY = 0x0C,
+  PAIRING_DH_KEY_CHECK = 0x0D,
+  PAIRING_KEYPRESS_NOTIFICATION = 0x0E,
+}
+
+packet Command {
+  code : Code,
+  _payload_,
+}
+
+enum IoCapability : 8 {
+  DISPLAY_ONLY = 0x00,
+  DISPLAY_YES_NO = 0x01,
+  KEYBOARD_ONLY = 0x02,
+  NO_INPUT_NO_OUTPUT = 0x03,
+  KEYBOARD_DISPLAY = 0x04,
+}
+
+enum OobDataFlag : 8 {
+  NOT_PRESENT = 0x00,
+  PRESENT = 0x01,
+}
+
+enum BondingFlags : 2 {
+  NO_BONDING = 0,
+  BONDING = 1,
+}
+
+group PairingInfo {
+  io_capability : IoCapability,
+  oob_data_flag : OobDataFlag,
+  auth_req: 8,
+  maximum_encryption_key_size : 5, // 7 - 16
+  _reserved_ : 3,
+  initiator_key_distribution : 8,
+  responder_key_distribution : 8,
+}
+
+packet PairingRequest : Command (code = PAIRING_REQUEST) {
+  PairingInfo,
+}
+
+packet PairingResponse : Command (code = PAIRING_RESPONSE) {
+  PairingInfo,
+}
+
+packet PairingConfirm : Command (code = PAIRING_CONFIRM) {
+  confirm_value : 8[16],  // Initiating device sends Mconfirm, responding device sends Sconfirm
+}
+
+packet PairingRandom : Command (code = PAIRING_RANDOM) {
+  random_value : 8[16],  // Initiating device sends Mrand, responding device sends Srand
+}
+
+enum PairingFailedReason : 8 {
+  PASSKEY_ENTRY_FAILED = 0x01,
+  OOB_NOT_AVAILABLE = 0x02,
+  AUTHENTICATION_REQUIREMENTS = 0x03,
+  CONFIRM_VALUE_FAILED = 0x04,
+  PAIRING_NOT_SUPPORTED = 0x05,
+  ENCRYPTION_KEY_SIZE = 0x06,
+  COMMAND_NOT_SUPPORTED = 0x07,
+  UNSPECIFIED_REASON = 0x08,
+  REPEATED_ATTEMPTS = 0x09,
+  INVALID_PARAMETERS = 0x0A,
+  DHKEY_CHECK_FAILED = 0x0B,
+  NUMERIC_COMPARISON_FAILED = 0x0C,
+  BR_EDR_PAIRING_IN_PROGRESS = 0x0D,
+  CROSS_TRANSPORT_KEY_DERIVATION_NOT_ALLOWED = 0x0E,
+}
+
+packet PairingFailed : Command (code = PAIRING_FAILED) {
+  reason : PairingFailedReason,
+}
+
+packet EncryptionInformation : Command (code = ENCRYPTION_INFORMATION) {
+ long_term_key : 8[16],
+}
+
+packet MasterIdentification : Command (code = MASTER_IDENTIFICATION) {
+  ediv : 16,
+  rand : 8[8],
+}
+
+packet IdentityInformation : Command (code = IDENTITY_INFORMATION) {
+  identity_resolving_key : 8[16],
+}
+
+enum AddrType : 8 {
+  PUBLIC = 0x00,
+  STATIC_RANDOM = 0x01,
+}
+
+packet IdentityAddressInformation : Command (code = IDENTITY_ADDRESS_INFORMATION) {
+  addr_type : AddrType,
+  bd_addr : Address,
+}
+
+packet SigningInformation : Command (code = SIGNING_INFORMATION) {
+  signature_key : 8[16],
+}
+
+packet SecurityRequest : Command (code = SECURITY_REQUEST) {
+  auth_req: 8,
+}
+
+packet PairingPublicKey : Command (code = PAIRING_PUBLIC_KEY) {
+  public_key_x : 8[32],
+  public_key_y : 8[32],
+}
+
+packet PairingDhKeyCheck : Command (code = PAIRING_DH_KEY_CHECK) {
+  dh_key_check : 8[16],
+}
+
+enum KeypressNotificationType : 8 {
+  ENTRY_STARTED = 0,
+  DIGIT_ENTERED = 1,
+  DIGIT_ERASED = 2,
+  CLEARED = 3,
+  ENTRY_COMPLETED = 4,
+}
+
+packet PairingKeypressNotification : Command (code = PAIRING_KEYPRESS_NOTIFICATION) {
+  notification_type : KeypressNotificationType,
+}
diff --git a/gd/security/test/ecdh_keys_test.cc b/gd/security/test/ecdh_keys_test.cc
new file mode 100644
index 0000000..89e7dd6
--- /dev/null
+++ b/gd/security/test/ecdh_keys_test.cc
@@ -0,0 +1,113 @@
+/******************************************************************************
+ *
+ *  Copyright 2020 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <memory>
+
+#include <base/strings/string_number_conversions.h>
+#include "hci/le_security_interface.h"
+#include "os/log.h"
+#include "security/ecc/p_256_ecc_pp.h"
+#include "security/ecdh_keys.h"
+#include "security/test/mocks.h"
+
+using namespace std::chrono_literals;
+
+namespace bluetooth {
+namespace security {
+
+class EcdhKeysTest : public testing::Test {
+ protected:
+  void SetUp() {}
+
+  void TearDown() {}
+
+ public:
+};
+
+/* This test generates two pairs of keys, computes the Diffie–Hellman key using both, and verifies that they match */
+TEST_F(EcdhKeysTest, test_generated) {
+  std::srand(std::time(nullptr));
+
+  auto [private_key_a, public_key_a] = GenerateECDHKeyPair();
+  auto [private_key_b, public_key_b] = GenerateECDHKeyPair();
+
+  std::array<uint8_t, 32> dhkeya = ComputeDHKey(private_key_a, public_key_b);
+  std::array<uint8_t, 32> dhkeyb = ComputeDHKey(private_key_b, public_key_a);
+
+  EXPECT_EQ(dhkeya, dhkeyb);
+
+  if (dhkeya != dhkeyb) {
+    LOG_ERROR("private key a : %s", base::HexEncode(private_key_a.data(), private_key_a.size()).c_str());
+    LOG_ERROR("public key a.x : %s", base::HexEncode(public_key_a.x.data(), public_key_a.x.size()).c_str());
+    LOG_ERROR("public key a.y : %s", base::HexEncode(public_key_a.y.data(), public_key_a.y.size()).c_str());
+
+    LOG_ERROR("private key b : %s", base::HexEncode(private_key_b.data(), private_key_b.size()).c_str());
+    LOG_ERROR("public key b.x : %s", base::HexEncode(public_key_b.x.data(), public_key_b.x.size()).c_str());
+    LOG_ERROR("public key b.y : %s", base::HexEncode(public_key_b.y.data(), public_key_b.y.size()).c_str());
+
+    LOG_ERROR("dhkeya : %s", base::HexEncode(dhkeya.data(), dhkeya.size()).c_str());
+    LOG_ERROR("dhkeyb : %s", base::HexEncode(dhkeyb.data(), dhkeyb.size()).c_str());
+  }
+}
+
+/* This test uses two fixed pairs of keys, computes the Diffie–Hellman key using both, and verifies that they match
+precomputed value.
+
+This code is also useful during debuging - one can replace fixed values with values from failed exchagne to verify which
+side did bad computation. */
+TEST_F(EcdhKeysTest, test_static) {
+  std::array<uint8_t, 32> private_key_a = {0x3E, 0xC8, 0x2A, 0x32, 0xB3, 0x75, 0x76, 0xBA, 0x7D, 0xB8, 0xB4,
+                                           0x7B, 0xA0, 0x8A, 0xA3, 0xC3, 0xF2, 0x03, 0x1A, 0x53, 0xF6, 0x52,
+                                           0x26, 0x32, 0xB6, 0xAE, 0x57, 0x3F, 0x13, 0x15, 0x29, 0x51};
+  bluetooth::security::EcdhPublicKey public_key_a;
+  uint8_t ax[32] = {0xDC, 0x88, 0xD0, 0xE5, 0x59, 0x73, 0xF2, 0x41, 0x88, 0x6C, 0xB4, 0x45, 0x8B, 0x61, 0x3B, 0x10,
+                    0xF5, 0xD4, 0xD2, 0x5B, 0x4E, 0xA1, 0x7F, 0x94, 0xE3, 0xA9, 0x38, 0xF8, 0x84, 0xD4, 0x98, 0x10};
+  uint8_t ay[32] = {0x3D, 0x13, 0x76, 0x4F, 0xD1, 0x29, 0x6E, 0xEC, 0x8D, 0xF6, 0x70, 0x33, 0x8B, 0xA7, 0x18, 0xEA,
+                    0x84, 0x15, 0xE8, 0x8C, 0x4A, 0xC8, 0x76, 0x45, 0x90, 0x98, 0xBA, 0x52, 0x8B, 0x00, 0x69, 0xAF};
+  memcpy(public_key_a.x.data(), ax, 32);
+  memcpy(public_key_a.y.data(), ay, 32);
+
+  std::array<uint8_t, 32> private_key_b = {0xDD, 0x53, 0x84, 0x91, 0xC8, 0xFA, 0x4B, 0x45, 0xB2, 0xFF, 0xC0,
+                                           0x53, 0x89, 0x64, 0x16, 0x7B, 0x67, 0x30, 0xCE, 0x5D, 0x82, 0xF4,
+                                           0x8F, 0x38, 0xA2, 0xE6, 0x78, 0xB6, 0xFB, 0xA1, 0x07, 0xD8};
+  bluetooth::security::EcdhPublicKey public_key_b;
+  uint8_t bx[32] = {0x23, 0x1A, 0xEC, 0xFE, 0x7D, 0xC1, 0x20, 0x2F, 0x03, 0x3E, 0x9A, 0xAA, 0x99, 0x55, 0x78, 0x86,
+                    0x58, 0xCB, 0x37, 0x68, 0x7D, 0xE1, 0xFF, 0x19, 0x33, 0xF8, 0xCB, 0x7A, 0x17, 0xAB, 0x0B, 0x73};
+  uint8_t by[32] = {0x4C, 0x25, 0xE2, 0x42, 0x3C, 0x69, 0x0E, 0x3B, 0xC0, 0xEF, 0x94, 0x09, 0x4D, 0x3F, 0x96, 0xBB,
+                    0x18, 0xF2, 0x55, 0x81, 0x71, 0x5A, 0xDE, 0xC4, 0x3E, 0xF9, 0x6F, 0xA9, 0xAF, 0x04, 0x4E, 0x86};
+  memcpy(public_key_b.x.data(), bx, 32);
+  memcpy(public_key_b.y.data(), by, 32);
+
+  std::array<uint8_t, 32> dhkey;
+  uint8_t dhkey_val[32] = {0x3B, 0xF8, 0xDF, 0x33, 0x99, 0x94, 0x66, 0x55, 0x4F, 0x2C, 0x4A,
+                           0x78, 0x2B, 0x51, 0xD1, 0x49, 0x0F, 0xF1, 0x96, 0x63, 0x51, 0x75,
+                           0x9E, 0x65, 0x7F, 0x3C, 0xFE, 0x77, 0xB4, 0x3F, 0x7A, 0x93};
+  memcpy(dhkey.data(), dhkey_val, 32);
+
+  std::array<uint8_t, 32> dhkey_a = ComputeDHKey(private_key_a, public_key_b);
+  std::array<uint8_t, 32> dhkey_b = ComputeDHKey(private_key_b, public_key_a);
+
+  EXPECT_EQ(dhkey_a, dhkey);
+  EXPECT_EQ(dhkey_b, dhkey);
+}
+
+}  // namespace security
+}  // namespace bluetooth
diff --git a/gd/security/test/fake_hci_layer.h b/gd/security/test/fake_hci_layer.h
new file mode 100644
index 0000000..6866b34
--- /dev/null
+++ b/gd/security/test/fake_hci_layer.h
@@ -0,0 +1,115 @@
+/*
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+#include "common/bind.h"
+#include "hci/hci_layer.h"
+
+namespace bluetooth {
+namespace security {
+
+using common::OnceCallback;
+using hci::CommandCompleteView;
+using hci::CommandPacketBuilder;
+using hci::CommandStatusView;
+using hci::EventCode;
+using hci::EventPacketBuilder;
+using hci::EventPacketView;
+using hci::HciLayer;
+using os::Handler;
+
+namespace {
+
+PacketView<kLittleEndian> GetPacketView(std::unique_ptr<packet::BasePacketBuilder> packet) {
+  auto bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter i(*bytes);
+  bytes->reserve(packet->size());
+  packet->Serialize(i);
+  return packet::PacketView<packet::kLittleEndian>(bytes);
+}
+
+class CommandQueueEntry {
+ public:
+  CommandQueueEntry(std::unique_ptr<CommandPacketBuilder> command_packet,
+                    OnceCallback<void(CommandCompleteView)> on_complete_function, Handler* handler)
+      : command(std::move(command_packet)), waiting_for_status_(false), on_complete(std::move(on_complete_function)),
+        caller_handler(handler) {}
+
+  CommandQueueEntry(std::unique_ptr<CommandPacketBuilder> command_packet,
+                    OnceCallback<void(CommandStatusView)> on_status_function, Handler* handler)
+      : command(std::move(command_packet)), waiting_for_status_(true), on_status(std::move(on_status_function)),
+        caller_handler(handler) {}
+
+  std::unique_ptr<CommandPacketBuilder> command;
+  bool waiting_for_status_;
+  OnceCallback<void(CommandStatusView)> on_status;
+  OnceCallback<void(CommandCompleteView)> on_complete;
+  Handler* caller_handler;
+};
+
+}  // namespace
+
+class FakeHciLayer : public HciLayer {
+ public:
+  void EnqueueCommand(std::unique_ptr<CommandPacketBuilder> command, OnceCallback<void(CommandStatusView)> on_status,
+                      Handler* handler) override {
+    auto command_queue_entry = std::make_unique<CommandQueueEntry>(std::move(command), std::move(on_status), handler);
+    command_queue_.push(std::move(command_queue_entry));
+  }
+
+  void EnqueueCommand(std::unique_ptr<CommandPacketBuilder> command,
+                      OnceCallback<void(CommandCompleteView)> on_complete, Handler* handler) override {
+    auto command_queue_entry = std::make_unique<CommandQueueEntry>(std::move(command), std::move(on_complete), handler);
+    command_queue_.push(std::move(command_queue_entry));
+  }
+
+  std::unique_ptr<CommandQueueEntry> GetLastCommand() {
+    EXPECT_FALSE(command_queue_.empty());
+    auto last = std::move(command_queue_.front());
+    command_queue_.pop();
+    return last;
+  }
+
+  void RegisterEventHandler(EventCode event_code, common::Callback<void(EventPacketView)> event_handler,
+                            Handler* handler) override {
+    registered_events_[event_code] = event_handler;
+  }
+
+  void UnregisterEventHandler(EventCode event_code) override {
+    registered_events_.erase(event_code);
+  }
+
+  void IncomingEvent(std::unique_ptr<EventPacketBuilder> event_builder) {
+    auto packet = GetPacketView(std::move(event_builder));
+    EventPacketView event = EventPacketView::Create(packet);
+    ASSERT_TRUE(event.IsValid());
+    EventCode event_code = event.GetEventCode();
+    ASSERT_TRUE(registered_events_.find(event_code) != registered_events_.end());
+    registered_events_[event_code].Run(event);
+  }
+
+  void ListDependencies(ModuleList* list) override {}
+  void Start() override {}
+  void Stop() override {}
+
+ private:
+  std::map<EventCode, common::Callback<void(EventPacketView)>> registered_events_;
+  std::queue<std::unique_ptr<CommandQueueEntry>> command_queue_;
+};
+
+}  // namespace security
+}  // namespace bluetooth
diff --git a/gd/security/test/fake_l2cap_test.cc b/gd/security/test/fake_l2cap_test.cc
new file mode 100644
index 0000000..2d885e7
--- /dev/null
+++ b/gd/security/test/fake_l2cap_test.cc
@@ -0,0 +1,131 @@
+/******************************************************************************
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <memory>
+
+#include "hci/le_security_interface.h"
+#include "packet/raw_builder.h"
+#include "security/pairing_handler_le.h"
+#include "security/test/mocks.h"
+
+#include "os/handler.h"
+#include "os/queue.h"
+#include "os/thread.h"
+
+using namespace std::chrono_literals;
+using testing::_;
+using testing::Invoke;
+using testing::InvokeWithoutArgs;
+using testing::Matcher;
+using testing::SaveArg;
+
+using bluetooth::hci::CommandCompleteView;
+using bluetooth::hci::CommandStatusView;
+using bluetooth::hci::EncryptionChangeBuilder;
+using bluetooth::hci::EncryptionEnabled;
+using bluetooth::hci::ErrorCode;
+using bluetooth::hci::EventPacketBuilder;
+using bluetooth::hci::EventPacketView;
+using bluetooth::hci::LeSecurityCommandBuilder;
+
+namespace bluetooth {
+namespace security {
+
+namespace {
+
+template <class T>
+PacketView<kLittleEndian> GetPacketView(std::unique_ptr<T> packet) {
+  auto bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter i(*bytes);
+  bytes->reserve(packet->size());
+  packet->Serialize(i);
+  return packet::PacketView<packet::kLittleEndian>(bytes);
+}
+
+void sync_handler(os::Handler* handler) {
+  std::promise<void> promise;
+  auto future = promise.get_future();
+  handler->Post(common::BindOnce(&std::promise<void>::set_value, common::Unretained(&promise)));
+  auto status = future.wait_for(std::chrono::milliseconds(3));
+  EXPECT_EQ(status, std::future_status::ready);
+}
+}  // namespace
+
+class FakeL2capTest : public testing::Test {
+ protected:
+  void SetUp() {}
+
+  void TearDown() {}
+
+ public:
+};
+
+void my_enqueue_callback() {
+  LOG_INFO("packet ready for dequeue!");
+}
+
+/* This test verifies that Just Works pairing flow works.
+ * Both simulated devices specify capabilities as NO_INPUT_NO_OUTPUT, and secure connecitons support */
+TEST_F(FakeL2capTest, test_bidi_queue_example) {
+  os::Thread* thread_ = new os::Thread("test_thread", os::Thread::Priority::NORMAL);
+  os::Handler* handler_ = new os::Handler(thread_);
+
+  common::BidiQueue<packet::BasePacketBuilder, packet::PacketView<packet::kLittleEndian>> bidi_queue{10};
+
+  os::EnqueueBuffer<packet::BasePacketBuilder> enqueue_buffer{bidi_queue.GetDownEnd()};
+
+  // This is test packet we are sending down the queue to the other end;
+  auto test_packet = EncryptionChangeBuilder::Create(ErrorCode::SUCCESS, 0x0020, EncryptionEnabled::ON);
+
+  // send the packet through the queue
+  enqueue_buffer.Enqueue(std::move(test_packet), handler_);
+
+  // give queue some time to push the packet through
+  sync_handler(handler_);
+
+  // packet is through the queue, receive it on the other end.
+  auto test_packet_from_other_end = bidi_queue.GetUpEnd()->TryDequeue();
+
+  EXPECT_TRUE(test_packet_from_other_end != nullptr);
+
+  // This is how we receive data
+  os::EnqueueBuffer<packet::PacketView<packet::kLittleEndian>> up_end_enqueue_buffer{bidi_queue.GetUpEnd()};
+  bidi_queue.GetDownEnd()->RegisterDequeue(handler_, common::Bind(&my_enqueue_callback));
+
+  auto packet_one = std::make_unique<packet::RawBuilder>();
+  packet_one->AddOctets({1, 2, 3});
+
+  up_end_enqueue_buffer.Enqueue(std::make_unique<PacketView<kLittleEndian>>(GetPacketView(std::move(packet_one))),
+                                handler_);
+
+  sync_handler(handler_);
+
+  auto other_end_packet = bidi_queue.GetDownEnd()->TryDequeue();
+  EXPECT_TRUE(other_end_packet != nullptr);
+
+  bidi_queue.GetDownEnd()->UnregisterDequeue();
+  handler_->Clear();
+  delete handler_;
+  delete thread_;
+}
+
+}  // namespace security
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/security/test/mocks.h b/gd/security/test/mocks.h
new file mode 100644
index 0000000..bbc3a2c
--- /dev/null
+++ b/gd/security/test/mocks.h
@@ -0,0 +1,57 @@
+/******************************************************************************
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#pragma once
+
+#include <gmock/gmock.h>
+
+#include "hci/address.h"
+#include "hci/le_security_interface.h"
+#include "security/ui.h"
+
+namespace bluetooth {
+namespace security {
+
+class UIMock : public UI {
+ public:
+  UIMock() {}
+  ~UIMock() override = default;
+
+  MOCK_METHOD2(DisplayPairingPrompt, void(const bluetooth::hci::AddressWithType&, std::string));
+  MOCK_METHOD1(Cancel, void(const bluetooth::hci::AddressWithType&));
+  MOCK_METHOD3(DisplayConfirmValue, void(const bluetooth::hci::AddressWithType&, std::string, uint32_t));
+  MOCK_METHOD2(DisplayYesNoDialog, void(const bluetooth::hci::AddressWithType&, std::string));
+  MOCK_METHOD2(DisplayEnterPasskeyDialog, void(const bluetooth::hci::AddressWithType&, std::string));
+  MOCK_METHOD3(DisplayPasskey, void(const bluetooth::hci::AddressWithType&, std::string, uint32_t));
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(UIMock);
+};
+
+class LeSecurityInterfaceMock : public hci::LeSecurityInterface {
+ public:
+  MOCK_METHOD3(EnqueueCommand,
+               void(std::unique_ptr<hci::LeSecurityCommandBuilder> command,
+                    common::OnceCallback<void(hci::CommandCompleteView)> on_complete, os::Handler* handler));
+  MOCK_METHOD3(EnqueueCommand,
+               void(std::unique_ptr<hci::LeSecurityCommandBuilder> command,
+                    common::OnceCallback<void(hci::CommandStatusView)> on_status, os::Handler* handler));
+};
+
+}  // namespace security
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/security/test/pairing_handler_le_pair_test.cc b/gd/security/test/pairing_handler_le_pair_test.cc
new file mode 100644
index 0000000..36dc365
--- /dev/null
+++ b/gd/security/test/pairing_handler_le_pair_test.cc
@@ -0,0 +1,655 @@
+/******************************************************************************
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <memory>
+
+#include "common/testing/wired_pair_of_bidi_queues.h"
+#include "hci/le_security_interface.h"
+#include "packet/raw_builder.h"
+#include "security/pairing_handler_le.h"
+#include "security/test/mocks.h"
+
+using namespace std::chrono_literals;
+using testing::_;
+using testing::Invoke;
+using testing::InvokeWithoutArgs;
+using testing::Matcher;
+using testing::SaveArg;
+
+using bluetooth::hci::Address;
+using bluetooth::hci::AddressType;
+using bluetooth::hci::CommandCompleteView;
+using bluetooth::hci::CommandStatusView;
+using bluetooth::hci::EncryptionChangeBuilder;
+using bluetooth::hci::EncryptionEnabled;
+using bluetooth::hci::ErrorCode;
+using bluetooth::hci::EventPacketBuilder;
+using bluetooth::hci::EventPacketView;
+using bluetooth::hci::LeSecurityCommandBuilder;
+
+// run:
+// out/host/linux-x86/nativetest/bluetooth_test_gd/bluetooth_test_gd --gtest_filter=Pairing*
+// adb shell /data/nativetest/bluetooth_test_gd/bluetooth_test_gd  --gtest_filter=PairingHandlerPairTest.*
+// --gtest_repeat=10 --gtest_shuffle
+
+namespace bluetooth {
+namespace security {
+CommandView CommandBuilderToView(std::unique_ptr<BasePacketBuilder> builder) {
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  builder->Serialize(it);
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  auto temp_cmd_view = CommandView::Create(packet_bytes_view);
+  return CommandView::Create(temp_cmd_view);
+}
+
+EventPacketView EventBuilderToView(std::unique_ptr<EventPacketBuilder> builder) {
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  builder->Serialize(it);
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  auto temp_evt_view = EventPacketView::Create(packet_bytes_view);
+  return EventPacketView::Create(temp_evt_view);
+}
+}  // namespace security
+}  // namespace bluetooth
+
+namespace {
+
+constexpr uint16_t CONN_HANDLE_MASTER = 0x31, CONN_HANDLE_SLAVE = 0x32;
+std::unique_ptr<bluetooth::security::PairingHandlerLe> pairing_handler_a, pairing_handler_b;
+
+}  // namespace
+
+namespace bluetooth {
+namespace security {
+
+namespace {
+Address ADDRESS_MASTER{{0x26, 0x64, 0x76, 0x86, 0xab, 0xba}};
+AddressType ADDRESS_TYPE_MASTER = AddressType::RANDOM_DEVICE_ADDRESS;
+
+Address ADDRESS_SLAVE{{0x33, 0x58, 0x24, 0x76, 0x11, 0x89}};
+AddressType ADDRESS_TYPE_SLAVE = AddressType::RANDOM_DEVICE_ADDRESS;
+
+std::optional<PairingResultOrFailure> pairing_result_master;
+std::optional<PairingResultOrFailure> pairing_result_slave;
+
+void OnPairingFinishedMaster(PairingResultOrFailure r) {
+  pairing_result_master = r;
+  if (std::holds_alternative<PairingResult>(r)) {
+    LOG_INFO("pairing finished successfully with %s", std::get<PairingResult>(r).connection_address.ToString().c_str());
+  } else {
+    LOG_INFO("pairing with ... failed: %s", std::get<PairingFailure>(r).message.c_str());
+  }
+}
+
+void OnPairingFinishedSlave(PairingResultOrFailure r) {
+  pairing_result_slave = r;
+  if (std::holds_alternative<PairingResult>(r)) {
+    LOG_INFO("pairing finished successfully with %s", std::get<PairingResult>(r).connection_address.ToString().c_str());
+  } else {
+    LOG_INFO("pairing with ... failed: %s", std::get<PairingFailure>(r).message.c_str());
+  }
+}
+
+};  // namespace
+
+// We obtain this mutex when we start initializing the handlers, and relese it when both handlers are initialized
+std::mutex handlers_initialization_guard;
+
+class PairingHandlerPairTest : public testing::Test {
+  void dequeue_callback_master() {
+    auto packet_bytes_view = l2cap_->GetQueueAUpEnd()->TryDequeue();
+    if (!packet_bytes_view) LOG_ERROR("Received dequeue, but no data ready...");
+
+    auto temp_cmd_view = CommandView::Create(*packet_bytes_view);
+    if (!first_command_sent) {
+      first_command = std::make_unique<CommandView>(CommandView::Create(temp_cmd_view));
+      first_command_sent = true;
+      return;
+    }
+
+    if (!pairing_handler_a) LOG_ALWAYS_FATAL("Slave handler not initlized yet!");
+
+    pairing_handler_a->OnCommandView(CommandView::Create(temp_cmd_view));
+  }
+
+  void dequeue_callback_slave() {
+    auto packet_bytes_view = l2cap_->GetQueueBUpEnd()->TryDequeue();
+    if (!packet_bytes_view) LOG_ERROR("Received dequeue, but no data ready...");
+
+    auto temp_cmd_view = CommandView::Create(*packet_bytes_view);
+    if (!first_command_sent) {
+      first_command = std::make_unique<CommandView>(CommandView::Create(temp_cmd_view));
+      first_command_sent = true;
+      return;
+    }
+
+    if (!pairing_handler_b) LOG_ALWAYS_FATAL("Master handler not initlized yet!");
+
+    pairing_handler_b->OnCommandView(CommandView::Create(temp_cmd_view));
+  }
+
+ protected:
+  void SetUp() {
+    thread_ = new os::Thread("test_thread", os::Thread::Priority::NORMAL);
+    handler_ = new os::Handler(thread_);
+
+    l2cap_ = new common::testing::WiredPairOfL2capQueues(handler_);
+    // master sends it's packet into l2cap->down_buffer_b_
+    // slave sends it's packet into l2cap->down_buffer_a_
+    l2cap_->GetQueueAUpEnd()->RegisterDequeue(
+        handler_, common::Bind(&PairingHandlerPairTest::dequeue_callback_master, common::Unretained(this)));
+    l2cap_->GetQueueBUpEnd()->RegisterDequeue(
+        handler_, common::Bind(&PairingHandlerPairTest::dequeue_callback_slave, common::Unretained(this)));
+
+    up_buffer_a_ = std::make_unique<os::EnqueueBuffer<packet::BasePacketBuilder>>(l2cap_->GetQueueAUpEnd());
+    up_buffer_b_ = std::make_unique<os::EnqueueBuffer<packet::BasePacketBuilder>>(l2cap_->GetQueueBUpEnd());
+
+    master_setup = {
+        .my_role = hci::Role::MASTER,
+        .my_connection_address = {ADDRESS_MASTER, ADDRESS_TYPE_MASTER},
+
+        .myPairingCapabilities = {.io_capability = IoCapability::NO_INPUT_NO_OUTPUT,
+                                  .oob_data_flag = OobDataFlag::NOT_PRESENT,
+                                  .auth_req = AuthReqMaskBondingFlag | AuthReqMaskMitm | AuthReqMaskSc,
+                                  .maximum_encryption_key_size = 16,
+                                  .initiator_key_distribution = KeyMaskId | KeyMaskSign,
+                                  .responder_key_distribution = KeyMaskId | KeyMaskSign},
+
+        .remotely_initiated = false,
+        .connection_handle = CONN_HANDLE_MASTER,
+        .remote_connection_address = {ADDRESS_SLAVE, ADDRESS_TYPE_SLAVE},
+        .user_interface = &master_user_interface,
+        .user_interface_handler = handler_,
+        .le_security_interface = &master_le_security_mock,
+        .proper_l2cap_interface = up_buffer_a_.get(),
+        .l2cap_handler = handler_,
+        .OnPairingFinished = OnPairingFinishedMaster,
+    };
+
+    slave_setup = {
+        .my_role = hci::Role::SLAVE,
+
+        .my_connection_address = {ADDRESS_SLAVE, ADDRESS_TYPE_SLAVE},
+        .myPairingCapabilities = {.io_capability = IoCapability::NO_INPUT_NO_OUTPUT,
+                                  .oob_data_flag = OobDataFlag::NOT_PRESENT,
+                                  .auth_req = AuthReqMaskBondingFlag | AuthReqMaskMitm | AuthReqMaskSc,
+                                  .maximum_encryption_key_size = 16,
+                                  .initiator_key_distribution = KeyMaskId | KeyMaskSign,
+                                  .responder_key_distribution = KeyMaskId | KeyMaskSign},
+        .remotely_initiated = true,
+        .connection_handle = CONN_HANDLE_SLAVE,
+        .remote_connection_address = {ADDRESS_MASTER, ADDRESS_TYPE_MASTER},
+        .user_interface = &slave_user_interface,
+        .user_interface_handler = handler_,
+        .le_security_interface = &slave_le_security_mock,
+        .proper_l2cap_interface = up_buffer_b_.get(),
+        .l2cap_handler = handler_,
+        .OnPairingFinished = OnPairingFinishedSlave,
+    };
+
+    RecordSuccessfulEncryptionComplete();
+  }
+
+  void TearDown() {
+    ::testing::Mock::VerifyAndClearExpectations(&slave_user_interface);
+    ::testing::Mock::VerifyAndClearExpectations(&master_user_interface);
+    ::testing::Mock::VerifyAndClearExpectations(&slave_le_security_mock);
+    ::testing::Mock::VerifyAndClearExpectations(&master_le_security_mock);
+
+    pairing_handler_a.reset();
+    pairing_handler_b.reset();
+    pairing_result_master.reset();
+    pairing_result_slave.reset();
+
+    first_command_sent = false;
+    first_command.reset();
+
+    l2cap_->GetQueueAUpEnd()->UnregisterDequeue();
+    l2cap_->GetQueueBUpEnd()->UnregisterDequeue();
+
+    delete l2cap_;
+    handler_->Clear();
+    delete handler_;
+    delete thread_;
+  }
+
+  void RecordPairingPromptHandling(UIMock& ui_mock, std::unique_ptr<PairingHandlerLe>* handler) {
+    EXPECT_CALL(ui_mock, DisplayPairingPrompt(_, _)).Times(1).WillOnce(InvokeWithoutArgs([handler]() {
+      LOG_INFO("UI mock received pairing prompt");
+
+      {
+        // By grabbing the lock, we ensure initialization of both pairing handlers is finished.
+        std::lock_guard<std::mutex> lock(handlers_initialization_guard);
+      }
+
+      if (!(*handler)) LOG_ALWAYS_FATAL("handler not initalized yet!");
+      // Simulate user accepting the pairing in UI
+      (*handler)->OnUiAction(PairingEvent::PAIRING_ACCEPTED, 0x01 /* Non-zero value means success */);
+    }));
+  }
+
+  void RecordSuccessfulEncryptionComplete() {
+    // For now, all tests are succeeding to go through Encryption. Record that in the setup.
+    //  Once we test failure cases, move this to each test
+    EXPECT_CALL(master_le_security_mock,
+                EnqueueCommand(_, Matcher<common::OnceCallback<void(CommandStatusView)>>(_), _))
+        .Times(1)
+        .WillOnce([](std::unique_ptr<LeSecurityCommandBuilder> command,
+                     common::OnceCallback<void(CommandStatusView)> on_status, os::Handler* handler) {
+          // TODO: on_status.Run();
+
+          pairing_handler_a->OnHciEvent(EventBuilderToView(
+              EncryptionChangeBuilder::Create(ErrorCode::SUCCESS, CONN_HANDLE_MASTER, EncryptionEnabled::ON)));
+
+          pairing_handler_b->OnHciEvent(EventBuilderToView(
+              EncryptionChangeBuilder::Create(ErrorCode::SUCCESS, CONN_HANDLE_SLAVE, EncryptionEnabled::ON)));
+        });
+  }
+
+ public:
+  std::unique_ptr<bluetooth::security::CommandView> WaitFirstL2capCommand() {
+    while (!first_command_sent) {
+      std::this_thread::sleep_for(1ms);
+      LOG_INFO("waiting for first command...");
+    }
+
+    return std::move(first_command);
+  }
+
+  InitialInformations master_setup;
+  InitialInformations slave_setup;
+  UIMock master_user_interface;
+  UIMock slave_user_interface;
+  LeSecurityInterfaceMock master_le_security_mock;
+  LeSecurityInterfaceMock slave_le_security_mock;
+
+  uint16_t first_command_sent = false;
+  std::unique_ptr<bluetooth::security::CommandView> first_command;
+
+  os::Thread* thread_;
+  os::Handler* handler_;
+  common::testing::WiredPairOfL2capQueues* l2cap_;
+
+  std::unique_ptr<os::EnqueueBuffer<packet::BasePacketBuilder>> up_buffer_a_;
+  std::unique_ptr<os::EnqueueBuffer<packet::BasePacketBuilder>> up_buffer_b_;
+};
+
+/* This test verifies that Just Works pairing flow works.
+ * Both simulated devices specify capabilities as NO_INPUT_NO_OUTPUT, and secure connecitons support */
+TEST_F(PairingHandlerPairTest, test_secure_connections_just_works) {
+  master_setup.myPairingCapabilities.io_capability = IoCapability::NO_INPUT_NO_OUTPUT;
+  master_setup.myPairingCapabilities.oob_data_flag = OobDataFlag::NOT_PRESENT;
+  slave_setup.myPairingCapabilities.io_capability = IoCapability::NO_INPUT_NO_OUTPUT;
+  slave_setup.myPairingCapabilities.oob_data_flag = OobDataFlag::NOT_PRESENT;
+
+  {
+    std::unique_lock<std::mutex> lock(handlers_initialization_guard);
+
+    pairing_handler_a = std::make_unique<PairingHandlerLe>(PairingHandlerLe::PHASE1, master_setup);
+
+    auto first_pkt = WaitFirstL2capCommand();
+    slave_setup.pairing_request = PairingRequestView::Create(*first_pkt);
+
+    EXPECT_CALL(slave_user_interface, DisplayPairingPrompt(_, _)).Times(1).WillOnce(InvokeWithoutArgs([] {
+      LOG_INFO("UI mock received pairing prompt");
+
+      {
+        // By grabbing the lock, we ensure initialization of both pairing handlers is finished.
+        std::lock_guard<std::mutex> lock(handlers_initialization_guard);
+      }
+
+      if (!pairing_handler_b) LOG_ALWAYS_FATAL("handler not initalized yet!");
+
+      // Simulate user accepting the pairing in UI
+      pairing_handler_b->OnUiAction(PairingEvent::PAIRING_ACCEPTED, 0x01 /* Non-zero value means success */);
+    }));
+
+    pairing_handler_b = std::make_unique<PairingHandlerLe>(PairingHandlerLe::PHASE1, slave_setup);
+  }
+
+  pairing_handler_a->WaitUntilPairingFinished();
+  pairing_handler_b->WaitUntilPairingFinished();
+
+  EXPECT_TRUE(std::holds_alternative<PairingResult>(pairing_result_master.value()));
+  EXPECT_TRUE(std::holds_alternative<PairingResult>(pairing_result_slave.value()));
+}
+
+TEST_F(PairingHandlerPairTest, test_secure_connections_just_works_slave_initiated) {
+  master_setup = {
+      .my_role = hci::Role::MASTER,
+      .my_connection_address = {ADDRESS_MASTER, ADDRESS_TYPE_MASTER},
+      .myPairingCapabilities = {.io_capability = IoCapability::NO_INPUT_NO_OUTPUT,
+                                .oob_data_flag = OobDataFlag::NOT_PRESENT,
+                                .auth_req = AuthReqMaskBondingFlag | AuthReqMaskMitm | AuthReqMaskSc,
+                                .maximum_encryption_key_size = 16,
+                                .initiator_key_distribution = KeyMaskId | KeyMaskSign,
+                                .responder_key_distribution = KeyMaskId | KeyMaskSign},
+      .remotely_initiated = true,
+      .connection_handle = CONN_HANDLE_MASTER,
+      .remote_connection_address = {ADDRESS_SLAVE, ADDRESS_TYPE_SLAVE},
+      .user_interface = &master_user_interface,
+      .user_interface_handler = handler_,
+      .le_security_interface = &master_le_security_mock,
+      .proper_l2cap_interface = up_buffer_a_.get(),
+      .l2cap_handler = handler_,
+      .OnPairingFinished = OnPairingFinishedMaster,
+  };
+
+  slave_setup = {
+      .my_role = hci::Role::SLAVE,
+      .my_connection_address = {ADDRESS_SLAVE, ADDRESS_TYPE_SLAVE},
+      .myPairingCapabilities = {.io_capability = IoCapability::NO_INPUT_NO_OUTPUT,
+                                .oob_data_flag = OobDataFlag::NOT_PRESENT,
+                                .auth_req = AuthReqMaskBondingFlag | AuthReqMaskMitm | AuthReqMaskSc,
+                                .maximum_encryption_key_size = 16,
+                                .initiator_key_distribution = KeyMaskId | KeyMaskSign,
+                                .responder_key_distribution = KeyMaskId | KeyMaskSign},
+      .remotely_initiated = false,
+      .connection_handle = CONN_HANDLE_SLAVE,
+      .remote_connection_address = {ADDRESS_MASTER, ADDRESS_TYPE_MASTER},
+      .user_interface = &slave_user_interface,
+      .user_interface_handler = handler_,
+      .le_security_interface = &slave_le_security_mock,
+      .proper_l2cap_interface = up_buffer_b_.get(),
+      .l2cap_handler = handler_,
+      .OnPairingFinished = OnPairingFinishedSlave,
+  };
+
+  std::unique_ptr<bluetooth::security::CommandView> first_pkt;
+  {
+    std::unique_lock<std::mutex> lock(handlers_initialization_guard);
+    pairing_handler_b = std::make_unique<PairingHandlerLe>(PairingHandlerLe::PHASE1, slave_setup);
+
+    first_pkt = WaitFirstL2capCommand();
+
+    EXPECT_CALL(master_user_interface, DisplayPairingPrompt(_, _))
+        .Times(1)
+        .WillOnce(InvokeWithoutArgs([&first_pkt, this] {
+          LOG_INFO("UI mock received pairing prompt");
+
+          {
+            // By grabbing the lock, we ensure initialization of both pairing handlers is finished.
+            std::lock_guard<std::mutex> lock(handlers_initialization_guard);
+          }
+          if (!pairing_handler_a) LOG_ALWAYS_FATAL("handler not initalized yet!");
+          // Simulate user accepting the pairing in UI
+          pairing_handler_a->OnUiAction(PairingEvent::PAIRING_ACCEPTED, 0x01 /* Non-zero value means success */);
+
+          // Send the first packet from the slave to master
+          auto view_to_packet = std::make_unique<packet::RawBuilder>();
+          view_to_packet->AddOctets(std::vector(first_pkt->begin(), first_pkt->end()));
+          up_buffer_b_->Enqueue(std::move(view_to_packet), handler_);
+        }));
+    pairing_handler_a = std::make_unique<PairingHandlerLe>(PairingHandlerLe::PHASE1, master_setup);
+  }
+
+  pairing_handler_a->WaitUntilPairingFinished();
+  pairing_handler_b->WaitUntilPairingFinished();
+
+  EXPECT_TRUE(std::holds_alternative<PairingResult>(pairing_result_master.value()));
+  EXPECT_TRUE(std::holds_alternative<PairingResult>(pairing_result_slave.value()));
+}
+
+TEST_F(PairingHandlerPairTest, test_secure_connections_numeric_comparison) {
+  master_setup.myPairingCapabilities.io_capability = IoCapability::DISPLAY_YES_NO;
+  master_setup.myPairingCapabilities.oob_data_flag = OobDataFlag::NOT_PRESENT;
+  master_setup.myPairingCapabilities.auth_req = AuthReqMaskBondingFlag | AuthReqMaskMitm | AuthReqMaskSc;
+
+  slave_setup.myPairingCapabilities.io_capability = IoCapability::DISPLAY_YES_NO;
+  slave_setup.myPairingCapabilities.oob_data_flag = OobDataFlag::NOT_PRESENT;
+  slave_setup.myPairingCapabilities.auth_req = AuthReqMaskBondingFlag | AuthReqMaskMitm | AuthReqMaskSc;
+
+  uint32_t num_value_slave = 0;
+  {
+    std::unique_lock<std::mutex> lock(handlers_initialization_guard);
+    // Initiator must be initialized after the responder.
+    pairing_handler_a = std::make_unique<PairingHandlerLe>(PairingHandlerLe::PHASE1, master_setup);
+
+    while (!first_command_sent) {
+      std::this_thread::sleep_for(1ms);
+      LOG_INFO("waiting for first command...");
+    }
+    slave_setup.pairing_request = PairingRequestView::Create(*first_command);
+
+    RecordPairingPromptHandling(slave_user_interface, &pairing_handler_b);
+
+    EXPECT_CALL(slave_user_interface, DisplayConfirmValue(_, _, _)).WillOnce(SaveArg<2>(&num_value_slave));
+    EXPECT_CALL(master_user_interface, DisplayConfirmValue(_, _, _))
+        .WillOnce(Invoke([&](const bluetooth::hci::AddressWithType&, std::string, uint32_t num_value) {
+          EXPECT_EQ(num_value_slave, num_value);
+          if (num_value_slave == num_value) {
+            pairing_handler_a->OnUiAction(PairingEvent::CONFIRM_YESNO, 0x01);
+            pairing_handler_b->OnUiAction(PairingEvent::CONFIRM_YESNO, 0x01);
+          }
+        }));
+
+    pairing_handler_b = std::make_unique<PairingHandlerLe>(PairingHandlerLe::PHASE1, slave_setup);
+  }
+  pairing_handler_a->WaitUntilPairingFinished();
+  pairing_handler_b->WaitUntilPairingFinished();
+
+  EXPECT_TRUE(std::holds_alternative<PairingResult>(pairing_result_master.value()));
+  EXPECT_TRUE(std::holds_alternative<PairingResult>(pairing_result_slave.value()));
+}
+
+TEST_F(PairingHandlerPairTest, test_secure_connections_passkey_entry) {
+  master_setup.myPairingCapabilities.io_capability = IoCapability::KEYBOARD_ONLY;
+  master_setup.myPairingCapabilities.oob_data_flag = OobDataFlag::NOT_PRESENT;
+  master_setup.myPairingCapabilities.auth_req = AuthReqMaskBondingFlag | AuthReqMaskMitm | AuthReqMaskSc;
+
+  slave_setup.myPairingCapabilities.io_capability = IoCapability::DISPLAY_ONLY;
+  slave_setup.myPairingCapabilities.oob_data_flag = OobDataFlag::NOT_PRESENT;
+  slave_setup.myPairingCapabilities.auth_req = AuthReqMaskBondingFlag | AuthReqMaskMitm | AuthReqMaskSc;
+
+  // In this test either master or slave display the UI prompt first. This variable makes sure both prompts are
+  // displayed before passkey is confirmed. Since both UI handlers are same thread, it's safe.
+  int ui_prompts_count = 0;
+  uint32_t passkey_ = std::numeric_limits<uint32_t>::max();
+  {
+    std::unique_lock<std::mutex> lock(handlers_initialization_guard);
+    pairing_handler_a = std::make_unique<PairingHandlerLe>(PairingHandlerLe::PHASE1, master_setup);
+
+    while (!first_command_sent) {
+      std::this_thread::sleep_for(1ms);
+      LOG_INFO("waiting for first command...");
+    }
+    slave_setup.pairing_request = PairingRequestView::Create(*first_command);
+
+    RecordPairingPromptHandling(slave_user_interface, &pairing_handler_b);
+
+    EXPECT_CALL(slave_user_interface, DisplayPasskey(_, _, _))
+        .WillOnce(Invoke([&](const bluetooth::hci::AddressWithType& address, std::string name, uint32_t passkey) {
+          passkey_ = passkey;
+          ui_prompts_count++;
+          if (ui_prompts_count == 2) {
+            pairing_handler_a->OnUiAction(PairingEvent::PASSKEY, passkey);
+          }
+        }));
+
+    EXPECT_CALL(master_user_interface, DisplayEnterPasskeyDialog(_, _))
+        .WillOnce(Invoke([&](const bluetooth::hci::AddressWithType& address, std::string name) {
+          ui_prompts_count++;
+          if (ui_prompts_count == 2) {
+            pairing_handler_a->OnUiAction(PairingEvent::PASSKEY, passkey_);
+          }
+        }));
+
+    pairing_handler_b = std::make_unique<PairingHandlerLe>(PairingHandlerLe::PHASE1, slave_setup);
+  }
+  // Initiator must be initialized after the responder.
+  pairing_handler_a->WaitUntilPairingFinished();
+  pairing_handler_b->WaitUntilPairingFinished();
+
+  EXPECT_TRUE(std::holds_alternative<PairingResult>(pairing_result_master.value()));
+  EXPECT_TRUE(std::holds_alternative<PairingResult>(pairing_result_slave.value()));
+}
+
+TEST_F(PairingHandlerPairTest, test_secure_connections_out_of_band) {
+  master_setup.myPairingCapabilities.io_capability = IoCapability::KEYBOARD_ONLY;
+  master_setup.myPairingCapabilities.oob_data_flag = OobDataFlag::NOT_PRESENT;
+  master_setup.myPairingCapabilities.auth_req = AuthReqMaskBondingFlag | AuthReqMaskMitm | AuthReqMaskSc,
+
+  slave_setup.myPairingCapabilities.io_capability = IoCapability::DISPLAY_ONLY;
+  slave_setup.myPairingCapabilities.oob_data_flag = OobDataFlag::PRESENT;
+  slave_setup.myPairingCapabilities.auth_req = AuthReqMaskBondingFlag | AuthReqMaskMitm | AuthReqMaskSc,
+
+  master_setup.my_oob_data = std::make_optional<MyOobData>(PairingHandlerLe::GenerateOobData());
+  slave_setup.remote_oob_data =
+      std::make_optional<InitialInformations::out_of_band_data>(InitialInformations::out_of_band_data{
+          .le_sc_c = master_setup.my_oob_data->c,
+          .le_sc_r = master_setup.my_oob_data->r,
+      });
+
+  {
+    std::unique_lock<std::mutex> lock(handlers_initialization_guard);
+    pairing_handler_a = std::make_unique<PairingHandlerLe>(PairingHandlerLe::PHASE1, master_setup);
+    while (!first_command_sent) {
+      std::this_thread::sleep_for(1ms);
+      LOG_INFO("waiting for first command...");
+    }
+    slave_setup.pairing_request = PairingRequestView::Create(*first_command);
+
+    RecordPairingPromptHandling(slave_user_interface, &pairing_handler_b);
+
+    pairing_handler_b = std::make_unique<PairingHandlerLe>(PairingHandlerLe::PHASE1, slave_setup);
+  }
+  pairing_handler_a->WaitUntilPairingFinished();
+  pairing_handler_b->WaitUntilPairingFinished();
+
+  EXPECT_TRUE(std::holds_alternative<PairingResult>(pairing_result_master.value()));
+  EXPECT_TRUE(std::holds_alternative<PairingResult>(pairing_result_slave.value()));
+}
+
+TEST_F(PairingHandlerPairTest, test_secure_connections_out_of_band_two_way) {
+  master_setup.myPairingCapabilities.io_capability = IoCapability::KEYBOARD_ONLY;
+  master_setup.myPairingCapabilities.oob_data_flag = OobDataFlag::PRESENT;
+  master_setup.myPairingCapabilities.auth_req = AuthReqMaskBondingFlag | AuthReqMaskMitm | AuthReqMaskSc,
+
+  slave_setup.myPairingCapabilities.io_capability = IoCapability::DISPLAY_ONLY;
+  slave_setup.myPairingCapabilities.oob_data_flag = OobDataFlag::PRESENT;
+  slave_setup.myPairingCapabilities.auth_req = AuthReqMaskBondingFlag | AuthReqMaskMitm | AuthReqMaskSc,
+
+  master_setup.my_oob_data = std::make_optional<MyOobData>(PairingHandlerLe::GenerateOobData());
+  slave_setup.remote_oob_data =
+      std::make_optional<InitialInformations::out_of_band_data>(InitialInformations::out_of_band_data{
+          .le_sc_c = master_setup.my_oob_data->c,
+          .le_sc_r = master_setup.my_oob_data->r,
+      });
+
+  slave_setup.my_oob_data = std::make_optional<MyOobData>(PairingHandlerLe::GenerateOobData());
+  master_setup.remote_oob_data =
+      std::make_optional<InitialInformations::out_of_band_data>(InitialInformations::out_of_band_data{
+          .le_sc_c = slave_setup.my_oob_data->c,
+          .le_sc_r = slave_setup.my_oob_data->r,
+      });
+
+  {
+    std::unique_lock<std::mutex> lock(handlers_initialization_guard);
+    pairing_handler_a = std::make_unique<PairingHandlerLe>(PairingHandlerLe::PHASE1, master_setup);
+    while (!first_command_sent) {
+      std::this_thread::sleep_for(1ms);
+      LOG_INFO("waiting for first command...");
+    }
+    slave_setup.pairing_request = PairingRequestView::Create(*first_command);
+
+    RecordPairingPromptHandling(slave_user_interface, &pairing_handler_b);
+
+    pairing_handler_b = std::make_unique<PairingHandlerLe>(PairingHandlerLe::PHASE1, slave_setup);
+  }
+  pairing_handler_a->WaitUntilPairingFinished();
+  pairing_handler_b->WaitUntilPairingFinished();
+
+  EXPECT_TRUE(std::holds_alternative<PairingResult>(pairing_result_master.value()));
+  EXPECT_TRUE(std::holds_alternative<PairingResult>(pairing_result_slave.value()));
+}
+
+TEST_F(PairingHandlerPairTest, test_legacy_just_works) {
+  master_setup.myPairingCapabilities.io_capability = IoCapability::NO_INPUT_NO_OUTPUT;
+  master_setup.myPairingCapabilities.oob_data_flag = OobDataFlag::NOT_PRESENT;
+  master_setup.myPairingCapabilities.auth_req = AuthReqMaskBondingFlag | AuthReqMaskMitm,
+
+  slave_setup.myPairingCapabilities.io_capability = IoCapability::NO_INPUT_NO_OUTPUT;
+  slave_setup.myPairingCapabilities.oob_data_flag = OobDataFlag::NOT_PRESENT;
+  slave_setup.myPairingCapabilities.auth_req = AuthReqMaskBondingFlag | AuthReqMaskMitm;
+
+  {
+    std::unique_lock<std::mutex> lock(handlers_initialization_guard);
+    pairing_handler_a = std::make_unique<PairingHandlerLe>(PairingHandlerLe::PHASE1, master_setup);
+    while (!first_command_sent) {
+      std::this_thread::sleep_for(1ms);
+      LOG_INFO("waiting for first command...");
+    }
+    slave_setup.pairing_request = PairingRequestView::Create(*first_command);
+
+    RecordPairingPromptHandling(slave_user_interface, &pairing_handler_b);
+
+    pairing_handler_b = std::make_unique<PairingHandlerLe>(PairingHandlerLe::PHASE1, slave_setup);
+  }
+  pairing_handler_a->WaitUntilPairingFinished();
+  pairing_handler_b->WaitUntilPairingFinished();
+
+  EXPECT_TRUE(std::holds_alternative<PairingResult>(pairing_result_master.value()));
+  EXPECT_TRUE(std::holds_alternative<PairingResult>(pairing_result_slave.value()));
+}
+
+TEST_F(PairingHandlerPairTest, test_legacy_passkey_entry) {
+  master_setup.myPairingCapabilities.io_capability = IoCapability::KEYBOARD_DISPLAY;
+  master_setup.myPairingCapabilities.oob_data_flag = OobDataFlag::NOT_PRESENT;
+  master_setup.myPairingCapabilities.auth_req = AuthReqMaskBondingFlag | AuthReqMaskMitm,
+
+  slave_setup.myPairingCapabilities.io_capability = IoCapability::KEYBOARD_ONLY;
+  slave_setup.myPairingCapabilities.oob_data_flag = OobDataFlag::NOT_PRESENT;
+  slave_setup.myPairingCapabilities.auth_req = AuthReqMaskBondingFlag | AuthReqMaskMitm;
+
+  {
+    std::unique_lock<std::mutex> lock(handlers_initialization_guard);
+    pairing_handler_a = std::make_unique<PairingHandlerLe>(PairingHandlerLe::PHASE1, master_setup);
+    while (!first_command_sent) {
+      std::this_thread::sleep_for(1ms);
+      LOG_INFO("waiting for first command...");
+    }
+    slave_setup.pairing_request = PairingRequestView::Create(*first_command);
+
+    RecordPairingPromptHandling(slave_user_interface, &pairing_handler_b);
+
+    EXPECT_CALL(slave_user_interface, DisplayEnterPasskeyDialog(_, _));
+    EXPECT_CALL(master_user_interface, DisplayConfirmValue(_, _, _))
+        .WillOnce(Invoke([&](const bluetooth::hci::AddressWithType&, std::string, uint32_t passkey) {
+          LOG_INFO("Passkey prompt displayed entering passkey: %08x", passkey);
+          std::this_thread::sleep_for(1ms);
+
+          // TODO: handle case where prompts are displayed in different order in the test!
+          pairing_handler_b->OnUiAction(PairingEvent::PASSKEY, passkey);
+        }));
+
+    pairing_handler_b = std::make_unique<PairingHandlerLe>(PairingHandlerLe::PHASE1, slave_setup);
+  }
+  pairing_handler_a->WaitUntilPairingFinished();
+  pairing_handler_b->WaitUntilPairingFinished();
+
+  EXPECT_TRUE(std::holds_alternative<PairingResult>(pairing_result_master.value()));
+  EXPECT_TRUE(std::holds_alternative<PairingResult>(pairing_result_slave.value()));
+}
+
+}  // namespace security
+}  // namespace bluetooth
diff --git a/gd/security/ui.h b/gd/security/ui.h
new file mode 100644
index 0000000..ceecd14
--- /dev/null
+++ b/gd/security/ui.h
@@ -0,0 +1,68 @@
+/******************************************************************************
+ *
+ *  Copyright 2019 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#pragma once
+
+#include "hci/address_with_type.h"
+
+namespace bluetooth {
+namespace security {
+
+// Through this interface we talk to the user, asking for confirmations/acceptance.
+class UI {
+ public:
+  virtual ~UI(){};
+
+  /* Remote LE device tries to initiate pairing, ask user to confirm */
+  virtual void DisplayPairingPrompt(const bluetooth::hci::AddressWithType& address, std::string name) = 0;
+
+  /* Remove the pairing prompt from DisplayPairingPrompt, i.e. remote device disconnected, or some application requested
+   * bond with this device */
+  virtual void Cancel(const bluetooth::hci::AddressWithType& address) = 0;
+
+  /* Display value for Comprision, user responds yes/no */
+  virtual void DisplayConfirmValue(const bluetooth::hci::AddressWithType& address, std::string name,
+                                   uint32_t numeric_value) = 0;
+
+  /* Display Yes/No dialog, Classic pairing, numeric comparison with NoInputNoOutput device */
+  virtual void DisplayYesNoDialog(const bluetooth::hci::AddressWithType& address, std::string name) = 0;
+
+  /* Display a dialog box that will let user enter the Passkey */
+  virtual void DisplayEnterPasskeyDialog(const bluetooth::hci::AddressWithType& address, std::string name) = 0;
+
+  /* Present the passkey value to the user, user compares with other device */
+  virtual void DisplayPasskey(const bluetooth::hci::AddressWithType& address, std::string name, uint32_t passkey) = 0;
+};
+
+/* Through this interface, UI provides us with user choices. */
+class UICallbacks {
+ public:
+  virtual ~UICallbacks() = default;
+
+  /* User accepted pairing prompt */
+  virtual void OnPairingPromptAccepted(const bluetooth::hci::AddressWithType& address, bool confirmed) = 0;
+
+  /* User confirmed that displayed value matches the value on the other device */
+  virtual void OnConfirmYesNo(const bluetooth::hci::AddressWithType& address, bool confirmed) = 0;
+
+  /* User typed the value displayed on the other device. This is either Passkey or the Confirm value */
+  virtual void OnPasskeyEntry(const bluetooth::hci::AddressWithType& address, uint32_t passkey) = 0;
+};
+
+}  // namespace security
+}  // namespace bluetooth
diff --git a/gd/shim/Android.bp b/gd/shim/Android.bp
new file mode 100644
index 0000000..a38272b
--- /dev/null
+++ b/gd/shim/Android.bp
@@ -0,0 +1,17 @@
+filegroup {
+    name: "BluetoothShimSources",
+    srcs: [
+            "dumpsys.cc",
+            "l2cap.cc",
+            "stack.cc",
+    ]
+}
+
+filegroup {
+    name: "BluetoothShimTestSources",
+    srcs: [
+            "l2cap_test.cc",
+    ],
+}
+
+
diff --git a/gd/shim/cert/stack_test.py b/gd/shim/cert/stack_test.py
new file mode 100644
index 0000000..ad4fb9b
--- /dev/null
+++ b/gd/shim/cert/stack_test.py
@@ -0,0 +1,41 @@
+#!/usr/bin/env python3
+#
+#   Copyright 2020 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import os
+import sys
+
+from cert.gd_base_test_facade_only import GdFacadeOnlyBaseTestClass
+from google.protobuf import empty_pb2 as empty_proto
+from facade import rootservice_pb2 as facade_rootservice
+from hci.facade import controller_facade_pb2 as controller_facade
+
+
+class StackTest(GdFacadeOnlyBaseTestClass):
+
+    def setup_test(self):
+        self.device_under_test.rootservice.StartStack(
+            facade_rootservice.StartStackRequest(
+                module_under_test=facade_rootservice.BluetoothModule.Value(
+                    'SHIM'),))
+
+        self.device_under_test.wait_channel_ready()
+
+    def teardown_test(self):
+        self.device_under_test.rootservice.StopStack(
+            facade_rootservice.StopStackRequest())
+
+    def test_test(self):
+        return True
diff --git a/gd/shim/dumpsys.cc b/gd/shim/dumpsys.cc
new file mode 100644
index 0000000..6f66770
--- /dev/null
+++ b/gd/shim/dumpsys.cc
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_TAG "bt_gd_shim"
+
+#include <algorithm>
+#include <functional>
+#include <future>
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <utility>
+
+#include "module.h"
+#include "os/handler.h"
+#include "os/log.h"
+#include "shim/dumpsys.h"
+
+namespace bluetooth {
+namespace shim {
+
+namespace {
+constexpr char kModuleName[] = "shim::Dumpsys";
+}  // namespace
+
+struct Dumpsys::impl {
+ public:
+  void Dump(int fd, std::promise<void> promise);
+  void RegisterDumpsysFunction(const void* token, DumpsysFunction func);
+  void UnregisterDumpsysFunction(const void* token);
+
+  ~impl() = default;
+
+ private:
+  std::unordered_map<const void*, DumpsysFunction> dumpsys_functions_;
+};
+
+const ModuleFactory Dumpsys::Factory = ModuleFactory([]() { return new Dumpsys(); });
+
+void Dumpsys::impl::Dump(int fd, std::promise<void> promise) {
+  dprintf(fd, "%s Registered submodules:%zd\n", kModuleName, dumpsys_functions_.size());
+  std::for_each(dumpsys_functions_.begin(), dumpsys_functions_.end(),
+                [fd](std::pair<const void*, DumpsysFunction> element) { element.second(fd); });
+  promise.set_value();
+}
+
+void Dumpsys::impl::RegisterDumpsysFunction(const void* token, DumpsysFunction func) {
+  ASSERT(dumpsys_functions_.find(token) == dumpsys_functions_.end());
+  dumpsys_functions_[token] = func;
+}
+
+void Dumpsys::impl::UnregisterDumpsysFunction(const void* token) {
+  ASSERT(dumpsys_functions_.find(token) != dumpsys_functions_.end());
+  dumpsys_functions_.erase(token);
+}
+
+void Dumpsys::Dump(int fd) {
+  std::promise<void> promise;
+  auto future = promise.get_future();
+  GetHandler()->Post(common::BindOnce(&Dumpsys::impl::Dump, common::Unretained(pimpl_.get()), fd, std::move(promise)));
+  future.get();
+}
+
+void Dumpsys::RegisterDumpsysFunction(const void* token, DumpsysFunction func) {
+  GetHandler()->Post(
+      common::BindOnce(&Dumpsys::impl::RegisterDumpsysFunction, common::Unretained(pimpl_.get()), token, func));
+}
+
+void Dumpsys::UnregisterDumpsysFunction(const void* token) {
+  GetHandler()->Post(
+      common::BindOnce(&Dumpsys::impl::UnregisterDumpsysFunction, common::Unretained(pimpl_.get()), token));
+}
+
+os::Handler* Dumpsys::GetGdShimHandler() {
+  return GetHandler();
+}
+
+/**
+ * Module methods
+ */
+void Dumpsys::ListDependencies(ModuleList* list) {}
+
+void Dumpsys::Start() {
+  pimpl_ = std::make_unique<impl>();
+}
+
+void Dumpsys::Stop() {
+  pimpl_.reset();
+}
+
+std::string Dumpsys::ToString() const {
+  return kModuleName;
+}
+
+}  // namespace shim
+}  // namespace bluetooth
diff --git a/gd/shim/dumpsys.h b/gd/shim/dumpsys.h
new file mode 100644
index 0000000..197ad3e
--- /dev/null
+++ b/gd/shim/dumpsys.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <memory>
+#include <string>
+
+#include "module.h"
+
+namespace bluetooth {
+namespace shim {
+
+using DumpsysFunction = std::function<void(int fd)>;
+
+class Dumpsys : public bluetooth::Module {
+ public:
+  void Dump(int fd);
+  void RegisterDumpsysFunction(const void* token, DumpsysFunction func);
+  void UnregisterDumpsysFunction(const void* token);
+
+  /* This is not a dumpsys-specific method, we just must grab thread from of one modules */
+  os::Handler* GetGdShimHandler();
+
+  Dumpsys() = default;
+  ~Dumpsys() = default;
+
+  static const ModuleFactory Factory;
+
+ protected:
+  void ListDependencies(ModuleList* list) override;  // Module
+  void Start() override;                             // Module
+  void Stop() override;                              // Module
+  std::string ToString() const override;             // Module
+
+ private:
+  struct impl;
+  std::unique_ptr<impl> pimpl_;
+  DISALLOW_COPY_AND_ASSIGN(Dumpsys);
+};
+
+}  // namespace shim
+}  // namespace bluetooth
diff --git a/gd/shim/l2cap.cc b/gd/shim/l2cap.cc
new file mode 100644
index 0000000..60ec5c8
--- /dev/null
+++ b/gd/shim/l2cap.cc
@@ -0,0 +1,717 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_TAG "bt_gd_shim"
+
+#include <cstdint>
+#include <functional>
+#include <memory>
+#include <queue>
+#include <set>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include "common/bind.h"
+#include "hci/address.h"
+#include "hci/hci_packets.h"
+#include "l2cap/classic/dynamic_channel_manager.h"
+#include "l2cap/classic/l2cap_classic_module.h"
+#include "l2cap/psm.h"
+#include "l2cap/security_policy.h"
+#include "module.h"
+#include "os/handler.h"
+#include "os/log.h"
+#include "packet/packet_view.h"
+#include "packet/raw_builder.h"
+#include "shim/dumpsys.h"
+#include "shim/l2cap.h"
+
+namespace bluetooth {
+namespace shim {
+
+namespace {
+
+constexpr char kModuleName[] = "shim::L2cap";
+
+constexpr bool kConnectionFailed = false;
+constexpr bool kConnectionOpened = true;
+constexpr bool kRegistrationFailed = false;
+constexpr bool kRegistrationSuccess = true;
+
+using ConnectionInterfaceDescriptor = uint16_t;
+constexpr ConnectionInterfaceDescriptor kInvalidConnectionInterfaceDescriptor = 0;
+constexpr ConnectionInterfaceDescriptor kStartConnectionInterfaceDescriptor = 64;
+constexpr ConnectionInterfaceDescriptor kMaxConnections = UINT16_MAX - kStartConnectionInterfaceDescriptor - 1;
+
+using PendingConnectionId = int;
+
+using ConnectionClosed = std::function<void(ConnectionInterfaceDescriptor)>;
+using PendingConnectionOpen = std::function<void(std::unique_ptr<l2cap::classic::DynamicChannel>)>;
+using PendingConnectionFail = std::function<void(l2cap::classic::DynamicChannelManager::ConnectionResult)>;
+using RegisterServiceComplete = std::function<void(l2cap::Psm, bool is_registered)>;
+using UnregisterServiceDone = std::function<void()>;
+using ServiceConnectionOpen =
+    std::function<void(ConnectionCompleteCallback, std::unique_ptr<l2cap::classic::DynamicChannel>)>;
+
+std::unique_ptr<packet::RawBuilder> MakeUniquePacket(const uint8_t* data, size_t len) {
+  packet::RawBuilder builder;
+  std::vector<uint8_t> bytes(data, data + len);
+  auto payload = std::make_unique<packet::RawBuilder>();
+  payload->AddOctets(bytes);
+  return payload;
+}
+
+}  // namespace
+
+class ConnectionInterface {
+ public:
+  ConnectionInterface(ConnectionInterfaceDescriptor cid, std::unique_ptr<l2cap::classic::DynamicChannel> channel,
+                      os::Handler* handler, ConnectionClosed on_closed)
+      : cid_(cid), channel_(std::move(channel)), handler_(handler), on_data_ready_callback_(nullptr),
+        on_connection_closed_callback_(nullptr), address_(channel_->GetDevice()), on_closed_(on_closed) {
+    channel_->RegisterOnCloseCallback(
+        handler_, common::BindOnce(&ConnectionInterface::OnConnectionClosed, common::Unretained(this)));
+    channel_->GetQueueUpEnd()->RegisterDequeue(
+        handler_, common::Bind(&ConnectionInterface::OnReadReady, common::Unretained(this)));
+    dequeue_registered_ = true;
+  }
+
+  ~ConnectionInterface() {
+    ASSERT(!dequeue_registered_);
+  }
+
+  void OnReadReady() {
+    std::unique_ptr<packet::PacketView<packet::kLittleEndian>> packet = channel_->GetQueueUpEnd()->TryDequeue();
+    if (packet == nullptr) {
+      LOG_WARN("Got read ready from gd l2cap but no packet is ready");
+      return;
+    }
+    std::vector<const uint8_t> data(packet->begin(), packet->end());
+    ASSERT(on_data_ready_callback_ != nullptr);
+    on_data_ready_callback_(cid_, data);
+  }
+
+  void SetReadDataReadyCallback(ReadDataReadyCallback on_data_ready) {
+    ASSERT(on_data_ready != nullptr);
+    ASSERT(on_data_ready_callback_ == nullptr);
+    on_data_ready_callback_ = on_data_ready;
+  }
+
+  std::unique_ptr<packet::BasePacketBuilder> WriteReady() {
+    auto data = std::move(write_queue_.front());
+    write_queue_.pop();
+    if (write_queue_.empty()) {
+      channel_->GetQueueUpEnd()->UnregisterEnqueue();
+      enqueue_registered_ = false;
+    }
+    return data;
+  }
+
+  void Write(std::unique_ptr<packet::RawBuilder> packet) {
+    LOG_DEBUG("Writing packet cid:%hd size:%zd", cid_, packet->size());
+    write_queue_.push(std::move(packet));
+    if (!enqueue_registered_) {
+      enqueue_registered_ = true;
+      channel_->GetQueueUpEnd()->RegisterEnqueue(
+          handler_, common::Bind(&ConnectionInterface::WriteReady, common::Unretained(this)));
+    }
+  }
+
+  void Close() {
+    if (dequeue_registered_) {
+      channel_->GetQueueUpEnd()->UnregisterDequeue();
+      dequeue_registered_ = false;
+    }
+    ASSERT(write_queue_.empty());
+    channel_->Close();
+  }
+
+  void OnConnectionClosed(hci::ErrorCode error_code) {
+    LOG_DEBUG("Channel interface closed reason:%s cid:%hd device:%s", hci::ErrorCodeText(error_code).c_str(), cid_,
+              address_.ToString().c_str());
+    if (dequeue_registered_) {
+      channel_->GetQueueUpEnd()->UnregisterDequeue();
+      dequeue_registered_ = false;
+    }
+    ASSERT(on_connection_closed_callback_ != nullptr);
+    on_connection_closed_callback_(cid_, static_cast<int>(error_code));
+    on_closed_(cid_);
+  }
+
+  void SetConnectionClosedCallback(::bluetooth::shim::ConnectionClosedCallback on_connection_closed) {
+    ASSERT(on_connection_closed != nullptr);
+    ASSERT(on_connection_closed_callback_ == nullptr);
+    on_connection_closed_callback_ = std::move(on_connection_closed);
+  }
+
+  hci::Address GetRemoteAddress() const {
+    return address_;
+  }
+
+ private:
+  const ConnectionInterfaceDescriptor cid_;
+  const std::unique_ptr<l2cap::classic::DynamicChannel> channel_;
+  os::Handler* handler_;
+
+  ReadDataReadyCallback on_data_ready_callback_;
+  ConnectionClosedCallback on_connection_closed_callback_;
+
+  const hci::Address address_;
+
+  ConnectionClosed on_closed_{};
+
+  std::queue<std::unique_ptr<packet::PacketBuilder<hci::kLittleEndian>>> write_queue_;
+
+  bool enqueue_registered_{false};
+  bool dequeue_registered_{false};
+
+  DISALLOW_COPY_AND_ASSIGN(ConnectionInterface);
+};
+
+class ConnectionInterfaceManager {
+ public:
+  void AddConnection(ConnectionInterfaceDescriptor cid, std::unique_ptr<l2cap::classic::DynamicChannel> channel);
+  void RemoveConnection(ConnectionInterfaceDescriptor cid);
+
+  void SetReadDataReadyCallback(ConnectionInterfaceDescriptor cid, ReadDataReadyCallback on_data_ready);
+  void SetConnectionClosedCallback(ConnectionInterfaceDescriptor cid, ConnectionClosedCallback on_closed);
+
+  bool Write(ConnectionInterfaceDescriptor cid, std::unique_ptr<packet::RawBuilder> packet);
+
+  size_t NumberOfActiveConnections() const {
+    return cid_to_interface_map_.size();
+  }
+
+  void ConnectionOpened(ConnectionCompleteCallback on_complete, l2cap::Psm psm, ConnectionInterfaceDescriptor cid) {
+    hci::Address address = cid_to_interface_map_[cid]->GetRemoteAddress();
+    LOG_DEBUG("Connection opened address:%s psm:%hd cid:%hd", address.ToString().c_str(), psm, cid);
+    on_complete(address.ToString(), static_cast<uint16_t>(psm), static_cast<uint16_t>(cid), kConnectionOpened);
+  }
+
+  void ConnectionFailed(ConnectionCompleteCallback on_complete, hci::Address address, l2cap::Psm psm,
+                        ConnectionInterfaceDescriptor cid) {
+    LOG_DEBUG("Connection failed address:%s psm:%hd", address.ToString().c_str(), psm);
+    on_complete(address.ToString(), static_cast<uint16_t>(psm), static_cast<uint16_t>(cid), kConnectionFailed);
+  }
+
+  ConnectionInterfaceManager(os::Handler* handler);
+
+  ConnectionInterfaceDescriptor AllocateConnectionInterfaceDescriptor();
+  void FreeConnectionInterfaceDescriptor(ConnectionInterfaceDescriptor cid);
+
+ private:
+  os::Handler* handler_;
+  ConnectionInterfaceDescriptor current_connection_interface_descriptor_;
+
+  bool HasResources() const;
+  bool ConnectionExists(ConnectionInterfaceDescriptor id) const;
+  bool CidExists(ConnectionInterfaceDescriptor id) const;
+  void ConnectionClosed(ConnectionInterfaceDescriptor cid, std::unique_ptr<ConnectionInterface> connection);
+
+  std::unordered_map<ConnectionInterfaceDescriptor, std::unique_ptr<ConnectionInterface>> cid_to_interface_map_;
+  std::set<ConnectionInterfaceDescriptor> active_cid_set_;
+
+  ConnectionInterfaceManager() = delete;
+};
+
+ConnectionInterfaceManager::ConnectionInterfaceManager(os::Handler* handler)
+    : handler_(handler), current_connection_interface_descriptor_(kStartConnectionInterfaceDescriptor) {}
+
+bool ConnectionInterfaceManager::ConnectionExists(ConnectionInterfaceDescriptor cid) const {
+  return cid_to_interface_map_.find(cid) != cid_to_interface_map_.end();
+}
+
+bool ConnectionInterfaceManager::CidExists(ConnectionInterfaceDescriptor cid) const {
+  return active_cid_set_.find(cid) != active_cid_set_.end();
+}
+
+ConnectionInterfaceDescriptor ConnectionInterfaceManager::AllocateConnectionInterfaceDescriptor() {
+  ASSERT(HasResources());
+  while (CidExists(current_connection_interface_descriptor_)) {
+    if (++current_connection_interface_descriptor_ == kInvalidConnectionInterfaceDescriptor) {
+      current_connection_interface_descriptor_ = kStartConnectionInterfaceDescriptor;
+    }
+  }
+  active_cid_set_.insert(current_connection_interface_descriptor_);
+  return current_connection_interface_descriptor_++;
+}
+
+void ConnectionInterfaceManager::FreeConnectionInterfaceDescriptor(ConnectionInterfaceDescriptor cid) {
+  ASSERT(CidExists(cid));
+  active_cid_set_.erase(cid);
+}
+
+void ConnectionInterfaceManager::ConnectionClosed(ConnectionInterfaceDescriptor cid,
+                                                  std::unique_ptr<ConnectionInterface> connection) {
+  cid_to_interface_map_.erase(cid);
+  FreeConnectionInterfaceDescriptor(cid);
+}
+
+void ConnectionInterfaceManager::AddConnection(ConnectionInterfaceDescriptor cid,
+                                               std::unique_ptr<l2cap::classic::DynamicChannel> channel) {
+  ASSERT(cid_to_interface_map_.count(cid) == 0);
+  cid_to_interface_map_.emplace(
+      cid, std::make_unique<ConnectionInterface>(
+               cid, std::move(channel), handler_, [this](ConnectionInterfaceDescriptor cid) {
+                 LOG_DEBUG("Deleting connection interface cid:%hd", cid);
+                 auto connection = std::move(cid_to_interface_map_.at(cid));
+                 handler_->Post(common::BindOnce(&ConnectionInterfaceManager::ConnectionClosed,
+                                                 common::Unretained(this), cid, std::move(connection)));
+               }));
+}
+
+void ConnectionInterfaceManager::RemoveConnection(ConnectionInterfaceDescriptor cid) {
+  if (cid_to_interface_map_.count(cid) == 1) {
+    cid_to_interface_map_.find(cid)->second->Close();
+  } else {
+    LOG_WARN("Closing a pending connection cid:%hd", cid);
+  }
+}
+
+bool ConnectionInterfaceManager::HasResources() const {
+  return cid_to_interface_map_.size() < kMaxConnections;
+}
+
+void ConnectionInterfaceManager::SetReadDataReadyCallback(ConnectionInterfaceDescriptor cid,
+                                                          ReadDataReadyCallback on_data_ready) {
+  ASSERT(ConnectionExists(cid));
+  return cid_to_interface_map_[cid]->SetReadDataReadyCallback(on_data_ready);
+}
+
+void ConnectionInterfaceManager::SetConnectionClosedCallback(ConnectionInterfaceDescriptor cid,
+                                                             ConnectionClosedCallback on_closed) {
+  ASSERT(ConnectionExists(cid));
+  return cid_to_interface_map_[cid]->SetConnectionClosedCallback(on_closed);
+}
+
+bool ConnectionInterfaceManager::Write(ConnectionInterfaceDescriptor cid, std::unique_ptr<packet::RawBuilder> packet) {
+  if (!ConnectionExists(cid)) {
+    return false;
+  }
+  cid_to_interface_map_[cid]->Write(std::move(packet));
+  return true;
+}
+
+class PendingConnection {
+ public:
+  PendingConnection(ConnectionInterfaceDescriptor cid, l2cap::Psm psm, hci::Address address,
+                    ConnectionCompleteCallback on_complete, PendingConnectionOpen pending_open,
+                    PendingConnectionFail pending_fail)
+      : cid_(cid), psm_(psm), address_(address), on_complete_(std::move(on_complete)), pending_open_(pending_open),
+        pending_fail_(pending_fail) {}
+
+  void OnConnectionOpen(std::unique_ptr<l2cap::classic::DynamicChannel> channel) {
+    LOG_DEBUG("Local initiated connection is open to device:%s for psm:%hd", address_.ToString().c_str(), psm_);
+    ASSERT_LOG(address_ == channel->GetDevice(), " Expected remote device does not match actual remote device");
+    pending_open_(std::move(channel));
+  }
+
+  void OnConnectionFailure(l2cap::classic::DynamicChannelManager::ConnectionResult result) {
+    LOG_DEBUG("Connection failed to device:%s for psm:%hd", address_.ToString().c_str(), psm_);
+    switch (result.connection_result_code) {
+      case l2cap::classic::DynamicChannelManager::ConnectionResultCode::SUCCESS:
+        LOG_WARN("Connection failed result:success hci:%s", hci::ErrorCodeText(result.hci_error).c_str());
+        break;
+      case l2cap::classic::DynamicChannelManager::ConnectionResultCode::FAIL_NO_SERVICE_REGISTERED:
+        LOG_DEBUG("Connection failed result:no service registered hci:%s",
+                  hci::ErrorCodeText(result.hci_error).c_str());
+        break;
+      case l2cap::classic::DynamicChannelManager::ConnectionResultCode::FAIL_HCI_ERROR:
+        LOG_DEBUG("Connection failed result:hci error hci:%s", hci::ErrorCodeText(result.hci_error).c_str());
+        break;
+      case l2cap::classic::DynamicChannelManager::ConnectionResultCode::FAIL_L2CAP_ERROR:
+        LOG_DEBUG("Connection failed result:l2cap error hci:%s l2cap:%s", hci::ErrorCodeText(result.hci_error).c_str(),
+                  l2cap::ConnectionResponseResultText(result.l2cap_connection_response_result).c_str());
+        break;
+    }
+    pending_fail_(result);
+  }
+
+  std::string ToString() const {
+    return address_.ToString() + "." + std::to_string(psm_);
+  }
+
+  const ConnectionInterfaceDescriptor cid_;
+  const l2cap::Psm psm_;
+  const hci::Address address_;
+  const ConnectionCompleteCallback on_complete_;
+
+ private:
+  const PendingConnectionOpen pending_open_;
+  const PendingConnectionFail pending_fail_;
+
+  DISALLOW_COPY_AND_ASSIGN(PendingConnection);
+};
+
+class ServiceInterface {
+ public:
+  ServiceInterface(l2cap::Psm psm, l2cap::SecurityPolicy security_policy, ConnectionCompleteCallback on_complete,
+                   RegisterServiceComplete register_complete, ServiceConnectionOpen connection_open,
+                   RegisterServicePromise register_promise)
+      : psm_(psm), security_policy_(security_policy), on_complete_(on_complete),
+        register_complete_(std::move(register_complete)), connection_open_(std::move(connection_open)),
+        register_promise_(std::move(register_promise)) {}
+
+  void NotifyRegistered(l2cap::Psm psm) {
+    register_promise_.set_value(psm);
+  }
+
+  void NotifyUnregistered() {
+    unregister_promise_.set_value();
+  }
+
+  void UnregisterService(os::Handler* handler, UnregisterServicePromise unregister_promise,
+                         UnregisterServiceDone unregister_done) {
+    unregister_promise_ = std::move(unregister_promise);
+    unregister_done_ = std::move(unregister_done);
+
+    service_->Unregister(common::BindOnce(&ServiceInterface::OnUnregistrationComplete, common::Unretained(this)),
+                         handler);
+  }
+
+  l2cap::SecurityPolicy GetSecurityPolicy() const {
+    return security_policy_;
+  }
+
+  void OnRegistrationComplete(l2cap::classic::DynamicChannelManager::RegistrationResult result,
+                              std::unique_ptr<l2cap::classic::DynamicChannelService> service) {
+    ASSERT(service_ == nullptr);
+    ASSERT(service->GetPsm() == psm_);
+    service_ = std::move(service);
+
+    switch (result) {
+      case l2cap::classic::DynamicChannelManager::RegistrationResult::SUCCESS:
+        LOG_DEBUG("Service is registered for psm:%hd", psm_);
+        register_complete_(psm_, kRegistrationSuccess);
+        break;
+      case l2cap::classic::DynamicChannelManager::RegistrationResult::FAIL_DUPLICATE_SERVICE:
+        LOG_WARN("Failed to register duplicate service has psm:%hd", psm_);
+        register_complete_(l2cap::kDefaultPsm, kRegistrationFailed);
+        break;
+      case l2cap::classic::DynamicChannelManager::RegistrationResult::FAIL_INVALID_SERVICE:
+        LOG_WARN("Failed to register invalid service psm:%hd", psm_);
+        register_complete_(l2cap::kDefaultPsm, kRegistrationFailed);
+        break;
+    }
+  }
+
+  void OnUnregistrationComplete() {
+    LOG_DEBUG("Unregistered psm:%hd", psm_);
+    unregister_done_();
+  }
+
+  void OnConnectionOpen(std::unique_ptr<l2cap::classic::DynamicChannel> channel) {
+    LOG_DEBUG("Remote initiated connection is open from device:%s for psm:%hd", channel->GetDevice().ToString().c_str(),
+              psm_);
+    connection_open_(on_complete_, std::move(channel));
+  }
+
+ private:
+  const l2cap::Psm psm_;
+  const l2cap::SecurityPolicy security_policy_;
+  const ConnectionCompleteCallback on_complete_;
+  const RegisterServiceComplete register_complete_;
+  const ServiceConnectionOpen connection_open_;
+  RegisterServicePromise register_promise_;
+  UnregisterServicePromise unregister_promise_;
+  UnregisterServiceDone unregister_done_;
+
+  std::unique_ptr<l2cap::classic::DynamicChannelService> service_;
+
+  DISALLOW_COPY_AND_ASSIGN(ServiceInterface);
+};
+
+struct L2cap::impl {
+  void RegisterService(l2cap::Psm psm, l2cap::classic::DynamicChannelConfigurationOption option,
+                       ConnectionCompleteCallback on_complete, RegisterServicePromise register_promise);
+  void UnregisterService(l2cap::Psm psm, UnregisterServicePromise unregister_promise);
+
+  void CreateConnection(l2cap::Psm psm, hci::Address address, ConnectionCompleteCallback on_complete,
+                        CreateConnectionPromise create_promise);
+  void CloseConnection(ConnectionInterfaceDescriptor cid);
+
+  void SetReadDataReadyCallback(ConnectionInterfaceDescriptor cid, ReadDataReadyCallback on_data_ready);
+  void SetConnectionClosedCallback(ConnectionInterfaceDescriptor cid, ConnectionClosedCallback on_closed);
+
+  void Write(ConnectionInterfaceDescriptor cid, std::unique_ptr<packet::RawBuilder> packet);
+
+  void SendLoopbackResponse(std::function<void()> function);
+
+  void Dump(int fd);
+
+  impl(L2cap& module, l2cap::classic::L2capClassicModule* l2cap_module);
+
+ private:
+  L2cap& module_;
+  l2cap::classic::L2capClassicModule* l2cap_module_;
+  os::Handler* handler_;
+  ConnectionInterfaceManager connection_interface_manager_;
+
+  std::unique_ptr<l2cap::classic::DynamicChannelManager> dynamic_channel_manager_;
+
+  std::unordered_map<l2cap::Psm, std::unique_ptr<ServiceInterface>> psm_to_service_interface_map_;
+
+  PendingConnectionId pending_connection_id_{0};
+  std::unordered_map<PendingConnectionId, std::unique_ptr<PendingConnection>> pending_connection_map_;
+
+  void PendingConnectionOpen(PendingConnectionId id, std::unique_ptr<PendingConnection> connection,
+                             std::unique_ptr<l2cap::classic::DynamicChannel> channel);
+  void PendingConnectionFail(PendingConnectionId id, std::unique_ptr<PendingConnection> connection,
+                             l2cap::classic::DynamicChannelManager::ConnectionResult result);
+  void ServiceUnregistered(l2cap::Psm psm, std::unique_ptr<ServiceInterface> service);
+  const l2cap::SecurityPolicy GetSecurityPolicy(l2cap::Psm psm) const;
+};
+
+const ModuleFactory L2cap::Factory = ModuleFactory([]() { return new L2cap(); });
+
+L2cap::impl::impl(L2cap& module, l2cap::classic::L2capClassicModule* l2cap_module)
+    : module_(module), l2cap_module_(l2cap_module), handler_(module_.GetHandler()),
+      connection_interface_manager_(handler_) {
+  dynamic_channel_manager_ = l2cap_module_->GetDynamicChannelManager();
+}
+
+void L2cap::impl::Dump(int fd) {
+  if (psm_to_service_interface_map_.empty()) {
+    dprintf(fd, "%s no psms registered\n", kModuleName);
+  } else {
+    for (auto& service : psm_to_service_interface_map_) {
+      dprintf(fd, "%s psm registered:%hd\n", kModuleName, service.first);
+    }
+  }
+
+  if (pending_connection_map_.empty()) {
+    dprintf(fd, "%s no pending classic connections\n", kModuleName);
+  } else {
+    for (auto& pending : pending_connection_map_) {
+      if (pending.second != nullptr) {
+        dprintf(fd, "%s pending connection:%s\n", kModuleName, pending.second->ToString().c_str());
+      } else {
+        dprintf(fd, "%s old pending connection:%d\n", kModuleName, pending.first);
+      }
+    }
+  }
+}
+
+void L2cap::impl::ServiceUnregistered(l2cap::Psm psm, std::unique_ptr<ServiceInterface> service) {
+  LOG_INFO("Unregistered service psm:%hd", psm);
+  psm_to_service_interface_map_.erase(psm);
+  service->NotifyUnregistered();
+}
+
+const l2cap::SecurityPolicy L2cap::impl::GetSecurityPolicy(l2cap::Psm psm) const {
+  l2cap::SecurityPolicy security_policy;
+  if (psm == 1) {
+    security_policy.security_level_ = l2cap::SecurityPolicy::Level::LEVEL_0;
+  } else {
+    security_policy.security_level_ = l2cap::SecurityPolicy::Level::LEVEL_3;
+  }
+  return security_policy;
+}
+
+void L2cap::impl::RegisterService(l2cap::Psm psm, l2cap::classic::DynamicChannelConfigurationOption option,
+                                  ConnectionCompleteCallback on_complete, RegisterServicePromise register_promise) {
+  ASSERT(psm_to_service_interface_map_.find(psm) == psm_to_service_interface_map_.end());
+
+  const l2cap::SecurityPolicy security_policy = GetSecurityPolicy(psm);
+
+  psm_to_service_interface_map_.emplace(
+      psm,
+      std::make_unique<ServiceInterface>(
+          psm, security_policy, on_complete,
+          [this, psm](l2cap::Psm actual_psm, bool is_registered) {
+            psm_to_service_interface_map_.at(psm)->NotifyRegistered(actual_psm);
+            if (!is_registered) {
+              auto service = std::move(psm_to_service_interface_map_.at(psm));
+            }
+          },
+          [this, psm](ConnectionCompleteCallback on_complete, std::unique_ptr<l2cap::classic::DynamicChannel> channel) {
+            ConnectionInterfaceDescriptor cid = connection_interface_manager_.AllocateConnectionInterfaceDescriptor();
+            connection_interface_manager_.AddConnection(cid, std::move(channel));
+            connection_interface_manager_.ConnectionOpened(on_complete, psm, cid);
+            LOG_DEBUG("connection open");
+          },
+          std::move(register_promise)));
+
+  bool rc = dynamic_channel_manager_->RegisterService(
+      psm, option, security_policy,
+      common::BindOnce(&ServiceInterface::OnRegistrationComplete,
+                       common::Unretained(psm_to_service_interface_map_.at(psm).get())),
+      common::Bind(&ServiceInterface::OnConnectionOpen,
+                   common::Unretained(psm_to_service_interface_map_.at(psm).get())),
+      handler_);
+  ASSERT_LOG(rc == true, "Failed to register classic service");
+}
+
+void L2cap::impl::UnregisterService(l2cap::Psm psm, UnregisterServicePromise unregister_promise) {
+  ASSERT(psm_to_service_interface_map_.find(psm) != psm_to_service_interface_map_.end());
+  psm_to_service_interface_map_[psm]->UnregisterService(handler_, std::move(unregister_promise), [this, psm]() {
+    auto service = std::move(psm_to_service_interface_map_.at(psm));
+    handler_->Post(
+        common::BindOnce(&L2cap::impl::ServiceUnregistered, common::Unretained(this), psm, std::move(service)));
+  });
+}
+
+void L2cap::impl::PendingConnectionOpen(PendingConnectionId id, std::unique_ptr<PendingConnection> connection,
+                                        std::unique_ptr<l2cap::classic::DynamicChannel> channel) {
+  connection_interface_manager_.AddConnection(connection->cid_, std::move(channel));
+  connection_interface_manager_.ConnectionOpened(std::move(connection->on_complete_), connection->psm_,
+                                                 connection->cid_);
+  pending_connection_map_.erase(id);
+}
+
+void L2cap::impl::PendingConnectionFail(PendingConnectionId id, std::unique_ptr<PendingConnection> connection,
+                                        l2cap::classic::DynamicChannelManager::ConnectionResult result) {
+  connection_interface_manager_.ConnectionFailed(std::move(connection->on_complete_), connection->address_,
+                                                 connection->psm_, connection->cid_);
+  connection_interface_manager_.FreeConnectionInterfaceDescriptor(connection->cid_);
+  pending_connection_map_.erase(id);
+}
+
+void L2cap::impl::CreateConnection(l2cap::Psm psm, hci::Address address, ConnectionCompleteCallback on_complete,
+                                   CreateConnectionPromise create_promise) {
+  ConnectionInterfaceDescriptor cid = connection_interface_manager_.AllocateConnectionInterfaceDescriptor();
+  create_promise.set_value(cid);
+
+  if (cid == kInvalidConnectionInterfaceDescriptor) {
+    LOG_WARN("No resources to create a connection");
+    return;
+  }
+
+  PendingConnectionId id = ++pending_connection_id_;
+  pending_connection_map_.emplace(
+      id, std::make_unique<PendingConnection>(
+              cid, psm, address, on_complete,
+              [this, id](std::unique_ptr<l2cap::classic::DynamicChannel> channel) {
+                auto connection = std::move(pending_connection_map_.at(id));
+                handler_->Post(common::BindOnce(&L2cap::impl::PendingConnectionOpen, common::Unretained(this), id,
+                                                std::move(connection), std::move(channel)));
+              },
+              [this, id](l2cap::classic::DynamicChannelManager::ConnectionResult result) {
+                auto connection = std::move(pending_connection_map_.at(id));
+                handler_->Post(common::BindOnce(&L2cap::impl::PendingConnectionFail, common::Unretained(this), id,
+                                                std::move(connection), result));
+              }));
+
+  bool rc = dynamic_channel_manager_->ConnectChannel(
+      address, l2cap::classic::DynamicChannelConfigurationOption(), psm,
+      common::Bind(&PendingConnection::OnConnectionOpen, common::Unretained(pending_connection_map_.at(id).get())),
+      common::BindOnce(&PendingConnection::OnConnectionFailure,
+                       common::Unretained(pending_connection_map_.at(id).get())),
+      handler_);
+  ASSERT_LOG(rc == true, "Failed to create classic connection");
+}
+
+void L2cap::impl::CloseConnection(ConnectionInterfaceDescriptor cid) {
+  connection_interface_manager_.RemoveConnection(cid);
+}
+
+void L2cap::impl::SetReadDataReadyCallback(ConnectionInterfaceDescriptor cid, ReadDataReadyCallback on_data_ready) {
+  connection_interface_manager_.SetReadDataReadyCallback(cid, on_data_ready);
+}
+
+void L2cap::impl::SetConnectionClosedCallback(ConnectionInterfaceDescriptor cid, ConnectionClosedCallback on_closed) {
+  connection_interface_manager_.SetConnectionClosedCallback(cid, std::move(on_closed));
+}
+
+void L2cap::impl::Write(ConnectionInterfaceDescriptor cid, std::unique_ptr<packet::RawBuilder> packet) {
+  connection_interface_manager_.Write(cid, std::move(packet));
+}
+
+void L2cap::impl::SendLoopbackResponse(std::function<void()> function) {
+  function();
+}
+
+void L2cap::RegisterService(uint16_t raw_psm, bool use_ertm, uint16_t mtu, ConnectionCompleteCallback on_complete,
+                            RegisterServicePromise register_promise) {
+  l2cap::Psm psm{raw_psm};
+  l2cap::classic::DynamicChannelConfigurationOption option;
+  if (use_ertm) {
+    option.channel_mode =
+        l2cap::classic::DynamicChannelConfigurationOption::RetransmissionAndFlowControlMode::ENHANCED_RETRANSMISSION;
+  }
+  option.incoming_mtu = mtu;
+  GetHandler()->Post(common::BindOnce(&L2cap::impl::RegisterService, common::Unretained(pimpl_.get()), psm, option,
+                                      on_complete, std::move(register_promise)));
+}
+
+void L2cap::UnregisterService(uint16_t raw_psm, UnregisterServicePromise unregister_promise) {
+  l2cap::Psm psm{raw_psm};
+  GetHandler()->Post(common::BindOnce(&L2cap::impl::UnregisterService, common::Unretained(pimpl_.get()), psm,
+                                      std::move(unregister_promise)));
+}
+
+void L2cap::CreateConnection(uint16_t raw_psm, const std::string address_string, ConnectionCompleteCallback on_complete,
+                             CreateConnectionPromise create_promise) {
+  l2cap::Psm psm{raw_psm};
+  hci::Address address;
+  hci::Address::FromString(address_string, address);
+
+  GetHandler()->Post(common::BindOnce(&L2cap::impl::CreateConnection, common::Unretained(pimpl_.get()), psm, address,
+                                      on_complete, std::move(create_promise)));
+}
+
+void L2cap::CloseConnection(uint16_t raw_cid) {
+  ConnectionInterfaceDescriptor cid(raw_cid);
+  GetHandler()->Post(common::Bind(&L2cap::impl::CloseConnection, common::Unretained(pimpl_.get()), cid));
+}
+
+void L2cap::SetReadDataReadyCallback(uint16_t raw_cid, ReadDataReadyCallback on_data_ready) {
+  ConnectionInterfaceDescriptor cid(raw_cid);
+  GetHandler()->Post(
+      common::Bind(&L2cap::impl::SetReadDataReadyCallback, common::Unretained(pimpl_.get()), cid, on_data_ready));
+}
+
+void L2cap::SetConnectionClosedCallback(uint16_t raw_cid, ConnectionClosedCallback on_closed) {
+  ConnectionInterfaceDescriptor cid(raw_cid);
+  GetHandler()->Post(common::Bind(&L2cap::impl::SetConnectionClosedCallback, common::Unretained(pimpl_.get()), cid,
+                                  std::move(on_closed)));
+}
+
+void L2cap::Write(uint16_t raw_cid, const uint8_t* data, size_t len) {
+  ConnectionInterfaceDescriptor cid(raw_cid);
+  auto packet = MakeUniquePacket(data, len);
+  GetHandler()->Post(common::BindOnce(&L2cap::impl::Write, common::Unretained(pimpl_.get()), cid, std::move(packet)));
+}
+
+void L2cap::SendLoopbackResponse(std::function<void()> function) {
+  GetHandler()->Post(common::BindOnce(&L2cap::impl::SendLoopbackResponse, common::Unretained(pimpl_.get()), function));
+}
+
+/**
+ * Module methods
+ */
+void L2cap::ListDependencies(ModuleList* list) {
+  list->add<shim::Dumpsys>();
+  list->add<l2cap::classic::L2capClassicModule>();
+}
+
+void L2cap::Start() {
+  pimpl_ = std::make_unique<impl>(*this, GetDependency<l2cap::classic::L2capClassicModule>());
+  GetDependency<shim::Dumpsys>()->RegisterDumpsysFunction(static_cast<void*>(this),
+                                                          [this](int fd) { pimpl_->Dump(fd); });
+}
+
+void L2cap::Stop() {
+  GetDependency<shim::Dumpsys>()->UnregisterDumpsysFunction(static_cast<void*>(this));
+  pimpl_.reset();
+}
+
+std::string L2cap::ToString() const {
+  return kModuleName;
+}
+
+}  // namespace shim
+}  // namespace bluetooth
diff --git a/gd/shim/l2cap.h b/gd/shim/l2cap.h
new file mode 100644
index 0000000..61b4ede
--- /dev/null
+++ b/gd/shim/l2cap.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <cstdint>
+#include <functional>
+#include <future>
+#include <memory>
+#include <string>
+
+#include "module.h"
+
+namespace bluetooth {
+namespace shim {
+
+using ConnectionClosedCallback = std::function<void(uint16_t cid, int error_code)>;
+using ConnectionCompleteCallback =
+    std::function<void(std::string string_address, uint16_t psm, uint16_t cid, bool is_connected)>;
+using ReadDataReadyCallback = std::function<void(uint16_t cid, std::vector<const uint8_t> data)>;
+
+using RegisterServicePromise = std::promise<uint16_t>;
+using UnregisterServicePromise = std::promise<void>;
+using CreateConnectionPromise = std::promise<uint16_t>;
+
+class L2cap : public bluetooth::Module {
+ public:
+  void RegisterService(uint16_t psm, bool use_ertm, uint16_t mtu, ConnectionCompleteCallback on_complete,
+                       RegisterServicePromise register_promise);
+  void UnregisterService(uint16_t psm, UnregisterServicePromise unregister_promise);
+
+  void CreateConnection(uint16_t psm, const std::string address_string, ConnectionCompleteCallback on_complete,
+                        CreateConnectionPromise create_promise);
+  void CloseConnection(uint16_t cid);
+
+  void SetReadDataReadyCallback(uint16_t cid, ReadDataReadyCallback on_data_ready);
+  void SetConnectionClosedCallback(uint16_t cid, ConnectionClosedCallback on_closed);
+
+  void Write(uint16_t cid, const uint8_t* data, size_t len);
+
+  void SendLoopbackResponse(std::function<void()>);
+
+  L2cap() = default;
+  ~L2cap() = default;
+
+  static const ModuleFactory Factory;
+
+ protected:
+  void ListDependencies(ModuleList* list) override;  // Module
+  void Start() override;                             // Module
+  void Stop() override;                              // Module
+  std::string ToString() const override;             // Module
+
+ private:
+  struct impl;
+  std::unique_ptr<impl> pimpl_;
+  DISALLOW_COPY_AND_ASSIGN(L2cap);
+};
+
+}  // namespace shim
+}  // namespace bluetooth
diff --git a/gd/shim/l2cap_test.cc b/gd/shim/l2cap_test.cc
new file mode 100644
index 0000000..9a24c11
--- /dev/null
+++ b/gd/shim/l2cap_test.cc
@@ -0,0 +1,459 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "shim/l2cap.h"
+
+#include <gtest/gtest.h>
+#include <future>
+#include <memory>
+
+#include "common/bind.h"
+#include "hci/address.h"
+#include "hci/address_with_type.h"
+#include "l2cap/classic/dynamic_channel_configuration_option.h"
+#include "l2cap/classic/dynamic_channel_manager.h"
+#include "l2cap/classic/internal/dynamic_channel_service_manager_impl_mock.h"
+#include "l2cap/classic/l2cap_classic_module.h"
+#include "l2cap/internal/ilink.h"
+#include "l2cap/psm.h"
+#include "l2cap/security_policy.h"
+#include "os/handler.h"
+
+namespace bluetooth {
+namespace shim {
+namespace {
+
+constexpr uint16_t kPsm = 123;
+constexpr uint16_t kPsm2 = kPsm + 2;
+constexpr uint16_t kCid = 456;
+constexpr uint16_t kCid2 = kCid + 1;
+constexpr char device_address[] = "11:22:33:44:55:66";
+constexpr char device_address2[] = "aa:bb:cc:dd:ee:ff";
+constexpr bool kNoUseErtm = false;
+constexpr uint16_t kMtu = 1000;
+
+class TestDynamicChannelService : public l2cap::classic::DynamicChannelService {
+ public:
+  TestDynamicChannelService(l2cap::Psm psm, l2cap::classic::internal::DynamicChannelServiceManagerImpl* manager,
+                            os::Handler* handler)
+      : DynamicChannelService(psm, manager, handler) {}
+};
+
+class TestLink : public l2cap::internal::ILink {
+ public:
+  hci::AddressWithType GetDevice() {
+    return device_with_type_;
+  }
+  hci::AddressWithType device_with_type_;
+
+  void SendLeCredit(l2cap::Cid local_cid, uint16_t credit) {}
+
+  void SendDisconnectionRequest(l2cap::Cid cid, l2cap::Cid remote_cid) {
+    connection_closed_promise_.set_value();
+  }
+  std::promise<void> connection_closed_promise_;
+};
+
+class TestDynamicChannelManagerImpl {
+ public:
+  bool ConnectChannel(hci::Address device, l2cap::classic::DynamicChannelConfigurationOption configuration_option,
+                      l2cap::Psm psm, l2cap::classic::DynamicChannelManager::OnConnectionOpenCallback on_open_callback,
+                      l2cap::classic::DynamicChannelManager::OnConnectionFailureCallback on_fail_callback,
+                      os::Handler* handler) {
+    connections_++;
+    on_open_callback_ = std::move(on_open_callback);
+    on_fail_callback_ = std::move(on_fail_callback);
+
+    connected_promise_.set_value();
+    return true;
+  }
+  int connections_{0};
+
+  bool RegisterService(l2cap::Psm psm, l2cap::classic::DynamicChannelConfigurationOption configuration_option,
+                       const l2cap::SecurityPolicy& security_policy,
+                       l2cap::classic::DynamicChannelManager::OnRegistrationCompleteCallback on_registration_complete,
+                       l2cap::classic::DynamicChannelManager::OnConnectionOpenCallback on_open_callback,
+                       os::Handler* handler) {
+    services_++;
+    on_registration_complete_ = std::move(on_registration_complete);
+    on_open_callback_ = std::move(on_open_callback);
+
+    register_promise_.set_value();
+    return true;
+  }
+  int services_{0};
+
+  void SetConnectionFuture() {
+    connected_promise_ = std::promise<void>();
+  }
+
+  void WaitConnectionFuture() {
+    connected_future_ = connected_promise_.get_future();
+    connected_future_.wait();
+  }
+
+  void SetRegistrationFuture() {
+    register_promise_ = std::promise<void>();
+  }
+
+  void WaitRegistrationFuture() {
+    register_future_ = register_promise_.get_future();
+    register_future_.wait();
+  }
+
+  void SetConnectionOnFail(l2cap::classic::DynamicChannelManager::ConnectionResult result, std::promise<void> promise) {
+    std::move(on_fail_callback_).Run(result);
+    promise.set_value();
+  }
+
+  void SetConnectionOnOpen(std::unique_ptr<l2cap::DynamicChannel> channel, std::promise<void> promise) {
+    std::move(on_open_callback_).Run(std::move(channel));
+    promise.set_value();
+  }
+
+  l2cap::classic::DynamicChannelManager::OnRegistrationCompleteCallback on_registration_complete_{};
+  l2cap::classic::DynamicChannelManager::OnConnectionOpenCallback on_open_callback_{};
+  l2cap::classic::DynamicChannelManager::OnConnectionFailureCallback on_fail_callback_{};
+
+  TestDynamicChannelManagerImpl() = default;
+  ~TestDynamicChannelManagerImpl() = default;
+
+ private:
+  std::promise<void> connected_promise_;
+  std::future<void> connected_future_;
+
+  std::promise<void> register_promise_;
+  std::future<void> register_future_;
+};
+
+class TestDynamicChannelManager : public l2cap::classic::DynamicChannelManager {
+ public:
+  bool ConnectChannel(hci::Address device, l2cap::classic::DynamicChannelConfigurationOption configuration_option,
+                      l2cap::Psm psm, l2cap::classic::DynamicChannelManager::OnConnectionOpenCallback on_open_callback,
+                      l2cap::classic::DynamicChannelManager::OnConnectionFailureCallback on_fail_callback,
+                      os::Handler* handler) override {
+    return impl_.ConnectChannel(device, configuration_option, psm, std::move(on_open_callback),
+                                std::move(on_fail_callback), handler);
+  }
+
+  bool RegisterService(l2cap::Psm psm, l2cap::classic::DynamicChannelConfigurationOption configuration_option,
+                       const l2cap::SecurityPolicy& security_policy,
+                       l2cap::classic::DynamicChannelManager::OnRegistrationCompleteCallback on_registration_complete,
+                       l2cap::classic::DynamicChannelManager::OnConnectionOpenCallback on_open_callback,
+                       os::Handler* handler) override {
+    return impl_.RegisterService(psm, configuration_option, security_policy, std::move(on_registration_complete),
+                                 std::move(on_open_callback), handler);
+  }
+  TestDynamicChannelManager(TestDynamicChannelManagerImpl& impl) : impl_(impl) {}
+  TestDynamicChannelManagerImpl& impl_;
+};
+
+class TestL2capClassicModule : public l2cap::classic::L2capClassicModule {
+ public:
+  std::unique_ptr<l2cap::classic::DynamicChannelManager> GetDynamicChannelManager() override {
+    ASSERT(impl_ != nullptr);
+    return std::make_unique<TestDynamicChannelManager>(*impl_);
+  }
+
+  void ListDependencies(ModuleList* list) override {}
+  void Start() override;
+  void Stop() override;
+
+  std::unique_ptr<TestDynamicChannelManagerImpl> impl_;
+};
+
+void TestL2capClassicModule::Start() {
+  impl_ = std::make_unique<TestDynamicChannelManagerImpl>();
+}
+
+void TestL2capClassicModule::Stop() {
+  impl_.reset();
+}
+
+class ShimL2capTest : public ::testing::Test {
+ public:
+  void OnConnectionComplete(std::string string_address, uint16_t psm, uint16_t cid, bool connected) {
+    connection_string_address_ = string_address;
+    connection_psm_ = psm;
+    connection_cid_ = cid;
+    connection_connected_ = connected;
+    connection_complete_promise_.set_value();
+  }
+
+  uint16_t CreateConnection(uint16_t psm, std::string device_address) {
+    std::promise<uint16_t> promise;
+    auto future = promise.get_future();
+
+    shim_l2cap_->CreateConnection(
+        psm, device_address,
+        std::bind(&bluetooth::shim::ShimL2capTest::OnConnectionComplete, this, std::placeholders::_1,
+                  std::placeholders::_2, std::placeholders::_3, std::placeholders::_4),
+        std::move(promise));
+    return future.get();
+  }
+
+  void SetConnectionFuture() {
+    test_l2cap_classic_module_->impl_->SetConnectionFuture();
+  }
+
+  void WaitConnectionFuture() {
+    test_l2cap_classic_module_->impl_->WaitConnectionFuture();
+  }
+
+  void SetRegistrationFuture() {
+    test_l2cap_classic_module_->impl_->SetRegistrationFuture();
+  }
+
+  void WaitRegistrationFuture() {
+    test_l2cap_classic_module_->impl_->WaitRegistrationFuture();
+  }
+
+  int NumberOfConnections() const {
+    return test_l2cap_classic_module_->impl_->connections_;
+  }
+
+  int NumberOfServices() const {
+    return test_l2cap_classic_module_->impl_->services_;
+  }
+
+  std::string connection_string_address_;
+  uint16_t connection_psm_{0};
+  uint16_t connection_cid_{0};
+  bool connection_connected_{false};
+
+  shim::L2cap* shim_l2cap_ = nullptr;
+  TestL2capClassicModule* test_l2cap_classic_module_{nullptr};
+
+  TestLink test_link_;
+  std::promise<void> connection_complete_promise_;
+
+ protected:
+  void SetUp() override {
+    handler_ = new os::Handler(&thread_);
+
+    test_l2cap_classic_module_ = new TestL2capClassicModule();
+    test_l2cap_classic_module_->Start();
+    fake_registry_.InjectTestModule(&l2cap::classic::L2capClassicModule::Factory, test_l2cap_classic_module_);
+
+    fake_registry_.Start<shim::L2cap>(&thread_);
+    shim_l2cap_ = static_cast<shim::L2cap*>(fake_registry_.GetModuleUnderTest(&shim::L2cap::Factory));
+  }
+
+  void TearDown() override {
+    fake_registry_.StopAll();
+    handler_->Clear();
+    delete handler_;
+  }
+  os::Handler* handler_ = nullptr;
+  l2cap::classic::internal::testing::MockDynamicChannelServiceManagerImpl mock_;
+
+ private:
+  TestModuleRegistry fake_registry_;
+  os::Thread& thread_ = fake_registry_.GetTestThread();
+};
+
+TEST_F(ShimL2capTest, CreateThenDisconnectBeforeCompletion) {
+  SetConnectionFuture();
+
+  ASSERT(NumberOfConnections() == 0);
+  uint16_t cid = CreateConnection(kPsm, device_address);
+  ASSERT(cid != 0);
+
+  WaitConnectionFuture();
+  ASSERT(NumberOfConnections() == 1);
+
+  shim_l2cap_->CloseConnection(cid);
+}
+
+TEST_F(ShimL2capTest, MaxCreatedConnections) {
+  for (int i = 0; i < 65536 - 64; i++) {
+    SetConnectionFuture();
+    uint16_t cid = CreateConnection(kPsm, device_address);
+    ASSERT(cid != 0);
+    WaitConnectionFuture();
+
+    ASSERT(NumberOfConnections() == i + 1);
+  }
+  uint16_t cid = CreateConnection(kPsm, device_address);
+  ASSERT(cid == 0);
+
+  ASSERT(NumberOfConnections() == 65536 - 64);
+}
+
+TEST_F(ShimL2capTest, TwoDifferentCreatedConnections) {
+  {
+    SetConnectionFuture();
+    uint16_t cid = CreateConnection(kPsm, device_address);
+    ASSERT(cid != 0);
+    WaitConnectionFuture();
+
+    ASSERT(NumberOfConnections() == 1);
+  }
+
+  {
+    SetConnectionFuture();
+    uint16_t cid = CreateConnection(kPsm2, device_address2);
+    ASSERT(cid != 0);
+    WaitConnectionFuture();
+
+    ASSERT(NumberOfConnections() == 2);
+  }
+}
+
+TEST_F(ShimL2capTest, ConnectFail) {
+  SetConnectionFuture();
+  uint16_t cid = CreateConnection(kPsm, device_address);
+  ASSERT(cid != 0);
+  WaitConnectionFuture();
+
+  ASSERT(NumberOfConnections() == 1);
+
+  l2cap::classic::DynamicChannelManager::ConnectionResult result{
+      .connection_result_code = TestDynamicChannelManager::ConnectionResultCode::FAIL_NO_SERVICE_REGISTERED,
+      .hci_error = hci::ErrorCode::SUCCESS,
+      .l2cap_connection_response_result = l2cap::ConnectionResponseResult::SUCCESS,
+  };
+
+  std::promise<void> on_fail_promise;
+  auto on_fail_future = on_fail_promise.get_future();
+  handler_->Post(common::BindOnce(&TestDynamicChannelManagerImpl::SetConnectionOnFail,
+                                  common::Unretained(test_l2cap_classic_module_->impl_.get()), result,
+                                  std::move(on_fail_promise)));
+  on_fail_future.wait();
+
+  ASSERT(connection_connected_ == false);
+
+  shim_l2cap_->CloseConnection(cid);
+}
+
+TEST_F(ShimL2capTest, ConnectOpen) {
+  SetConnectionFuture();
+  uint16_t cid = CreateConnection(kPsm, device_address);
+  ASSERT(cid != 0);
+  WaitConnectionFuture();
+
+  ASSERT(NumberOfConnections() == 1);
+
+  hci::Address address;
+  hci::Address::FromString(device_address, address);
+  test_link_.device_with_type_ = hci::AddressWithType(address, hci::AddressType::PUBLIC_DEVICE_ADDRESS);
+
+  l2cap::Psm psm = kPsm;
+  l2cap::Cid local_cid = kCid;
+  l2cap::Cid remote_cid = kCid2;
+
+  std::shared_ptr<l2cap::internal::DynamicChannelImpl> impl =
+      std::make_shared<l2cap::internal::DynamicChannelImpl>(psm, local_cid, remote_cid, &test_link_, handler_);
+
+  auto channel = std::make_unique<l2cap::DynamicChannel>(impl, handler_);
+
+  std::promise<void> on_fail_promise;
+  auto on_fail_future = on_fail_promise.get_future();
+
+  auto connection_complete_future = connection_complete_promise_.get_future();
+  handler_->Post(common::BindOnce(&TestDynamicChannelManagerImpl::SetConnectionOnOpen,
+                                  common::Unretained(test_l2cap_classic_module_->impl_.get()), std::move(channel),
+                                  std::move(on_fail_promise)));
+  connection_complete_future.wait();
+
+  on_fail_future.wait();
+
+  ASSERT(connection_connected_ == true);
+
+  auto future = test_link_.connection_closed_promise_.get_future();
+  shim_l2cap_->CloseConnection(cid);
+  future.wait();
+}
+
+TEST_F(ShimL2capTest, RegisterService_Success) {
+  std::promise<uint16_t> registration_promise;
+  auto registration_pending = registration_promise.get_future();
+
+  SetRegistrationFuture();
+  shim_l2cap_->RegisterService(
+      kPsm, kNoUseErtm, kMtu,
+      std::bind(&bluetooth::shim::ShimL2capTest::OnConnectionComplete, this, std::placeholders::_1,
+                std::placeholders::_2, std::placeholders::_3, std::placeholders::_4),
+      std::move(registration_promise));
+  WaitRegistrationFuture();
+  ASSERT_LOG(test_l2cap_classic_module_->impl_->on_registration_complete_, "Synchronization failure");
+  ASSERT(test_l2cap_classic_module_->impl_->services_ == 1);
+
+  l2cap::classic::DynamicChannelManager::RegistrationResult result{
+      l2cap::classic::DynamicChannelManager::RegistrationResult::SUCCESS,
+  };
+  auto service = std::make_unique<TestDynamicChannelService>(kPsm, &mock_, handler_);
+
+  handler_->Post(common::BindOnce(std::move(test_l2cap_classic_module_->impl_->on_registration_complete_), result,
+                                  std::move(service)));
+  uint16_t psm = registration_pending.get();
+  ASSERT(psm == kPsm);
+}
+
+TEST_F(ShimL2capTest, RegisterService_Duplicate) {
+  std::promise<uint16_t> promise;
+  auto future = promise.get_future();
+
+  SetRegistrationFuture();
+  shim_l2cap_->RegisterService(
+      kPsm, kNoUseErtm, kMtu,
+      std::bind(&bluetooth::shim::ShimL2capTest::OnConnectionComplete, this, std::placeholders::_1,
+                std::placeholders::_2, std::placeholders::_3, std::placeholders::_4),
+      std::move(promise));
+  WaitRegistrationFuture();
+  ASSERT_LOG(test_l2cap_classic_module_->impl_->on_registration_complete_, "Synchronization failure");
+  ASSERT(test_l2cap_classic_module_->impl_->services_ == 1);
+
+  l2cap::classic::DynamicChannelManager::RegistrationResult result{
+      l2cap::classic::DynamicChannelManager::RegistrationResult::FAIL_DUPLICATE_SERVICE,
+  };
+  auto service = std::make_unique<TestDynamicChannelService>(kPsm, &mock_, handler_);
+
+  handler_->Post(common::BindOnce(std::move(test_l2cap_classic_module_->impl_->on_registration_complete_), result,
+                                  std::move(service)));
+  uint16_t psm = future.get();
+  ASSERT(psm == l2cap::kDefaultPsm);
+}
+
+TEST_F(ShimL2capTest, RegisterService_Invalid) {
+  std::promise<uint16_t> promise;
+  auto future = promise.get_future();
+
+  SetRegistrationFuture();
+
+  shim_l2cap_->RegisterService(
+      kPsm, kNoUseErtm, kMtu,
+      std::bind(&bluetooth::shim::ShimL2capTest::OnConnectionComplete, this, std::placeholders::_1,
+                std::placeholders::_2, std::placeholders::_3, std::placeholders::_4),
+      std::move(promise));
+
+  l2cap::classic::DynamicChannelManager::RegistrationResult result{
+      l2cap::classic::DynamicChannelManager::RegistrationResult::FAIL_INVALID_SERVICE,
+  };
+  auto service = std::make_unique<TestDynamicChannelService>(kPsm, &mock_, handler_);
+  WaitRegistrationFuture();
+
+  ASSERT_LOG(test_l2cap_classic_module_->impl_->on_registration_complete_, "Synchronization failure");
+  handler_->Post(common::BindOnce(std::move(test_l2cap_classic_module_->impl_->on_registration_complete_), result,
+                                  std::move(service)));
+  uint16_t psm = future.get();
+  ASSERT(psm == l2cap::kDefaultPsm);
+  ASSERT(test_l2cap_classic_module_->impl_->services_ == 1);
+}
+
+}  // namespace
+}  // namespace shim
+}  // namespace bluetooth
diff --git a/gd/shim/only_include_this_file_into_legacy_stack___ever.h b/gd/shim/only_include_this_file_into_legacy_stack___ever.h
new file mode 100644
index 0000000..d1197e4
--- /dev/null
+++ b/gd/shim/only_include_this_file_into_legacy_stack___ever.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+/**
+ * This common file provides the only visibility from the legacy stack into GD stack.
+ *
+ * Only interfaces or APIs should be exported.
+ *
+ * Only common data structures should be used to pass data between the stacks.
+ *
+ */
+namespace bluetooth {
+namespace shim {
+class Dumpsys;
+class L2cap;
+}  // namespace shim
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/gd/shim/stack.cc b/gd/shim/stack.cc
new file mode 100644
index 0000000..8b7ebfe
--- /dev/null
+++ b/gd/shim/stack.cc
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "bt_gd_shim"
+
+#include "shim/stack.h"
+#include "att/att_module.h"
+#include "hal/hci_hal.h"
+#include "hci/acl_manager.h"
+#include "hci/hci_layer.h"
+#include "hci/le_advertising_manager.h"
+#include "hci/le_scanning_manager.h"
+#include "l2cap/classic/l2cap_classic_module.h"
+#include "l2cap/le/l2cap_le_module.h"
+#include "neighbor/connectability.h"
+#include "neighbor/discoverability.h"
+#include "neighbor/inquiry.h"
+#include "neighbor/name.h"
+#include "neighbor/name_db.h"
+#include "neighbor/page.h"
+#include "neighbor/scan.h"
+#include "os/log.h"
+#include "os/thread.h"
+#include "security/security_module.h"
+#include "shim/dumpsys.h"
+#include "shim/l2cap.h"
+#include "stack_manager.h"
+#include "storage/legacy.h"
+
+using ::bluetooth::os::Thread;
+
+struct bluetooth::shim::Stack::impl {
+  void Start() {
+    if (is_running_) {
+      LOG_ERROR("%s Gd stack already running", __func__);
+      return;
+    }
+
+    LOG_INFO("%s Starting Gd stack", __func__);
+    ModuleList modules;
+    modules.add<::bluetooth::att::AttModule>();
+    modules.add<::bluetooth::hal::HciHal>();
+    modules.add<::bluetooth::hci::AclManager>();
+    modules.add<::bluetooth::hci::HciLayer>();
+    modules.add<::bluetooth::hci::LeAdvertisingManager>();
+    modules.add<::bluetooth::hci::LeScanningManager>();
+    modules.add<::bluetooth::l2cap::classic::L2capClassicModule>();
+    modules.add<::bluetooth::l2cap::le::L2capLeModule>();
+    modules.add<::bluetooth::neighbor::ConnectabilityModule>();
+    modules.add<::bluetooth::neighbor::DiscoverabilityModule>();
+    modules.add<::bluetooth::neighbor::InquiryModule>();
+    modules.add<::bluetooth::neighbor::NameModule>();
+    modules.add<::bluetooth::neighbor::NameDbModule>();
+    modules.add<::bluetooth::neighbor::PageModule>();
+    modules.add<::bluetooth::neighbor::ScanModule>();
+    modules.add<::bluetooth::security::SecurityModule>();
+    modules.add<::bluetooth::storage::LegacyModule>();
+    modules.add<::bluetooth::shim::Dumpsys>();
+    modules.add<::bluetooth::shim::L2cap>();
+
+    stack_thread_ = new Thread("gd_stack_thread", Thread::Priority::NORMAL);
+    stack_manager_.StartUp(&modules, stack_thread_);
+    // TODO(cmanton) Gd stack has spun up another thread with no
+    // ability to ascertain the completion
+    is_running_ = true;
+    LOG_INFO("%s Successfully toggled Gd stack", __func__);
+  }
+
+  void Stop() {
+    if (!is_running_) {
+      LOG_ERROR("%s Gd stack not running", __func__);
+      return;
+    }
+
+    stack_manager_.ShutDown();
+    delete stack_thread_;
+    is_running_ = false;
+    LOG_INFO("%s Successfully shut down Gd stack", __func__);
+  }
+
+  StackManager* GetStackManager() {
+    ASSERT(is_running_);
+    return &stack_manager_;
+  }
+
+ private:
+  os::Thread* stack_thread_ = nullptr;
+  bool is_running_ = false;
+  StackManager stack_manager_;
+};
+
+bluetooth::shim::Stack::Stack() {
+  pimpl_ = std::make_unique<impl>();
+  LOG_INFO("%s Created gd stack", __func__);
+}
+
+void bluetooth::shim::Stack::Start() {
+  pimpl_->Start();
+}
+
+void bluetooth::shim::Stack::Stop() {
+  pimpl_->Stop();
+}
+
+bluetooth::StackManager* bluetooth::shim::Stack::GetStackManager() {
+  return pimpl_->GetStackManager();
+}
+
+bluetooth::shim::Stack* bluetooth::shim::GetGabeldorscheStack() {
+  static Stack* instance = new Stack();
+  return instance;
+}
diff --git a/gd/shim/stack.h b/gd/shim/stack.h
new file mode 100644
index 0000000..e9ef3ff
--- /dev/null
+++ b/gd/shim/stack.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include "neighbor/connectability.h"
+#include "neighbor/discoverability.h"
+#include "neighbor/inquiry.h"
+#include "neighbor/name.h"
+#include "neighbor/page.h"
+#include "security/security_module.h"
+#include "shim/dumpsys.h"
+#include "shim/l2cap.h"
+#include "stack_manager.h"
+#include "storage/legacy.h"
+
+/**
+ * The shim layer implementation on the Gd stack side.
+ */
+namespace bluetooth {
+namespace shim {
+
+class Stack {
+ public:
+  Stack();
+  ~Stack() = default;
+
+  void Start();
+  void Stop();
+
+  StackManager* GetStackManager();
+
+ private:
+  struct impl;
+  std::unique_ptr<impl> pimpl_;
+
+  Stack(const Stack&) = delete;
+  void operator=(const Stack&) = delete;
+};
+
+Stack* GetGabeldorscheStack();
+
+}  // namespace shim
+}  // namespace bluetooth
diff --git a/gd/stack_manager.cc b/gd/stack_manager.cc
new file mode 100644
index 0000000..9c01d56
--- /dev/null
+++ b/gd/stack_manager.cc
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "stack_manager.h"
+
+#include <chrono>
+#include <future>
+#include <queue>
+
+#include "common/bind.h"
+#include "hal/hci_hal.h"
+#include "module.h"
+#include "os/handler.h"
+#include "os/log.h"
+#include "os/thread.h"
+
+using ::bluetooth::os::Handler;
+using ::bluetooth::os::Thread;
+
+namespace bluetooth {
+
+void StackManager::StartUp(ModuleList* modules, Thread* stack_thread) {
+  management_thread_ = new Thread("management_thread", Thread::Priority::NORMAL);
+  handler_ = new Handler(management_thread_);
+
+  std::promise<void> promise;
+  auto future = promise.get_future();
+  handler_->Post(common::BindOnce(&StackManager::handle_start_up, common::Unretained(this), modules, stack_thread,
+                                  std::move(promise)));
+
+  auto init_status = future.wait_for(std::chrono::seconds(3));
+  ASSERT_LOG(init_status == std::future_status::ready, "Can't start stack");
+
+  LOG_INFO("init complete");
+}
+
+void StackManager::handle_start_up(ModuleList* modules, Thread* stack_thread, std::promise<void> promise) {
+  registry_.Start(modules, stack_thread);
+  promise.set_value();
+}
+
+void StackManager::ShutDown() {
+  std::promise<void> promise;
+  auto future = promise.get_future();
+  handler_->Post(common::BindOnce(&StackManager::handle_shut_down, common::Unretained(this), std::move(promise)));
+
+  auto stop_status = future.wait_for(std::chrono::seconds(3));
+  ASSERT_LOG(stop_status == std::future_status::ready, "Can't stop stack");
+
+  handler_->Clear();
+  handler_->WaitUntilStopped(std::chrono::milliseconds(20));
+  delete handler_;
+  delete management_thread_;
+}
+
+void StackManager::handle_shut_down(std::promise<void> promise) {
+  registry_.StopAll();
+  promise.set_value();
+}
+
+}  // namespace bluetooth
diff --git a/gd/stack_manager.h b/gd/stack_manager.h
new file mode 100644
index 0000000..9a86480
--- /dev/null
+++ b/gd/stack_manager.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "module.h"
+#include "os/thread.h"
+#include "os/handler.h"
+
+namespace bluetooth {
+
+class StackManager {
+ public:
+  void StartUp(ModuleList *modules, os::Thread* stack_thread);
+  void ShutDown();
+
+  template <class T>
+  T* GetInstance() const {
+    return static_cast<T*>(registry_.Get(&T::Factory));
+  }
+
+ private:
+  os::Thread* management_thread_ = nullptr;
+  os::Handler* handler_ = nullptr;
+  ModuleRegistry registry_;
+
+  void handle_start_up(ModuleList* modules, os::Thread* stack_thread, std::promise<void> promise);
+  void handle_shut_down(std::promise<void> promise);
+};
+
+}  // namespace bluetooth
diff --git a/gd/stack_manager_unittest.cc b/gd/stack_manager_unittest.cc
new file mode 100644
index 0000000..e79a94f
--- /dev/null
+++ b/gd/stack_manager_unittest.cc
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "stack_manager.h"
+
+#include "gtest/gtest.h"
+#include "os/thread.h"
+
+namespace bluetooth {
+namespace {
+
+TEST(StackManagerTest, start_and_shutdown_no_module) {
+  StackManager stack_manager;
+  ModuleList module_list;
+  os::Thread thread{"test_thread", os::Thread::Priority::NORMAL};
+  stack_manager.StartUp(&module_list, &thread);
+  stack_manager.ShutDown();
+}
+
+class TestModuleNoDependency : public Module {
+ public:
+  static const ModuleFactory Factory;
+
+ protected:
+  void ListDependencies(ModuleList* list) override {}
+  void Start() override {}
+  void Stop() override {}
+};
+
+const ModuleFactory TestModuleNoDependency::Factory = ModuleFactory([]() { return new TestModuleNoDependency(); });
+
+TEST(StackManagerTest, get_module_instance) {
+  StackManager stack_manager;
+  ModuleList module_list;
+  module_list.add<TestModuleNoDependency>();
+  os::Thread thread{"test_thread", os::Thread::Priority::NORMAL};
+  stack_manager.StartUp(&module_list, &thread);
+  EXPECT_NE(stack_manager.GetInstance<TestModuleNoDependency>(), nullptr);
+  stack_manager.ShutDown();
+}
+
+}  // namespace
+}  // namespace bluetooth
diff --git a/gd/storage/Android.bp b/gd/storage/Android.bp
new file mode 100644
index 0000000..a320f6a
--- /dev/null
+++ b/gd/storage/Android.bp
@@ -0,0 +1,16 @@
+filegroup {
+    name: "BluetoothStorageSources",
+    srcs: [
+            "legacy.cc",
+            "legacy_osi_config.cc",
+    ],
+}
+
+filegroup {
+    name: "BluetoothStorageTestSources",
+    srcs: [
+            "legacy_test.cc",
+            "legacy_osi_config.cc",
+    ],
+}
+
diff --git a/gd/storage/legacy.cc b/gd/storage/legacy.cc
new file mode 100644
index 0000000..74d4992
--- /dev/null
+++ b/gd/storage/legacy.cc
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_TAG "bt_storage"
+
+#include "storage/legacy.h"
+#include "storage/legacy_osi_config.h"
+
+namespace bluetooth {
+namespace storage {
+
+struct LegacyModule::impl {
+  void config_read(const std::string filename, LegacyReadConfigCallback callback, os::Handler* handler) {
+    std::unique_ptr<config_t> config = legacy::osi::config::config_new(filename.c_str());
+    if (config && !legacy::osi::config::config_has_section(*config, "Adapter")) {
+      LOG_ERROR("Config is missing adapter section");
+      config = nullptr;
+    }
+    if (!config) {
+      config = legacy::osi::config::config_new_empty();
+    }
+    handler->Post(common::BindOnce(std::move(callback), filename, std::move(config)));
+  }
+
+  void config_write(const std::string filename, const config_t config, LegacyWriteConfigCallback callback,
+                    os::Handler* handler) {
+    handler->Post(common::BindOnce(std::move(callback), filename, legacy::osi::config::config_save(config, filename)));
+  }
+
+  void checksum_read(const std::string filename, LegacyReadChecksumCallback callback, os::Handler* handler) {
+    handler->Post(
+        common::BindOnce(std::move(callback), filename, legacy::osi::config::checksum_read(filename.c_str())));
+  }
+
+  void checksum_write(const std::string filename, const std::string checksum, LegacyWriteChecksumCallback callback,
+                      os::Handler* handler) {
+    handler->Post(
+        common::BindOnce(std::move(callback), filename, legacy::osi::config::checksum_save(checksum, filename)));
+  }
+
+  void Start();
+  void Stop();
+
+  impl(const LegacyModule& name_module);
+
+ private:
+  const LegacyModule& module_;
+  os::Handler* handler_;
+};
+
+const ModuleFactory storage::LegacyModule::Factory = ModuleFactory([]() { return new storage::LegacyModule(); });
+
+storage::LegacyModule::impl::impl(const storage::LegacyModule& module) : module_(module) {}
+
+void storage::LegacyModule::impl::Start() {
+  handler_ = module_.GetHandler();
+}
+
+void storage::LegacyModule::impl::Stop() {}
+
+void storage::LegacyModule::ConfigRead(const std::string filename, LegacyReadConfigCallback callback,
+                                       os::Handler* handler) {
+  GetHandler()->Post(common::BindOnce(&LegacyModule::impl::config_read, common::Unretained(pimpl_.get()), filename,
+                                      std::move(callback), handler));
+}
+
+void storage::LegacyModule::ConfigWrite(const std::string filename, const config_t& config,
+                                        LegacyWriteConfigCallback callback, os::Handler* handler) {
+  GetHandler()->Post(common::BindOnce(&LegacyModule::impl::config_write, common::Unretained(pimpl_.get()), filename,
+                                      config, std::move(callback), handler));
+}
+
+void storage::LegacyModule::ChecksumRead(const std::string filename, LegacyReadChecksumCallback callback,
+                                         os::Handler* handler) {
+  GetHandler()->Post(common::BindOnce(&LegacyModule::impl::checksum_read, common::Unretained(pimpl_.get()), filename,
+                                      std::move(callback), handler));
+}
+
+void storage::LegacyModule::ChecksumWrite(const std::string filename, const std::string checksum,
+                                          LegacyWriteChecksumCallback callback, os::Handler* handler) {
+  GetHandler()->Post(common::BindOnce(&LegacyModule::impl::checksum_write, common::Unretained(pimpl_.get()), filename,
+                                      checksum, std::move(callback), handler));
+}
+
+/**
+ * General API here
+ */
+storage::LegacyModule::LegacyModule() : pimpl_(std::make_unique<impl>(*this)) {}
+
+storage::LegacyModule::~LegacyModule() {
+  pimpl_.reset();
+}
+
+/**
+ * Module methods here
+ */
+void storage::LegacyModule::ListDependencies(ModuleList* list) {}
+
+void storage::LegacyModule::Start() {
+  pimpl_->Start();
+}
+
+void storage::LegacyModule::Stop() {
+  pimpl_->Stop();
+}
+
+}  // namespace storage
+}  // namespace bluetooth
diff --git a/gd/storage/legacy.h b/gd/storage/legacy.h
new file mode 100644
index 0000000..4a4a8c6
--- /dev/null
+++ b/gd/storage/legacy.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <stdbool.h>
+#include <array>
+#include <cstdint>
+#include <list>
+#include <memory>
+#include <string>
+
+#include "common/bind.h"
+#include "hci/address.h"
+#include "hci/hci_packets.h"
+#include "module.h"
+#include "storage/legacy_osi_config.h"
+
+namespace bluetooth {
+namespace storage {
+
+using LegacyReadConfigCallback = common::OnceCallback<void(const std::string, std::unique_ptr<config_t>)>;
+using LegacyWriteConfigCallback = common::OnceCallback<void(const std::string, bool success)>;
+using LegacyReadChecksumCallback = common::OnceCallback<void(const std::string, std::string)>;
+using LegacyWriteChecksumCallback = common::OnceCallback<void(const std::string, bool success)>;
+
+class LegacyModule : public bluetooth::Module {
+ public:
+  void ConfigRead(const std::string filename, LegacyReadConfigCallback callback, os::Handler* handler);
+  void ConfigWrite(const std::string filename, const config_t& config, LegacyWriteConfigCallback callback,
+                   os::Handler* handler);
+  void ChecksumRead(const std::string filename, LegacyReadChecksumCallback callback, os::Handler* handler);
+  void ChecksumWrite(const std::string filename, const std::string checksum, LegacyWriteChecksumCallback callback,
+                     os::Handler* handler);
+
+  static const ModuleFactory Factory;
+
+  LegacyModule();
+  ~LegacyModule();
+
+ protected:
+  void ListDependencies(ModuleList* list) override;
+  void Start() override;
+  void Stop() override;
+
+ private:
+  struct impl;
+  std::unique_ptr<impl> pimpl_;
+
+  DISALLOW_COPY_AND_ASSIGN(LegacyModule);
+};
+
+}  // namespace storage
+}  // namespace bluetooth
diff --git a/gd/storage/legacy_osi_config.cc b/gd/storage/legacy_osi_config.cc
new file mode 100644
index 0000000..b75d96c
--- /dev/null
+++ b/gd/storage/legacy_osi_config.cc
@@ -0,0 +1,456 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "storage/legacy_osi_config.h"
+
+#include <base/files/file_util.h>
+#include <fcntl.h>
+#include <string>
+
+#include "os/log.h"
+
+namespace {
+
+bool config_parse(FILE* fp, config_t* config);
+
+template <typename T,
+          class = typename std::enable_if<std::is_same<config_t, typename std::remove_const<T>::type>::value>>
+auto section_find(T& config, const std::string& section) {
+  return std::find_if(config.sections.begin(), config.sections.end(),
+                      [&section](const section_t& sec) { return sec.name == section; });
+}
+
+const entry_t* entry_find(const config_t& config, const std::string& section, const std::string& key) {
+  auto sec = section_find(config, section);
+  if (sec == config.sections.end()) return nullptr;
+
+  for (const entry_t& entry : sec->entries) {
+    if (entry.key == key) return &entry;
+  }
+
+  return nullptr;
+}
+
+char* trim(char* str) {
+  while (isspace(*str)) ++str;
+
+  if (!*str) return str;
+
+  char* end_str = str + strlen(str) - 1;
+  while (end_str > str && isspace(*end_str)) --end_str;
+
+  end_str[1] = '\0';
+  return str;
+}
+
+bool config_parse(FILE* fp, config_t* config) {
+  CHECK(fp != nullptr);
+  CHECK(config != nullptr);
+
+  int line_num = 0;
+  char line[1024];
+  char section[1024];
+  strcpy(section, CONFIG_DEFAULT_SECTION);
+
+  while (fgets(line, sizeof(line), fp)) {
+    char* line_ptr = trim(line);
+    ++line_num;
+
+    // Skip blank and comment lines.
+    if (*line_ptr == '\0' || *line_ptr == '#') continue;
+
+    if (*line_ptr == '[') {
+      size_t len = strlen(line_ptr);
+      if (line_ptr[len - 1] != ']') {
+        VLOG(1) << __func__ << ": unterminated section name on line " << line_num;
+        return false;
+      }
+      strncpy(section, line_ptr + 1, len - 2);  // NOLINT (len < 1024)
+      section[len - 2] = '\0';
+    } else {
+      char* split = strchr(line_ptr, '=');
+      if (!split) {
+        VLOG(1) << __func__ << ": no key/value separator found on line " << line_num;
+        return false;
+      }
+
+      *split = '\0';
+      bluetooth::legacy::osi::config::config_set_string(config, section, trim(line_ptr), trim(split + 1));
+    }
+  }
+  return true;
+}
+
+}  // namespace
+
+std::unique_ptr<config_t> bluetooth::legacy::osi::config::config_new_empty(void) {
+  return std::make_unique<config_t>();
+}
+
+std::unique_ptr<config_t> bluetooth::legacy::osi::config::config_new(const char* filename) {
+  CHECK(filename != nullptr);
+
+  std::unique_ptr<config_t> config = config_new_empty();
+
+  FILE* fp = fopen(filename, "rt");
+  if (!fp) {
+    LOG(ERROR) << __func__ << ": unable to open file '" << filename << "': " << strerror(errno);
+    return nullptr;
+  }
+
+  if (!config_parse(fp, config.get())) {
+    config.reset();
+  }
+
+  fclose(fp);
+  return config;
+}
+
+std::string bluetooth::legacy::osi::config::checksum_read(const char* filename) {
+  base::FilePath path(filename);
+  if (!base::PathExists(path)) {
+    LOG(ERROR) << __func__ << ": unable to locate file '" << filename << "'";
+    return "";
+  }
+  std::string encrypted_hash;
+  if (!base::ReadFileToString(path, &encrypted_hash)) {
+    LOG(ERROR) << __func__ << ": unable to read file '" << filename << "'";
+  }
+  return encrypted_hash;
+}
+
+std::unique_ptr<config_t> bluetooth::legacy::osi::config::config_new_clone(const config_t& src) {
+  std::unique_ptr<config_t> ret = config_new_empty();
+
+  for (const section_t& sec : src.sections) {
+    for (const entry_t& entry : sec.entries) {
+      legacy::osi::config::config_set_string(ret.get(), sec.name, entry.key, entry.value);
+    }
+  }
+
+  return ret;
+}
+
+bool bluetooth::legacy::osi::config::config_has_section(const config_t& config, const std::string& section) {
+  return (section_find(config, section) != config.sections.end());
+}
+
+bool bluetooth::legacy::osi::config::config_has_key(const config_t& config, const std::string& section,
+                                                    const std::string& key) {
+  return (entry_find(config, section, key) != nullptr);
+}
+
+int bluetooth::legacy::osi::config::config_get_int(const config_t& config, const std::string& section,
+                                                   const std::string& key, int def_value) {
+  const entry_t* entry = entry_find(config, section, key);
+  if (!entry) return def_value;
+
+  char* endptr;
+  int ret = strtol(entry->value.c_str(), &endptr, 0);
+  return (*endptr == '\0') ? ret : def_value;
+}
+
+uint64_t bluetooth::legacy::osi::config::config_get_uint64(const config_t& config, const std::string& section,
+                                                           const std::string& key, uint64_t def_value) {
+  const entry_t* entry = entry_find(config, section, key);
+  if (!entry) return def_value;
+
+  char* endptr;
+  uint64_t ret = strtoull(entry->value.c_str(), &endptr, 0);
+  return (*endptr == '\0') ? ret : def_value;
+}
+
+bool bluetooth::legacy::osi::config::config_get_bool(const config_t& config, const std::string& section,
+                                                     const std::string& key, bool def_value) {
+  const entry_t* entry = entry_find(config, section, key);
+  if (!entry) return def_value;
+
+  if (entry->value == "true") return true;
+  if (entry->value == "false") return false;
+
+  return def_value;
+}
+
+const std::string* bluetooth::legacy::osi::config::config_get_string(const config_t& config, const std::string& section,
+                                                                     const std::string& key,
+                                                                     const std::string* def_value) {
+  const entry_t* entry = entry_find(config, section, key);
+  if (!entry) return def_value;
+
+  return &entry->value;
+}
+
+void bluetooth::legacy::osi::config::config_set_int(config_t* config, const std::string& section,
+                                                    const std::string& key, int value) {
+  legacy::osi::config::config_set_string(config, section, key, std::to_string(value));
+}
+
+void bluetooth::legacy::osi::config::config_set_uint64(config_t* config, const std::string& section,
+                                                       const std::string& key, uint64_t value) {
+  legacy::osi::config::config_set_string(config, section, key, std::to_string(value));
+}
+
+void bluetooth::legacy::osi::config::config_set_bool(config_t* config, const std::string& section,
+                                                     const std::string& key, bool value) {
+  legacy::osi::config::config_set_string(config, section, key, value ? "true" : "false");
+}
+
+void bluetooth::legacy::osi::config::config_set_string(config_t* config, const std::string& section,
+                                                       const std::string& key, const std::string& value) {
+  CHECK(config);
+
+  auto sec = section_find(*config, section);
+  if (sec == config->sections.end()) {
+    config->sections.emplace_back(section_t{.name = section});
+    sec = std::prev(config->sections.end());
+  }
+
+  std::string value_no_newline;
+  size_t newline_position = value.find('\n');
+  if (newline_position != std::string::npos) {
+    LOG_WARN("%s", "android_errorWriteLog(0x534e4554, 70808273)");
+    value_no_newline = value.substr(0, newline_position);
+  } else {
+    value_no_newline = value;
+  }
+
+  for (entry_t& entry : sec->entries) {
+    if (entry.key == key) {
+      entry.value = value_no_newline;
+      return;
+    }
+  }
+
+  sec->entries.emplace_back(entry_t{.key = key, .value = value_no_newline});
+}
+
+bool bluetooth::legacy::osi::config::config_remove_section(config_t* config, const std::string& section) {
+  CHECK(config);
+
+  auto sec = section_find(*config, section);
+  if (sec == config->sections.end()) return false;
+
+  config->sections.erase(sec);
+  return true;
+}
+
+bool bluetooth::legacy::osi::config::config_remove_key(config_t* config, const std::string& section,
+                                                       const std::string& key) {
+  CHECK(config);
+  auto sec = section_find(*config, section);
+  if (sec == config->sections.end()) return false;
+
+  for (auto entry = sec->entries.begin(); entry != sec->entries.end(); ++entry) {
+    if (entry->key == key) {
+      sec->entries.erase(entry);
+      return true;
+    }
+  }
+
+  return false;
+}
+
+bool bluetooth::legacy::osi::config::config_save(const config_t& config, const std::string& filename) {
+  CHECK(!filename.empty());
+
+  // Steps to ensure content of config file gets to disk:
+  //
+  // 1) Open and write to temp file (e.g. bt_config.conf.new).
+  // 2) Flush the stream buffer to the temp file.
+  // 3) Sync the temp file to disk with fsync().
+  // 4) Rename temp file to actual config file (e.g. bt_config.conf).
+  //    This ensures atomic update.
+  // 5) Sync directory that has the conf file with fsync().
+  //    This ensures directory entries are up-to-date.
+  int dir_fd = -1;
+  FILE* fp = nullptr;
+  std::stringstream serialized;
+
+  // Build temp config file based on config file (e.g. bt_config.conf.new).
+  const std::string temp_filename = filename + ".new";
+
+  // Extract directory from file path (e.g. /data/misc/bluedroid).
+  const std::string directoryname = base::FilePath(filename).DirName().value();
+  if (directoryname.empty()) {
+    LOG(ERROR) << __func__ << ": error extracting directory from '" << filename << "': " << strerror(errno);
+    goto error;
+  }
+
+  dir_fd = open(directoryname.c_str(), O_RDONLY);
+  if (dir_fd < 0) {
+    LOG(ERROR) << __func__ << ": unable to open dir '" << directoryname << "': " << strerror(errno);
+    goto error;
+  }
+
+  fp = fopen(temp_filename.c_str(), "wt");
+  if (!fp) {
+    LOG(ERROR) << __func__ << ": unable to write to file '" << temp_filename << "': " << strerror(errno);
+    goto error;
+  }
+
+  for (const section_t& section : config.sections) {
+    serialized << "[" << section.name << "]" << std::endl;
+
+    for (const entry_t& entry : section.entries) serialized << entry.key << " = " << entry.value << std::endl;
+
+    serialized << std::endl;
+  }
+
+  if (fprintf(fp, "%s", serialized.str().c_str()) < 0) {
+    LOG(ERROR) << __func__ << ": unable to write to file '" << temp_filename << "': " << strerror(errno);
+    goto error;
+  }
+
+  // Flush the stream buffer to the temp file.
+  if (fflush(fp) < 0) {
+    LOG(ERROR) << __func__ << ": unable to write flush buffer to file '" << temp_filename << "': " << strerror(errno);
+    goto error;
+  }
+
+  // Sync written temp file out to disk. fsync() is blocking until data makes it
+  // to disk.
+  if (fsync(fileno(fp)) < 0) {
+    LOG(WARNING) << __func__ << ": unable to fsync file '" << temp_filename << "': " << strerror(errno);
+  }
+
+  if (fclose(fp) == EOF) {
+    LOG(ERROR) << __func__ << ": unable to close file '" << temp_filename << "': " << strerror(errno);
+    goto error;
+  }
+  fp = nullptr;
+
+  // Change the file's permissions to Read/Write by User and Group
+  if (chmod(temp_filename.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) == -1) {
+    LOG(ERROR) << __func__ << ": unable to change file permissions '" << filename << "': " << strerror(errno);
+    goto error;
+  }
+
+  // Rename written temp file to the actual config file.
+  if (rename(temp_filename.c_str(), filename.c_str()) == -1) {
+    LOG(ERROR) << __func__ << ": unable to commit file '" << filename << "': " << strerror(errno);
+    goto error;
+  }
+
+  // This should ensure the directory is updated as well.
+  if (fsync(dir_fd) < 0) {
+    LOG(WARNING) << __func__ << ": unable to fsync dir '" << directoryname << "': " << strerror(errno);
+  }
+
+  if (close(dir_fd) < 0) {
+    LOG(ERROR) << __func__ << ": unable to close dir '" << directoryname << "': " << strerror(errno);
+    goto error;
+  }
+
+  return true;
+
+error:
+  // This indicates there is a write issue.  Unlink as partial data is not
+  // acceptable.
+  unlink(temp_filename.c_str());
+  if (fp) fclose(fp);
+  if (dir_fd != -1) close(dir_fd);
+  return false;
+}
+
+bool bluetooth::legacy::osi::config::checksum_save(const std::string& checksum, const std::string& filename) {
+  CHECK(!checksum.empty()) << __func__ << ": checksum cannot be empty";
+  CHECK(!filename.empty()) << __func__ << ": filename cannot be empty";
+
+  // Steps to ensure content of config checksum file gets to disk:
+  //
+  // 1) Open and write to temp file (e.g.
+  // bt_config.conf.encrypted-checksum.new). 2) Sync the temp file to disk with
+  // fsync(). 3) Rename temp file to actual config checksum file (e.g.
+  // bt_config.conf.encrypted-checksum).
+  //    This ensures atomic update.
+  // 4) Sync directory that has the conf file with fsync().
+  //    This ensures directory entries are up-to-date.
+  FILE* fp = nullptr;
+  int dir_fd = -1;
+
+  // Build temp config checksum file based on config checksum file (e.g.
+  // bt_config.conf.encrypted-checksum.new).
+  const std::string temp_filename = filename + ".new";
+  base::FilePath path(temp_filename);
+
+  // Extract directory from file path (e.g. /data/misc/bluedroid).
+  const std::string directoryname = base::FilePath(filename).DirName().value();
+  if (directoryname.empty()) {
+    LOG(ERROR) << __func__ << ": error extracting directory from '" << filename << "': " << strerror(errno);
+    goto error2;
+  }
+
+  dir_fd = open(directoryname.c_str(), O_RDONLY);
+  if (dir_fd < 0) {
+    LOG(ERROR) << __func__ << ": unable to open dir '" << directoryname << "': " << strerror(errno);
+    goto error2;
+  }
+
+  if (base::WriteFile(path, checksum.data(), checksum.size()) != (int)checksum.size()) {
+    LOG(ERROR) << __func__ << ": unable to write file '" << filename.c_str();
+    goto error2;
+  }
+
+  fp = fopen(temp_filename.c_str(), "rb");
+  if (!fp) {
+    LOG(ERROR) << __func__ << ": unable to write to file '" << temp_filename << "': " << strerror(errno);
+    goto error2;
+  }
+
+  // Sync written temp file out to disk. fsync() is blocking until data makes it
+  // to disk.
+  if (fsync(fileno(fp)) < 0) {
+    LOG(WARNING) << __func__ << ": unable to fsync file '" << temp_filename << "': " << strerror(errno);
+  }
+
+  if (fclose(fp) == EOF) {
+    LOG(ERROR) << __func__ << ": unable to close file '" << temp_filename << "': " << strerror(errno);
+    goto error2;
+  }
+  fp = nullptr;
+
+  // Change the file's permissions to Read/Write by User and Group
+  if (chmod(temp_filename.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) == -1) {
+    LOG(ERROR) << __func__ << ": unable to change file permissions '" << filename << "': " << strerror(errno);
+    goto error2;
+  }
+
+  // Rename written temp file to the actual config file.
+  if (rename(temp_filename.c_str(), filename.c_str()) == -1) {
+    LOG(ERROR) << __func__ << ": unable to commit file '" << filename << "': " << strerror(errno);
+    goto error2;
+  }
+
+  // This should ensure the directory is updated as well.
+  if (fsync(dir_fd) < 0) {
+    LOG(WARNING) << __func__ << ": unable to fsync dir '" << directoryname << "': " << strerror(errno);
+  }
+
+  if (close(dir_fd) < 0) {
+    LOG(ERROR) << __func__ << ": unable to close dir '" << directoryname << "': " << strerror(errno);
+    goto error2;
+  }
+
+  return true;
+
+error2:
+  // This indicates there is a write issue.  Unlink as partial data is not
+  // acceptable.
+  unlink(temp_filename.c_str());
+  if (fp) fclose(fp);
+  if (dir_fd != -1) close(dir_fd);
+  return false;
+}
diff --git a/gd/storage/legacy_osi_config.h b/gd/storage/legacy_osi_config.h
new file mode 100644
index 0000000..36f6dee
--- /dev/null
+++ b/gd/storage/legacy_osi_config.h
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+// This code wraps osi/include/config.h
+
+#include <stdbool.h>
+#include <list>
+#include <memory>
+#include <string>
+
+#ifndef CONFIG_DEFAULT_SECTION
+
+// The default section name to use if a key/value pair is not defined within
+// a section.
+#define CONFIG_DEFAULT_SECTION "Global"
+
+struct entry_t {
+  std::string key;
+  std::string value;
+};
+
+struct section_t {
+  std::string name;
+  std::list<entry_t> entries;
+};
+
+struct config_t {
+  std::list<section_t> sections;
+};
+
+#endif /* CONFIG_DEFAULT_SECTION */
+
+namespace bluetooth {
+namespace legacy {
+namespace osi {
+namespace config {
+
+// Creates a new config object with no entries (i.e. not backed by a file).
+// This function returns a unique pointer to config object.
+std::unique_ptr<config_t> config_new_empty(void);
+
+// Loads the specified file and returns a handle to the config file. If there
+// was a problem loading the file, this function returns
+// NULL. |filename| must not be NULL and must point to a readable
+// file on the filesystem.
+std::unique_ptr<config_t> config_new(const char* filename);
+
+// Read the checksum from the |filename|
+std::string checksum_read(const char* filename);
+
+// Clones |src|, including all of it's sections, keys, and values.
+// Returns a new config which is a copy and separated from the original;
+// changes to the new config are not reflected in any way in the original.
+//
+// This function will not return NULL.
+std::unique_ptr<config_t> config_new_clone(const config_t& src);
+
+// Returns true if the config file contains a section named |section|. If
+// the section has no key/value pairs in it, this function will return false.
+bool config_has_section(const config_t& config, const std::string& section);
+
+// Returns true if the config file has a key named |key| under |section|.
+// Returns false otherwise.
+bool config_has_key(const config_t& config, const std::string& section, const std::string& key);
+
+// Returns the integral value for a given |key| in |section|. If |section|
+// or |key| do not exist, or the value cannot be fully converted to an integer,
+// this function returns |def_value|.
+int config_get_int(const config_t& config, const std::string& section, const std::string& key, int def_value);
+
+// Returns the uint64_t value for a given |key| in |section|. If |section|
+// or |key| do not exist, or the value cannot be fully converted to an integer,
+// this function returns |def_value|.
+uint64_t config_get_uint64(const config_t& config, const std::string& section, const std::string& key,
+                           uint64_t def_value);
+
+// Returns the boolean value for a given |key| in |section|. If |section|
+// or |key| do not exist, or the value cannot be converted to a boolean, this
+// function returns |def_value|.
+bool config_get_bool(const config_t& config, const std::string& section, const std::string& key, bool def_value);
+
+// Returns the string value for a given |key| in |section|. If |section| or
+// |key| do not exist, this function returns |def_value|. The returned string
+// is owned by the config module and must not be freed or modified. |def_value|
+// may be NULL.
+const std::string* config_get_string(const config_t& config, const std::string& section, const std::string& key,
+                                     const std::string* def_value);
+
+// Sets an integral value for the |key| in |section|. If |key| or |section| do
+// not already exist, this function creates them. |config| must not be NULL.
+void config_set_int(config_t* config, const std::string& section, const std::string& key, int value);
+
+// Sets a uint64_t value for the |key| in |section|. If |key| or |section| do
+// not already exist, this function creates them. |config| must not be NULL.
+void config_set_uint64(config_t* config, const std::string& section, const std::string& key, uint64_t value);
+
+// Sets a boolean value for the |key| in |section|. If |key| or |section| do
+// not already exist, this function creates them. |config| must not be NULL.
+void config_set_bool(config_t* config, const std::string& section, const std::string& key, bool value);
+
+// Sets a string value for the |key| in |section|. If |key| or |section| do
+// not already exist, this function creates them. |config| must not be NULL.
+void config_set_string(config_t* config, const std::string& section, const std::string& key, const std::string& value);
+
+// Removes |section| from the |config| (and, as a result, all keys in the
+// section).
+// Returns true if |section| was found and removed from |config|, false
+// otherwise.
+// |config| may be NULL.
+bool config_remove_section(config_t* config, const std::string& section);
+
+// Removes one specific |key| residing in |section| of the |config|. Returns
+// true
+// if the section and key were found and the key was removed, false otherwise.
+// |config|may not be NULL.
+bool config_remove_key(config_t* config, const std::string& section, const std::string& key);
+
+// Saves |config| to a file given by |filename|. Note that this could be a
+// destructive operation: if |filename| already exists, it will be overwritten.
+// The config module does not preserve comments or formatting so if a config
+// file was opened with |config_new| and subsequently overwritten with
+// |config_save|, all comments and special formatting in the original file will
+// be lost. Neither |config| nor |filename| may be NULL.
+bool config_save(const config_t& config, const std::string& filename);
+
+// Saves the encrypted |checksum| of config file to a given |filename| Note
+// that this could be a destructive operation: if |filename| already exists,
+// it will be overwritten.
+bool checksum_save(const std::string& checksum, const std::string& filename);
+
+}  // namespace config
+}  // namespace osi
+}  // namespace legacy
+}  // namespace bluetooth
diff --git a/gd/storage/legacy_test.cc b/gd/storage/legacy_test.cc
new file mode 100644
index 0000000..0d0c1df
--- /dev/null
+++ b/gd/storage/legacy_test.cc
@@ -0,0 +1,287 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "storage/legacy.h"
+
+#include <gtest/gtest.h>
+
+#include "os/handler.h"
+#include "os/thread.h"
+
+#include <base/files/file_util.h>
+
+#ifdef OS_ANDROID
+constexpr char CONFIG_FILE[] = "/data/local/tmp/config_test.conf";
+#else
+constexpr char CONFIG_FILE[] = "/tmp/config_test.conf";
+#endif
+
+constexpr char CONFIG_FILE_CONTENT[] =
+    "                                                                                \n\
+first_key=value                                                                      \n\
+                                                                                     \n\
+# Device ID (DID) configuration                                                      \n\
+[DID]                                                                                \n\
+                                                                                     \n\
+# Record Number: 1, 2 or 3 - maximum of 3 records                                    \n\
+recordNumber = 1                                                                     \n\
+                                                                                     \n\
+# Primary Record - true or false (default)                                           \n\
+# There can be only one primary record                                               \n\
+primaryRecord = true                                                                 \n\
+                                                                                     \n\
+# Vendor ID '0xFFFF' indicates no Device ID Service Record is present in the device  \n\
+# 0x000F = Broadcom Corporation (default)                                            \n\
+#vendorId = 0x000F                                                                   \n\
+                                                                                     \n\
+# Vendor ID Source                                                                   \n\
+# 0x0001 = Bluetooth SIG assigned Device ID Vendor ID value (default)                \n\
+# 0x0002 = USB Implementer's Forum assigned Device ID Vendor ID value                \n\
+#vendorIdSource = 0x0001                                                             \n\
+                                                                                     \n\
+# Product ID & Product Version                                                       \n\
+# Per spec DID v1.3 0xJJMN for version is interpreted as JJ.M.N                      \n\
+# JJ: major version number, M: minor version number, N: sub-minor version number     \n\
+# For example: 1200, v14.3.6                                                         \n\
+productId = 0x1200                                                                   \n\
+version = 0x1111                                                                     \n\
+                                                                                     \n\
+# Optional attributes                                                                \n\
+#clientExecutableURL =                                                               \n\
+#serviceDescription =                                                                \n\
+#documentationURL =                                                                  \n\
+                                                                                     \n\
+# Additional optional DID records. Bluedroid supports up to 3 records.               \n\
+[DID]                                                                                \n\
+[DID]                                                                                \n\
+version = 0x1436                                                                     \n\
+                                                                                     \n\
+HiSyncId = 18446744073709551615                                                      \n\
+HiSyncId2 = 15001900                                                                 \n\
+";
+
+namespace bluetooth {
+namespace storage {
+namespace {
+
+class LegacyStorageTest : public ::testing::Test {
+ public:
+  void OnConfigRead(std::string filename, std::unique_ptr<config_t> config) {
+    promise.set_value();
+  }
+
+  void OnConfigWrite(std::string filename, bool success) {
+    promise.set_value();
+  }
+
+  void OnChecksumRead(std::string filename, std::string hash) {
+    promise.set_value();
+  }
+
+  void OnChecksumWrite(std::string filename, bool success) {
+    promise.set_value();
+  }
+
+ protected:
+  void SetUp() override {
+    handler_ = new os::Handler(&thread_);
+    fake_registry_.Start<LegacyModule>(&thread_);
+    legacy_module_ = static_cast<LegacyModule*>(fake_registry_.GetModuleUnderTest(&LegacyModule::Factory));
+    FILE* fp = fopen(CONFIG_FILE, "wt");
+    fwrite(CONFIG_FILE_CONTENT, 1, sizeof(CONFIG_FILE_CONTENT), fp);
+    fclose(fp);
+  }
+
+  void TearDown() override {
+    handler_->Clear();
+    fake_registry_.StopAll();
+    delete handler_;
+  }
+
+  TestModuleRegistry fake_registry_;
+  os::Thread& thread_ = fake_registry_.GetTestThread();
+
+  LegacyModule* legacy_module_ = nullptr;
+  os::Handler* handler_ = nullptr;
+  std::promise<void> promise;
+};
+
+TEST_F(LegacyStorageTest, Module) {}
+
+TEST_F(LegacyStorageTest, ConfigRead) {
+  std::string filename(CONFIG_FILE);
+  auto future = promise.get_future();
+  legacy_module_->ConfigRead(filename, common::BindOnce(&LegacyStorageTest::OnConfigRead, common::Unretained(this)),
+                             handler_);
+  future.wait();
+}
+
+TEST_F(LegacyStorageTest, ConfigWrite) {
+  std::string filename(CONFIG_FILE);
+  config_t config;
+  auto future = promise.get_future();
+  legacy_module_->ConfigWrite(filename, config,
+                              common::BindOnce(&LegacyStorageTest::OnConfigWrite, common::Unretained(this)), handler_);
+  future.wait();
+}
+
+TEST_F(LegacyStorageTest, ChecksumRead) {
+  std::string filename(CONFIG_FILE);
+  auto future = promise.get_future();
+  legacy_module_->ChecksumRead(filename, common::BindOnce(&LegacyStorageTest::OnChecksumRead, common::Unretained(this)),
+                               handler_);
+  future.wait();
+}
+
+TEST_F(LegacyStorageTest, ChecksumWrite) {
+  std::string filename(CONFIG_FILE);
+  std::string hash("0123456789abcdef");
+  auto future = promise.get_future();
+  legacy_module_->ChecksumWrite(
+      filename, hash, common::BindOnce(&LegacyStorageTest::OnChecksumWrite, common::Unretained(this)), handler_);
+  future.wait();
+}
+
+TEST_F(LegacyStorageTest, config_new_empty) {
+  std::unique_ptr<config_t> config = bluetooth::legacy::osi::config::config_new_empty();
+  EXPECT_TRUE(config.get() != NULL);
+}
+
+TEST_F(LegacyStorageTest, config_new_no_file) {
+  std::unique_ptr<config_t> config = bluetooth::legacy::osi::config::config_new("/meow");
+  EXPECT_TRUE(config.get() == NULL);
+}
+
+TEST_F(LegacyStorageTest, config_new) {
+  std::unique_ptr<config_t> config = bluetooth::legacy::osi::config::config_new(CONFIG_FILE);
+  EXPECT_TRUE(config.get() != NULL);
+}
+
+TEST_F(LegacyStorageTest, config_new_clone) {
+  std::unique_ptr<config_t> config = bluetooth::legacy::osi::config::config_new(CONFIG_FILE);
+  std::unique_ptr<config_t> clone = bluetooth::legacy::osi::config::config_new_clone(*config);
+
+  bluetooth::legacy::osi::config::config_set_string(clone.get(), CONFIG_DEFAULT_SECTION, "first_key", "not_value");
+
+  std::string one = std::string("one");
+  EXPECT_STRNE(
+      bluetooth::legacy::osi::config::config_get_string(*config, CONFIG_DEFAULT_SECTION, "first_key", &one)->c_str(),
+      bluetooth::legacy::osi::config::config_get_string(*clone, CONFIG_DEFAULT_SECTION, "first_key", &one)->c_str());
+}
+
+TEST_F(LegacyStorageTest, config_has_section) {
+  std::unique_ptr<config_t> config = bluetooth::legacy::osi::config::config_new(CONFIG_FILE);
+  EXPECT_TRUE(bluetooth::legacy::osi::config::config_has_section(*config, "DID"));
+}
+
+TEST_F(LegacyStorageTest, config_has_key_in_default_section) {
+  std::unique_ptr<config_t> config = bluetooth::legacy::osi::config::config_new(CONFIG_FILE);
+  EXPECT_TRUE(bluetooth::legacy::osi::config::config_has_key(*config, CONFIG_DEFAULT_SECTION, "first_key"));
+  EXPECT_STREQ(
+      bluetooth::legacy::osi::config::config_get_string(*config, CONFIG_DEFAULT_SECTION, "first_key", nullptr)->c_str(),
+      "value");
+}
+
+TEST_F(LegacyStorageTest, config_has_keys) {
+  std::unique_ptr<config_t> config = bluetooth::legacy::osi::config::config_new(CONFIG_FILE);
+  EXPECT_TRUE(bluetooth::legacy::osi::config::config_has_key(*config, "DID", "recordNumber"));
+  EXPECT_TRUE(bluetooth::legacy::osi::config::config_has_key(*config, "DID", "primaryRecord"));
+  EXPECT_TRUE(bluetooth::legacy::osi::config::config_has_key(*config, "DID", "productId"));
+  EXPECT_TRUE(bluetooth::legacy::osi::config::config_has_key(*config, "DID", "version"));
+}
+
+TEST_F(LegacyStorageTest, config_no_bad_keys) {
+  std::unique_ptr<config_t> config = bluetooth::legacy::osi::config::config_new(CONFIG_FILE);
+  EXPECT_FALSE(bluetooth::legacy::osi::config::config_has_key(*config, "DID_BAD", "primaryRecord"));
+  EXPECT_FALSE(bluetooth::legacy::osi::config::config_has_key(*config, "DID", "primaryRecord_BAD"));
+  EXPECT_FALSE(bluetooth::legacy::osi::config::config_has_key(*config, CONFIG_DEFAULT_SECTION, "primaryRecord"));
+}
+
+TEST_F(LegacyStorageTest, config_get_int_version) {
+  std::unique_ptr<config_t> config = bluetooth::legacy::osi::config::config_new(CONFIG_FILE);
+  EXPECT_EQ(bluetooth::legacy::osi::config::config_get_int(*config, "DID", "version", 0), 0x1436);
+}
+
+TEST_F(LegacyStorageTest, config_get_int_default) {
+  std::unique_ptr<config_t> config = bluetooth::legacy::osi::config::config_new(CONFIG_FILE);
+  EXPECT_EQ(bluetooth::legacy::osi::config::config_get_int(*config, "DID", "primaryRecord", 123), 123);
+}
+
+TEST_F(LegacyStorageTest, config_get_uint64) {
+  std::unique_ptr<config_t> config = bluetooth::legacy::osi::config::config_new(CONFIG_FILE);
+  EXPECT_EQ(bluetooth::legacy::osi::config::config_get_uint64(*config, "DID", "HiSyncId", 0), 0xFFFFFFFFFFFFFFFF);
+  EXPECT_EQ(bluetooth::legacy::osi::config::config_get_uint64(*config, "DID", "HiSyncId2", 0), uint64_t(15001900));
+}
+
+TEST_F(LegacyStorageTest, config_get_uint64_default) {
+  std::unique_ptr<config_t> config = bluetooth::legacy::osi::config::config_new(CONFIG_FILE);
+  EXPECT_EQ(bluetooth::legacy::osi::config::config_get_uint64(*config, "DID", "primaryRecord", 123), uint64_t(123));
+}
+
+TEST_F(LegacyStorageTest, config_remove_section) {
+  std::unique_ptr<config_t> config = bluetooth::legacy::osi::config::config_new(CONFIG_FILE);
+  EXPECT_TRUE(bluetooth::legacy::osi::config::config_remove_section(config.get(), "DID"));
+  EXPECT_FALSE(bluetooth::legacy::osi::config::config_has_section(*config, "DID"));
+  EXPECT_FALSE(bluetooth::legacy::osi::config::config_has_key(*config, "DID", "productId"));
+}
+
+TEST_F(LegacyStorageTest, config_remove_section_missing) {
+  std::unique_ptr<config_t> config = bluetooth::legacy::osi::config::config_new(CONFIG_FILE);
+  EXPECT_FALSE(bluetooth::legacy::osi::config::config_remove_section(config.get(), "not a section"));
+}
+
+TEST_F(LegacyStorageTest, config_remove_key) {
+  std::unique_ptr<config_t> config = bluetooth::legacy::osi::config::config_new(CONFIG_FILE);
+  EXPECT_EQ(bluetooth::legacy::osi::config::config_get_int(*config, "DID", "productId", 999), 0x1200);
+  EXPECT_TRUE(bluetooth::legacy::osi::config::config_remove_key(config.get(), "DID", "productId"));
+  EXPECT_FALSE(bluetooth::legacy::osi::config::config_has_key(*config, "DID", "productId"));
+}
+
+TEST_F(LegacyStorageTest, config_remove_key_missing) {
+  std::unique_ptr<config_t> config = bluetooth::legacy::osi::config::config_new(CONFIG_FILE);
+  EXPECT_EQ(bluetooth::legacy::osi::config::config_get_int(*config, "DID", "productId", 999), 0x1200);
+  EXPECT_TRUE(bluetooth::legacy::osi::config::config_remove_key(config.get(), "DID", "productId"));
+  EXPECT_EQ(bluetooth::legacy::osi::config::config_get_int(*config, "DID", "productId", 999), 999);
+}
+
+TEST_F(LegacyStorageTest, config_save_basic) {
+  std::unique_ptr<config_t> config = bluetooth::legacy::osi::config::config_new(CONFIG_FILE);
+  EXPECT_TRUE(bluetooth::legacy::osi::config::config_save(*config, CONFIG_FILE));
+}
+
+TEST_F(LegacyStorageTest, checksum_read) {
+  std::string filename = "/tmp/test.checksum_read";
+  std::string checksum = "0x1234";
+  base::FilePath file_path(filename);
+
+  EXPECT_EQ(base::WriteFile(file_path, checksum.data(), checksum.size()), (int)checksum.size());
+
+  EXPECT_EQ(bluetooth::legacy::osi::config::checksum_read(filename.c_str()), checksum.c_str());
+}
+
+TEST_F(LegacyStorageTest, checksum_save) {
+  std::string filename = "/tmp/test.checksum_save";
+  std::string checksum = "0x1234";
+  base::FilePath file_path(filename);
+
+  EXPECT_TRUE(bluetooth::legacy::osi::config::checksum_save(checksum, filename));
+
+  EXPECT_TRUE(base::PathExists(file_path));
+}
+
+}  // namespace
+}  // namespace storage
+}  // namespace bluetooth
diff --git a/hci/Android.bp b/hci/Android.bp
index 5702177..3fd2fec 100644
--- a/hci/Android.bp
+++ b/hci/Android.bp
@@ -3,9 +3,8 @@
     defaults: ["fluoride_defaults"],
     shared_libs: [
         "android.hardware.bluetooth@1.0",
+        "android.hardware.bluetooth@1.1",
         "libhidlbase",
-        "libhidltransport",
-        "libhwbinder",
     ],
 }
 
@@ -77,3 +76,76 @@
         "libbt-protos-lite",
     ],
 }
+
+// HCI native unit tests for target
+// ========================================================
+cc_test {
+    name: "net_test_hci_native",
+    test_suites: ["device-tests"],
+    defaults: ["libbt-hci_defaults"],
+    host_supported: true,
+    local_include_dirs: [
+        "include",
+    ],
+    include_dirs: [
+        "system/bt",
+        "system/bt/stack/include",
+    ],
+    srcs: [
+        "test/hci_layer_test.cc",
+        "test/other_stack_stub.cc",
+    ],
+    shared_libs: [
+        "libcrypto",
+        "liblog",
+        "libprotobuf-cpp-lite",
+    ],
+    static_libs: [
+        "libbt-common",
+        "libbt-protos-lite",
+        "libosi",
+        "libosi-AllocationTestHarness",
+    ],
+    sanitize: {
+        address: true,
+        cfi: true,
+        misc_undefined: ["bounds"],
+    },
+}
+
+cc_test {
+    name: "net_test_hci_fragmenter_native",
+    test_suites: ["device-tests"],
+    defaults: ["fluoride_defaults"],
+    host_supported: true,
+    local_include_dirs: [
+        "include",
+    ],
+    include_dirs: [
+        "system/bt",
+        "system/bt/stack/include",
+        "system/bt/btcore/include",
+        "system/bt/osi/test",
+    ],
+    srcs: [
+        "src/buffer_allocator.cc",
+        "test/packet_fragmenter_host_test.cc",
+    ],
+    shared_libs: [
+        "libcrypto",
+        "liblog",
+        "libprotobuf-cpp-lite",
+    ],
+    static_libs: [
+        "libbt-common",
+        "libbt-protos-lite",
+        "libosi",
+        "libosi-AllocationTestHarness",
+    ],
+    sanitize: {
+        address: true,
+        cfi: true,
+        misc_undefined: ["bounds"],
+    },
+}
+
diff --git a/hci/include/bt_hci_bdroid.h b/hci/include/bt_hci_bdroid.h
index cf2c113..110354f 100644
--- a/hci/include/bt_hci_bdroid.h
+++ b/hci/include/bt_hci_bdroid.h
@@ -53,15 +53,17 @@
 #define MSG_SUB_EVT_MASK 0x00FF /* eq. BT_SUB_EVT_MASK */
 
 /* Message event ID passed from Host/Controller lib to stack */
-#define MSG_HC_TO_STACK_HCI_ERR 0x1300      /* eq. BT_EVT_TO_BTU_HCIT_ERR */
 #define MSG_HC_TO_STACK_HCI_ACL 0x1100      /* eq. BT_EVT_TO_BTU_HCI_ACL */
 #define MSG_HC_TO_STACK_HCI_SCO 0x1200      /* eq. BT_EVT_TO_BTU_HCI_SCO */
+#define MSG_HC_TO_STACK_HCI_ERR 0x1300      /* eq. BT_EVT_TO_BTU_HCIT_ERR */
+#define MSG_HC_TO_STACK_HCI_ISO 0x1700      /* eq. BT_EVT_TO_BTU_HCI_ISO */
 #define MSG_HC_TO_STACK_HCI_EVT 0x1000      /* eq. BT_EVT_TO_BTU_HCI_EVT */
 #define MSG_HC_TO_STACK_L2C_SEG_XMIT 0x1900 /* BT_EVT_TO_BTU_L2C_SEG_XMIT */
 
 /* Message event ID passed from stack to vendor lib */
 #define MSG_STACK_TO_HC_HCI_ACL 0x2100 /* eq. BT_EVT_TO_LM_HCI_ACL */
 #define MSG_STACK_TO_HC_HCI_SCO 0x2200 /* eq. BT_EVT_TO_LM_HCI_SCO */
+#define MSG_STACK_TO_HC_HCI_ISO 0x2d00 /* eq. BT_EVT_TO_LM_HCI_ISO */
 #define MSG_STACK_TO_HC_HCI_CMD 0x2000 /* eq. BT_EVT_TO_LM_HCI_CMD */
 
 /* Local Bluetooth Controller ID for BR/EDR */
diff --git a/hci/include/hci_hal.h b/hci/include/hci_hal.h
index f73e116..6fbb13e 100644
--- a/hci/include/hci_hal.h
+++ b/hci/include/hci_hal.h
@@ -29,7 +29,8 @@
   DATA_TYPE_COMMAND = 1,
   DATA_TYPE_ACL = 2,
   DATA_TYPE_SCO = 3,
-  DATA_TYPE_EVENT = 4
+  DATA_TYPE_EVENT = 4,
+  DATA_TYPE_ISO = 5
 } serial_data_type_t;
 
 typedef void (*data_ready_cb)(serial_data_type_t type);
diff --git a/hci/include/hci_internals.h b/hci/include/hci_internals.h
index 8fe0041..bf9430f 100644
--- a/hci/include/hci_internals.h
+++ b/hci/include/hci_internals.h
@@ -26,3 +26,5 @@
 #define HCI_SCO_PREAMBLE_SIZE 3
 // 1 byte for event code, 1 byte for parameter length (Volume 2, Part E, 5.4.4)
 #define HCI_EVENT_PREAMBLE_SIZE 2
+// 2 bytes for handle, 2 bytes for data length (Volume 2, Part E, 5.4.5)
+#define HCI_ISO_PREAMBLE_SIZE 4
\ No newline at end of file
diff --git a/hci/include/hci_layer.h b/hci/include/hci_layer.h
index 5eb2bd5..401e90a 100644
--- a/hci/include/hci_layer.h
+++ b/hci/include/hci_layer.h
@@ -37,15 +37,17 @@
 #define MSG_SUB_EVT_MASK 0x00FF /* eq. BT_SUB_EVT_MASK */
 
 /* Message event ID passed from Host/Controller lib to stack */
-#define MSG_HC_TO_STACK_HCI_ERR 0x1300      /* eq. BT_EVT_TO_BTU_HCIT_ERR */
 #define MSG_HC_TO_STACK_HCI_ACL 0x1100      /* eq. BT_EVT_TO_BTU_HCI_ACL */
 #define MSG_HC_TO_STACK_HCI_SCO 0x1200      /* eq. BT_EVT_TO_BTU_HCI_SCO */
+#define MSG_HC_TO_STACK_HCI_ERR 0x1300      /* eq. BT_EVT_TO_BTU_HCIT_ERR */
+#define MSG_HC_TO_STACK_HCI_ISO 0x1700      /* eq. BT_EVT_TO_BTU_HCI_ISO */
 #define MSG_HC_TO_STACK_HCI_EVT 0x1000      /* eq. BT_EVT_TO_BTU_HCI_EVT */
 #define MSG_HC_TO_STACK_L2C_SEG_XMIT 0x1900 /* BT_EVT_TO_BTU_L2C_SEG_XMIT */
 
 /* Message event ID passed from stack to vendor lib */
 #define MSG_STACK_TO_HC_HCI_ACL 0x2100 /* eq. BT_EVT_TO_LM_HCI_ACL */
 #define MSG_STACK_TO_HC_HCI_SCO 0x2200 /* eq. BT_EVT_TO_LM_HCI_SCO */
+#define MSG_STACK_TO_HC_HCI_ISO 0x2d00 /* eq. BT_EVT_TO_LM_HCI_ISO */
 #define MSG_STACK_TO_HC_HCI_CMD 0x2000 /* eq. BT_EVT_TO_LM_HCI_CMD */
 
 /* Local Bluetooth Controller ID for BR/EDR */
@@ -83,6 +85,11 @@
   void (*transmit_downward)(uint16_t type, void* data);
 } hci_t;
 
+namespace bluetooth {
+namespace legacy {
+const hci_t* hci_layer_get_interface();
+}  // namespace legacy
+}  // namespace bluetooth
 const hci_t* hci_layer_get_interface();
 
 const hci_t* hci_layer_get_test_interface(
@@ -93,3 +100,4 @@
 void post_to_main_message_loop(const base::Location& from_here, BT_HDR* p_msg);
 
 void hci_layer_cleanup_interface();
+bool hci_is_root_inflammation_event_received();
diff --git a/hci/src/btsnoop_net.cc b/hci/src/btsnoop_net.cc
index a549205..d0756e6 100644
--- a/hci/src/btsnoop_net.cc
+++ b/hci/src/btsnoop_net.cc
@@ -32,6 +32,7 @@
 
 #include <mutex>
 
+#include "bt_types.h"
 #include "osi/include/log.h"
 #include "osi/include/osi.h"
 
diff --git a/hci/src/hci_inject.cc b/hci/src/hci_inject.cc
index 01b8cfd..6d3e8e0 100644
--- a/hci/src/hci_inject.cc
+++ b/hci/src/hci_inject.cc
@@ -39,6 +39,7 @@
   HCI_PACKET_ACL_DATA = 2,
   HCI_PACKET_SCO_DATA = 3,
   HCI_PACKET_EVENT = 4,
+  HCI_PACKET_ISO_DATA = 5,
 } hci_packet_t;
 
 typedef struct {
@@ -118,6 +119,8 @@
       return MSG_STACK_TO_HC_HCI_ACL;
     case HCI_PACKET_SCO_DATA:
       return MSG_STACK_TO_HC_HCI_SCO;
+    case HCI_PACKET_ISO_DATA:
+      return MSG_STACK_TO_HC_HCI_ISO;
     default:
       LOG_ERROR(LOG_TAG, "%s unsupported packet type: %d", __func__, packet);
       return -1;
diff --git a/hci/src/hci_layer.cc b/hci/src/hci_layer.cc
index 80f63de..976d91d 100644
--- a/hci/src/hci_layer.cc
+++ b/hci/src/hci_layer.cc
@@ -46,6 +46,7 @@
 #include "hci_internals.h"
 #include "hcidefs.h"
 #include "hcimsgs.h"
+#include "main/shim/shim.h"
 #include "osi/include/alarm.h"
 #include "osi/include/list.h"
 #include "osi/include/log.h"
@@ -87,6 +88,7 @@
 static const uint32_t COMMAND_PENDING_TIMEOUT_MS = 2000;
 static const uint32_t COMMAND_PENDING_MUTEX_ACQUIRE_TIMEOUT_MS = 500;
 static const uint32_t COMMAND_TIMEOUT_RESTART_MS = 5000;
+static const uint32_t ROOT_INFLAMMED_RESTART_MS = 5000;
 static const int HCI_UNKNOWN_COMMAND_TIMED_OUT = 0x00ffffff;
 static const int HCI_STARTUP_TIMED_OUT = 0x00eeeeee;
 
@@ -115,6 +117,10 @@
 static std::recursive_timed_mutex commands_pending_response_mutex;
 static OnceTimer abort_timer;
 
+// Root inflammation error codes
+static uint8_t root_inflamed_error_code = 0;
+static uint8_t root_inflamed_vendor_error_code = 0;
+
 // The hand-off point for data going to a higher layer, set by the higher layer
 static base::Callback<void(const base::Location&, BT_HDR*)> send_data_upwards;
 
@@ -122,6 +128,8 @@
 static waiting_command_t* get_waiting_command(command_opcode_t opcode);
 static int get_num_waiting_commands();
 
+static void hci_root_inflamed_abort();
+static void hci_timeout_abort(void);
 static void event_finish_startup(void* context);
 static void startup_timer_expired(void* context);
 
@@ -137,6 +145,8 @@
 static void dispatch_reassembled(BT_HDR* packet);
 static void fragmenter_transmit_finished(BT_HDR* packet,
                                          bool all_fragments_sent);
+static bool filter_bqr_event(int16_t bqr_parameter_length,
+                             uint8_t* p_bqr_event);
 
 static const packet_fragmenter_callbacks_t packet_fragmenter_callbacks = {
     transmit_fragment, dispatch_reassembled, fragmenter_transmit_finished};
@@ -163,9 +173,18 @@
   packet_fragmenter->reassemble_and_dispatch(packet);
 }
 
+void iso_data_received(BT_HDR* packet) {
+  btsnoop->capture(packet, true);
+  packet_fragmenter->reassemble_and_dispatch(packet);
+}
+
 void hal_service_died() {
   if (abort_timer.IsScheduled()) {
-    LOG(ERROR) << "abort_timer is scheduled, wait for timeout";
+    if (root_inflamed_vendor_error_code != 0 || root_inflamed_error_code != 0) {
+      hci_root_inflamed_abort();
+    } else {
+      hci_timeout_abort();
+    }
     return;
   }
   abort();
@@ -360,6 +379,13 @@
   LOG_ERROR(LOG_TAG, "%s", __func__);
 
   LOG_EVENT_INT(BT_HCI_TIMEOUT_TAG_NUM, HCI_STARTUP_TIMED_OUT);
+
+  hci_close();
+  if (abort_timer.IsScheduled()) {
+    LOG_ERROR(LOG_TAG, "%s: waiting for abort_timer", __func__);
+    return;
+  }
+
   abort();
 }
 
@@ -450,10 +476,11 @@
   abort();
 }
 
-static void hci_root_inflamed_abort(uint8_t error_code,
-                                    uint8_t vendor_error_code) {
-  LOG(FATAL) << __func__ << ": error_code = " << std::to_string(error_code)
-             << ", vendor_error_code = " << std::to_string(vendor_error_code);
+static void hci_root_inflamed_abort() {
+  LOG(FATAL) << __func__
+             << ": error_code = " << std::to_string(root_inflamed_error_code)
+             << ", vendor_error_code = "
+             << std::to_string(root_inflamed_vendor_error_code);
 }
 
 static void command_timed_out_log_info(void* original_wait_entry) {
@@ -521,8 +548,7 @@
 
   uint8_t* hci_packet = reinterpret_cast<uint8_t*>(bt_hdr + 1);
 
-  UINT16_TO_STREAM(hci_packet,
-                   HCI_GRP_VENDOR_SPECIFIC | HCI_CONTROLLER_DEBUG_INFO_OCF);
+  UINT16_TO_STREAM(hci_packet, HCI_CONTROLLER_DEBUG_INFO);
   UINT8_TO_STREAM(hci_packet, 0);  // No parameters
 
   hci_firmware_log_fd = hci_open_firmware_log_file();
@@ -562,6 +588,49 @@
   }
 }
 
+bool hci_is_root_inflammation_event_received() {
+  return abort_timer.IsScheduled();
+}
+
+void handle_root_inflammation_event() {
+  LOG(ERROR) << __func__
+             << ": Root inflammation event! setting timer to restart.";
+  // TODO(ugoyu) Report to bluetooth metrics here
+  {
+    // Try to stop hci command and startup timers
+    std::unique_lock<std::recursive_timed_mutex> lock(
+        commands_pending_response_mutex, std::defer_lock);
+    if (lock.try_lock_for(std::chrono::milliseconds(
+            COMMAND_PENDING_MUTEX_ACQUIRE_TIMEOUT_MS))) {
+      if (alarm_is_scheduled(startup_timer)) {
+        alarm_cancel(startup_timer);
+      }
+      if (alarm_is_scheduled(command_response_timer)) {
+        alarm_cancel(command_response_timer);
+      }
+      // Cleanup the hci/startup timers so they will not be scheduled again and
+      // expire before the abort_timer.
+      alarm_free(command_response_timer);
+      command_response_timer = NULL;
+      alarm_free(startup_timer);
+      startup_timer = NULL;
+    } else {
+      LOG(ERROR) << __func__ << ": Failed to obtain mutex";
+      hci_root_inflamed_abort();
+    }
+  }
+
+  // HwBinder thread post to hci_thread
+  if (!hci_thread.IsRunning() ||
+      !abort_timer.Schedule(
+          hci_thread.GetWeakPtr(), FROM_HERE,
+          base::Bind(hci_root_inflamed_abort),
+          base::TimeDelta::FromMilliseconds(ROOT_INFLAMMED_RESTART_MS))) {
+    LOG(ERROR) << "Failed to schedule abort_timer or hci has already closed!";
+    hci_root_inflamed_abort();
+  }
+}
+
 // Returns true if the event was intercepted and should not proceed to
 // higher layers. Also inspects an incoming event for interesting
 // information, like how many commands are now able to be sent.
@@ -569,11 +638,12 @@
   waiting_command_t* wait_entry = NULL;
   uint8_t* stream = packet->data;
   uint8_t event_code;
+  uint8_t length;
   int credits = 0;
   command_opcode_t opcode;
 
   STREAM_TO_UINT8(event_code, stream);
-  STREAM_SKIP_UINT8(stream);  // Skip the parameter total length field
+  STREAM_TO_UINT8(length, stream);
 
   if (event_code == HCI_COMMAND_COMPLETE_EVT) {
     STREAM_TO_UINT8(credits, stream);
@@ -601,6 +671,9 @@
 
     goto intercepted;
   } else if (event_code == HCI_COMMAND_STATUS_EVT) {
+    if (length < (sizeof(uint8_t) + sizeof(uint8_t) + sizeof(uint16_t))) {
+      goto intercepted;
+    }
     uint8_t status;
     STREAM_TO_UINT8(status, stream);
     STREAM_TO_UINT8(credits, stream);
@@ -639,46 +712,12 @@
       buffer_allocator->free(packet);
       return true;
     } else if (sub_event_code == HCI_VSE_SUBCODE_BQR_SUB_EVT) {
-      uint8_t bqr_report_id;
-      STREAM_TO_UINT8(bqr_report_id, stream);
-
-      if (bqr_report_id ==
-              bluetooth::bqr::QUALITY_REPORT_ID_ROOT_INFLAMMATION &&
-          packet->len >= bluetooth::bqr::kRootInflammationPacketMinSize) {
-        uint8_t error_code;
-        uint8_t vendor_error_code;
-        STREAM_TO_UINT8(error_code, stream);
-        STREAM_TO_UINT8(vendor_error_code, stream);
-        // TODO(ugoyu) Report to bluetooth metrics here
-
-        LOG(ERROR) << __func__
-                   << ": Root inflammation event! setting timer to restart.";
-        {
-          // Try to stop hci command and startup timers
-          std::unique_lock<std::recursive_timed_mutex> lock(
-              commands_pending_response_mutex, std::defer_lock);
-          if (lock.try_lock_for(std::chrono::milliseconds(
-                  COMMAND_PENDING_MUTEX_ACQUIRE_TIMEOUT_MS))) {
-            if (alarm_is_scheduled(startup_timer)) {
-              alarm_cancel(startup_timer);
-            }
-            if (alarm_is_scheduled(command_response_timer)) {
-              alarm_cancel(command_response_timer);
-            }
-          } else {
-            LOG(ERROR) << __func__ << ": Failed to obtain mutex";
-          }
-        }
-
-        // HwBinder thread post to hci_thread
-        if (!abort_timer.Schedule(hci_thread.GetWeakPtr(), FROM_HERE,
-                                  base::Bind(hci_root_inflamed_abort,
-                                             error_code, vendor_error_code),
-                                  base::TimeDelta::FromMilliseconds(
-                                      COMMAND_TIMEOUT_RESTART_MS))) {
-          LOG(ERROR) << "Failed to schedule abort_timer!";
-          hci_root_inflamed_abort(error_code, vendor_error_code);
-        }
+      // Excluding the HCI Event packet header and 1 octet sub-event code
+      int16_t bqr_parameter_length = packet->len - HCIE_PREAMBLE_SIZE - 1;
+      // The stream currently points to the BQR sub-event parameters
+      if (filter_bqr_event(bqr_parameter_length, stream)) {
+        buffer_allocator->free(packet);
+        return true;
       }
     }
   }
@@ -753,6 +792,60 @@
   }
 }
 
+// Returns true if the BQR event is handled and should not proceed to
+// higher layers.
+static bool filter_bqr_event(int16_t bqr_parameter_length,
+                             uint8_t* p_bqr_event) {
+  if (bqr_parameter_length <= 0) {
+    LOG(ERROR) << __func__ << ": Invalid parameter length : "
+               << std::to_string(bqr_parameter_length);
+    return true;
+  }
+
+  bool intercepted = false;
+  uint8_t quality_report_id = p_bqr_event[0];
+  switch (quality_report_id) {
+    case bluetooth::bqr::QUALITY_REPORT_ID_ROOT_INFLAMMATION:
+      if (bqr_parameter_length >=
+          bluetooth::bqr::kRootInflammationParamTotalLen) {
+        STREAM_TO_UINT8(quality_report_id, p_bqr_event);
+        STREAM_TO_UINT8(root_inflamed_error_code, p_bqr_event);
+        STREAM_TO_UINT8(root_inflamed_vendor_error_code, p_bqr_event);
+        handle_root_inflammation_event();
+      }
+      intercepted = true;
+      break;
+
+    case bluetooth::bqr::QUALITY_REPORT_ID_LMP_LL_MESSAGE_TRACE:
+      if (bqr_parameter_length >= bluetooth::bqr::kLogDumpParamTotalLen) {
+        bluetooth::bqr::DumpLmpLlMessage(bqr_parameter_length, p_bqr_event);
+      }
+      intercepted = true;
+      break;
+
+    case bluetooth::bqr::QUALITY_REPORT_ID_BT_SCHEDULING_TRACE:
+      if (bqr_parameter_length >= bluetooth::bqr::kLogDumpParamTotalLen) {
+        bluetooth::bqr::DumpBtScheduling(bqr_parameter_length, p_bqr_event);
+      }
+      intercepted = true;
+      break;
+
+    case bluetooth::bqr::QUALITY_REPORT_ID_CONTROLLER_DBG_INFO:
+      // TODO: Integrate with the HCI_VSE_SUBCODE_DEBUG_INFO_SUB_EVT
+      intercepted = true;
+      break;
+
+    case bluetooth::bqr::QUALITY_REPORT_ID_MONITOR_MODE:
+    case bluetooth::bqr::QUALITY_REPORT_ID_APPROACH_LSTO:
+    case bluetooth::bqr::QUALITY_REPORT_ID_A2DP_AUDIO_CHOPPY:
+    case bluetooth::bqr::QUALITY_REPORT_ID_SCO_VOICE_CHOPPY:
+    default:
+      break;
+  }
+
+  return intercepted;
+}
+
 static void init_layer_interface() {
   if (!interface_created) {
     // It's probably ok for this to live forever. It's small and
@@ -778,7 +871,21 @@
   }
 }
 
+namespace bluetooth {
+namespace shim {
+const hci_t* hci_layer_get_interface();
+}  // namespace shim
+}  // namespace bluetooth
+
 const hci_t* hci_layer_get_interface() {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::hci_layer_get_interface();
+  } else {
+    return bluetooth::legacy::hci_layer_get_interface();
+  }
+}
+
+const hci_t* bluetooth::legacy::hci_layer_get_interface() {
   buffer_allocator = buffer_allocator_get_interface();
   btsnoop = btsnoop_get_interface();
   packet_fragmenter = packet_fragmenter_get_interface();
diff --git a/hci/src/hci_layer_android.cc b/hci/src/hci_layer_android.cc
index a9fde50..d86a79b 100644
--- a/hci/src/hci_layer_android.cc
+++ b/hci/src/hci_layer_android.cc
@@ -32,6 +32,8 @@
 #include <android/hardware/bluetooth/1.0/IBluetoothHci.h>
 #include <android/hardware/bluetooth/1.0/IBluetoothHciCallbacks.h>
 #include <android/hardware/bluetooth/1.0/types.h>
+#include <android/hardware/bluetooth/1.1/IBluetoothHci.h>
+#include <android/hardware/bluetooth/1.1/IBluetoothHciCallbacks.h>
 #include <hwbinder/ProcessState.h>
 
 #define LOG_PATH "/data/misc/bluetooth/logs/firmware_events.log"
@@ -43,17 +45,20 @@
 using ::android::hardware::Return;
 using ::android::hardware::Void;
 using ::android::hardware::bluetooth::V1_0::HciPacket;
-using ::android::hardware::bluetooth::V1_0::IBluetoothHci;
-using ::android::hardware::bluetooth::V1_0::IBluetoothHciCallbacks;
 using ::android::hardware::bluetooth::V1_0::Status;
 
+using namespace ::android::hardware::bluetooth;
+
 extern void initialization_complete();
 extern void hci_event_received(const base::Location& from_here, BT_HDR* packet);
 extern void acl_event_received(BT_HDR* packet);
 extern void sco_data_received(BT_HDR* packet);
+extern void iso_data_received(BT_HDR* packet);
 extern void hal_service_died();
+extern bool hci_is_root_inflammation_event_received();
 
-android::sp<IBluetoothHci> btHci;
+android::sp<V1_0::IBluetoothHci> btHci;
+android::sp<V1_1::IBluetoothHci> btHci_1_1;
 
 class BluetoothHciDeathRecipient : public hidl_death_recipient {
  public:
@@ -64,7 +69,7 @@
 };
 android::sp<BluetoothHciDeathRecipient> bluetoothHciDeathRecipient = new BluetoothHciDeathRecipient();
 
-class BluetoothHciCallbacks : public IBluetoothHciCallbacks {
+class BluetoothHciCallbacks : public V1_1::IBluetoothHciCallbacks {
  public:
   BluetoothHciCallbacks() {
     buffer_allocator = buffer_allocator_get_interface();
@@ -84,37 +89,59 @@
     return packet;
   }
 
-  Return<void> initializationComplete(Status status) {
+  Return<void> initializationComplete(Status status) override {
+    if (hci_is_root_inflammation_event_received()) {
+      // Ignore the initializationComplete here as we have already received
+      // root inflammation event earlier.
+      LOG_ERROR(
+          LOG_TAG,
+          "initializationComplete after root inflammation event! status=%d",
+          status);
+      return Void();
+    }
     CHECK(status == Status::SUCCESS);
     initialization_complete();
     return Void();
   }
 
-  Return<void> hciEventReceived(const hidl_vec<uint8_t>& event) {
+  Return<void> hciEventReceived(const hidl_vec<uint8_t>& event) override {
     BT_HDR* packet = WrapPacketAndCopy(MSG_HC_TO_STACK_HCI_EVT, event);
     hci_event_received(FROM_HERE, packet);
     return Void();
   }
 
-  Return<void> aclDataReceived(const hidl_vec<uint8_t>& data) {
+  Return<void> aclDataReceived(const hidl_vec<uint8_t>& data) override {
     BT_HDR* packet = WrapPacketAndCopy(MSG_HC_TO_STACK_HCI_ACL, data);
     acl_event_received(packet);
     return Void();
   }
 
-  Return<void> scoDataReceived(const hidl_vec<uint8_t>& data) {
+  Return<void> scoDataReceived(const hidl_vec<uint8_t>& data) override {
     BT_HDR* packet = WrapPacketAndCopy(MSG_HC_TO_STACK_HCI_SCO, data);
     sco_data_received(packet);
     return Void();
   }
 
+  Return<void> isoDataReceived(const hidl_vec<uint8_t>& data) override {
+    BT_HDR* packet = WrapPacketAndCopy(MSG_HC_TO_STACK_HCI_ISO, data);
+    iso_data_received(packet);
+    return Void();
+  }
+
   const allocator_t* buffer_allocator;
 };
 
 void hci_initialize() {
   LOG_INFO(LOG_TAG, "%s", __func__);
 
-  btHci = IBluetoothHci::getService();
+  btHci_1_1 = V1_1::IBluetoothHci::getService();
+
+  if (btHci_1_1 != nullptr) {
+    btHci = btHci_1_1;
+  } else {
+    btHci = V1_0::IBluetoothHci::getService();
+  }
+
   // If android.hardware.bluetooth* is not found, Bluetooth can not continue.
   CHECK(btHci != nullptr);
   auto death_link = btHci->linkToDeath(bluetoothHciDeathRecipient, 0);
@@ -127,8 +154,13 @@
 
   // Block allows allocation of a variable that might be bypassed by goto.
   {
-    android::sp<IBluetoothHciCallbacks> callbacks = new BluetoothHciCallbacks();
-    btHci->initialize(callbacks);
+    android::sp<V1_1::IBluetoothHciCallbacks> callbacks =
+        new BluetoothHciCallbacks();
+    if (btHci_1_1 != nullptr) {
+      btHci_1_1->initialize_1_1(callbacks);
+    } else {
+      btHci->initialize(callbacks);
+    }
   }
 }
 
@@ -138,9 +170,12 @@
     if (!death_unlink.isOk()) {
       LOG_ERROR(LOG_TAG, "%s: Error unlinking death recipient from the Bluetooth HAL", __func__);
     }
+    auto close_status = btHci->close();
+    if (!close_status.isOk()) {
+      LOG_ERROR(LOG_TAG, "%s: Error closing the Bluetooth HAL", __func__);
+    }
+    btHci = nullptr;
   }
-  btHci->close();
-  btHci = nullptr;
 }
 
 void hci_transmit(BT_HDR* packet) {
@@ -158,6 +193,13 @@
     case MSG_STACK_TO_HC_HCI_SCO:
       btHci->sendScoData(data);
       break;
+    case MSG_STACK_TO_HC_HCI_ISO:
+      if (btHci_1_1 != nullptr) {
+        btHci_1_1->sendIsoData(data);
+      } else {
+        LOG_ERROR(LOG_TAG, "ISO is not supported in HAL v1.0");
+      }
+      break;
     default:
       LOG_ERROR(LOG_TAG, "Unknown packet type (%d)", event);
       break;
diff --git a/hci/src/packet_fragmenter.cc b/hci/src/packet_fragmenter.cc
index 08de402..4c235ae 100644
--- a/hci/src/packet_fragmenter.cc
+++ b/hci/src/packet_fragmenter.cc
@@ -121,7 +121,7 @@
   return (UINT16_MAX - a) < b;
 }
 
-static void reassemble_and_dispatch(UNUSED_ATTR BT_HDR* packet) {
+static void reassemble_and_dispatch(BT_HDR* packet) {
   if ((packet->event & MSG_EVT_MASK) == MSG_HC_TO_STACK_HCI_ACL) {
     uint8_t* stream = packet->data;
     uint16_t handle;
@@ -225,14 +225,13 @@
       packet->offset = HCI_ACL_PREAMBLE_SIZE;
       uint16_t projected_offset =
           partial_packet->offset + (packet->len - HCI_ACL_PREAMBLE_SIZE);
-      if (projected_offset >
-          partial_packet->len) {  // len stores the expected length
+      if ((packet->len - packet->offset) >
+          (partial_packet->len - partial_packet->offset)) {
         LOG_WARN(LOG_TAG,
                  "%s got packet which would exceed expected length of %d. "
                  "Truncating.",
                  __func__, partial_packet->len);
-        packet->len =
-            (partial_packet->len - partial_packet->offset) + packet->offset;
+        packet->len = (partial_packet->len - partial_packet->offset) + packet->offset;
         projected_offset = partial_packet->len;
       }
 
diff --git a/hci/test/hci_layer_test.cc b/hci/test/hci_layer_test.cc
new file mode 100644
index 0000000..6855fea
--- /dev/null
+++ b/hci/test/hci_layer_test.cc
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <base/logging.h>
+#include <gtest/gtest.h>
+#include <stdint.h>
+
+#include "common/message_loop_thread.h"
+#include "hci/src/hci_layer.cc"
+#include "hci_internals.h"
+#include "osi/include/allocator.h"
+#include "osi/include/osi.h"
+#include "osi/test/AllocationTestHarness.h"
+#include "osi/test/test_stubs.h"
+#include "stack/include/bt_types.h"
+#include "stack/include/hcidefs.h"
+
+extern void allocation_tracker_uninit(void);
+
+allocator_t buffer_allocator_ = {
+    .alloc = osi_malloc,
+    .free = osi_free,
+};
+
+void monitor_socket(int ctrl_fd, int fd) {
+  LOG(INFO) << __func__ << " UNIMPLEMENTED";
+}
+void hci_initialize() { LOG(INFO) << __func__ << " UNIMPLEMENTED"; }
+void hci_close() { LOG(INFO) << __func__ << " UNIMPLEMENTED"; }
+void hci_transmit(BT_HDR* packet) { LOG(INFO) << __func__ << " UNIMPLEMENTED"; }
+int hci_open_firmware_log_file() { return INVALID_FD; }
+void hci_close_firmware_log_file(int fd) {}
+void hci_log_firmware_debug_packet(int fd, BT_HDR* packet) {}
+const allocator_t* buffer_allocator_get_interface() {
+  return &buffer_allocator_;
+}
+
+/**
+ * Test class to test selected functionality in hci/src/hci_layer.cc
+ */
+class HciLayerTest : public AllocationTestHarness {
+ protected:
+  void SetUp() override {
+    AllocationTestHarness::SetUp();
+    // Disable our allocation tracker to allow ASAN full range
+    allocation_tracker_uninit();
+    commands_pending_response = list_new(NULL);
+    buffer_allocator = &buffer_allocator_;
+  }
+
+  void TearDown() override {
+    list_free(commands_pending_response);
+    AllocationTestHarness::TearDown();
+  }
+
+  BT_HDR* AllocateHciEventPacket(size_t packet_length) const {
+    return AllocatePacket(packet_length, MSG_HC_TO_STACK_HCI_EVT);
+  }
+
+  uint8_t* GetPayloadPointer(BT_HDR* packet) const {
+    return static_cast<uint8_t*>(packet->data);
+  }
+
+ private:
+  BT_HDR* AllocatePacket(size_t packet_length, uint16_t event) const {
+    BT_HDR* packet =
+        static_cast<BT_HDR*>(osi_calloc(sizeof(BT_HDR) + packet_length));
+    packet->offset = 0;
+    packet->len = packet_length;
+    packet->layer_specific = 0;
+    packet->event = MSG_HC_TO_STACK_HCI_EVT;
+    return packet;
+  }
+};
+
+TEST_F(HciLayerTest, FilterIncomingEvent) {
+  {
+    BT_HDR* packet = AllocateHciEventPacket(3);
+
+    auto p = GetPayloadPointer(packet);
+    *p++ = HCI_COMMAND_STATUS_EVT;
+    *p++ = 0x0;  // length
+
+    CHECK(filter_incoming_event(packet));
+  }
+
+  {
+    BT_HDR* packet = AllocateHciEventPacket(3);
+
+    auto p = GetPayloadPointer(packet);
+    *p++ = HCI_COMMAND_STATUS_EVT;
+    *p++ = 0x1;  // length
+    *p++ = 0xff;
+
+    CHECK(filter_incoming_event(packet));
+  }
+
+  {
+    BT_HDR* packet = AllocateHciEventPacket(6);
+
+    auto p = GetPayloadPointer(packet);
+    *p++ = HCI_COMMAND_STATUS_EVT;
+    *p++ = 0x04;  // length
+    *p++ = 0x00;  // status
+    *p++ = 0x01;  // credits
+    *p++ = 0x34;  // opcode0
+    *p++ = 0x12;  // opcode1
+
+    CHECK(filter_incoming_event(packet));
+  }
+}
diff --git a/hci/test/other_stack_stub.cc b/hci/test/other_stack_stub.cc
new file mode 100644
index 0000000..4271fc0
--- /dev/null
+++ b/hci/test/other_stack_stub.cc
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Gabeldorsche related legacy-only-stack-side expansion and support code.
+ */
+#include "base/bind.h"
+#include "btcore/include/module.h"  // base::OnceClosure
+#include "hci/include/btsnoop.h"
+#include "hci/include/hci_layer.h"
+
+const btsnoop_t* btsnoop_get_interface() { return nullptr; }
+const packet_fragmenter_t* packet_fragmenter_get_interface() { return nullptr; }
+base::MessageLoop* get_main_message_loop() { return nullptr; }
+
+namespace bluetooth {
+namespace bqr {
+
+void DumpLmpLlMessage(uint8_t length, uint8_t* p_lmp_ll_message_event) {}
+void DumpBtScheduling(unsigned char, unsigned char*) {}
+
+}  // namespace bqr
+
+namespace shim {
+
+bool is_gd_shim_enabled() { return false; }
+bool is_gd_stack_started_up() { return false; }
+void Post(base::OnceClosure task) {}
+const hci_t* hci_layer_get_interface() { return nullptr; }
+
+}  // namespace shim
+}  // namespace bluetooth
diff --git a/hci/test/packet_fragmenter_host_test.cc b/hci/test/packet_fragmenter_host_test.cc
new file mode 100644
index 0000000..36f1e7b
--- /dev/null
+++ b/hci/test/packet_fragmenter_host_test.cc
@@ -0,0 +1,421 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <base/logging.h>
+#include <gtest/gtest.h>
+#include <cstdint>
+#include <queue>
+
+#include "hci/src/packet_fragmenter.cc"
+#include "osi/test/AllocationTestHarness.h"
+
+extern void allocation_tracker_uninit(void);
+
+enum kPacketOrder {
+  kStart = 1,
+  kContinuation = 2,
+};
+
+struct AclPacketHeader {
+  struct {
+    uint16_t handle : 12;
+    uint16_t continuation : 1;
+    uint16_t start : 1;
+    uint16_t reserved : 2;
+  } s;
+  uint16_t length;
+
+  uint16_t GetRawHandle() const { return *(uint16_t*)(this); }
+
+  uint16_t GetHandle() const { return s.handle; }
+  uint16_t GetLength() const { return length; }
+} __attribute__((packed));
+
+struct L2capPacketHeader {
+  uint16_t length;
+  uint16_t cid;
+} __attribute__((packed));
+
+struct AclL2capPacketHeader {
+  struct AclPacketHeader acl_header;
+  struct L2capPacketHeader l2cap_header;
+} __attribute__((packed));
+
+namespace {
+
+constexpr uint16_t kHandle = 0x123;
+constexpr uint16_t kCid = 0x4567;
+constexpr uint16_t kMaxPacketSize = BT_DEFAULT_BUFFER_SIZE - sizeof(BT_HDR) -
+                                    L2CAP_HEADER_SIZE - HCI_ACL_PREAMBLE_SIZE;
+constexpr size_t kTypicalPacketSizes[] = {
+    1, 2, 3, 4, 8, 16, 32, 64, 127, 128, 129, 256, 1024, 2048, kMaxPacketSize};
+constexpr size_t kNumberOfTypicalPacketSizes =
+    sizeof(kTypicalPacketSizes) / sizeof(kTypicalPacketSizes[0]);
+
+void FreeBuffer(BT_HDR* bt_hdr) { osi_free(bt_hdr); }
+
+struct TestMutables {
+  struct {
+    int access_count_{0};
+  } fragmented;
+  struct {
+    int access_count_{0};
+    std::queue<std::unique_ptr<BT_HDR, decltype(&FreeBuffer)>> queue;
+  } reassembled;
+  struct {
+    int access_count_{0};
+  } transmit_finished;
+};
+
+TestMutables test_state_;
+
+void OnFragmented(BT_HDR* packet, bool send_transmit_finished) {
+  test_state_.fragmented.access_count_++;
+}
+
+void OnReassembled(BT_HDR* packet) {
+  test_state_.reassembled.access_count_++;
+  test_state_.reassembled.queue.push(
+      std::unique_ptr<BT_HDR, decltype(&FreeBuffer)>(packet, &FreeBuffer));
+}
+
+void OnTransmitFinished(BT_HDR* packet, bool all_fragments_sent) {
+  test_state_.transmit_finished.access_count_++;
+}
+
+packet_fragmenter_callbacks_t result_callbacks = {
+    .fragmented = OnFragmented,
+    .reassembled = OnReassembled,
+    .transmit_finished = OnTransmitFinished,
+};
+
+AclPacketHeader* AclHeader(BT_HDR* packet) {
+  return (AclPacketHeader*)packet->data;
+}
+L2capPacketHeader* L2capHeader(BT_HDR* packet) {
+  return &((AclL2capPacketHeader*)packet->data)->l2cap_header;
+}
+
+uint8_t* Data(BT_HDR* packet) {
+  AclPacketHeader* acl_header =
+      reinterpret_cast<AclPacketHeader*>(packet->data);
+  return acl_header->s.start
+             ? (uint8_t*)(packet->data + sizeof(AclL2capPacketHeader))
+             : (uint8_t*)(packet->data + sizeof(AclPacketHeader));
+}
+
+}  // namespace
+
+// Needed for linkage
+const controller_t* controller_get_interface() { return nullptr; }
+
+/**
+ * Test class to test selected functionality in hci/src/hci_layer.cc
+ */
+class HciPacketFragmenterTest : public AllocationTestHarness {
+ protected:
+  void SetUp() override {
+    AllocationTestHarness::SetUp();
+    // Disable our allocation tracker to allow ASAN full range
+    allocation_tracker_uninit();
+    packet_fragmenter_ = packet_fragmenter_get_interface();
+    packet_fragmenter_->init(&result_callbacks);
+    test_state_ = TestMutables();
+  }
+
+  void TearDown() override {
+    FlushPartialPackets();
+    while (!test_state_.reassembled.queue.empty()) {
+      test_state_.reassembled.queue.pop();
+    }
+    packet_fragmenter_->cleanup();
+    AllocationTestHarness::TearDown();
+  }
+  const packet_fragmenter_t* packet_fragmenter_;
+
+  // Start acl packet
+  BT_HDR* AllocateL2capPacket(size_t l2cap_length,
+                              const std::vector<uint8_t> data) const {
+    auto packet =
+        AllocateAclPacket(data.size() + sizeof(L2capPacketHeader), kStart);
+    L2capHeader(packet)->length = l2cap_length;
+    L2capHeader(packet)->cid = kCid;
+    std::copy(data.cbegin(), data.cend(), Data(packet));
+    return packet;
+  }
+
+  // Continuation acl packet
+  BT_HDR* AllocateL2capPacket(const std::vector<uint8_t> data) const {
+    auto packet = AllocateAclPacket(data.size(), kContinuation);
+    std::copy(data.cbegin(), data.cend(), Data(packet));
+    return packet;
+  }
+
+  const std::vector<uint8_t> CreateData(size_t size) const {
+    CHECK(size > 0);
+    std::vector<uint8_t> v(size);
+    uint8_t sum = 0;
+    for (size_t s = 0; s < size; s++) {
+      sum += v[s] = s;
+    }
+    v[0] = (~sum + 1);  // First byte has sum complement
+    return v;
+  }
+
+  // Verify packet integrity
+  bool VerifyData(const uint8_t* data, size_t size) const {
+    CHECK(size > 0);
+    uint8_t sum = 0;
+    for (size_t s = 0; s < size; s++) {
+      sum += data[s];
+    }
+    return sum == 0;
+  }
+
+ private:
+  BT_HDR* AllocateAclPacket(size_t acl_length,
+                            kPacketOrder packet_order) const {
+    BT_HDR* packet = AllocatePacket(sizeof(AclPacketHeader) + acl_length,
+                                    MSG_HC_TO_STACK_HCI_ACL);
+    AclHeader(packet)->s.handle = kHandle;
+    AclHeader(packet)->length = acl_length;
+    switch (packet_order) {
+      case kStart:
+        AclHeader(packet)->s.start = 1;
+        break;
+      case kContinuation:
+        AclHeader(packet)->s.continuation = 1;
+        break;
+    }
+    return packet;
+  }
+
+  BT_HDR* AllocatePacket(size_t packet_length, uint16_t event_mask) const {
+    BT_HDR* packet =
+        static_cast<BT_HDR*>(osi_calloc(sizeof(BT_HDR) + packet_length));
+    packet->event = event_mask;
+    packet->len = static_cast<uint16_t>(packet_length);
+    return packet;
+  }
+
+  void FlushPartialPackets() const {
+    while (!partial_packets.empty()) {
+      BT_HDR* partial_packet = partial_packets.at(kHandle);
+      partial_packets.erase(kHandle);
+      osi_free(partial_packet);
+    }
+  }
+};
+
+TEST_F(HciPacketFragmenterTest, TestStruct_Handle) {
+  AclPacketHeader acl_header;
+  memset(&acl_header, 0, sizeof(acl_header));
+
+  for (uint16_t h = 0; h < UINT16_MAX; h++) {
+    acl_header.s.handle = h;
+    CHECK(acl_header.GetHandle() == (h & HANDLE_MASK));
+    CHECK(acl_header.s.continuation == 0);
+    CHECK(acl_header.s.start == 0);
+    CHECK(acl_header.s.reserved == 0);
+
+    CHECK((acl_header.GetRawHandle() & HANDLE_MASK) == (h & HANDLE_MASK));
+    GET_BOUNDARY_FLAG(acl_header.GetRawHandle() == 0);
+  }
+}
+
+TEST_F(HciPacketFragmenterTest, TestStruct_Continuation) {
+  AclPacketHeader acl_header;
+  memset(&acl_header, 0, sizeof(acl_header));
+
+  for (uint16_t h = 0; h < UINT16_MAX; h++) {
+    acl_header.s.continuation = h;
+    CHECK(acl_header.GetHandle() == 0);
+    CHECK(acl_header.s.continuation == (h & 0x1));
+    CHECK(acl_header.s.start == 0);
+    CHECK(acl_header.s.reserved == 0);
+
+    CHECK((acl_header.GetRawHandle() & HANDLE_MASK) == 0);
+    GET_BOUNDARY_FLAG(acl_header.GetRawHandle() == (h & 0x3));
+  }
+}
+
+TEST_F(HciPacketFragmenterTest, TestStruct_Start) {
+  AclPacketHeader acl_header;
+  memset(&acl_header, 0, sizeof(acl_header));
+
+  for (uint16_t h = 0; h < UINT16_MAX; h++) {
+    acl_header.s.start = h;
+    CHECK(acl_header.GetHandle() == 0);
+    CHECK(acl_header.s.continuation == 0);
+    CHECK(acl_header.s.start == (h & 0x1));
+    CHECK(acl_header.s.reserved == 0);
+
+    CHECK((acl_header.GetRawHandle() & HANDLE_MASK) == 0);
+    GET_BOUNDARY_FLAG(acl_header.GetRawHandle() == (h & 0x3));
+  }
+}
+
+TEST_F(HciPacketFragmenterTest, TestStruct_Reserved) {
+  AclPacketHeader acl_header;
+  memset(&acl_header, 0, sizeof(acl_header));
+
+  for (uint16_t h = 0; h < UINT16_MAX; h++) {
+    acl_header.s.reserved = h;
+    CHECK(acl_header.GetHandle() == 0);
+    CHECK(acl_header.s.continuation == 0);
+    CHECK(acl_header.s.start == 0);
+    CHECK(acl_header.s.reserved == (h & 0x3));
+  }
+}
+TEST_F(HciPacketFragmenterTest, CreateAndVerifyPackets) {
+  const size_t size_check[] = {1,  2,   3,   4,   8,   16,   32,
+                               64, 127, 128, 129, 256, 1024, 0xfff0};
+  const std::vector<size_t> sizes(
+      size_check, size_check + sizeof(size_check) / sizeof(size_check[0]));
+
+  for (const auto packet_size : sizes) {
+    const std::vector<uint8_t> data = CreateData(packet_size);
+    uint8_t buf[packet_size];
+    std::copy(data.cbegin(), data.cend(), buf);
+    CHECK(VerifyData(buf, packet_size));
+  }
+}
+
+TEST_F(HciPacketFragmenterTest, OnePacket_Immediate) {
+  const std::vector<size_t> sizes(
+      kTypicalPacketSizes, kTypicalPacketSizes + kNumberOfTypicalPacketSizes);
+
+  int reassembled_access_count = 0;
+  for (const auto packet_size : sizes) {
+    const std::vector<uint8_t> data = CreateData(packet_size);
+    reassemble_and_dispatch(AllocateL2capPacket(data.size(), data));
+
+    CHECK(partial_packets.size() == 0);
+    CHECK(test_state_.reassembled.access_count_ == ++reassembled_access_count);
+    auto packet = std::move(test_state_.reassembled.queue.front());
+    test_state_.reassembled.queue.pop();
+    CHECK(VerifyData(Data(packet.get()), packet_size));
+  }
+}
+
+TEST_F(HciPacketFragmenterTest, OnePacket_ImmediateTooBig) {
+  const size_t packet_size = kMaxPacketSize + 1;
+  const std::vector<uint8_t> data = CreateData(packet_size);
+  reassemble_and_dispatch(AllocateL2capPacket(data.size(), data));
+
+  CHECK(partial_packets.size() == 0);
+  CHECK(test_state_.reassembled.access_count_ == 0);
+}
+
+TEST_F(HciPacketFragmenterTest, ThreePackets_Immediate) {
+  const size_t packet_size = 512;
+  const std::vector<uint8_t> data = CreateData(packet_size);
+  reassemble_and_dispatch(AllocateL2capPacket(data.size(), data));
+  reassemble_and_dispatch(AllocateL2capPacket(data.size(), data));
+  reassemble_and_dispatch(AllocateL2capPacket(data.size(), data));
+  CHECK(partial_packets.size() == 0);
+  CHECK(test_state_.reassembled.access_count_ == 3);
+}
+
+TEST_F(HciPacketFragmenterTest, OnePacket_SplitTwo) {
+  const std::vector<size_t> sizes(
+      kTypicalPacketSizes, kTypicalPacketSizes + kNumberOfTypicalPacketSizes);
+
+  int reassembled_access_count = 0;
+  for (auto packet_size : sizes) {
+    const std::vector<uint8_t> data = CreateData(packet_size);
+    const std::vector<uint8_t> part1(data.cbegin(),
+                                     data.cbegin() + packet_size / 2);
+    reassemble_and_dispatch(AllocateL2capPacket(data.size(), part1));
+
+    CHECK(partial_packets.size() == 1);
+    CHECK(test_state_.reassembled.access_count_ == reassembled_access_count);
+
+    const std::vector<uint8_t> part2(data.cbegin() + packet_size / 2,
+                                     data.cend());
+    reassemble_and_dispatch(AllocateL2capPacket(part2));
+
+    CHECK(partial_packets.size() == 0);
+    CHECK(test_state_.reassembled.access_count_ == ++reassembled_access_count);
+
+    auto packet = std::move(test_state_.reassembled.queue.front());
+    test_state_.reassembled.queue.pop();
+    CHECK(VerifyData(Data(packet.get()), packet_size));
+  }
+}
+
+TEST_F(HciPacketFragmenterTest, OnePacket_SplitALot) {
+  const size_t packet_size = 512;
+  const size_t stride = 2;
+
+  const std::vector<uint8_t> data = CreateData(packet_size);
+  const std::vector<uint8_t> first_part(data.cbegin(), data.cbegin() + stride);
+  reassemble_and_dispatch(AllocateL2capPacket(data.size(), first_part));
+  CHECK(partial_packets.size() == 1);
+
+  for (size_t i = 2; i < packet_size - stride; i += stride) {
+    const std::vector<uint8_t> middle_part(data.cbegin() + i,
+                                           data.cbegin() + i + stride);
+    reassemble_and_dispatch(AllocateL2capPacket(middle_part));
+  }
+  CHECK(partial_packets.size() == 1);
+  CHECK(test_state_.reassembled.access_count_ == 0);
+
+  const std::vector<uint8_t> last_part(data.cbegin() + packet_size - stride,
+                                       data.cend());
+  reassemble_and_dispatch(AllocateL2capPacket(last_part));
+
+  CHECK(partial_packets.size() == 0);
+  CHECK(test_state_.reassembled.access_count_ == 1);
+  auto packet = std::move(test_state_.reassembled.queue.front());
+  CHECK(VerifyData(Data(packet.get()), packet_size));
+}
+
+TEST_F(HciPacketFragmenterTest, TwoPacket_InvalidLength) {
+  const size_t packet_size = UINT16_MAX;
+  const std::vector<uint8_t> data = CreateData(packet_size);
+  const std::vector<uint8_t> first_part(data.cbegin(),
+                                        data.cbegin() + packet_size / 2);
+  reassemble_and_dispatch(AllocateL2capPacket(data.size(), first_part));
+
+  CHECK(partial_packets.size() == 0);
+  CHECK(test_state_.reassembled.access_count_ == 0);
+
+  const std::vector<uint8_t> second_part(data.cbegin() + packet_size / 2,
+                                         data.cend());
+  reassemble_and_dispatch(AllocateL2capPacket(second_part));
+
+  CHECK(partial_packets.size() == 0);
+  CHECK(test_state_.reassembled.access_count_ == 0);
+}
+
+TEST_F(HciPacketFragmenterTest, TwoPacket_HugeBogusSecond) {
+  const size_t packet_size = kMaxPacketSize;
+  const std::vector<uint8_t> data = CreateData(UINT16_MAX);
+  const std::vector<uint8_t> first_part(data.cbegin(),
+                                        data.cbegin() + packet_size - 1);
+  reassemble_and_dispatch(AllocateL2capPacket(packet_size, first_part));
+
+  CHECK(partial_packets.size() == 1);
+  CHECK(test_state_.reassembled.access_count_ == 0);
+
+  const std::vector<uint8_t> second_part(data.cbegin() + packet_size - 1,
+                                         data.cend());
+  reassemble_and_dispatch(AllocateL2capPacket(second_part));
+
+  CHECK(partial_packets.size() == 0);
+  CHECK(test_state_.reassembled.access_count_ == 1);
+}
diff --git a/hci/test/packet_fragmenter_test.cc b/hci/test/packet_fragmenter_test.cc
index 483a66d..ca83dfa 100644
--- a/hci/test/packet_fragmenter_test.cc
+++ b/hci/test/packet_fragmenter_test.cc
@@ -291,7 +291,7 @@
 
 class PacketFragmenterTest : public AllocationTestHarness {
  protected:
-  virtual void SetUp() {
+  void SetUp() override {
     AllocationTestHarness::SetUp();
     fragmenter =
         packet_fragmenter_get_test_interface(&controller, &allocator_malloc);
@@ -309,7 +309,7 @@
     fragmenter->init(&callbacks);
   }
 
-  virtual void TearDown() {
+  void TearDown() override {
     fragmenter->cleanup();
     AllocationTestHarness::TearDown();
   }
diff --git a/include/Android.bp b/include/Android.bp
index 01a01b5..1f35206 100644
--- a/include/Android.bp
+++ b/include/Android.bp
@@ -1,7 +1,6 @@
 cc_library_headers {
     name: "avrcp_headers",
     defaults: ["libchrome_support_defaults"],
-    include_dirs: ["system/bt/internal_include"],
     export_include_dirs: ["./hardware/avrcp/"],
     header_libs: ["internal_include_headers"],
     export_header_lib_headers: ["internal_include_headers"],
diff --git a/include/hardware/bluetooth.h b/include/hardware/bluetooth.h
index ae58ac2..4251005 100644
--- a/include/hardware/bluetooth.h
+++ b/include/hardware/bluetooth.h
@@ -48,6 +48,7 @@
 #define BT_PROFILE_AV_RC_ID "avrcp"
 #define BT_PROFILE_AV_RC_CTRL_ID "avrcp_ctrl"
 #define BT_PROFILE_HEARING_AID_ID "hearing_aid"
+#define BT_KEYSTORE_ID "bluetooth_keystore"
 
 /** Bluetooth Device Name */
 typedef struct { uint8_t name[249]; } __attribute__((packed)) bt_bdname_t;
@@ -470,9 +471,12 @@
    * restricted mode, bonds that are created are marked as restricted in the
    * config file. These devices are deleted upon leaving restricted mode.
    * The |is_niap_mode| flag inits the adapter in NIAP mode.
+   * The |config_compare_result| flag show the config checksum check result if
+   * is in NIAP mode.
    * The |is_atv| flag indicates whether the local device is an Android TV
    */
-  int (*init)(bt_callbacks_t* callbacks, bool guest_mode, bool is_niap_mode, bool is_atv);
+  int (*init)(bt_callbacks_t* callbacks, bool guest_mode, bool is_niap_mode,
+              int config_compare_result, bool is_atv);
 
   /** Enable Bluetooth. */
   int (*enable)();
@@ -625,6 +629,14 @@
    * @return a string of uint8_t that is unique to this MAC address
    */
   std::string (*obfuscate_address)(const RawAddress& address);
+
+  /**
+   * Get an incremental id for as primary key for Bluetooth metric and log
+   *
+   * @param address Bluetooth MAC address of Bluetooth device
+   * @return int incremental Bluetooth id
+   */
+  int (*get_metric_id)(const RawAddress& address);
 } bt_interface_t;
 
 #define BLUETOOTH_INTERFACE_STRING "bluetoothInterface"
diff --git a/include/hardware/bt_av.h b/include/hardware/bt_av.h
index d38beaa..c4c0ecc 100644
--- a/include/hardware/bt_av.h
+++ b/include/hardware/bt_av.h
@@ -266,6 +266,13 @@
                                                 uint32_t sample_rate,
                                                 uint8_t channel_count);
 
+/** Callback for querying whether the mandatory codec is more preferred.
+ *  Used only for the A2DP Source interface.
+ *  Return true if optional codecs are not preferred.
+ */
+typedef bool (*btav_mandatory_codec_preferred_callback)(
+    const RawAddress& bd_addr);
+
 /** BT-AV A2DP Source callback structure. */
 typedef struct {
   /** set to sizeof(btav_source_callbacks_t) */
@@ -273,6 +280,7 @@
   btav_connection_state_callback connection_state_cb;
   btav_audio_state_callback audio_state_cb;
   btav_audio_source_config_callback audio_config_cb;
+  btav_mandatory_codec_preferred_callback mandatory_codec_preferred_cb;
 } btav_source_callbacks_t;
 
 /** BT-AV A2DP Sink callback structure. */
@@ -303,9 +311,10 @@
   /**
    * Register the BtAv callbacks.
    */
-  bt_status_t (*init)(btav_source_callbacks_t* callbacks,
-                      int max_connected_audio_devices,
-                      std::vector<btav_a2dp_codec_config_t> codec_priorities);
+  bt_status_t (*init)(
+      btav_source_callbacks_t* callbacks, int max_connected_audio_devices,
+      const std::vector<btav_a2dp_codec_config_t>& codec_priorities,
+      const std::vector<btav_a2dp_codec_config_t>& offloading_preference);
 
   /** connect to headset */
   bt_status_t (*connect)(const RawAddress& bd_addr);
@@ -353,6 +362,9 @@
 
   /** Sets the audio track gain. */
   void (*set_audio_track_gain)(float gain);
+
+  /** sets the connected device as active */
+  bt_status_t (*set_active_device)(const RawAddress& bd_addr);
 } btav_sink_interface_t;
 
 __END_DECLS
diff --git a/include/hardware/bt_hf_client.h b/include/hardware/bt_hf_client.h
index a31de0f..d8f3a50 100644
--- a/include/hardware/bt_hf_client.h
+++ b/include/hardware/bt_hf_client.h
@@ -292,6 +292,12 @@
  */
 typedef void (*bthf_client_ring_indication_callback)(const RawAddress* bd_addr);
 
+/**
+ * Callback for sending unknown (vendor specific) event
+ */
+typedef void (*bthf_client_unknown_event_callback)(const RawAddress* bd_addr,
+                                                   const char* unknow_event);
+
 /** BT-HF callback structure. */
 typedef struct {
   /** set to sizeof(BtHfClientCallbacks) */
@@ -317,6 +323,7 @@
   bthf_client_in_band_ring_tone_callback in_band_ring_tone_cb;
   bthf_client_last_voice_tag_number_callback last_voice_tag_number_callback;
   bthf_client_ring_indication_callback ring_indication_cb;
+  bthf_client_unknown_event_callback unknown_event_cb;
 } bthf_client_callbacks_t;
 
 /** Represents the standard BT-HF interface. */
diff --git a/include/hardware/bt_keystore.h b/include/hardware/bt_keystore.h
new file mode 100644
index 0000000..5442c98
--- /dev/null
+++ b/include/hardware/bt_keystore.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace bluetooth {
+namespace bluetooth_keystore {
+
+class BluetoothKeystoreCallbacks {
+ public:
+  virtual ~BluetoothKeystoreCallbacks() = default;
+
+  /** Callback for key encrypt or remove key */
+  virtual void set_encrypt_key_or_remove_key(std::string prefix,
+                                             std::string encryptedString) = 0;
+
+  /** Callback for get key. */
+  virtual std::string get_key(std::string prefix) = 0;
+};
+
+class BluetoothKeystoreInterface {
+ public:
+  virtual ~BluetoothKeystoreInterface() = default;
+
+  /** Register the bluetooth keystore callbacks */
+  virtual void init(BluetoothKeystoreCallbacks* callbacks) = 0;
+
+  /** Interface for key encrypt or remove key */
+  virtual void set_encrypt_key_or_remove_key(std::string prefix,
+                                             std::string encryptedString) = 0;
+
+  /** Interface for get key. */
+  virtual std::string get_key(std::string prefix) = 0;
+
+  /** Interface for clear map. */
+  virtual void clear_map() = 0;
+};
+
+}  // namespace bluetooth_keystore
+}  // namespace bluetooth
diff --git a/include/hardware/bt_rc.h b/include/hardware/bt_rc.h
old mode 100644
new mode 100755
index dc46eca..a4e349d
--- a/include/hardware/bt_rc.h
+++ b/include/hardware/bt_rc.h
@@ -54,6 +54,7 @@
 #define BTRC_MEDIA_ATTR_ID_NUM_TRACKS 0x00000005
 #define BTRC_MEDIA_ATTR_ID_GENRE 0x00000006
 #define BTRC_MEDIA_ATTR_ID_PLAYING_TIME 0x00000007 /* in miliseconds */
+#define BTRC_MEDIA_ATTR_ID_COVER_ARTWORK_HANDLE 0x00000008
 
 /* Macros for folder types */
 #define BTRC_FOLDER_TYPE_MIXED 0x00
@@ -86,6 +87,7 @@
   BTRC_FEAT_METADATA = 0x01,        /* AVRCP 1.3 */
   BTRC_FEAT_ABSOLUTE_VOLUME = 0x02, /* Supports TG role and volume sync */
   BTRC_FEAT_BROWSE = 0x04, /* AVRCP 1.4 and up, with Browsing support */
+  BTRC_FEAT_COVER_ARTWORK = 0x8,    /* AVRCP 1.6 and up, Cover Art */
 } btrc_remote_features_t;
 
 typedef enum {
@@ -617,6 +619,11 @@
     const RawAddress& bd_addr, uint16_t id);
 typedef void (*btrc_ctrl_now_playing_contents_changed_callback)(
     const RawAddress& bd_addr);
+typedef void (*btrc_ctrl_available_player_changed_callback)(
+    const RawAddress& bd_addr);
+
+typedef void (*btrc_ctrl_get_cover_art_psm_callback)(const RawAddress& bd_addr,
+    const uint16_t psm);
 
 /** BT-RC Controller callback structure. */
 typedef struct {
@@ -643,6 +650,8 @@
   btrc_ctrl_addressed_player_changed_callback addressed_player_changed_cb;
   btrc_ctrl_now_playing_contents_changed_callback
       now_playing_contents_changed_cb;
+  btrc_ctrl_available_player_changed_callback available_player_changed_cb;
+  btrc_ctrl_get_cover_art_psm_callback get_cover_art_psm_cb;
 } btrc_ctrl_callbacks_t;
 
 /** Represents the standard BT-RC AVRCP Controller interface. */
@@ -672,6 +681,9 @@
   bt_status_t (*play_item_cmd)(const RawAddress& bd_addr, uint8_t scope,
                                uint8_t* uid, uint16_t uid_counter);
 
+  /** get the current track's media metadata */
+  bt_status_t (*get_current_metadata_cmd)(const RawAddress& bd_addr);
+
   /** get the playback state */
   bt_status_t (*get_playback_state_cmd)(const RawAddress& bd_addr);
 
diff --git a/internal_include/Android.bp b/internal_include/Android.bp
index 3ef202b..e550660 100644
--- a/internal_include/Android.bp
+++ b/internal_include/Android.bp
@@ -3,4 +3,4 @@
     export_include_dirs: ["./"],
     vendor_available: true,
     host_supported: true,
-}
\ No newline at end of file
+}
diff --git a/internal_include/bt_target.h b/internal_include/bt_target.h
index d122234..4aca868 100644
--- a/internal_include/bt_target.h
+++ b/internal_include/bt_target.h
@@ -534,7 +534,7 @@
 
 /* Whether link wants to be the master or the slave. */
 #ifndef L2CAP_DESIRED_LINK_ROLE
-#define L2CAP_DESIRED_LINK_ROLE HCI_ROLE_SLAVE
+#define L2CAP_DESIRED_LINK_ROLE HCI_ROLE_MASTER
 #endif
 
 /* Include Non-Flushable Packet Boundary Flag feature of Lisbon */
@@ -773,11 +773,6 @@
 #define SDP_SERVICE_NAME "Service Discovery"
 #endif
 
-/* The security level for BTM. */
-#ifndef SDP_SECURITY_LEVEL
-#define SDP_SECURITY_LEVEL BTM_SEC_NONE
-#endif
-
 /******************************************************************************
  *
  * RFCOMM
diff --git a/internal_include/stack_config.h b/internal_include/stack_config.h
index d623fa4..19968ca 100644
--- a/internal_include/stack_config.h
+++ b/internal_include/stack_config.h
@@ -20,7 +20,7 @@
 
 #include <stdbool.h>
 
-#include "module.h"
+#include "btcore/include/module.h"
 #include "osi/include/config.h"
 
 static const char STACK_CONFIG_MODULE[] = "stack_config_module";
diff --git a/main/Android.bp b/main/Android.bp
index f787e9e..90490ef 100644
--- a/main/Android.bp
+++ b/main/Android.bp
@@ -1,22 +1,61 @@
-
 // Bluetooth main HW module / shared library for target
 // ========================================================
-cc_library_shared {
-    name: "libbluetooth",
-    defaults: ["fluoride_defaults"],
-    header_libs: ["libbluetooth_headers"],
-    export_header_lib_headers: ["libbluetooth_headers"],
+filegroup {
+    name: "LibBluetoothSources",
     srcs: [
-        // platform specific
         "bte_conf.cc",
         "bte_init.cc",
         "bte_init_cpp_logging.cc",
         "bte_logmsg.cc",
         "bte_main.cc",
         "stack_config.cc",
+    ]
+}
+
+cc_library_static {
+    name: "libbte",
+    defaults: ["fluoride_defaults"],
+    srcs: [
+        ":LibBluetoothSources",
+        ":LibBluetoothShimSources",
     ],
     include_dirs: [
         "system/bt",
+        "system/bt/gd",
+        "system/bt/bta/include",
+        "system/bt/bta/sys",
+        "system/bt/bta/dm",
+        "system/bt/btcore/include",
+        "system/bt/internal_include",
+        "system/bt/stack/include",
+        "system/bt/stack/l2cap",
+        "system/bt/stack/a2dp",
+        "system/bt/stack/btm",
+        "system/bt/stack/avdt",
+        "system/bt/udrv/include",
+        "system/bt/btif/include",
+        "system/bt/btif/co",
+        "system/bt/hci/include",
+        "system/bt/vnd/include",
+        "system/bt/embdrv/sbc/encoder/include",
+        "system/bt/embdrv/sbc/decoder/include",
+        "system/bt/utils/include",
+        "system/security/keystore/include",
+        "hardware/interfaces/keymaster/4.0/support/include",
+    ],
+    generated_headers: [
+        "BluetoothGeneratedPackets_h",
+    ],
+    host_supported: true,
+}
+
+cc_library_shared {
+    name: "libbluetooth",
+    defaults: ["fluoride_defaults"],
+    header_libs: ["libbluetooth_headers"],
+    export_header_lib_headers: ["libbluetooth_headers"],
+    include_dirs: [
+        "system/bt",
         "system/bt/bta/include",
         "system/bt/bta/sys",
         "system/bt/bta/dm",
@@ -41,15 +80,14 @@
     logtags: ["../EventLogTags.logtags"],
     shared_libs: [
         "android.hardware.bluetooth@1.0",
+        "android.hardware.bluetooth@1.1",
         "android.hardware.bluetooth.a2dp@1.0",
         "android.hardware.bluetooth.audio@2.0",
-        "libaudioclient",
+        "libaaudio",
         "libcutils",
         "libdl",
         "libfmq",
         "libhidlbase",
-        "libhidltransport",
-        "libhwbinder",
         "liblog",
         "libprocessgroup",
         "libprotobuf-cpp-lite",
@@ -57,19 +95,15 @@
         "libtinyxml2",
         "libz",
         "libcrypto",
-        "android.hardware.keymaster@4.0",
-        "android.hardware.keymaster@3.0",
-        "libkeymaster4support",
-        "libkeystore_aidl",
-        "libkeystore_binder",
-        "libkeystore_parcelables",
     ],
     static_libs: [
+        "libbte",
         "libbt-sbc-decoder",
         "libbt-sbc-encoder",
         "libFraunhoferAAC",
         "libg722codec",
         "libudrv-uipc",
+        "libbluetooth_gd", // Gabeldorsche
     ],
     whole_static_libs: [
         "libbt-bta",
@@ -108,15 +142,13 @@
     defaults: ["fluoride_defaults"],
 
     srcs: [
-        "bte_conf.cc",
-        "bte_init.cc",
-        "bte_init_cpp_logging.cc",
-        "bte_logmsg.cc",
-        "bte_main.cc",
-        "stack_config.cc",
+        ":LibBluetoothSources",
+        ":LibBluetoothShimSources",
     ],
+    host_supported: true,
     include_dirs: [
         "system/bt",
+        "system/bt/gd",
         "system/bt/bta/include",
         "system/bt/btcore/include",
         "system/bt/btif/include",
@@ -125,7 +157,13 @@
         "system/bt/stack/include",
         "system/bt/utils/include",
     ],
+    generated_headers: [
+        "BluetoothGeneratedPackets_h",
+    ],
     cflags: [
         "-DBUILDCFG",
     ],
+    whole_static_libs: [
+        "libbluetooth_gd", // Gabeldorsche
+    ],
 }
diff --git a/main/bte_main.cc b/main/bte_main.cc
index 0498439..1628976 100644
--- a/main/bte_main.cc
+++ b/main/bte_main.cc
@@ -53,6 +53,8 @@
 #include "osi/include/future.h"
 #include "osi/include/log.h"
 #include "osi/include/osi.h"
+#include "shim/hci_layer.h"
+#include "shim/shim.h"
 #include "stack_config.h"
 
 /*******************************************************************************
@@ -155,8 +157,14 @@
 void bte_main_enable() {
   APPL_TRACE_DEBUG("%s", __func__);
 
-  module_start_up(get_module(BTSNOOP_MODULE));
-  module_start_up(get_module(HCI_MODULE));
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    LOG_INFO(LOG_TAG, "%s Gd shim module enabled", __func__);
+    module_start_up(get_module(GD_SHIM_MODULE));
+    module_start_up(get_module(GD_HCI_MODULE));
+  } else {
+    module_start_up(get_module(BTSNOOP_MODULE));
+    module_start_up(get_module(HCI_MODULE));
+  }
 
   BTU_StartUp();
 }
@@ -174,8 +182,14 @@
 void bte_main_disable(void) {
   APPL_TRACE_DEBUG("%s", __func__);
 
-  module_shut_down(get_module(HCI_MODULE));
-  module_shut_down(get_module(BTSNOOP_MODULE));
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    LOG_INFO(LOG_TAG, "%s Gd shim module enabled", __func__);
+    module_shut_down(get_module(GD_HCI_MODULE));
+    module_shut_down(get_module(GD_SHIM_MODULE));
+  } else {
+    module_shut_down(get_module(HCI_MODULE));
+    module_shut_down(get_module(BTSNOOP_MODULE));
+  }
 
   BTU_ShutDown();
 }
diff --git a/main/shim/Android.bp b/main/shim/Android.bp
new file mode 100644
index 0000000..9b21e8b
--- /dev/null
+++ b/main/shim/Android.bp
@@ -0,0 +1,16 @@
+filegroup {
+    name: "LibBluetoothShimSources",
+    srcs: [
+        "btm.cc",
+        "btm_api.cc",
+        "controller.cc",
+        "config.cc",
+        "dumpsys.cc",
+        "entry.cc",
+        "hci_layer.cc",
+        "l2c_api.cc",
+        "l2cap.cc",
+        "shim.cc",
+        "timer.cc",
+    ]
+}
diff --git a/main/shim/btm.cc b/main/shim/btm.cc
new file mode 100644
index 0000000..12d73e4
--- /dev/null
+++ b/main/shim/btm.cc
@@ -0,0 +1,830 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "bt_shim_btm"
+
+#include <algorithm>
+#include <cstddef>
+#include <cstdint>
+#include <cstring>
+#include <mutex>
+
+#include "main/shim/btm.h"
+#include "main/shim/controller.h"
+#include "main/shim/entry.h"
+#include "main/shim/shim.h"
+#include "osi/include/log.h"
+#include "stack/btm/btm_int_types.h"
+#include "types/class_of_device.h"
+#include "types/raw_address.h"
+
+#include "hci/le_advertising_manager.h"
+#include "hci/le_scanning_manager.h"
+#include "main/shim/helpers.h"
+#include "neighbor/connectability.h"
+#include "neighbor/discoverability.h"
+#include "neighbor/inquiry.h"
+#include "neighbor/name.h"
+#include "neighbor/page.h"
+#include "security/security_module.h"
+#include "shim/controller.h"
+
+extern tBTM_CB btm_cb;
+
+static constexpr size_t kRemoteDeviceNameLength = 248;
+
+static constexpr uint8_t kAdvDataInfoNotPresent = 0xff;
+static constexpr uint8_t kTxPowerInformationNotPresent = 0x7f;
+static constexpr uint8_t kNotPeriodicAdvertisement = 0x00;
+
+static constexpr bool kActiveScanning = true;
+static constexpr bool kPassiveScanning = false;
+
+using BtmRemoteDeviceName = tBTM_REMOTE_DEV_NAME;
+
+extern void btm_process_cancel_complete(uint8_t status, uint8_t mode);
+extern void btm_process_inq_complete(uint8_t status, uint8_t result_type);
+extern void btm_ble_process_adv_addr(RawAddress& raw_address,
+                                     uint8_t* address_type);
+extern void btm_ble_process_adv_pkt_cont(
+    uint16_t event_type, uint8_t address_type, const RawAddress& raw_address,
+    uint8_t primary_phy, uint8_t secondary_phy, uint8_t advertising_sid,
+    int8_t tx_power, int8_t rssi, uint16_t periodic_adv_int, uint8_t data_len,
+    uint8_t* data);
+
+extern void btm_api_process_inquiry_result(const RawAddress& raw_address,
+                                           uint8_t page_scan_rep_mode,
+                                           DEV_CLASS device_class,
+                                           uint16_t clock_offset);
+
+extern void btm_api_process_inquiry_result_with_rssi(RawAddress raw_address,
+                                                     uint8_t page_scan_rep_mode,
+                                                     DEV_CLASS device_class,
+                                                     uint16_t clock_offset,
+                                                     int8_t rssi);
+
+extern void btm_api_process_extended_inquiry_result(
+    RawAddress raw_address, uint8_t page_scan_rep_mode, DEV_CLASS device_class,
+    uint16_t clock_offset, int8_t rssi, const uint8_t* eir_data,
+    size_t eir_len);
+
+void bluetooth::shim::Btm::StartUp(bluetooth::shim::Btm* btm) {
+  CHECK(btm != nullptr);
+  std::unique_lock<std::mutex> lock(btm->sync_mutex_);
+  CHECK(btm->observing_timer_ == nullptr);
+  CHECK(btm->scanning_timer_ == nullptr);
+  btm->observing_timer_ = new bluetooth::shim::Timer("observing_timer");
+  btm->scanning_timer_ = new bluetooth::shim::Timer("scanning_timer");
+
+}
+
+void bluetooth::shim::Btm::ShutDown(bluetooth::shim::Btm* btm) {
+  CHECK(btm != nullptr);
+  std::unique_lock<std::mutex> lock(btm->sync_mutex_);
+  CHECK(btm->observing_timer_ != nullptr);
+  CHECK(btm->scanning_timer_ != nullptr);
+  delete btm->scanning_timer_;
+  delete btm->observing_timer_;
+  btm->scanning_timer_ = nullptr;
+  btm->observing_timer_ = nullptr;
+}
+
+void bluetooth::shim::Btm::OnInquiryResult(
+    bluetooth::hci::InquiryResultView view) {
+  for (auto& response : view.GetInquiryResults()) {
+    btm_api_process_inquiry_result(
+        RawAddress(response.bd_addr_.address),
+        static_cast<uint8_t>(response.page_scan_repetition_mode_),
+        response.class_of_device_.cod, response.clock_offset_);
+  }
+}
+
+void bluetooth::shim::Btm::OnInquiryResultWithRssi(
+    bluetooth::hci::InquiryResultWithRssiView view) {
+  for (auto& response : view.GetInquiryResults()) {
+    btm_api_process_inquiry_result_with_rssi(
+        RawAddress(response.address_.address),
+        static_cast<uint8_t>(response.page_scan_repetition_mode_),
+        response.class_of_device_.cod, response.clock_offset_, response.rssi_);
+  }
+}
+
+void bluetooth::shim::Btm::OnExtendedInquiryResult(
+    bluetooth::hci::ExtendedInquiryResultView view) {
+  constexpr size_t kMaxExtendedInquiryResponse = 240;
+  uint8_t gap_data_buffer[kMaxExtendedInquiryResponse];
+  uint8_t* data = nullptr;
+  size_t data_len = 0;
+
+  if (!view.GetExtendedInquiryResponse().empty()) {
+    bzero(gap_data_buffer, sizeof(gap_data_buffer));
+    uint8_t* p = gap_data_buffer;
+    for (auto gap_data : view.GetExtendedInquiryResponse()) {
+      *p++ = gap_data.data_.size() + sizeof(gap_data.data_type_);
+      *p++ = static_cast<uint8_t>(gap_data.data_type_);
+      p = (uint8_t*)memcpy(p, &gap_data.data_[0], gap_data.data_.size()) +
+          gap_data.data_.size();
+    }
+    data = gap_data_buffer;
+    data_len = p - data;
+  }
+
+  btm_api_process_extended_inquiry_result(
+      RawAddress(view.GetAddress().address),
+      static_cast<uint8_t>(view.GetPageScanRepetitionMode()),
+      view.GetClassOfDevice().cod, view.GetClockOffset(), view.GetRssi(), data,
+      data_len);
+}
+
+void bluetooth::shim::Btm::OnInquiryComplete(bluetooth::hci::ErrorCode status) {
+  limited_inquiry_active_ = false;
+  general_inquiry_active_ = false;
+  legacy_inquiry_complete_callback_((static_cast<uint16_t>(status) == 0)
+                                        ? (BTM_SUCCESS)
+                                        : (BTM_ERR_PROCESSING),
+                                    active_inquiry_mode_);
+
+  active_inquiry_mode_ = kInquiryModeOff;
+}
+
+bool bluetooth::shim::Btm::SetInquiryFilter(uint8_t mode, uint8_t type,
+                                            tBTM_INQ_FILT_COND data) {
+  switch (mode) {
+    case kInquiryModeOff:
+      break;
+    case kLimitedInquiryMode:
+      LOG_WARN(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+      break;
+    case kGeneralInquiryMode:
+      LOG_WARN(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+      break;
+    default:
+      LOG_WARN(LOG_TAG, "%s Unknown inquiry mode:%d", __func__, mode);
+      return false;
+  }
+  return true;
+}
+
+void bluetooth::shim::Btm::SetFilterInquiryOnAddress() {
+  LOG_WARN(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+}
+
+void bluetooth::shim::Btm::SetFilterInquiryOnDevice() {
+  LOG_WARN(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+}
+
+void bluetooth::shim::Btm::ClearInquiryFilter() {
+  LOG_WARN(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+}
+
+void bluetooth::shim::Btm::SetStandardInquiryResultMode() {
+  bluetooth::shim::GetInquiry()->SetStandardInquiryResultMode();
+}
+
+void bluetooth::shim::Btm::SetInquiryWithRssiResultMode() {
+  bluetooth::shim::GetInquiry()->SetInquiryWithRssiResultMode();
+}
+
+void bluetooth::shim::Btm::SetExtendedInquiryResultMode() {
+  bluetooth::shim::GetInquiry()->SetExtendedInquiryResultMode();
+}
+
+void bluetooth::shim::Btm::SetInterlacedInquiryScan() {
+  bluetooth::shim::GetInquiry()->SetInterlacedScan();
+}
+
+void bluetooth::shim::Btm::SetStandardInquiryScan() {
+  bluetooth::shim::GetInquiry()->SetStandardScan();
+}
+
+bool bluetooth::shim::Btm::IsInterlacedScanSupported() const {
+  return controller_get_interface()->supports_interlaced_inquiry_scan();
+}
+
+/**
+ * One shot inquiry
+ */
+bool bluetooth::shim::Btm::StartInquiry(
+    uint8_t mode, uint8_t duration, uint8_t max_responses,
+    LegacyInquiryCompleteCallback legacy_inquiry_complete_callback) {
+  switch (mode) {
+    case kInquiryModeOff:
+      LOG_DEBUG(LOG_TAG, "%s Stopping inquiry mode", __func__);
+      if (limited_inquiry_active_ || general_inquiry_active_) {
+        bluetooth::shim::GetInquiry()->StopInquiry();
+        limited_inquiry_active_ = false;
+        general_inquiry_active_ = false;
+      }
+      active_inquiry_mode_ = kInquiryModeOff;
+      break;
+
+    case kLimitedInquiryMode:
+    case kGeneralInquiryMode: {
+      if (mode == kLimitedInquiryMode) {
+        LOG_DEBUG(
+            LOG_TAG,
+            "%s Starting limited inquiry mode duration:%hhd max responses:%hhd",
+            __func__, duration, max_responses);
+        limited_inquiry_active_ = true;
+        bluetooth::shim::GetInquiry()->StartLimitedInquiry(duration,
+                                                           max_responses);
+        active_inquiry_mode_ = kLimitedInquiryMode;
+      } else {
+        LOG_DEBUG(
+            LOG_TAG,
+            "%s Starting general inquiry mode duration:%hhd max responses:%hhd",
+            __func__, duration, max_responses);
+        general_inquiry_active_ = true;
+        bluetooth::shim::GetInquiry()->StartGeneralInquiry(duration,
+                                                           max_responses);
+        legacy_inquiry_complete_callback_ = legacy_inquiry_complete_callback;
+      }
+    } break;
+
+    default:
+      LOG_WARN(LOG_TAG, "%s Unknown inquiry mode:%d", __func__, mode);
+      return false;
+  }
+  return true;
+}
+
+void bluetooth::shim::Btm::CancelInquiry() {
+  LOG_DEBUG(LOG_TAG, "%s", __func__);
+  if (limited_inquiry_active_ || general_inquiry_active_) {
+    bluetooth::shim::GetInquiry()->StopInquiry();
+    limited_inquiry_active_ = false;
+    general_inquiry_active_ = false;
+  }
+}
+
+bool bluetooth::shim::Btm::IsInquiryActive() const {
+  return IsGeneralInquiryActive() || IsLimitedInquiryActive();
+}
+
+bool bluetooth::shim::Btm::IsGeneralInquiryActive() const {
+  return general_inquiry_active_;
+}
+
+bool bluetooth::shim::Btm::IsLimitedInquiryActive() const {
+  return limited_inquiry_active_;
+}
+
+/**
+ * Periodic
+ */
+bool bluetooth::shim::Btm::StartPeriodicInquiry(
+    uint8_t mode, uint8_t duration, uint8_t max_responses, uint16_t max_delay,
+    uint16_t min_delay, tBTM_INQ_RESULTS_CB* p_results_cb) {
+  switch (mode) {
+    case kInquiryModeOff:
+      limited_periodic_inquiry_active_ = false;
+      general_periodic_inquiry_active_ = false;
+      bluetooth::shim::GetInquiry()->StopPeriodicInquiry();
+      break;
+
+    case kLimitedInquiryMode:
+    case kGeneralInquiryMode: {
+      if (mode == kLimitedInquiryMode) {
+        LOG_DEBUG(LOG_TAG, "%s Starting limited periodic inquiry mode",
+                  __func__);
+        limited_periodic_inquiry_active_ = true;
+        bluetooth::shim::GetInquiry()->StartLimitedPeriodicInquiry(
+            duration, max_responses, max_delay, min_delay);
+      } else {
+        LOG_DEBUG(LOG_TAG, "%s Starting general periodic inquiry mode",
+                  __func__);
+        general_periodic_inquiry_active_ = true;
+        bluetooth::shim::GetInquiry()->StartGeneralPeriodicInquiry(
+            duration, max_responses, max_delay, min_delay);
+      }
+    } break;
+
+    default:
+      LOG_WARN(LOG_TAG, "%s Unknown inquiry mode:%d", __func__, mode);
+      return false;
+  }
+  return true;
+}
+
+void bluetooth::shim::Btm::CancelPeriodicInquiry() {
+  limited_periodic_inquiry_active_ = false;
+  general_periodic_inquiry_active_ = false;
+  bluetooth::shim::GetInquiry()->StopPeriodicInquiry();
+}
+
+bool bluetooth::shim::Btm::IsGeneralPeriodicInquiryActive() const {
+  return general_periodic_inquiry_active_;
+}
+
+bool bluetooth::shim::Btm::IsLimitedPeriodicInquiryActive() const {
+  return limited_periodic_inquiry_active_;
+}
+
+/**
+ * Discoverability
+ */
+
+void bluetooth::shim::Btm::RegisterInquiryCallbacks() {
+  bluetooth::neighbor::InquiryCallbacks inquiry_callbacks;
+  inquiry_callbacks.result =
+      std::bind(&Btm::OnInquiryResult, this, std::placeholders::_1);
+  inquiry_callbacks.result_with_rssi =
+      std::bind(&Btm::OnInquiryResultWithRssi, this, std::placeholders::_1);
+  inquiry_callbacks.extended_result =
+      std::bind(&Btm::OnExtendedInquiryResult, this, std::placeholders::_1);
+  inquiry_callbacks.complete =
+      std::bind(&Btm::OnInquiryComplete, this, std::placeholders::_1);
+  bluetooth::shim::GetInquiry()->RegisterCallbacks(inquiry_callbacks);
+}
+
+bluetooth::neighbor::ScanParameters params_{
+    .interval = 0,
+    .window = 0,
+};
+
+void bluetooth::shim::Btm::SetClassicGeneralDiscoverability(uint16_t window,
+                                                            uint16_t interval) {
+  params_.window = window;
+  params_.interval = interval;
+
+  bluetooth::shim::GetInquiry()->SetScanActivity(params_);
+  bluetooth::shim::GetDiscoverability()->StartGeneralDiscoverability();
+}
+
+void bluetooth::shim::Btm::SetClassicLimitedDiscoverability(uint16_t window,
+                                                            uint16_t interval) {
+  params_.window = window;
+  params_.interval = interval;
+  bluetooth::shim::GetInquiry()->SetScanActivity(params_);
+  bluetooth::shim::GetDiscoverability()->StartLimitedDiscoverability();
+}
+
+void bluetooth::shim::Btm::SetClassicDiscoverabilityOff() {
+  bluetooth::shim::GetDiscoverability()->StopDiscoverability();
+}
+
+DiscoverabilityState bluetooth::shim::Btm::GetClassicDiscoverabilityState()
+    const {
+  DiscoverabilityState state{.mode = BTM_NON_DISCOVERABLE,
+                             .interval = params_.interval,
+                             .window = params_.window};
+
+  if (bluetooth::shim::GetDiscoverability()
+          ->IsGeneralDiscoverabilityEnabled()) {
+    state.mode = BTM_GENERAL_DISCOVERABLE;
+  } else if (bluetooth::shim::GetDiscoverability()
+                 ->IsLimitedDiscoverabilityEnabled()) {
+    state.mode = BTM_LIMITED_DISCOVERABLE;
+  }
+  return state;
+}
+
+void bluetooth::shim::Btm::SetLeGeneralDiscoverability() {
+  LOG_WARN(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+}
+
+void bluetooth::shim::Btm::SetLeLimitedDiscoverability() {
+  LOG_WARN(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+}
+
+void bluetooth::shim::Btm::SetLeDiscoverabilityOff() {
+  LOG_WARN(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+}
+
+DiscoverabilityState bluetooth::shim::Btm::GetLeDiscoverabilityState() const {
+  DiscoverabilityState state{
+      .mode = kDiscoverableModeOff,
+      .interval = 0,
+      .window = 0,
+  };
+  LOG_WARN(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  return state;
+}
+
+/**
+ * Connectability
+ */
+void bluetooth::shim::Btm::SetClassicConnectibleOn() {
+  bluetooth::shim::GetConnectability()->StartConnectability();
+}
+
+void bluetooth::shim::Btm::SetClassicConnectibleOff() {
+  bluetooth::shim::GetConnectability()->StopConnectability();
+}
+
+ConnectabilityState bluetooth::shim::Btm::GetClassicConnectabilityState()
+    const {
+  ConnectabilityState state{.interval = params_.interval,
+                            .window = params_.window};
+
+  if (bluetooth::shim::GetConnectability()->IsConnectable()) {
+    state.mode = BTM_CONNECTABLE;
+  } else {
+    state.mode = BTM_NON_CONNECTABLE;
+  }
+  return state;
+}
+
+void bluetooth::shim::Btm::SetInterlacedPageScan() {
+  bluetooth::shim::GetPage()->SetInterlacedScan();
+}
+
+void bluetooth::shim::Btm::SetStandardPageScan() {
+  bluetooth::shim::GetPage()->SetStandardScan();
+}
+
+void bluetooth::shim::Btm::SetLeConnectibleOn() {
+  LOG_WARN(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+}
+
+void bluetooth::shim::Btm::SetLeConnectibleOff() {
+  LOG_WARN(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+}
+
+ConnectabilityState bluetooth::shim::Btm::GetLeConnectabilityState() const {
+  ConnectabilityState state{
+      .mode = kConnectibleModeOff,
+      .interval = 0,
+      .window = 0,
+  };
+  LOG_WARN(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  return state;
+}
+
+bool bluetooth::shim::Btm::IsLeAclConnected(
+    const RawAddress& raw_address) const {
+  // TODO(cmanton) Check current acl's for this address and indicate if there is
+  // an LE option.  For now ignore and default to classic.
+  LOG_INFO(LOG_TAG, "%s Le acl connection check is temporarily unsupported",
+           __func__);
+  return false;
+}
+
+bluetooth::shim::BtmStatus bluetooth::shim::Btm::ReadClassicRemoteDeviceName(
+    const RawAddress& raw_address, tBTM_CMPL_CB* callback) {
+  if (!CheckClassicAclLink(raw_address)) {
+    return bluetooth::shim::BTM_UNKNOWN_ADDR;
+  }
+
+  if (!classic_read_remote_name_.Start(raw_address)) {
+    LOG_INFO(LOG_TAG, "%s Read remote name is currently busy address:%s",
+             __func__, raw_address.ToString().c_str());
+    return bluetooth::shim::BTM_BUSY;
+  }
+
+  LOG_DEBUG(LOG_TAG, "%s Start read name from address:%s", __func__,
+            raw_address.ToString().c_str());
+  bluetooth::shim::GetName()->ReadRemoteNameRequest(
+      hci::Address(raw_address.address), hci::PageScanRepetitionMode::R1,
+      0 /* clock_offset */, hci::ClockOffsetValid::INVALID,
+
+      base::Bind(
+          [](tBTM_CMPL_CB* callback, ReadRemoteName* classic_read_remote_name_,
+             hci::ErrorCode status, hci::Address address,
+             std::array<uint8_t, kRemoteDeviceNameLength> remote_name) {
+            RawAddress raw_address(address.address);
+
+            BtmRemoteDeviceName name{
+                .status = (static_cast<uint8_t>(status) == 0)
+                              ? (BTM_SUCCESS)
+                              : (BTM_BAD_VALUE_RET),
+                .bd_addr = raw_address,
+                .length = kRemoteDeviceNameLength,
+            };
+            std::copy(remote_name.begin(), remote_name.end(),
+                      name.remote_bd_name);
+            LOG_DEBUG(LOG_TAG, "%s Finish read name from address:%s name:%s",
+                      __func__, address.ToString().c_str(),
+                      name.remote_bd_name);
+            callback(&name);
+            classic_read_remote_name_->Stop();
+          },
+          callback, &classic_read_remote_name_),
+      bluetooth::shim::GetGdShimHandler());
+  return bluetooth::shim::BTM_CMD_STARTED;
+}
+
+bluetooth::shim::BtmStatus bluetooth::shim::Btm::ReadLeRemoteDeviceName(
+    const RawAddress& raw_address, tBTM_CMPL_CB* callback) {
+  if (!CheckLeAclLink(raw_address)) {
+    return bluetooth::shim::BTM_UNKNOWN_ADDR;
+  }
+
+  if (!le_read_remote_name_.Start(raw_address)) {
+    return bluetooth::shim::BTM_BUSY;
+  }
+
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s need access to GATT module", __func__);
+  return bluetooth::shim::BTM_UNKNOWN_ADDR;
+}
+
+bluetooth::shim::BtmStatus
+bluetooth::shim::Btm::CancelAllReadRemoteDeviceName() {
+  if (classic_read_remote_name_.IsInProgress() ||
+      le_read_remote_name_.IsInProgress()) {
+    if (classic_read_remote_name_.IsInProgress()) {
+      hci::Address address;
+      hci::Address::FromString(classic_read_remote_name_.AddressString(),
+                               address);
+
+      bluetooth::shim::GetName()->CancelRemoteNameRequest(
+          address,
+          common::BindOnce(
+              [](ReadRemoteName* classic_read_remote_name_,
+                 hci::ErrorCode status,
+                 hci::Address address) { classic_read_remote_name_->Stop(); },
+              &classic_read_remote_name_),
+          bluetooth::shim::GetGdShimHandler());
+    }
+    if (le_read_remote_name_.IsInProgress()) {
+      LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s need access to GATT module",
+               __func__);
+    }
+    return bluetooth::shim::BTM_UNKNOWN_ADDR;
+  }
+  LOG_WARN(LOG_TAG,
+           "%s Cancelling classic remote device name without one in progress",
+           __func__);
+  return bluetooth::shim::BTM_WRONG_MODE;
+}
+
+void bluetooth::shim::Btm::StartAdvertising() {
+  if (advertiser_id_ == hci::LeAdvertisingManager::kInvalidId) {
+    LOG_WARN(LOG_TAG,
+             "%s Already advertising; please stop prior to starting again",
+             __func__);
+    return;
+  }
+
+  hci::AdvertisingConfig config;
+  advertiser_id_ = bluetooth::shim::GetAdvertising()->CreateAdvertiser(
+      config, common::Bind([](hci::Address, hci::AddressType) { /*OnScan*/ }),
+      common::Bind([](hci::ErrorCode, uint8_t, uint8_t) { /*OnTerminated*/ }),
+      bluetooth::shim::GetGdShimHandler());
+  if (advertiser_id_ == hci::LeAdvertisingManager::kInvalidId) {
+    LOG_WARN(LOG_TAG, "%s Unable to start advertising", __func__);
+    return;
+  }
+  LOG_DEBUG(LOG_TAG, "%s Started advertising", __func__);
+}
+
+void bluetooth::shim::Btm::StopAdvertising() {
+  if (advertiser_id_ == hci::LeAdvertisingManager::kInvalidId) {
+    LOG_WARN(LOG_TAG, "%s No active advertising", __func__);
+    return;
+  }
+  bluetooth::shim::GetAdvertising()->RemoveAdvertiser(advertiser_id_);
+  advertiser_id_ = hci::LeAdvertisingManager::kInvalidId;
+  LOG_DEBUG(LOG_TAG, "%s Stopped advertising", __func__);
+}
+
+void bluetooth::shim::Btm::StartConnectability() { StartAdvertising(); }
+
+void bluetooth::shim::Btm::StopConnectability() { StopAdvertising(); }
+
+void bluetooth::shim::Btm::StartActiveScanning() {
+  StartScanning(kActiveScanning);
+}
+
+void bluetooth::shim::Btm::StopActiveScanning() {
+  bluetooth::shim::GetScanning()->StopScan(base::Bind([]() {}));
+}
+
+void bluetooth::shim::Btm::SetScanningTimer(uint64_t duration_ms,
+                                            std::function<void()> func) {
+  scanning_timer_->Set(duration_ms, func);
+}
+
+void bluetooth::shim::Btm::CancelScanningTimer() { scanning_timer_->Cancel(); }
+
+void bluetooth::shim::Btm::StartObserving() { StartScanning(kPassiveScanning); }
+
+void bluetooth::shim::Btm::StopObserving() { StopActiveScanning(); }
+
+void bluetooth::shim::Btm::SetObservingTimer(uint64_t duration_ms,
+                                             std::function<void()> func) {
+  observing_timer_->Set(duration_ms, func);
+}
+
+void bluetooth::shim::Btm::CancelObservingTimer() {
+  observing_timer_->Cancel();
+}
+
+namespace bluetooth {
+namespace hci {
+
+constexpr int kAdvertisingReportBufferSize = 1024;
+
+struct ExtendedEventTypeOptions {
+  bool connectable{false};
+  bool scannable{false};
+  bool directed{false};
+  bool scan_response{false};
+  bool legacy{false};
+  bool continuing{false};
+  bool truncated{false};
+};
+
+constexpr uint16_t kBleEventConnectableBit =
+    (0x0001 << 0);  // BLE_EVT_CONNECTABLE_BIT
+constexpr uint16_t kBleEventScannableBit =
+    (0x0001 << 1);  // BLE_EVT_SCANNABLE_BIT
+constexpr uint16_t kBleEventDirectedBit =
+    (0x0001 << 2);  // BLE_EVT_DIRECTED_BIT
+constexpr uint16_t kBleEventScanResponseBit =
+    (0x0001 << 3);  // BLE_EVT_SCAN_RESPONSE_BIT
+constexpr uint16_t kBleEventLegacyBit = (0x0001 << 4);  // BLE_EVT_LEGACY_BIT
+constexpr uint16_t kBleEventIncompleteContinuing = (0x0001 << 5);
+constexpr uint16_t kBleEventIncompleteTruncated = (0x0001 << 6);
+
+static void TransformToExtendedEventType(uint16_t* extended_event_type,
+                                         ExtendedEventTypeOptions o) {
+  ASSERT(extended_event_type != nullptr);
+  *extended_event_type = (o.connectable ? kBleEventConnectableBit : 0) |
+                         (o.scannable ? kBleEventScannableBit : 0) |
+                         (o.directed ? kBleEventDirectedBit : 0) |
+                         (o.scan_response ? kBleEventScanResponseBit : 0) |
+                         (o.legacy ? kBleEventLegacyBit : 0) |
+                         (o.continuing ? kBleEventIncompleteContinuing : 0) |
+                         (o.truncated ? kBleEventIncompleteTruncated : 0);
+}
+
+class BtmScanningCallbacks : public bluetooth::hci::LeScanningManagerCallbacks {
+ public:
+  virtual void on_advertisements(
+      std::vector<std::shared_ptr<LeReport>> reports) {
+    for (auto le_report : reports) {
+      uint8_t address_type = static_cast<uint8_t>(le_report->address_type_);
+      uint16_t extended_event_type = 0;
+      uint8_t* report_data = nullptr;
+      size_t report_len = 0;
+
+      uint8_t advertising_data_buffer[kAdvertisingReportBufferSize];
+      // Copy gap data, if any, into temporary buffer as payload for legacy
+      // stack.
+      if (!le_report->gap_data_.empty()) {
+        bzero(advertising_data_buffer, kAdvertisingReportBufferSize);
+        uint8_t* p = advertising_data_buffer;
+        for (auto gap_data : le_report->gap_data_) {
+          *p++ = gap_data.data_.size() + sizeof(gap_data.data_type_);
+          *p++ = static_cast<uint8_t>(gap_data.data_type_);
+          p = (uint8_t*)memcpy(p, &gap_data.data_[0], gap_data.data_.size()) +
+              gap_data.data_.size();
+        }
+        report_data = advertising_data_buffer;
+        report_len = p - report_data;
+      }
+
+      switch (le_report->GetReportType()) {
+        case hci::LeReport::ReportType::ADVERTISING_EVENT: {
+          switch (le_report->advertising_event_type_) {
+            case hci::AdvertisingEventType::ADV_IND:
+              TransformToExtendedEventType(
+                  &extended_event_type,
+                  {.connectable = true, .scannable = true, .legacy = true});
+              break;
+            case hci::AdvertisingEventType::ADV_DIRECT_IND:
+              TransformToExtendedEventType(
+                  &extended_event_type,
+                  {.connectable = true, .directed = true, .legacy = true});
+              break;
+            case hci::AdvertisingEventType::ADV_SCAN_IND:
+              TransformToExtendedEventType(&extended_event_type,
+                                           {.scannable = true, .legacy = true});
+              break;
+            case hci::AdvertisingEventType::ADV_NONCONN_IND:
+              TransformToExtendedEventType(&extended_event_type,
+                                           {.legacy = true});
+              break;
+            case hci::AdvertisingEventType::
+                ADV_DIRECT_IND_LOW:  // SCAN_RESPONSE
+              TransformToExtendedEventType(&extended_event_type,
+                                           {.connectable = true,
+                                            .scannable = true,
+                                            .scan_response = true,
+                                            .legacy = true});
+              break;
+            default:
+              LOG_WARN(
+                  LOG_TAG, "%s Unsupported event type:%s", __func__,
+                  AdvertisingEventTypeText(le_report->advertising_event_type_)
+                      .c_str());
+              return;
+          }
+
+          RawAddress raw_address(le_report->address_.address);
+
+          btm_ble_process_adv_addr(raw_address, &address_type);
+          btm_ble_process_adv_pkt_cont(
+              extended_event_type, address_type, raw_address,
+              kPhyConnectionLe1M, kPhyConnectionNone, kAdvDataInfoNotPresent,
+              kTxPowerInformationNotPresent, le_report->rssi_,
+              kNotPeriodicAdvertisement, report_len, report_data);
+        } break;
+
+        case hci::LeReport::ReportType::DIRECTED_ADVERTISING_EVENT:
+          LOG_WARN(LOG_TAG,
+                   "%s Directed advertising is unsupported from device:%s",
+                   __func__, le_report->address_.ToString().c_str());
+          break;
+
+        case hci::LeReport::ReportType::EXTENDED_ADVERTISING_EVENT: {
+          std::shared_ptr<hci::ExtendedLeReport> extended_le_report =
+              std::static_pointer_cast<hci::ExtendedLeReport>(le_report);
+          TransformToExtendedEventType(
+              &extended_event_type,
+              {.connectable = extended_le_report->connectable_,
+               .scannable = extended_le_report->scannable_,
+               .directed = extended_le_report->directed_,
+               .scan_response = extended_le_report->scan_response_,
+               .legacy = false,
+               .continuing = !extended_le_report->complete_,
+               .truncated = extended_le_report->truncated_});
+          RawAddress raw_address(le_report->address_.address);
+          if (address_type != BLE_ADDR_ANONYMOUS) {
+            btm_ble_process_adv_addr(raw_address, &address_type);
+          }
+          btm_ble_process_adv_pkt_cont(
+              extended_event_type, address_type, raw_address,
+              kPhyConnectionLe1M, kPhyConnectionNone, kAdvDataInfoNotPresent,
+              kTxPowerInformationNotPresent, le_report->rssi_,
+              kNotPeriodicAdvertisement, report_len, report_data);
+
+        } break;
+      }
+    }
+  }
+
+  virtual void on_timeout() {
+    LOG_WARN(LOG_TAG, "%s Scanning timeout", __func__);
+  }
+  os::Handler* Handler() { return bluetooth::shim::GetGdShimHandler(); }
+};
+}  // namespace hci
+}  // namespace bluetooth
+
+bluetooth::hci::BtmScanningCallbacks btm_scanning_callbacks;
+
+void bluetooth::shim::Btm::StartScanning(bool use_active_scanning) {
+  bluetooth::shim::GetScanning()->StartScan(&btm_scanning_callbacks);
+}
+
+size_t bluetooth::shim::Btm::GetNumberOfAdvertisingInstances() const {
+  return bluetooth::shim::GetAdvertising()->GetNumberOfAdvertisingInstances();
+}
+
+tBTM_STATUS bluetooth::shim::Btm::CreateBond(const RawAddress& bd_addr,
+                                             tBLE_ADDR_TYPE addr_type,
+                                             tBT_TRANSPORT transport,
+                                             uint8_t pin_len, uint8_t* p_pin,
+                                             uint32_t trusted_mask[]) {
+  auto security_manager =
+      bluetooth::shim::GetSecurityModule()->GetSecurityManager();
+  switch (transport) {
+    case BT_TRANSPORT_BR_EDR:
+      security_manager->CreateBond(
+          ToAddressWithType(bd_addr.address, BLE_ADDR_PUBLIC));
+      break;
+    case BT_TRANSPORT_LE:
+      security_manager->CreateBondLe(ToAddressWithType(bd_addr, addr_type));
+      break;
+    default:
+      return bluetooth::shim::BTM_ILLEGAL_VALUE;
+  }
+  return bluetooth::shim::BTM_SUCCESS;
+}
+
+bool bluetooth::shim::Btm::CancelBond(const RawAddress& bd_addr) {
+  auto security_manager =
+      bluetooth::shim::GetSecurityModule()->GetSecurityManager();
+  security_manager->CancelBond(ToAddressWithType(bd_addr, BLE_ADDR_PUBLIC));
+  return true;
+}
+
+bool bluetooth::shim::Btm::RemoveBond(const RawAddress& bd_addr) {
+  // TODO(cmanton) Check if acl is connected
+  auto security_manager =
+      bluetooth::shim::GetSecurityModule()->GetSecurityManager();
+  security_manager->RemoveBond(ToAddressWithType(bd_addr, BLE_ADDR_PUBLIC));
+  return true;
+}
+
+void bluetooth::shim::Btm::SetSimplePairingCallback(
+    tBTM_SP_CALLBACK* callback) {
+  auto security_manager =
+      bluetooth::shim::GetSecurityModule()->GetSecurityManager();
+  simple_pairing_callback_ = callback;
+}
diff --git a/main/shim/btm.h b/main/shim/btm.h
new file mode 100644
index 0000000..6058e29
--- /dev/null
+++ b/main/shim/btm.h
@@ -0,0 +1,271 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstddef>
+#include <cstdint>
+#include <functional>
+#include <mutex>
+#include <unordered_map>
+#include <vector>
+
+#include "main/shim/timer.h"
+#include "osi/include/alarm.h"
+#include "osi/include/future.h"
+#include "osi/include/log.h"
+#include "stack/include/btm_api_types.h"
+
+#include "hci/hci_packets.h"
+#include "hci/le_advertising_manager.h"
+
+//
+// NOTE: limited and general constants for inquiry and discoverable are swapped
+//
+
+/* Discoverable modes */
+static constexpr int kDiscoverableModeOff = 0;      // BTM_NON_DISCOVERABLE
+static constexpr int kLimitedDiscoverableMode = 1;  // BTM_LIMITED_DISCOVERABLE
+static constexpr int kGeneralDiscoverableMode = 2;  // BTM_GENERAL_DISCOVERABLE
+
+/* Inquiry modes */
+static constexpr uint8_t kInquiryModeOff = 0;      // BTM_INQUIRY_NONE
+static constexpr uint8_t kGeneralInquiryMode = 1;  // BTM_GENERAL_INQUIRY
+static constexpr uint8_t kLimitedInquiryMode = 2;  // BTM_LIMITED_INQUIRY
+
+/* Connectable modes */
+static constexpr int kConnectibleModeOff = 0;  // BTM_NON_CONNECTABLE
+static constexpr int kConnectibleModeOn = 1;   // BTM_CONNECTABLE
+
+/* Inquiry and page scan modes */
+static constexpr int kStandardScanType = 0;
+static constexpr int kInterlacedScanType = 1;
+
+/* Inquiry result modes */
+static constexpr int kStandardInquiryResult = 0;
+static constexpr int kInquiryResultWithRssi = 1;
+static constexpr int kExtendedInquiryResult = 2;
+
+/* Inquiry filter types */
+static constexpr int kClearInquiryFilter = 0;
+static constexpr int kFilterOnDeviceClass = 1;
+static constexpr int kFilterOnAddress = 2;
+
+static constexpr uint8_t kPhyConnectionNone = 0x00;
+static constexpr uint8_t kPhyConnectionLe1M = 0x01;
+static constexpr uint8_t kPhyConnectionLe2M = 0x02;
+static constexpr uint8_t kPhyConnectionLeCoded = 0x03;
+
+using LegacyInquiryCompleteCallback =
+    std::function<void(uint16_t status, uint8_t inquiry_mode)>;
+
+using DiscoverabilityState = struct {
+  int mode;
+  uint16_t interval;
+  uint16_t window;
+};
+using ConnectabilityState = DiscoverabilityState;
+
+namespace bluetooth {
+namespace shim {
+
+using BtmStatus = enum : uint16_t {
+  BTM_SUCCESS = 0,              /* Command succeeded                 */
+  BTM_CMD_STARTED = 1,          /* Command started OK.               */
+  BTM_BUSY = 2,                 /* Device busy with another command  */
+  BTM_NO_RESOURCES = 3,         /* No resources to issue command     */
+  BTM_MODE_UNSUPPORTED = 4,     /* Request for 1 or more unsupported modes */
+  BTM_ILLEGAL_VALUE = 5,        /* Illegal parameter value           */
+  BTM_WRONG_MODE = 6,           /* Device in wrong mode for request  */
+  BTM_UNKNOWN_ADDR = 7,         /* Unknown remote BD address         */
+  BTM_DEVICE_TIMEOUT = 8,       /* Device timeout                    */
+  BTM_BAD_VALUE_RET = 9,        /* A bad value was received from HCI */
+  BTM_ERR_PROCESSING = 10,      /* Generic error                     */
+  BTM_NOT_AUTHORIZED = 11,      /* Authorization failed              */
+  BTM_DEV_RESET = 12,           /* Device has been reset             */
+  BTM_CMD_STORED = 13,          /* request is stored in control block */
+  BTM_ILLEGAL_ACTION = 14,      /* state machine gets illegal command */
+  BTM_DELAY_CHECK = 15,         /* delay the check on encryption */
+  BTM_SCO_BAD_LENGTH = 16,      /* Bad SCO over HCI data length */
+  BTM_SUCCESS_NO_SECURITY = 17, /* security passed, no security set  */
+  BTM_FAILED_ON_SECURITY = 18,  /* security failed                   */
+  BTM_REPEATED_ATTEMPTS = 19,   /* repeated attempts for LE security requests */
+  BTM_MODE4_LEVEL4_NOT_SUPPORTED = 20, /* Secure Connections Only Mode can't be
+                                     supported */
+  BTM_DEV_BLACKLISTED = 21,            /* The device is Blacklisted */
+};
+
+class ReadRemoteName {
+ public:
+  bool Start(RawAddress raw_address) {
+    std::unique_lock<std::mutex> lock(mutex_);
+    if (in_progress_) {
+      return false;
+    }
+    raw_address_ = raw_address;
+    in_progress_ = true;
+    return true;
+  }
+
+  void Stop() {
+    std::unique_lock<std::mutex> lock(mutex_);
+    raw_address_ = RawAddress::kEmpty;
+    in_progress_ = false;
+  }
+
+  bool IsInProgress() const { return in_progress_; }
+
+  std::string AddressString() const { return raw_address_.ToString(); }
+
+  ReadRemoteName() : in_progress_{false}, raw_address_(RawAddress::kEmpty) {}
+
+ private:
+  bool in_progress_;
+  RawAddress raw_address_;
+  std::mutex mutex_;
+};
+
+class Btm {
+ public:
+  Btm() = default;
+  ~Btm() = default;
+
+  // Inquiry result callbacks
+  void OnInquiryResult(bluetooth::hci::InquiryResultView view);
+  void OnInquiryResultWithRssi(bluetooth::hci::InquiryResultWithRssiView view);
+  void OnExtendedInquiryResult(bluetooth::hci::ExtendedInquiryResultView view);
+  void OnInquiryComplete(bluetooth::hci::ErrorCode status);
+
+  // Inquiry API
+  bool SetInquiryFilter(uint8_t mode, uint8_t type, tBTM_INQ_FILT_COND data);
+  void SetFilterInquiryOnAddress();
+  void SetFilterInquiryOnDevice();
+  void ClearInquiryFilter();
+
+  void SetStandardInquiryResultMode();
+  void SetInquiryWithRssiResultMode();
+  void SetExtendedInquiryResultMode();
+
+  void SetInterlacedInquiryScan();
+  void SetStandardInquiryScan();
+  bool IsInterlacedScanSupported() const;
+
+  bool StartInquiry(uint8_t mode, uint8_t duration, uint8_t max_responses,
+                    LegacyInquiryCompleteCallback inquiry_complete_callback);
+  void CancelInquiry();
+  bool IsInquiryActive() const;
+  bool IsGeneralInquiryActive() const;
+  bool IsLimitedInquiryActive() const;
+
+  bool StartPeriodicInquiry(uint8_t mode, uint8_t duration,
+                            uint8_t max_responses, uint16_t max_delay,
+                            uint16_t min_delay,
+                            tBTM_INQ_RESULTS_CB* p_results_cb);
+  void CancelPeriodicInquiry();
+  bool IsGeneralPeriodicInquiryActive() const;
+  bool IsLimitedPeriodicInquiryActive() const;
+
+  // Discoverability API
+  bool general_inquiry_active_{false};
+  bool limited_inquiry_active_{false};
+  bool general_periodic_inquiry_active_{false};
+  bool limited_periodic_inquiry_active_{false};
+  void RegisterInquiryCallbacks();
+  void SetClassicGeneralDiscoverability(uint16_t window, uint16_t interval);
+  void SetClassicLimitedDiscoverability(uint16_t window, uint16_t interval);
+  void SetClassicDiscoverabilityOff();
+  DiscoverabilityState GetClassicDiscoverabilityState() const;
+
+  void SetLeGeneralDiscoverability();
+  void SetLeLimitedDiscoverability();
+  void SetLeDiscoverabilityOff();
+  DiscoverabilityState GetLeDiscoverabilityState() const;
+
+  void SetClassicConnectibleOn();
+  void SetClassicConnectibleOff();
+  ConnectabilityState GetClassicConnectabilityState() const;
+  void SetInterlacedPageScan();
+  void SetStandardPageScan();
+
+  void SetLeConnectibleOn();
+  void SetLeConnectibleOff();
+  ConnectabilityState GetLeConnectabilityState() const;
+
+  bool IsLeAclConnected(const RawAddress& raw_address) const;
+
+  // Remote device name API
+  BtmStatus ReadClassicRemoteDeviceName(const RawAddress& raw_address,
+                                        tBTM_CMPL_CB* callback);
+  BtmStatus ReadLeRemoteDeviceName(const RawAddress& raw_address,
+                                   tBTM_CMPL_CB* callback);
+  BtmStatus CancelAllReadRemoteDeviceName();
+
+  // Le neighbor interaction API
+  bluetooth::hci::AdvertiserId advertiser_id_{
+      hci::LeAdvertisingManager::kInvalidId};
+  void StartAdvertising();
+  void StopAdvertising();
+  void StartConnectability();
+  void StopConnectability();
+
+  void StartActiveScanning();
+  void StopActiveScanning();
+
+  void StartObserving();
+  void StopObserving();
+
+  size_t GetNumberOfAdvertisingInstances() const;
+
+  void SetObservingTimer(uint64_t duration_ms, std::function<void()>);
+  void CancelObservingTimer();
+  void SetScanningTimer(uint64_t duration_ms, std::function<void()>);
+  void CancelScanningTimer();
+
+  // Lifecycle
+  static void StartUp(Btm* btm);
+  static void ShutDown(Btm* btm);
+
+  tBTM_STATUS CreateBond(const RawAddress& bd_addr, tBLE_ADDR_TYPE addr_type,
+                         tBT_TRANSPORT transport, uint8_t pin_len,
+                         uint8_t* p_pin, uint32_t trusted_mask[]);
+  bool CancelBond(const RawAddress& bd_addr);
+  bool RemoveBond(const RawAddress& bd_addr);
+
+  void SetSimplePairingCallback(tBTM_SP_CALLBACK* callback);
+
+ private:
+  ReadRemoteName le_read_remote_name_;
+  ReadRemoteName classic_read_remote_name_;
+
+  Timer* observing_timer_{nullptr};
+  Timer* scanning_timer_{nullptr};
+
+  std::mutex sync_mutex_;
+
+  LegacyInquiryCompleteCallback legacy_inquiry_complete_callback_{};
+
+  tBTM_SP_CALLBACK* simple_pairing_callback_{nullptr};
+
+  uint8_t active_inquiry_mode_ = 0;
+
+  // TODO(cmanton) abort if there is no classic acl link up
+  bool CheckClassicAclLink(const RawAddress& raw_address) { return true; }
+  bool CheckLeAclLink(const RawAddress& raw_address) { return true; }
+  void StartScanning(bool use_active_scanning);
+};
+
+}  // namespace shim
+}  // namespace bluetooth
diff --git a/main/shim/btm_api.cc b/main/shim/btm_api.cc
new file mode 100644
index 0000000..cc4408f
--- /dev/null
+++ b/main/shim/btm_api.cc
@@ -0,0 +1,1120 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "bt_shim_btm"
+
+#include <base/callback.h>
+
+#include <mutex>
+
+#include "common/time_util.h"
+#include "device/include/controller.h"
+#include "main/shim/btm.h"
+#include "main/shim/btm_api.h"
+#include "main/shim/controller.h"
+#include "main/shim/shim.h"
+#include "main/shim/timer.h"
+#include "osi/include/log.h"
+#include "stack/btm/btm_int_types.h"
+#include "types/class_of_device.h"
+#include "types/raw_address.h"
+
+bluetooth::shim::Btm shim_btm;
+
+/**
+ * Legacy bluetooth module global control block state
+ *
+ * Mutex is used to synchronize access from the shim
+ * layer into the global control block.  This is used
+ * by the shim despite potentially arbitrary
+ * unsynchronized access by the legacy stack.
+ */
+extern tBTM_CB btm_cb;
+std::mutex btm_cb_mutex_;
+
+extern bool btm_inq_find_bdaddr(const RawAddress& p_bda);
+extern tINQ_DB_ENT* btm_inq_db_find(const RawAddress& raw_address);
+extern tINQ_DB_ENT* btm_inq_db_new(const RawAddress& p_bda);
+
+/**
+ * Legacy bluetooth btm stack entry points
+ */
+extern void btm_acl_update_busy_level(tBTM_BLI_EVENT event);
+extern void btm_clear_all_pending_le_entry(void);
+extern void btm_clr_inq_result_flt(void);
+extern void btm_set_eir_uuid(uint8_t* p_eir, tBTM_INQ_RESULTS* p_results);
+extern void btm_sort_inq_result(void);
+extern void btm_process_inq_complete(uint8_t status, uint8_t result_type);
+
+static future_t* btm_module_start_up(void) {
+  bluetooth::shim::Btm::StartUp(&shim_btm);
+  return kReturnImmediate;
+}
+
+static future_t* btm_module_shut_down(void) {
+  bluetooth::shim::Btm::ShutDown(&shim_btm);
+  return kReturnImmediate;
+}
+
+EXPORT_SYMBOL extern const module_t gd_shim_btm_module = {
+    .name = GD_SHIM_BTM_MODULE,
+    .init = kUnusedModuleApi,
+    .start_up = btm_module_start_up,
+    .shut_down = btm_module_shut_down,
+    .clean_up = kUnusedModuleApi,
+    .dependencies = {kUnusedModuleDependencies}};
+
+static bool max_responses_reached() {
+  return (btm_cb.btm_inq_vars.inqparms.max_resps &&
+          btm_cb.btm_inq_vars.inq_cmpl_info.num_resp >=
+              btm_cb.btm_inq_vars.inqparms.max_resps);
+}
+
+static bool is_periodic_inquiry_active() {
+  return btm_cb.btm_inq_vars.inq_active & BTM_PERIODIC_INQUIRY_ACTIVE;
+}
+
+static bool has_le_device(tBT_DEVICE_TYPE device_type) {
+  return device_type & BT_DEVICE_TYPE_BLE;
+}
+
+static bool is_classic_device(tBT_DEVICE_TYPE device_type) {
+  return device_type == BT_DEVICE_TYPE_BREDR;
+}
+
+static bool has_classic_device(tBT_DEVICE_TYPE device_type) {
+  return device_type & BT_DEVICE_TYPE_BREDR;
+}
+
+static bool is_dual_mode_device(tBT_DEVICE_TYPE device_type) {
+  return device_type == BT_DEVICE_TYPE_DUMO;
+}
+
+static bool is_observing_or_active_scanning() {
+  return btm_cb.btm_inq_vars.inqparms.mode & BTM_BLE_INQUIRY_MASK;
+}
+
+static void check_exceeded_responses(tBT_DEVICE_TYPE device_type,
+                                     bool scan_rsp) {
+  if (!is_periodic_inquiry_active() && max_responses_reached() &&
+      ((is_observing_or_active_scanning() && is_dual_mode_device(device_type) &&
+        scan_rsp) ||
+       (is_observing_or_active_scanning()))) {
+    LOG_INFO(LOG_TAG,
+             "UNIMPLEMENTED %s Device max responses found...cancelling inquiry",
+             __func__);
+  }
+}
+
+void btm_api_process_inquiry_result(const RawAddress& raw_address,
+                                    uint8_t page_scan_rep_mode,
+                                    DEV_CLASS device_class,
+                                    uint16_t clock_offset) {
+  tINQ_DB_ENT* p_i = btm_inq_db_find(raw_address);
+  if (max_responses_reached()) {
+    if (p_i == nullptr || !has_le_device(p_i->inq_info.results.device_type)) {
+      return;
+    }
+  }
+
+  if (p_i == nullptr) {
+    p_i = btm_inq_db_new(raw_address);
+    CHECK(p_i != nullptr);
+  } else if (p_i->inq_count == btm_cb.btm_inq_vars.inq_counter &&
+             is_classic_device(p_i->inq_info.results.device_type)) {
+    return;
+  }
+
+  p_i->inq_info.results.page_scan_rep_mode = page_scan_rep_mode;
+  p_i->inq_info.results.page_scan_per_mode = 0;  // RESERVED
+  p_i->inq_info.results.page_scan_mode = 0;      // RESERVED
+  p_i->inq_info.results.dev_class[0] = device_class[0];
+  p_i->inq_info.results.dev_class[1] = device_class[1];
+  p_i->inq_info.results.dev_class[2] = device_class[2];
+  p_i->inq_info.results.clock_offset = clock_offset | BTM_CLOCK_OFFSET_VALID;
+  p_i->inq_info.results.inq_result_type = BTM_INQ_RESULT_BR;
+  p_i->inq_info.results.rssi = BTM_INQ_RES_IGNORE_RSSI;
+
+  p_i->time_of_resp = bluetooth::common::time_get_os_boottime_ms();
+  p_i->inq_count = btm_cb.btm_inq_vars.inq_counter;
+  p_i->inq_info.appl_knows_rem_name = false;
+
+  if (p_i->inq_count != btm_cb.btm_inq_vars.inq_counter) {
+    p_i->inq_info.results.device_type = BT_DEVICE_TYPE_BREDR;
+    btm_cb.btm_inq_vars.inq_cmpl_info.num_resp++;
+    p_i->scan_rsp = false;
+  } else {
+    p_i->inq_info.results.device_type |= BT_DEVICE_TYPE_BREDR;
+  }
+
+  check_exceeded_responses(p_i->inq_info.results.device_type, p_i->scan_rsp);
+  if (btm_cb.btm_inq_vars.p_inq_results_cb == nullptr) {
+    return;
+  }
+
+  (btm_cb.btm_inq_vars.p_inq_results_cb)(&p_i->inq_info.results, nullptr, 0);
+}
+
+void btm_api_process_inquiry_result_with_rssi(RawAddress raw_address,
+                                              uint8_t page_scan_rep_mode,
+                                              DEV_CLASS device_class,
+                                              uint16_t clock_offset,
+                                              int8_t rssi) {
+  tINQ_DB_ENT* p_i = btm_inq_db_find(raw_address);
+  if (max_responses_reached()) {
+    if (p_i == nullptr || !has_le_device(p_i->inq_info.results.device_type)) {
+      return;
+    }
+  }
+
+  bool update = false;
+  if (btm_inq_find_bdaddr(raw_address)) {
+    if (btm_cb.btm_inq_vars.inqparms.report_dup && p_i != nullptr &&
+        (rssi > p_i->inq_info.results.rssi || p_i->inq_info.results.rssi == 0 ||
+         has_classic_device(p_i->inq_info.results.device_type))) {
+      update = true;
+    }
+  }
+
+  bool is_new = true;
+  if (p_i == nullptr) {
+    p_i = btm_inq_db_new(raw_address);
+    CHECK(p_i != nullptr);
+  } else if (p_i->inq_count == btm_cb.btm_inq_vars.inq_counter &&
+             is_classic_device(p_i->inq_info.results.device_type)) {
+    is_new = false;
+  }
+
+  p_i->inq_info.results.rssi = rssi;
+
+  if (is_new) {
+    p_i->inq_info.results.page_scan_rep_mode = page_scan_rep_mode;
+    p_i->inq_info.results.page_scan_per_mode = 0;  // RESERVED
+    p_i->inq_info.results.page_scan_mode = 0;      // RESERVED
+    p_i->inq_info.results.dev_class[0] = device_class[0];
+    p_i->inq_info.results.dev_class[1] = device_class[1];
+    p_i->inq_info.results.dev_class[2] = device_class[2];
+    p_i->inq_info.results.clock_offset = clock_offset | BTM_CLOCK_OFFSET_VALID;
+    p_i->inq_info.results.inq_result_type = BTM_INQ_RESULT_BR;
+
+    p_i->time_of_resp = bluetooth::common::time_get_os_boottime_ms();
+    p_i->inq_count = btm_cb.btm_inq_vars.inq_counter;
+    p_i->inq_info.appl_knows_rem_name = false;
+
+    if (p_i->inq_count != btm_cb.btm_inq_vars.inq_counter) {
+      p_i->inq_info.results.device_type = BT_DEVICE_TYPE_BREDR;
+      btm_cb.btm_inq_vars.inq_cmpl_info.num_resp++;
+      p_i->scan_rsp = false;
+    } else {
+      p_i->inq_info.results.device_type |= BT_DEVICE_TYPE_BREDR;
+    }
+  }
+
+  check_exceeded_responses(p_i->inq_info.results.device_type, p_i->scan_rsp);
+  if (btm_cb.btm_inq_vars.p_inq_results_cb == nullptr) {
+    return;
+  }
+
+  if (is_new || update) {
+    (btm_cb.btm_inq_vars.p_inq_results_cb)(&p_i->inq_info.results, nullptr, 0);
+  }
+}
+void btm_api_process_extended_inquiry_result(RawAddress raw_address,
+                                             uint8_t page_scan_rep_mode,
+                                             DEV_CLASS device_class,
+                                             uint16_t clock_offset, int8_t rssi,
+                                             const uint8_t* eir_data,
+                                             size_t eir_len) {
+  tINQ_DB_ENT* p_i = btm_inq_db_find(raw_address);
+  if (max_responses_reached()) {
+    if (p_i == nullptr || !has_le_device(p_i->inq_info.results.device_type)) {
+      return;
+    }
+  }
+
+  bool update = false;
+  if (btm_inq_find_bdaddr(raw_address) && p_i != nullptr) {
+    update = true;
+  }
+
+  bool is_new = true;
+  if (p_i == nullptr) {
+    p_i = btm_inq_db_new(raw_address);
+  } else if (p_i->inq_count == btm_cb.btm_inq_vars.inq_counter &&
+             (p_i->inq_info.results.device_type == BT_DEVICE_TYPE_BREDR)) {
+    is_new = false;
+  }
+
+  p_i->inq_info.results.rssi = rssi;
+
+  if (is_new) {
+    p_i->inq_info.results.page_scan_rep_mode = page_scan_rep_mode;
+    p_i->inq_info.results.page_scan_per_mode = 0;  // RESERVED
+    p_i->inq_info.results.page_scan_mode = 0;      // RESERVED
+    p_i->inq_info.results.dev_class[0] = device_class[0];
+    p_i->inq_info.results.dev_class[1] = device_class[1];
+    p_i->inq_info.results.dev_class[2] = device_class[2];
+    p_i->inq_info.results.clock_offset = clock_offset | BTM_CLOCK_OFFSET_VALID;
+    p_i->inq_info.results.inq_result_type = BTM_INQ_RESULT_BR;
+
+    p_i->time_of_resp = bluetooth::common::time_get_os_boottime_ms();
+    p_i->inq_count = btm_cb.btm_inq_vars.inq_counter;
+    p_i->inq_info.appl_knows_rem_name = false;
+
+    if (p_i->inq_count != btm_cb.btm_inq_vars.inq_counter) {
+      p_i->inq_info.results.device_type = BT_DEVICE_TYPE_BREDR;
+      btm_cb.btm_inq_vars.inq_cmpl_info.num_resp++;
+      p_i->scan_rsp = false;
+    } else {
+      p_i->inq_info.results.device_type |= BT_DEVICE_TYPE_BREDR;
+    }
+  }
+
+  check_exceeded_responses(p_i->inq_info.results.device_type, p_i->scan_rsp);
+  if (btm_cb.btm_inq_vars.p_inq_results_cb == nullptr) {
+    return;
+  }
+
+  if (is_new || update) {
+    memset(p_i->inq_info.results.eir_uuid, 0,
+           BTM_EIR_SERVICE_ARRAY_SIZE * (BTM_EIR_ARRAY_BITS / 8));
+    btm_set_eir_uuid(const_cast<uint8_t*>(eir_data), &p_i->inq_info.results);
+    uint8_t* p_eir_data = const_cast<uint8_t*>(eir_data);
+    (btm_cb.btm_inq_vars.p_inq_results_cb)(&p_i->inq_info.results, p_eir_data,
+                                           eir_len);
+  }
+}
+
+tBTM_STATUS bluetooth::shim::BTM_StartInquiry(tBTM_INQ_PARMS* p_inqparms,
+                                              tBTM_INQ_RESULTS_CB* p_results_cb,
+                                              tBTM_CMPL_CB* p_cmpl_cb) {
+  CHECK(p_inqparms != nullptr);
+  CHECK(p_results_cb != nullptr);
+  CHECK(p_cmpl_cb != nullptr);
+
+  std::lock_guard<std::mutex> lock(btm_cb_mutex_);
+
+  btm_cb.btm_inq_vars.inq_cmpl_info.num_resp = 0;
+  btm_cb.btm_inq_vars.scan_type = INQ_GENERAL;
+
+  shim_btm.StartActiveScanning();
+  if (p_inqparms->duration != 0) {
+    shim_btm.SetScanningTimer(p_inqparms->duration * 1000, []() {
+      LOG_INFO(LOG_TAG, "%s scanning timeout popped", __func__);
+      std::lock_guard<std::mutex> lock(btm_cb_mutex_);
+      shim_btm.StopActiveScanning();
+    });
+  }
+
+  shim_btm.StartActiveScanning();
+
+  uint8_t classic_mode = p_inqparms->mode & 0x0f;
+  if (!shim_btm.SetInquiryFilter(classic_mode, p_inqparms->filter_cond_type,
+                                 p_inqparms->filter_cond)) {
+    LOG_WARN(LOG_TAG, "%s Unable to set inquiry filter", __func__);
+    return BTM_ERR_PROCESSING;
+  }
+
+  if (!shim_btm.StartInquiry(
+          classic_mode, p_inqparms->duration, p_inqparms->max_resps,
+          [](uint16_t status, uint8_t inquiry_mode) {
+            LOG_DEBUG(LOG_TAG,
+                      "%s Inquiry is complete status:%hd inquiry_mode:%hhd",
+                      __func__, status, inquiry_mode);
+            btm_cb.btm_inq_vars.inqparms.mode &= ~(inquiry_mode);
+
+            btm_acl_update_busy_level(BTM_BLI_INQ_DONE_EVT);
+            if (btm_cb.btm_inq_vars.inq_active) {
+              btm_cb.btm_inq_vars.inq_cmpl_info.status = status;
+              btm_clear_all_pending_le_entry();
+              btm_cb.btm_inq_vars.state = BTM_INQ_INACTIVE_STATE;
+
+              /* Increment so the start of a next inquiry has a new count */
+              btm_cb.btm_inq_vars.inq_counter++;
+
+              btm_clr_inq_result_flt();
+
+              if ((status == BTM_SUCCESS) &&
+                  controller_get_interface()
+                      ->supports_rssi_with_inquiry_results()) {
+                btm_sort_inq_result();
+              }
+
+              btm_cb.btm_inq_vars.inq_active = BTM_INQUIRY_INACTIVE;
+              btm_cb.btm_inq_vars.p_inq_results_cb = nullptr;
+              btm_cb.btm_inq_vars.p_inq_cmpl_cb = nullptr;
+
+              if (btm_cb.btm_inq_vars.p_inq_cmpl_cb != nullptr) {
+                LOG_DEBUG(LOG_TAG,
+                          "%s Sending inquiry completion to upper layer",
+                          __func__);
+                (btm_cb.btm_inq_vars.p_inq_cmpl_cb)(
+                    (tBTM_INQUIRY_CMPL*)&btm_cb.btm_inq_vars.inq_cmpl_info);
+                btm_cb.btm_inq_vars.p_inq_cmpl_cb = nullptr;
+              }
+            }
+            if (btm_cb.btm_inq_vars.inqparms.mode == BTM_INQUIRY_NONE &&
+                btm_cb.btm_inq_vars.scan_type == INQ_GENERAL) {
+              btm_cb.btm_inq_vars.scan_type = INQ_NONE;
+            }
+          })) {
+    LOG_WARN(LOG_TAG, "%s Unable to start inquiry", __func__);
+    return BTM_ERR_PROCESSING;
+  }
+
+  btm_cb.btm_inq_vars.state = BTM_INQ_ACTIVE_STATE;
+  btm_cb.btm_inq_vars.p_inq_cmpl_cb = p_cmpl_cb;
+  btm_cb.btm_inq_vars.p_inq_results_cb = p_results_cb;
+  btm_cb.btm_inq_vars.inq_active = p_inqparms->mode;
+
+  btm_acl_update_busy_level(BTM_BLI_INQ_EVT);
+
+  return BTM_CMD_STARTED;
+}
+
+tBTM_STATUS bluetooth::shim::BTM_SetDiscoverability(uint16_t discoverable_mode,
+                                                    uint16_t window,
+                                                    uint16_t interval) {
+  uint16_t classic_discoverable_mode = discoverable_mode & 0xff;
+  uint16_t le_discoverable_mode = discoverable_mode >> 8;
+
+  if (window == 0) window = BTM_DEFAULT_DISC_WINDOW;
+  if (interval == 0) interval = BTM_DEFAULT_DISC_INTERVAL;
+
+  switch (le_discoverable_mode) {
+    case kDiscoverableModeOff:
+      shim_btm.StopAdvertising();
+      break;
+    case kLimitedDiscoverableMode:
+    case kGeneralDiscoverableMode:
+      shim_btm.StartAdvertising();
+      break;
+    default:
+      LOG_WARN(LOG_TAG, "%s Unexpected le discoverability mode:%d", __func__,
+               le_discoverable_mode);
+  }
+
+  switch (classic_discoverable_mode) {
+    case kDiscoverableModeOff:
+      shim_btm.SetClassicDiscoverabilityOff();
+      break;
+    case kLimitedDiscoverableMode:
+      shim_btm.SetClassicLimitedDiscoverability(window, interval);
+      break;
+    case kGeneralDiscoverableMode:
+      shim_btm.SetClassicGeneralDiscoverability(window, interval);
+      break;
+    default:
+      LOG_WARN(LOG_TAG, "%s Unexpected classic discoverability mode:%d",
+               __func__, classic_discoverable_mode);
+  }
+  return BTM_SUCCESS;
+}
+
+tBTM_STATUS bluetooth::shim::BTM_SetInquiryScanType(uint16_t scan_type) {
+  switch (scan_type) {
+    case kInterlacedScanType:
+      shim_btm.SetInterlacedInquiryScan();
+      return BTM_SUCCESS;
+      break;
+    case kStandardScanType:
+      shim_btm.SetStandardInquiryScan();
+      return BTM_SUCCESS;
+      break;
+    default:
+      return BTM_ILLEGAL_VALUE;
+  }
+  return BTM_WRONG_MODE;
+}
+
+tBTM_STATUS bluetooth::shim::BTM_BleObserve(bool start, uint8_t duration_sec,
+                                            tBTM_INQ_RESULTS_CB* p_results_cb,
+                                            tBTM_CMPL_CB* p_cmpl_cb) {
+  if (start) {
+    CHECK(p_results_cb != nullptr);
+    CHECK(p_cmpl_cb != nullptr);
+
+    std::lock_guard<std::mutex> lock(btm_cb_mutex_);
+
+    if (btm_cb.ble_ctr_cb.scan_activity & BTM_LE_OBSERVE_ACTIVE) {
+      LOG_WARN(LOG_TAG, "%s Observing already active", __func__);
+      return BTM_WRONG_MODE;
+    }
+
+    btm_cb.ble_ctr_cb.p_obs_results_cb = p_results_cb;
+    btm_cb.ble_ctr_cb.p_obs_cmpl_cb = p_cmpl_cb;
+    shim_btm.StartObserving();
+    btm_cb.ble_ctr_cb.scan_activity |= BTM_LE_OBSERVE_ACTIVE;
+
+    if (duration_sec != 0) {
+      shim_btm.SetObservingTimer(duration_sec * 1000, []() {
+        LOG_DEBUG(LOG_TAG, "%s observing timeout popped", __func__);
+
+        shim_btm.CancelObservingTimer();
+        shim_btm.StopObserving();
+
+        std::lock_guard<std::mutex> lock(btm_cb_mutex_);
+        btm_cb.ble_ctr_cb.scan_activity &= ~BTM_LE_OBSERVE_ACTIVE;
+
+        if (btm_cb.ble_ctr_cb.p_obs_cmpl_cb) {
+          (btm_cb.ble_ctr_cb.p_obs_cmpl_cb)(&btm_cb.btm_inq_vars.inq_cmpl_info);
+        }
+        btm_cb.ble_ctr_cb.p_obs_results_cb = nullptr;
+        btm_cb.ble_ctr_cb.p_obs_cmpl_cb = nullptr;
+
+        btm_cb.btm_inq_vars.inqparms.mode &= ~(BTM_BLE_INQUIRY_MASK);
+        btm_cb.btm_inq_vars.scan_type = INQ_NONE;
+
+        btm_acl_update_busy_level(BTM_BLI_INQ_DONE_EVT);
+
+        btm_clear_all_pending_le_entry();
+        btm_cb.btm_inq_vars.state = BTM_INQ_INACTIVE_STATE;
+
+        btm_cb.btm_inq_vars.inq_counter++;
+        btm_clr_inq_result_flt();
+        btm_sort_inq_result();
+
+        btm_cb.btm_inq_vars.inq_active = BTM_INQUIRY_INACTIVE;
+        btm_cb.btm_inq_vars.p_inq_results_cb = NULL;
+        btm_cb.btm_inq_vars.p_inq_cmpl_cb = NULL;
+
+        if (btm_cb.btm_inq_vars.p_inq_cmpl_cb) {
+          (btm_cb.btm_inq_vars.p_inq_cmpl_cb)(
+              (tBTM_INQUIRY_CMPL*)&btm_cb.btm_inq_vars.inq_cmpl_info);
+          btm_cb.btm_inq_vars.p_inq_cmpl_cb = nullptr;
+        }
+      });
+    }
+  } else {
+    std::lock_guard<std::mutex> lock(btm_cb_mutex_);
+
+    if (!(btm_cb.ble_ctr_cb.scan_activity & BTM_LE_OBSERVE_ACTIVE)) {
+      LOG_WARN(LOG_TAG, "%s Observing already inactive", __func__);
+    }
+    shim_btm.CancelObservingTimer();
+    shim_btm.StopObserving();
+    btm_cb.ble_ctr_cb.scan_activity &= ~BTM_LE_OBSERVE_ACTIVE;
+    shim_btm.StopObserving();
+    if (btm_cb.ble_ctr_cb.p_obs_cmpl_cb) {
+      (btm_cb.ble_ctr_cb.p_obs_cmpl_cb)(&btm_cb.btm_inq_vars.inq_cmpl_info);
+    }
+    btm_cb.ble_ctr_cb.p_obs_results_cb = nullptr;
+    btm_cb.ble_ctr_cb.p_obs_cmpl_cb = nullptr;
+  }
+  return BTM_CMD_STARTED;
+}
+
+tBTM_STATUS bluetooth::shim::BTM_SetPageScanType(uint16_t scan_type) {
+  switch (scan_type) {
+    case kInterlacedScanType:
+      if (!shim_btm.IsInterlacedScanSupported()) {
+        return BTM_MODE_UNSUPPORTED;
+      }
+      shim_btm.SetInterlacedPageScan();
+      return BTM_SUCCESS;
+      break;
+    case kStandardScanType:
+      shim_btm.SetStandardPageScan();
+      return BTM_SUCCESS;
+      break;
+    default:
+      return BTM_ILLEGAL_VALUE;
+  }
+  return BTM_WRONG_MODE;
+}
+
+tBTM_STATUS bluetooth::shim::BTM_SetInquiryMode(uint8_t inquiry_mode) {
+  switch (inquiry_mode) {
+    case kStandardInquiryResult:
+      shim_btm.SetStandardInquiryResultMode();
+      break;
+    case kInquiryResultWithRssi:
+      shim_btm.SetInquiryWithRssiResultMode();
+      break;
+    case kExtendedInquiryResult:
+      shim_btm.SetExtendedInquiryResultMode();
+      break;
+    default:
+      return BTM_ILLEGAL_VALUE;
+  }
+  return BTM_SUCCESS;
+}
+
+uint16_t bluetooth::shim::BTM_ReadDiscoverability(uint16_t* p_window,
+                                                  uint16_t* p_interval) {
+  DiscoverabilityState state = shim_btm.GetClassicDiscoverabilityState();
+
+  if (p_interval) *p_interval = state.interval;
+  if (p_window) *p_window = state.window;
+
+  return state.mode;
+}
+
+tBTM_STATUS bluetooth::shim::BTM_CancelPeriodicInquiry(void) {
+  shim_btm.CancelPeriodicInquiry();
+  return BTM_SUCCESS;
+}
+
+tBTM_STATUS bluetooth::shim::BTM_SetConnectability(uint16_t page_mode,
+                                                   uint16_t window,
+                                                   uint16_t interval) {
+  uint16_t classic_connectible_mode = page_mode & 0xff;
+  uint16_t le_connectible_mode = page_mode >> 8;
+
+  if (!window) window = BTM_DEFAULT_CONN_WINDOW;
+  if (!interval) interval = BTM_DEFAULT_CONN_INTERVAL;
+
+  switch (le_connectible_mode) {
+    case kConnectibleModeOff:
+      shim_btm.StopConnectability();
+      break;
+    case kConnectibleModeOn:
+      shim_btm.StartConnectability();
+      break;
+    default:
+      return BTM_ILLEGAL_VALUE;
+      break;
+  }
+
+  switch (classic_connectible_mode) {
+    case kConnectibleModeOff:
+      shim_btm.SetClassicConnectibleOff();
+      break;
+    case kConnectibleModeOn:
+      shim_btm.SetClassicConnectibleOn();
+      break;
+    default:
+      return BTM_ILLEGAL_VALUE;
+      break;
+  }
+  return BTM_SUCCESS;
+}
+
+uint16_t bluetooth::shim::BTM_ReadConnectability(uint16_t* p_window,
+                                                 uint16_t* p_interval) {
+  ConnectabilityState state = shim_btm.GetClassicConnectabilityState();
+
+  if (p_window) *p_window = state.window;
+  if (p_interval) *p_interval = state.interval;
+
+  return state.mode;
+}
+
+uint16_t bluetooth::shim::BTM_IsInquiryActive(void) {
+  if (shim_btm.IsLimitedInquiryActive()) {
+    return BTM_LIMITED_INQUIRY_ACTIVE;
+  } else if (shim_btm.IsGeneralInquiryActive()) {
+    return BTM_GENERAL_INQUIRY_ACTIVE;
+  } else if (shim_btm.IsGeneralPeriodicInquiryActive() ||
+             shim_btm.IsLimitedPeriodicInquiryActive()) {
+    return BTM_PERIODIC_INQUIRY_ACTIVE;
+  }
+  return BTM_INQUIRY_INACTIVE;
+}
+
+tBTM_STATUS bluetooth::shim::BTM_CancelInquiry(void) {
+  LOG_DEBUG(LOG_TAG, "%s Cancel inquiry", __func__);
+  shim_btm.CancelInquiry();
+
+  btm_cb.btm_inq_vars.state = BTM_INQ_INACTIVE_STATE;
+  btm_clr_inq_result_flt();
+
+  shim_btm.CancelScanningTimer();
+  shim_btm.StopActiveScanning();
+
+  btm_cb.ble_ctr_cb.scan_activity &= ~BTM_BLE_INQUIRY_MASK;
+
+  btm_cb.btm_inq_vars.inqparms.mode &=
+      ~(btm_cb.btm_inq_vars.inqparms.mode & BTM_BLE_INQUIRY_MASK);
+
+  btm_acl_update_busy_level(BTM_BLI_INQ_DONE_EVT);
+  /* Ignore any stray or late complete messages if the inquiry is not active */
+  if (btm_cb.btm_inq_vars.inq_active) {
+    btm_cb.btm_inq_vars.inq_cmpl_info.status = BTM_SUCCESS;
+    btm_clear_all_pending_le_entry();
+
+    if (controller_get_interface()->supports_rssi_with_inquiry_results()) {
+      btm_sort_inq_result();
+    }
+
+    btm_cb.btm_inq_vars.inq_active = BTM_INQUIRY_INACTIVE;
+    btm_cb.btm_inq_vars.p_inq_results_cb = nullptr;
+    btm_cb.btm_inq_vars.p_inq_cmpl_cb = nullptr;
+    btm_cb.btm_inq_vars.inq_counter++;
+
+    if (btm_cb.btm_inq_vars.p_inq_cmpl_cb != nullptr) {
+      LOG_DEBUG(LOG_TAG, "%s Sending cancel inquiry completion to upper layer",
+                __func__);
+      (btm_cb.btm_inq_vars.p_inq_cmpl_cb)(
+          (tBTM_INQUIRY_CMPL*)&btm_cb.btm_inq_vars.inq_cmpl_info);
+      btm_cb.btm_inq_vars.p_inq_cmpl_cb = nullptr;
+    }
+  }
+  if (btm_cb.btm_inq_vars.inqparms.mode == BTM_INQUIRY_NONE &&
+      btm_cb.btm_inq_vars.scan_type == INQ_GENERAL) {
+    btm_cb.btm_inq_vars.scan_type = INQ_NONE;
+  }
+  return BTM_SUCCESS;
+}
+
+tBTM_STATUS bluetooth::shim::BTM_ReadRemoteDeviceName(
+    const RawAddress& raw_address, tBTM_CMPL_CB* callback,
+    tBT_TRANSPORT transport) {
+  CHECK(callback != nullptr);
+  tBTM_STATUS status = BTM_NO_RESOURCES;
+
+  switch (transport) {
+    case BT_TRANSPORT_LE:
+      status = shim_btm.ReadLeRemoteDeviceName(raw_address, callback);
+      break;
+    case BT_TRANSPORT_BR_EDR:
+      status = shim_btm.ReadClassicRemoteDeviceName(raw_address, callback);
+      break;
+    default:
+      LOG_WARN(LOG_TAG, "%s Unspecified transport:%d", __func__, transport);
+      break;
+  }
+  return status;
+}
+
+tBTM_STATUS bluetooth::shim::BTM_CancelRemoteDeviceName(void) {
+  return shim_btm.CancelAllReadRemoteDeviceName();
+}
+
+tBTM_INQ_INFO* bluetooth::shim::BTM_InqDbRead(const RawAddress& p_bda) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  return nullptr;
+}
+
+tBTM_INQ_INFO* bluetooth::shim::BTM_InqDbFirst(void) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  return nullptr;
+}
+
+tBTM_INQ_INFO* bluetooth::shim::BTM_InqDbNext(tBTM_INQ_INFO* p_cur) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  CHECK(p_cur != nullptr);
+  return nullptr;
+}
+
+tBTM_STATUS bluetooth::shim::BTM_ClearInqDb(const RawAddress* p_bda) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  if (p_bda == nullptr) {
+    // clear all entries
+  } else {
+    // clear specific entry
+  }
+  return BTM_NO_RESOURCES;
+}
+
+tBTM_STATUS bluetooth::shim::BTM_WriteEIR(BT_HDR* p_buff) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  CHECK(p_buff != nullptr);
+  return BTM_NO_RESOURCES;
+}
+
+bool bluetooth::shim::BTM_HasEirService(const uint32_t* p_eir_uuid,
+                                        uint16_t uuid16) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  CHECK(p_eir_uuid != nullptr);
+  return false;
+}
+
+tBTM_EIR_SEARCH_RESULT bluetooth::shim::BTM_HasInquiryEirService(
+    tBTM_INQ_RESULTS* p_results, uint16_t uuid16) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  CHECK(p_results != nullptr);
+  return BTM_EIR_UNKNOWN;
+}
+
+void bluetooth::shim::BTM_AddEirService(uint32_t* p_eir_uuid, uint16_t uuid16) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  CHECK(p_eir_uuid != nullptr);
+}
+
+void bluetooth::shim::BTM_RemoveEirService(uint32_t* p_eir_uuid,
+                                           uint16_t uuid16) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  CHECK(p_eir_uuid != nullptr);
+}
+
+uint8_t bluetooth::shim::BTM_GetEirSupportedServices(uint32_t* p_eir_uuid,
+                                                     uint8_t** p,
+                                                     uint8_t max_num_uuid16,
+                                                     uint8_t* p_num_uuid16) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  CHECK(p_eir_uuid != nullptr);
+  CHECK(p != nullptr);
+  CHECK(*p != nullptr);
+  CHECK(p_num_uuid16 != nullptr);
+  return BTM_NO_RESOURCES;
+}
+
+uint8_t bluetooth::shim::BTM_GetEirUuidList(uint8_t* p_eir, size_t eir_len,
+                                            uint8_t uuid_size,
+                                            uint8_t* p_num_uuid,
+                                            uint8_t* p_uuid_list,
+                                            uint8_t max_num_uuid) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  CHECK(p_eir != nullptr);
+  CHECK(p_num_uuid != nullptr);
+  CHECK(p_uuid_list != nullptr);
+  return 0;
+}
+
+bool bluetooth::shim::BTM_SecAddBleDevice(const RawAddress& bd_addr,
+                                          BD_NAME bd_name,
+                                          tBT_DEVICE_TYPE dev_type,
+                                          tBLE_ADDR_TYPE addr_type) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  return false;
+}
+
+bool bluetooth::shim::BTM_SecAddBleKey(const RawAddress& bd_addr,
+                                       tBTM_LE_KEY_VALUE* p_le_key,
+                                       tBTM_LE_KEY_TYPE key_type) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  CHECK(p_le_key != nullptr);
+  return false;
+}
+
+void bluetooth::shim::BTM_BleLoadLocalKeys(uint8_t key_type,
+                                           tBTM_BLE_LOCAL_KEYS* p_key) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  CHECK(p_key != nullptr);
+}
+
+static Octet16 bogus_root;
+
+/** Returns local device encryption root (ER) */
+const Octet16& bluetooth::shim::BTM_GetDeviceEncRoot() {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  return bogus_root;
+}
+
+/** Returns local device identity root (IR). */
+const Octet16& bluetooth::shim::BTM_GetDeviceIDRoot() {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  return bogus_root;
+}
+
+/** Return local device DHK. */
+const Octet16& bluetooth::shim::BTM_GetDeviceDHK() {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  return bogus_root;
+}
+
+void bluetooth::shim::BTM_ReadConnectionAddr(const RawAddress& remote_bda,
+                                             RawAddress& local_conn_addr,
+                                             tBLE_ADDR_TYPE* p_addr_type) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  CHECK(p_addr_type != nullptr);
+}
+
+bool bluetooth::shim::BTM_IsBleConnection(uint16_t conn_handle) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  return false;
+}
+
+bool bluetooth::shim::BTM_ReadRemoteConnectionAddr(
+    const RawAddress& pseudo_addr, RawAddress& conn_addr,
+    tBLE_ADDR_TYPE* p_addr_type) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  CHECK(p_addr_type != nullptr);
+  return false;
+}
+
+void bluetooth::shim::BTM_SecurityGrant(const RawAddress& bd_addr,
+                                        uint8_t res) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+}
+
+void bluetooth::shim::BTM_BlePasskeyReply(const RawAddress& bd_addr,
+                                          uint8_t res, uint32_t passkey) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+}
+
+void bluetooth::shim::BTM_BleConfirmReply(const RawAddress& bd_addr,
+                                          uint8_t res) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+}
+
+void bluetooth::shim::BTM_BleOobDataReply(const RawAddress& bd_addr,
+                                          uint8_t res, uint8_t len,
+                                          uint8_t* p_data) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  CHECK(p_data != nullptr);
+}
+
+void bluetooth::shim::BTM_BleSecureConnectionOobDataReply(
+    const RawAddress& bd_addr, uint8_t* p_c, uint8_t* p_r) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  CHECK(p_c != nullptr);
+  CHECK(p_r != nullptr);
+}
+
+void bluetooth::shim::BTM_BleSetConnScanParams(uint32_t scan_interval,
+                                               uint32_t scan_window) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+}
+
+void bluetooth::shim::BTM_BleSetPrefConnParams(const RawAddress& bd_addr,
+                                               uint16_t min_conn_int,
+                                               uint16_t max_conn_int,
+                                               uint16_t slave_latency,
+                                               uint16_t supervision_tout) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+}
+
+void bluetooth::shim::BTM_ReadDevInfo(const RawAddress& remote_bda,
+                                      tBT_DEVICE_TYPE* p_dev_type,
+                                      tBLE_ADDR_TYPE* p_addr_type) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  CHECK(p_dev_type != nullptr);
+  CHECK(p_addr_type != nullptr);
+}
+
+bool bluetooth::shim::BTM_ReadConnectedTransportAddress(
+    RawAddress* remote_bda, tBT_TRANSPORT transport) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  CHECK(remote_bda != nullptr);
+  return false;
+}
+
+void bluetooth::shim::BTM_BleReceiverTest(uint8_t rx_freq,
+                                          tBTM_CMPL_CB* p_cmd_cmpl_cback) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  CHECK(p_cmd_cmpl_cback != nullptr);
+}
+
+void bluetooth::shim::BTM_BleTransmitterTest(uint8_t tx_freq,
+                                             uint8_t test_data_len,
+                                             uint8_t packet_payload,
+                                             tBTM_CMPL_CB* p_cmd_cmpl_cback) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  CHECK(p_cmd_cmpl_cback != nullptr);
+}
+
+void bluetooth::shim::BTM_BleTestEnd(tBTM_CMPL_CB* p_cmd_cmpl_cback) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  CHECK(p_cmd_cmpl_cback != nullptr);
+}
+
+bool bluetooth::shim::BTM_UseLeLink(const RawAddress& raw_address) {
+  return shim_btm.IsLeAclConnected(raw_address);
+}
+
+tBTM_STATUS bluetooth::shim::BTM_SetBleDataLength(const RawAddress& bd_addr,
+                                                  uint16_t tx_pdu_length) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  return BTM_NO_RESOURCES;
+}
+
+void bluetooth::shim::BTM_BleReadPhy(
+    const RawAddress& bd_addr,
+    base::Callback<void(uint8_t tx_phy, uint8_t rx_phy, uint8_t status)> cb) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+}
+
+tBTM_STATUS bluetooth::shim::BTM_BleSetDefaultPhy(uint8_t all_phys,
+                                                  uint8_t tx_phys,
+                                                  uint8_t rx_phys) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  return BTM_NO_RESOURCES;
+}
+
+void bluetooth::shim::BTM_BleSetPhy(const RawAddress& bd_addr, uint8_t tx_phys,
+                                    uint8_t rx_phys, uint16_t phy_options) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+}
+
+bool bluetooth::shim::BTM_BleDataSignature(const RawAddress& bd_addr,
+                                           uint8_t* p_text, uint16_t len,
+                                           BLE_SIGNATURE signature) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  CHECK(p_text != nullptr);
+  return false;
+}
+
+bool bluetooth::shim::BTM_BleVerifySignature(const RawAddress& bd_addr,
+                                             uint8_t* p_orig, uint16_t len,
+                                             uint32_t counter,
+                                             uint8_t* p_comp) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  CHECK(p_orig != nullptr);
+  CHECK(p_comp != nullptr);
+  return false;
+}
+
+bool bluetooth::shim::BTM_GetLeSecurityState(const RawAddress& bd_addr,
+                                             uint8_t* p_le_dev_sec_flags,
+                                             uint8_t* p_le_key_size) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  CHECK(p_le_dev_sec_flags != nullptr);
+  CHECK(p_le_key_size != nullptr);
+  return false;
+}
+
+bool bluetooth::shim::BTM_BleSecurityProcedureIsRunning(
+    const RawAddress& bd_addr) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  return false;
+}
+
+uint8_t bluetooth::shim::BTM_BleGetSupportedKeySize(const RawAddress& bd_addr) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  return 0;
+}
+
+/**
+ * This function update(add,delete or clear) the adv local name filtering
+ * condition.
+ */
+void bluetooth::shim::BTM_LE_PF_local_name(tBTM_BLE_SCAN_COND_OP action,
+                                           tBTM_BLE_PF_FILT_INDEX filt_index,
+                                           std::vector<uint8_t> name,
+                                           tBTM_BLE_PF_CFG_CBACK cb) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+}
+
+void bluetooth::shim::BTM_LE_PF_srvc_data(tBTM_BLE_SCAN_COND_OP action,
+                                          tBTM_BLE_PF_FILT_INDEX filt_index) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+}
+
+void bluetooth::shim::BTM_LE_PF_manu_data(
+    tBTM_BLE_SCAN_COND_OP action, tBTM_BLE_PF_FILT_INDEX filt_index,
+    uint16_t company_id, uint16_t company_id_mask, std::vector<uint8_t> data,
+    std::vector<uint8_t> data_mask, tBTM_BLE_PF_CFG_CBACK cb) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+}
+
+void bluetooth::shim::BTM_LE_PF_srvc_data_pattern(
+    tBTM_BLE_SCAN_COND_OP action, tBTM_BLE_PF_FILT_INDEX filt_index,
+    std::vector<uint8_t> data, std::vector<uint8_t> data_mask,
+    tBTM_BLE_PF_CFG_CBACK cb) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+}
+
+void bluetooth::shim::BTM_LE_PF_addr_filter(tBTM_BLE_SCAN_COND_OP action,
+                                            tBTM_BLE_PF_FILT_INDEX filt_index,
+                                            tBLE_BD_ADDR addr,
+                                            tBTM_BLE_PF_CFG_CBACK cb) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+}
+
+void bluetooth::shim::BTM_LE_PF_uuid_filter(tBTM_BLE_SCAN_COND_OP action,
+                                            tBTM_BLE_PF_FILT_INDEX filt_index,
+                                            tBTM_BLE_PF_COND_TYPE filter_type,
+                                            const bluetooth::Uuid& uuid,
+                                            tBTM_BLE_PF_LOGIC_TYPE cond_logic,
+                                            const bluetooth::Uuid& uuid_mask,
+                                            tBTM_BLE_PF_CFG_CBACK cb) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+}
+
+void bluetooth::shim::BTM_LE_PF_set(tBTM_BLE_PF_FILT_INDEX filt_index,
+                                    std::vector<ApcfCommand> commands,
+                                    tBTM_BLE_PF_CFG_CBACK cb) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+}
+
+void bluetooth::shim::BTM_LE_PF_clear(tBTM_BLE_PF_FILT_INDEX filt_index,
+                                      tBTM_BLE_PF_CFG_CBACK cb) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+}
+
+void bluetooth::shim::BTM_BleAdvFilterParamSetup(
+    int action, tBTM_BLE_PF_FILT_INDEX filt_index,
+    std::unique_ptr<btgatt_filt_param_setup_t> p_filt_params,
+    tBTM_BLE_PF_PARAM_CB cb) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+}
+
+void bluetooth::shim::BTM_BleUpdateAdvFilterPolicy(tBTM_BLE_AFP adv_policy) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+}
+
+void bluetooth::shim::BTM_BleEnableDisableFilterFeature(
+    uint8_t enable, tBTM_BLE_PF_STATUS_CBACK p_stat_cback) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+}
+
+uint8_t bluetooth::shim::BTM_BleMaxMultiAdvInstanceCount() {
+  return shim_btm.GetNumberOfAdvertisingInstances();
+}
+
+bool bluetooth::shim::BTM_BleLocalPrivacyEnabled(void) {
+  return controller_get_interface()->supports_ble_privacy();
+}
+
+tBTM_STATUS bluetooth::shim::BTM_SecBond(const RawAddress& bd_addr,
+                                         tBLE_ADDR_TYPE addr_type,
+                                         tBT_TRANSPORT transport,
+                                         uint8_t pin_len, uint8_t* p_pin,
+                                         uint32_t trusted_mask[]) {
+  return shim_btm.CreateBond(bd_addr, addr_type, transport, pin_len, p_pin,
+                             trusted_mask);
+}
+
+bool bluetooth::shim::BTM_SecRegister(const tBTM_APPL_INFO* p_cb_info) {
+  CHECK(p_cb_info != nullptr);
+  LOG_DEBUG(LOG_TAG, "%s Registering security application", __func__);
+
+  if (p_cb_info->p_authorize_callback == nullptr) {
+    LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s authorize_callback", __func__);
+  }
+
+  if (p_cb_info->p_pin_callback == nullptr) {
+    LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s pin_callback", __func__);
+  }
+
+  if (p_cb_info->p_link_key_callback == nullptr) {
+    LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s link_key_callback", __func__);
+  }
+
+  if (p_cb_info->p_auth_complete_callback == nullptr) {
+    LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s auth_complete_callback", __func__);
+  }
+
+  if (p_cb_info->p_bond_cancel_cmpl_callback == nullptr) {
+    LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s bond_cancel_complete_callback",
+             __func__);
+  }
+
+  if (p_cb_info->p_le_callback == nullptr) {
+    LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s le_callback", __func__);
+  }
+
+  if (p_cb_info->p_le_key_callback == nullptr) {
+    LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s le_key_callback", __func__);
+  }
+
+  shim_btm.SetSimplePairingCallback(p_cb_info->p_sp_callback);
+  return true;
+}
+
+tBTM_STATUS bluetooth::shim::BTM_SecBondCancel(const RawAddress& bd_addr) {
+  if (shim_btm.CancelBond(bd_addr)) {
+    return BTM_SUCCESS;
+  } else {
+    return BTM_UNKNOWN_ADDR;
+  }
+}
+
+bool bluetooth::shim::BTM_SecDeleteDevice(const RawAddress& bd_addr) {
+  return shim_btm.RemoveBond(bd_addr);
+}
diff --git a/main/shim/btm_api.h b/main/shim/btm_api.h
new file mode 100644
index 0000000..70dbace
--- /dev/null
+++ b/main/shim/btm_api.h
@@ -0,0 +1,2386 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "stack/include/btm_api_types.h"
+#include "stack/include/btm_ble_api_types.h"
+
+namespace bluetooth {
+namespace shim {
+
+/*******************************************************************************
+ *
+ * Function         BTM_StartInquiry
+ *
+ * Description      This function is called to start an inquiry.
+ *
+ * Parameters:      p_inqparms - pointer to the inquiry information
+ *                      mode - GENERAL or LIMITED inquiry
+ *                      duration - length in 1.28 sec intervals (If '0', the
+ *                                 inquiry is CANCELLED)
+ *                      max_resps - maximum amount of devices to search for
+ *                                  before ending the inquiry
+ *                      filter_cond_type - BTM_CLR_INQUIRY_FILTER,
+ *                                         BTM_FILTER_COND_DEVICE_CLASS, or
+ *                                         BTM_FILTER_COND_BD_ADDR
+ *                      filter_cond - value for the filter (based on
+ *                                                          filter_cond_type)
+ *
+ *                  p_results_cb  - Pointer to the callback routine which gets
+ *                                called upon receipt of an inquiry result. If
+ *                                this field is NULL, the application is not
+ *                                notified.
+ *
+ *                  p_cmpl_cb   - Pointer to the callback routine which gets
+ *                                called upon completion.  If this field is
+ *                                NULL, the application is not notified when
+ *                                completed.
+ * Returns          tBTM_STATUS
+ *                  BTM_CMD_STARTED if successfully initiated
+ *                  BTM_BUSY if already in progress
+ *                  BTM_ILLEGAL_VALUE if parameter(s) are out of range
+ *                  BTM_NO_RESOURCES if could not allocate resources to start
+ *                                   the command
+ *                  BTM_WRONG_MODE if the device is not up.
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_StartInquiry(tBTM_INQ_PARMS* p_inqparms,
+                             tBTM_INQ_RESULTS_CB* p_results_cb,
+                             tBTM_CMPL_CB* p_cmpl_cb);
+
+/*******************************************************************************
+ *
+ * Function         BTM_SetDiscoverability
+ *
+ * Description      This function is called to set the device into or out of
+ *                  discoverable mode. Discoverable mode means inquiry
+ *                  scans are enabled.  If a value of '0' is entered for window
+ *                  or interval, the default values are used.
+ *
+ * Returns          BTM_SUCCESS if successful
+ *                  BTM_BUSY if a setting of the filter is already in progress
+ *                  BTM_NO_RESOURCES if couldn't get a memory pool buffer
+ *                  BTM_ILLEGAL_VALUE if a bad parameter was detected
+ *                  BTM_WRONG_MODE if the device is not up.
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_SetDiscoverability(uint16_t inq_mode, uint16_t window,
+                                   uint16_t interval);
+
+/*******************************************************************************
+ *
+ * Function         BTM_SetInquiryScanType
+ *
+ * Description      This function is called to set the iquiry scan-type to
+ *                  standard or interlaced.
+ *
+ * Input Params:    BTM_SCAN_TYPE_STANDARD or BTM_SCAN_TYPE_INTERLACED
+ *
+ * Returns          BTM_SUCCESS if successful
+ *                  BTM_MODE_UNSUPPORTED if not a 1.2 device
+ *                  BTM_WRONG_MODE if the device is not up.
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_SetInquiryScanType(uint16_t scan_type);
+
+/*******************************************************************************
+ *
+ * Function         BTM_BleObserve
+ *
+ * Description      This procedure keep the device listening for advertising
+ *                  events from a broadcast device.
+ *
+ * Parameters       start: start or stop observe.
+ *
+ * Returns          void
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_BleObserve(bool start, uint8_t duration,
+                           tBTM_INQ_RESULTS_CB* p_results_cb,
+                           tBTM_CMPL_CB* p_cmpl_cb);
+
+/*******************************************************************************
+ *
+ * Function         BTM_SetPageScanType
+ *
+ * Description      This function is called to set the page scan-type to
+ *                  standard or interlaced.
+ *
+ * Input Params:    BTM_SCAN_TYPE_STANDARD or BTM_SCAN_TYPE_INTERLACED
+ *
+ * Returns          BTM_SUCCESS if successful
+ *                  BTM_MODE_UNSUPPORTED if not a 1.2 device
+ *                  BTM_WRONG_MODE if the device is not up.
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_SetPageScanType(uint16_t scan_type);
+
+/*******************************************************************************
+ *
+ * Function         BTM_SetInquiryMode
+ *
+ * Description      This function is called to set standard, with RSSI
+ *                  mode or extended of the inquiry for local device.
+ *
+ * Input Params:    BTM_INQ_RESULT_STANDARD, BTM_INQ_RESULT_WITH_RSSI or
+ *                  BTM_INQ_RESULT_EXTENDED
+ *
+ * Returns          BTM_SUCCESS if successful
+ *                  BTM_NO_RESOURCES if couldn't get a memory pool buffer
+ *                  BTM_ILLEGAL_VALUE if a bad parameter was detected
+ *                  BTM_WRONG_MODE if the device is not up.
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_SetInquiryMode(uint8_t mode);
+
+/*******************************************************************************
+ *
+ * Function         BTM_ReadDiscoverability
+ *
+ * Description      This function is called to read the current discoverability
+ *                  mode of the device.
+ *
+ * Output Params:   p_window - current inquiry scan duration
+ *                  p_interval - current inquiry scan interval
+ *
+ * Returns          BTM_NON_DISCOVERABLE, BTM_LIMITED_DISCOVERABLE, or
+ *                  BTM_GENERAL_DISCOVERABLE
+ *
+ ******************************************************************************/
+uint16_t BTM_ReadDiscoverability(uint16_t* p_window, uint16_t* p_interval);
+
+/*******************************************************************************
+ *
+ * Function         BTM_CancelPeriodicInquiry
+ *
+ * Description      This function cancels a periodic inquiry
+ *
+ * Returns
+ *                  BTM_NO_RESOURCES if could not allocate a message buffer
+ *                  BTM_SUCCESS - if cancelling the periodic inquiry
+ *                  BTM_WRONG_MODE if the device is not up.
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_CancelPeriodicInquiry(void);
+
+/*******************************************************************************
+ *
+ * Function         BTM_SetConnectability
+ *
+ * Description      This function is called to set the device into or out of
+ *                  connectable mode. Discoverable mode means page scans are
+ *                  enabled.
+ *
+ * Returns          BTM_SUCCESS if successful
+ *                  BTM_ILLEGAL_VALUE if a bad parameter is detected
+ *                  BTM_NO_RESOURCES if could not allocate a message buffer
+ *                  BTM_WRONG_MODE if the device is not up.
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_SetConnectability(uint16_t page_mode, uint16_t window,
+                                  uint16_t interval);
+
+/*******************************************************************************
+ *
+ * Function         BTM_ReadConnectability
+ *
+ * Description      This function is called to read the current discoverability
+ *                  mode of the device.
+ * Output Params    p_window - current page scan duration
+ *                  p_interval - current time between page scans
+ *
+ * Returns          BTM_NON_CONNECTABLE or BTM_CONNECTABLE
+ *
+ ******************************************************************************/
+uint16_t BTM_ReadConnectability(uint16_t* p_window, uint16_t* p_interval);
+
+/*******************************************************************************
+ *
+ * Function         BTM_IsInquiryActive
+ *
+ * Description      Return a bit mask of the current inquiry state
+ *
+ * Returns          BTM_INQUIRY_INACTIVE if inactive (0)
+ *                  BTM_LIMITED_INQUIRY_ACTIVE if a limted inquiry is active
+ *                  BTM_GENERAL_INQUIRY_ACTIVE if a general inquiry is active
+ *                  BTM_PERIODIC_INQUIRY_ACTIVE if a periodic inquiry is active
+ *
+ ******************************************************************************/
+uint16_t BTM_IsInquiryActive(void);
+
+/*******************************************************************************
+ *
+ * Function         BTM_CancelInquiry
+ *
+ * Description      This function cancels an inquiry if active
+ *
+ * Returns          BTM_SUCCESS if successful
+ *                  BTM_NO_RESOURCES if could not allocate a message buffer
+ *                  BTM_WRONG_MODE if the device is not up.
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_CancelInquiry(void);
+
+/*******************************************************************************
+ *
+ * Function         BTM_ReadRemoteDeviceName
+ *
+ * Description      This function initiates a remote device HCI command to the
+ *                  controller and calls the callback when the process has
+ *                  completed.
+ *
+ * Input Params:    remote_bda      - device address of name to retrieve
+ *                  p_cb            - callback function called when
+ *                                    BTM_CMD_STARTED is returned.
+ *                                    A pointer to tBTM_REMOTE_DEV_NAME is
+ *                                    passed to the callback.
+ *
+ * Returns
+ *                  BTM_CMD_STARTED is returned if the request was successfully
+ *                                  sent to HCI.
+ *                  BTM_BUSY if already in progress
+ *                  BTM_UNKNOWN_ADDR if device address is bad
+ *                  BTM_NO_RESOURCES if resources could not be allocated to
+ *                                   start the command
+ *                  BTM_WRONG_MODE if the device is not up.
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_ReadRemoteDeviceName(const RawAddress& remote_bda,
+                                     tBTM_CMPL_CB* p_cb,
+                                     tBT_TRANSPORT transport);
+
+/*******************************************************************************
+ *
+ * Function         BTM_CancelRemoteDeviceName
+ *
+ * Description      This function initiates the cancel request for the specified
+ *                  remote device.
+ *
+ * Input Params:    None
+ *
+ * Returns
+ *                  BTM_CMD_STARTED is returned if the request was successfully
+ *                                  sent to HCI.
+ *                  BTM_NO_RESOURCES if resources could not be allocated to
+ *                                   start the command
+ *                  BTM_WRONG_MODE if there is no active remote name request.
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_CancelRemoteDeviceName(void);
+
+/*******************************************************************************
+ *
+ * Function         BTM_InqDbRead
+ *
+ * Description      This function looks through the inquiry database for a match
+ *                  based on Bluetooth Device Address. This is the application's
+ *                  interface to get the inquiry details of a specific BD
+ *                  address.
+ *
+ * Returns          pointer to entry, or NULL if not found
+ *
+ ******************************************************************************/
+tBTM_INQ_INFO* BTM_InqDbRead(const RawAddress& p_bda);
+
+/*******************************************************************************
+ *
+ * Function         BTM_InqDbFirst
+ *
+ * Description      This function looks through the inquiry database for the
+ *                  first used entry, and returns that. This is used in
+ *                  conjunction with BTM_InqDbNext by applications as a way to
+ *                  walk through the inquiry database.
+ *
+ * Returns          pointer to first in-use entry, or NULL if DB is empty
+ *
+ ******************************************************************************/
+tBTM_INQ_INFO* BTM_InqDbFirst(void);
+
+/*******************************************************************************
+ *
+ * Function         BTM_InqDbNext
+ *
+ * Description      This function looks through the inquiry database for the
+ *                  next used entry, and returns that.  If the input parameter
+ *                  is NULL, the first entry is returned.
+ *
+ * Returns          pointer to next in-use entry, or NULL if no more found.
+ *
+ ******************************************************************************/
+tBTM_INQ_INFO* BTM_InqDbNext(tBTM_INQ_INFO* p_cur);
+
+/*******************************************************************************
+ *
+ * Function         BTM_ClearInqDb
+ *
+ * Description      This function is called to clear out a device or all devices
+ *                  from the inquiry database.
+ *
+ * Parameter        p_bda - (input) BD_ADDR ->  Address of device to clear
+ *                                              (NULL clears all entries)
+ *
+ * Returns          BTM_BUSY if an inquiry, get remote name, or event filter
+ *                          is active, otherwise BTM_SUCCESS
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_ClearInqDb(const RawAddress* p_bda);
+
+/*******************************************************************************
+ *
+ * Function         BTM_WriteEIR
+ *
+ * Description      This function is called to write EIR data to controller.
+ *
+ * Parameters       p_buff - allocated HCI command buffer including extended
+ *                           inquriry response
+ *
+ * Returns          BTM_SUCCESS  - if successful
+ *                  BTM_MODE_UNSUPPORTED - if local device cannot support it
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_WriteEIR(BT_HDR* p_buff);
+
+/*******************************************************************************
+ *
+ * Function         BTM_HasEirService
+ *
+ * Description      This function is called to know if UUID in bit map of UUID.
+ *
+ * Parameters       p_eir_uuid - bit map of UUID list
+ *                  uuid16 - UUID 16-bit
+ *
+ * Returns          true - if found
+ *                  false - if not found
+ *
+ ******************************************************************************/
+bool BTM_HasEirService(const uint32_t* p_eir_uuid, uint16_t uuid16);
+
+/*******************************************************************************
+ *
+ * Function         BTM_HasInquiryEirService
+ *
+ * Description      Return if a UUID is in the bit map of a UUID list.
+ *
+ * Parameters       p_results - inquiry results
+ *                  uuid16 - UUID 16-bit
+ *
+ * Returns          BTM_EIR_FOUND - if found
+ *                  BTM_EIR_NOT_FOUND - if not found and it is a complete list
+ *                  BTM_EIR_UNKNOWN - if not found and it is not complete list
+ *
+ ******************************************************************************/
+tBTM_EIR_SEARCH_RESULT BTM_HasInquiryEirService(tBTM_INQ_RESULTS* p_results,
+                                                uint16_t uuid16);
+
+/*******************************************************************************
+ *
+ * Function         BTM_AddEirService
+ *
+ * Description      This function is called to add a service in the bit map UUID
+ *                  list.
+ *
+ * Parameters       p_eir_uuid - bit mask of UUID list for EIR
+ *                  uuid16 - UUID 16-bit
+ *
+ * Returns          None
+ *
+ ******************************************************************************/
+void BTM_AddEirService(uint32_t* p_eir_uuid, uint16_t uuid16);
+
+/*******************************************************************************
+ *
+ * Function         BTM_RemoveEirService
+ *
+ * Description      This function is called to remove a service from the bit map
+ *                  UUID list.
+ *
+ * Parameters       p_eir_uuid - bit mask of UUID list for EIR
+ *                  uuid16 - UUID 16-bit
+ *
+ * Returns          None
+ *
+ ******************************************************************************/
+void BTM_RemoveEirService(uint32_t* p_eir_uuid, uint16_t uuid16);
+
+/*******************************************************************************
+ *
+ * Function         BTM_GetEirSupportedServices
+ *
+ * Description      This function is called to get UUID list from bit map UUID
+ *                  list.
+ *
+ * Parameters       p_eir_uuid - bit mask of UUID list for EIR
+ *                  p - reference of current pointer of EIR
+ *                  max_num_uuid16 - max number of UUID can be written in EIR
+ *                  num_uuid16 - number of UUID have been written in EIR
+ *
+ * Returns          BTM_EIR_MORE_16BITS_UUID_TYPE, if it has more than max
+ *                  BTM_EIR_COMPLETE_16BITS_UUID_TYPE, otherwise
+ *
+ ******************************************************************************/
+uint8_t BTM_GetEirSupportedServices(uint32_t* p_eir_uuid, uint8_t** p,
+                                    uint8_t max_num_uuid16,
+                                    uint8_t* p_num_uuid16);
+
+/*******************************************************************************
+ *
+ * Function         BTM_GetEirUuidList
+ *
+ * Description      This function parses EIR and returns UUID list.
+ *
+ * Parameters       p_eir - EIR
+ *                  eirl_len - EIR len
+ *                  uuid_size - Uuid::kNumBytes16, Uuid::kNumBytes32,
+ *                              Uuid::kNumBytes128
+ *                  p_num_uuid - return number of UUID in found list
+ *                  p_uuid_list - return UUID 16-bit list
+ *                  max_num_uuid - maximum number of UUID to be returned
+ *
+ * Returns          0 - if not found
+ *                  BTM_EIR_COMPLETE_16BITS_UUID_TYPE
+ *                  BTM_EIR_MORE_16BITS_UUID_TYPE
+ *                  BTM_EIR_COMPLETE_32BITS_UUID_TYPE
+ *                  BTM_EIR_MORE_32BITS_UUID_TYPE
+ *                  BTM_EIR_COMPLETE_128BITS_UUID_TYPE
+ *                  BTM_EIR_MORE_128BITS_UUID_TYPE
+ *
+ ******************************************************************************/
+uint8_t BTM_GetEirUuidList(uint8_t* p_eir, size_t eir_len, uint8_t uuid_size,
+                           uint8_t* p_num_uuid, uint8_t* p_uuid_list,
+                           uint8_t max_num_uuid);
+
+/*******************************************************************************
+ *
+ * Function         BTM_SecAddBleDevice
+ *
+ * Description      Add/modify device.  This function will be normally called
+ *                  during host startup to restore all required information
+ *                  for a LE device stored in the NVRAM.
+ *
+ * Parameters:      bd_addr          - BD address of the peer
+ *                  bd_name          - Name of the peer device. NULL if unknown.
+ *                  dev_type         - Remote device's device type.
+ *                  addr_type        - LE device address type.
+ *
+ * Returns          true if added OK, else false
+ *
+ ******************************************************************************/
+bool BTM_SecAddBleDevice(const RawAddress& bd_addr, BD_NAME bd_name,
+                         tBT_DEVICE_TYPE dev_type, tBLE_ADDR_TYPE addr_type);
+
+/*******************************************************************************
+ *
+ * Function         BTM_SecAddBleKey
+ *
+ * Description      Add/modify LE device information.  This function will be
+ *                  normally called during host startup to restore all required
+ *                  information stored in the NVRAM.
+ *
+ * Parameters:      bd_addr          - BD address of the peer
+ *                  p_le_key         - LE key values.
+ *                  key_type         - LE SMP key type.
+ *
+ * Returns          true if added OK, else false
+ *
+ ******************************************************************************/
+bool BTM_SecAddBleKey(const RawAddress& bd_addr, tBTM_LE_KEY_VALUE* p_le_key,
+                      tBTM_LE_KEY_TYPE key_type);
+
+/*******************************************************************************
+ *
+ * Function         BTM_BleLoadLocalKeys
+ *
+ * Description      Local local identity key, encryption root or sign counter.
+ *
+ * Parameters:      key_type: type of key, can be BTM_BLE_KEY_TYPE_ID,
+ *                            BTM_BLE_KEY_TYPE_ER
+ *                            or BTM_BLE_KEY_TYPE_COUNTER.
+ *                  p_key: pointer to the key.
+ *
+ * Returns          non2.
+ *
+ ******************************************************************************/
+void BTM_BleLoadLocalKeys(uint8_t key_type, tBTM_BLE_LOCAL_KEYS* p_key);
+
+/** Returns local device encryption root (ER) */
+const Octet16& BTM_GetDeviceEncRoot();
+
+/** Returns local device identity root (IR) */
+const Octet16& BTM_GetDeviceIDRoot();
+
+/** Return local device DHK. */
+const Octet16& BTM_GetDeviceDHK();
+
+/*******************************************************************************
+ *
+ * Function         BTM_ReadConnectionAddr
+ *
+ * Description      Read the local device random address.
+ *
+ * Returns          void
+ *
+ ******************************************************************************/
+void BTM_ReadConnectionAddr(const RawAddress& remote_bda,
+                            RawAddress& local_conn_addr,
+                            tBLE_ADDR_TYPE* p_addr_type);
+
+/*******************************************************************************
+ *
+ * Function         BTM_IsBleConnection
+ *
+ * Description      This function is called to check if the connection handle
+ *                  for an LE link
+ *
+ * Returns          true if connection is LE link, otherwise false.
+ *
+ ******************************************************************************/
+bool BTM_IsBleConnection(uint16_t conn_handle);
+
+/*******************************************************************************
+ *
+ * Function         BTM_ReadRemoteConnectionAddr
+ *
+ * Description      Read the remote device address currently used.
+ *
+ * Returns          void
+ *
+ ******************************************************************************/
+bool BTM_ReadRemoteConnectionAddr(const RawAddress& pseudo_addr,
+                                  RawAddress& conn_addr,
+                                  tBLE_ADDR_TYPE* p_addr_type);
+
+/*******************************************************************************
+ *
+ * Function         BTM_SecurityGrant
+ *
+ * Description      This function is called to grant security process.
+ *
+ * Parameters       bd_addr - peer device bd address.
+ *                  res     - result of the operation BTM_SUCCESS if success.
+ *                            Otherwise, BTM_REPEATED_ATTEMPTS is too many
+ *                            attempts.
+ *
+ * Returns          None
+ *
+ ******************************************************************************/
+void BTM_SecurityGrant(const RawAddress& bd_addr, uint8_t res);
+
+/*******************************************************************************
+ *
+ * Function         BTM_BlePasskeyReply
+ *
+ * Description      This function is called after Security Manager submitted
+ *                  passkey request to the application.
+ *
+ * Parameters:      bd_addr - Address of the device for which passkey was
+ *                            requested
+ *                  res     - result of the operation SMP_SUCCESS if success
+ *                  passkey - numeric value in the range of
+ *                               BTM_MIN_PASSKEY_VAL(0) -
+ *                               BTM_MAX_PASSKEY_VAL(999999(0xF423F)).
+ *
+ ******************************************************************************/
+void BTM_BlePasskeyReply(const RawAddress& bd_addr, uint8_t res,
+                         uint32_t passkey);
+
+/*******************************************************************************
+ *
+ * Function         BTM_BleConfirmReply
+ *
+ * Description      This function is called after Security Manager submitted
+ *                  numeric comparison request to the application.
+ *
+ * Parameters:      bd_addr      - Address of the device with which numeric
+ *                                 comparison was requested
+ *                  res          - comparison result BTM_SUCCESS if success
+ *
+ ******************************************************************************/
+void BTM_BleConfirmReply(const RawAddress& bd_addr, uint8_t res);
+
+/*******************************************************************************
+ *
+ * Function         BTM_LeOobDataReply
+ *
+ * Description      This function is called to provide the OOB data for
+ *                  SMP in response to BTM_LE_OOB_REQ_EVT
+ *
+ * Parameters:      bd_addr     - Address of the peer device
+ *                  res         - result of the operation SMP_SUCCESS if success
+ *                  p_data      - simple pairing Randomizer  C.
+ *
+ ******************************************************************************/
+void BTM_BleOobDataReply(const RawAddress& bd_addr, uint8_t res, uint8_t len,
+                         uint8_t* p_data);
+
+/*******************************************************************************
+ *
+ * Function         BTM_BleSecureConnectionOobDataReply
+ *
+ * Description      This function is called to provide the OOB data for
+ *                  SMP in response to BTM_LE_OOB_REQ_EVT when secure connection
+ *                  data is available
+ *
+ * Parameters:      bd_addr     - Address of the peer device
+ *                  p_c         - pointer to Confirmation
+ *                  p_r         - pointer to Randomizer.
+ *
+ ******************************************************************************/
+void BTM_BleSecureConnectionOobDataReply(const RawAddress& bd_addr,
+                                         uint8_t* p_c, uint8_t* p_r);
+
+/******************************************************************************
+ *
+ * Function         BTM_BleSetConnScanParams
+ *
+ * Description      Set scan parameters used in BLE connection request
+ *
+ * Parameters:      scan_interval    - scan interval
+ *                  scan_window      - scan window
+ *
+ * Returns          void
+ *
+ ******************************************************************************/
+void BTM_BleSetConnScanParams(uint32_t scan_interval, uint32_t scan_window);
+
+/********************************************************
+ *
+ * Function         BTM_BleSetPrefConnParams
+ *
+ * Description      Set a peripheral's preferred connection parameters. When
+ *                  any of the value does not want to be updated while others
+ *                  do, use BTM_BLE_CONN_PARAM_UNDEF for the ones want to
+ *                  leave untouched.
+ *
+ * Parameters:      bd_addr          - BD address of the peripheral
+ *                  min_conn_int     - minimum preferred connection interval
+ *                  max_conn_int     - maximum preferred connection interval
+ *                  slave_latency    - preferred slave latency
+ *                  supervision_tout - preferred supervision timeout
+ *
+ * Returns          void
+ *
+ ******************************************************************************/
+void BTM_BleSetPrefConnParams(const RawAddress& bd_addr, uint16_t min_conn_int,
+                              uint16_t max_conn_int, uint16_t slave_latency,
+                              uint16_t supervision_tout);
+
+/*******************************************************************************
+ *
+ * Function         BTM_ReadDevInfo
+ *
+ * Description      This function is called to read the device/address type
+ *                  of BD address.
+ *
+ * Parameter        remote_bda: remote device address
+ *                  p_dev_type: output parameter to read the device type.
+ *                  p_addr_type: output parameter to read the address type.
+ *
+ ******************************************************************************/
+void BTM_ReadDevInfo(const RawAddress& remote_bda, tBT_DEVICE_TYPE* p_dev_type,
+                     tBLE_ADDR_TYPE* p_addr_type);
+
+/*******************************************************************************
+ *
+ * Function         BTM_ReadConnectedTransportAddress
+ *
+ * Description      This function is called to read the paired device/address
+ *                  type of other device paired corresponding to the BD_address
+ *
+ * Parameter        remote_bda: remote device address, carry out the transport
+ *                              address
+ *                  transport: active transport
+ *
+ * Return           true if an active link is identified; false otherwise
+ *
+ ******************************************************************************/
+bool BTM_ReadConnectedTransportAddress(RawAddress* remote_bda,
+                                       tBT_TRANSPORT transport);
+
+/*******************************************************************************
+ *
+ * Function         BTM_BleReceiverTest
+ *
+ * Description      This function is called to start the LE Receiver test
+ *
+ * Parameter       rx_freq - Frequency Range
+ *               p_cmd_cmpl_cback - Command Complete callback
+ *
+ ******************************************************************************/
+void BTM_BleReceiverTest(uint8_t rx_freq, tBTM_CMPL_CB* p_cmd_cmpl_cback);
+
+/*******************************************************************************
+ *
+ * Function         BTM_BleTransmitterTest
+ *
+ * Description      This function is called to start the LE Transmitter test
+ *
+ * Parameter       tx_freq - Frequency Range
+ *                       test_data_len - Length in bytes of payload data in each
+ *                                       packet
+ *                       packet_payload - Pattern to use in the payload
+ *                       p_cmd_cmpl_cback - Command Complete callback
+ *
+ ******************************************************************************/
+void BTM_BleTransmitterTest(uint8_t tx_freq, uint8_t test_data_len,
+                            uint8_t packet_payload,
+                            tBTM_CMPL_CB* p_cmd_cmpl_cback);
+
+/*******************************************************************************
+ *
+ * Function         BTM_BleTestEnd
+ *
+ * Description     This function is called to stop the in-progress TX or RX test
+ *
+ * Parameter       p_cmd_cmpl_cback - Command complete callback
+ *
+ ******************************************************************************/
+void BTM_BleTestEnd(tBTM_CMPL_CB* p_cmd_cmpl_cback);
+
+/*******************************************************************************
+ *
+ * Function         BTM_UseLeLink
+ *
+ * Description      Select the underlying physical link to use.
+ *
+ * Returns          true to use LE, false use BR/EDR.
+ *
+ ******************************************************************************/
+bool BTM_UseLeLink(const RawAddress& bd_addr);
+
+/*******************************************************************************
+ *
+ * Function         BTM_SetBleDataLength
+ *
+ * Description      Set the maximum BLE transmission packet size
+ *
+ * Returns          BTM_SUCCESS if success; otherwise failed.
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_SetBleDataLength(const RawAddress& bd_addr,
+                                 uint16_t tx_pdu_length);
+
+/*******************************************************************************
+ *
+ * Function         BTM_BleReadPhy
+ *
+ * Description      To read the current PHYs for specified LE connection
+ *
+ *
+ * Returns          BTM_SUCCESS if success; otherwise failed.
+ *
+ ******************************************************************************/
+void BTM_BleReadPhy(
+    const RawAddress& bd_addr,
+    base::Callback<void(uint8_t tx_phy, uint8_t rx_phy, uint8_t status)> cb);
+
+/*******************************************************************************
+ *
+ * Function         BTM_BleSetDefaultPhy
+ *
+ * Description      To set preferred PHY for ensuing LE connections
+ *
+ *
+ * Returns          BTM_SUCCESS if success; otherwise failed.
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_BleSetDefaultPhy(uint8_t all_phys, uint8_t tx_phys,
+                                 uint8_t rx_phys);
+
+/*******************************************************************************
+ *
+ * Function         BTM_BleSetPhy
+ *
+ * Description      To set PHY preferences for specified LE connection
+ *
+ *
+ * Returns          BTM_SUCCESS if success; otherwise failed.
+ *                  BTM_MODE_UNSUPPORTED if local controller doesn't support LE
+ *                  2M or LE Coded PHY,
+ *                  BTM_ILLEGAL_VALUE if specified remote doesn't support LE 2M
+ *                  or LE Coded PHY,
+ *                  BTM_WRONG_MODE if Device in wrong mode for request.
+ *
+ ******************************************************************************/
+void BTM_BleSetPhy(const RawAddress& bd_addr, uint8_t tx_phys, uint8_t rx_phys,
+                   uint16_t phy_options);
+
+/*******************************************************************************
+ *
+ * Function         BTM_BleDataSignature
+ *
+ * Description      This function is called to sign the data using AES128 CMAC
+ *                  algorith.
+ *
+ * Parameter        bd_addr: target device the data to be signed for.
+ *                  p_text: singing data
+ *                  len: length of the signing data
+ *                  signature: output parameter where data signature is going to
+ *                             be stored.
+ *
+ * Returns          true if signing sucessul, otherwise false.
+ *
+ ******************************************************************************/
+bool BTM_BleDataSignature(const RawAddress& bd_addr, uint8_t* p_text,
+                          uint16_t len, BLE_SIGNATURE signature);
+
+/*******************************************************************************
+ *
+ * Function         BTM_BleVerifySignature
+ *
+ * Description      This function is called to verify the data signature
+ *
+ * Parameter        bd_addr: target device the data to be signed for.
+ *                  p_orig:  original data before signature.
+ *                  len: length of the signing data
+ *                  counter: counter used when doing data signing
+ *                  p_comp: signature to be compared against.
+
+ * Returns          true if signature verified correctly; otherwise false.
+ *
+ ******************************************************************************/
+bool BTM_BleVerifySignature(const RawAddress& bd_addr, uint8_t* p_orig,
+                            uint16_t len, uint32_t counter, uint8_t* p_comp);
+
+/*******************************************************************************
+ *
+ * Function         BTM_GetLeSecurityState
+ *
+ * Description      This function is called to get security mode 1 flags and
+ *                  encryption key size for LE peer.
+ *
+ * Returns          bool    true if LE device is found, false otherwise.
+ *
+ ******************************************************************************/
+bool BTM_GetLeSecurityState(const RawAddress& bd_addr,
+                            uint8_t* p_le_dev_sec_flags,
+                            uint8_t* p_le_key_size);
+
+/*******************************************************************************
+ *
+ * Function         BTM_BleSecurityProcedureIsRunning
+ *
+ * Description      This function indicates if LE security procedure is
+ *                  currently running with the peer.
+ *
+ * Returns          bool true if security procedure is running, false otherwise.
+ *
+ ******************************************************************************/
+bool BTM_BleSecurityProcedureIsRunning(const RawAddress& bd_addr);
+
+/*******************************************************************************
+ *
+ * Function         BTM_BleGetSupportedKeySize
+ *
+ * Description      This function gets the maximum encryption key size in bytes
+ *                  the local device can suport.
+ *                  record.
+ *
+ * Returns          the key size or 0 if the size can't be retrieved.
+ *
+ ******************************************************************************/
+uint8_t BTM_BleGetSupportedKeySize(const RawAddress& bd_addr);
+
+void BTM_LE_PF_local_name(tBTM_BLE_SCAN_COND_OP action,
+                          tBTM_BLE_PF_FILT_INDEX filt_index,
+                          std::vector<uint8_t> name, tBTM_BLE_PF_CFG_CBACK cb);
+
+void BTM_LE_PF_srvc_data(tBTM_BLE_SCAN_COND_OP action,
+                         tBTM_BLE_PF_FILT_INDEX filt_index);
+
+void BTM_LE_PF_manu_data(tBTM_BLE_SCAN_COND_OP action,
+                         tBTM_BLE_PF_FILT_INDEX filt_index, uint16_t company_id,
+                         uint16_t company_id_mask, std::vector<uint8_t> data,
+                         std::vector<uint8_t> data_mask,
+                         tBTM_BLE_PF_CFG_CBACK cb);
+
+void BTM_LE_PF_srvc_data_pattern(tBTM_BLE_SCAN_COND_OP action,
+                                 tBTM_BLE_PF_FILT_INDEX filt_index,
+                                 std::vector<uint8_t> data,
+                                 std::vector<uint8_t> data_mask,
+                                 tBTM_BLE_PF_CFG_CBACK cb);
+
+void BTM_LE_PF_addr_filter(tBTM_BLE_SCAN_COND_OP action,
+                           tBTM_BLE_PF_FILT_INDEX filt_index, tBLE_BD_ADDR addr,
+                           tBTM_BLE_PF_CFG_CBACK cb);
+
+void BTM_LE_PF_uuid_filter(tBTM_BLE_SCAN_COND_OP action,
+                           tBTM_BLE_PF_FILT_INDEX filt_index,
+                           tBTM_BLE_PF_COND_TYPE filter_type,
+                           const bluetooth::Uuid& uuid,
+                           tBTM_BLE_PF_LOGIC_TYPE cond_logic,
+                           const bluetooth::Uuid& uuid_mask,
+                           tBTM_BLE_PF_CFG_CBACK cb);
+
+void BTM_LE_PF_set(tBTM_BLE_PF_FILT_INDEX filt_index,
+                   std::vector<ApcfCommand> commands, tBTM_BLE_PF_CFG_CBACK cb);
+void BTM_LE_PF_clear(tBTM_BLE_PF_FILT_INDEX filt_index,
+                     tBTM_BLE_PF_CFG_CBACK cb);
+
+/*******************************************************************************
+ *
+ * Function         BTM_BleAdvFilterParamSetup
+ *
+ * Description      This function is called to setup the adv data payload filter
+ *                  condition.
+ *
+ ******************************************************************************/
+void BTM_BleAdvFilterParamSetup(
+    int action, tBTM_BLE_PF_FILT_INDEX filt_index,
+    std::unique_ptr<btgatt_filt_param_setup_t> p_filt_params,
+    tBTM_BLE_PF_PARAM_CB cb);
+
+/*******************************************************************************
+ *
+ * Function         BTM_BleUpdateAdvFilterPolicy
+ *
+ * Description      This function update the filter policy of advertiser.
+ *
+ * Parameter        adv_policy: advertising filter policy
+ *
+ * Return           void
+ ******************************************************************************/
+void BTM_BleUpdateAdvFilterPolicy(tBTM_BLE_AFP adv_policy);
+
+/*******************************************************************************
+ *
+ * Function         BTM_BleEnableDisableFilterFeature
+ *
+ * Description      Enable or disable the APCF feature
+ *
+ * Parameters       enable - true - enables APCF, false - disables APCF
+ *
+ ******************************************************************************/
+void BTM_BleEnableDisableFilterFeature(uint8_t enable,
+                                       tBTM_BLE_PF_STATUS_CBACK p_stat_cback);
+
+/*******************************************************************************
+ *
+ * Function          BTM_BleMaxMultiAdvInstanceCount
+ *
+ * Description      Returns the maximum number of multi adv instances supported
+ *                  by the controller.
+ *
+ * Returns          Max multi adv instance count
+ *
+ ******************************************************************************/
+uint8_t BTM_BleMaxMultiAdvInstanceCount();
+
+/*******************************************************************************
+ *
+ * Function         BTM_DeviceReset
+ *
+ * Description      This function is called to reset the controller.  The
+ *                  Callback function if provided is called when startup of the
+ *                  device has completed.
+ *
+ * Returns          void
+ *
+ ******************************************************************************/
+void BTM_DeviceReset(tBTM_CMPL_CB* p_cb);
+
+/*******************************************************************************
+ *
+ * Function         BTM_IsDeviceUp
+ *
+ * Description      This function is called to check if the device is up.
+ *
+ * Returns          true if device is up, else false
+ *
+ ******************************************************************************/
+bool BTM_IsDeviceUp(void);
+
+/*******************************************************************************
+ *
+ * Function         BTM_SetLocalDeviceName
+ *
+ * Description      This function is called to set the local device name.
+ *
+ * Returns          BTM_CMD_STARTED if successful, otherwise an error
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_SetLocalDeviceName(char* p_name);
+
+/*******************************************************************************
+ *
+ * Function         BTM_SetDeviceClass
+ *
+ * Description      This function is called to set the local device class
+ *
+ * Returns          BTM_SUCCESS if successful, otherwise an error
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_SetDeviceClass(DEV_CLASS dev_class);
+
+/*******************************************************************************
+ *
+ * Function         BTM_ReadLocalDeviceName
+ *
+ * Description      This function is called to read the local device name.
+ *
+ * Returns          status of the operation
+ *                  If success, BTM_SUCCESS is returned and p_name points stored
+ *                              local device name
+ *                  If BTM doesn't store local device name, BTM_NO_RESOURCES is
+ *                              is returned and p_name is set to NULL
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_ReadLocalDeviceName(char** p_name);
+
+/*******************************************************************************
+ *
+ * Function         BTM_ReadLocalDeviceNameFromController
+ *
+ * Description      Get local device name from controller. Do not use cached
+ *                  name (used to get chip-id prior to btm reset complete).
+ *
+ * Returns          BTM_CMD_STARTED if successful, otherwise an error
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_ReadLocalDeviceNameFromController(
+    tBTM_CMPL_CB* p_rln_cmpl_cback);
+
+/*******************************************************************************
+ *
+ * Function         BTM_ReadDeviceClass
+ *
+ * Description      This function is called to read the local device class
+ *
+ * Returns          pointer to the device class
+ *
+ ******************************************************************************/
+uint8_t* BTM_ReadDeviceClass(void);
+
+/*******************************************************************************
+ *
+ * Function         BTM_ReadLocalFeatures
+ *
+ * Description      This function is called to read the local features
+ *
+ * Returns          pointer to the local features string
+ *
+ ******************************************************************************/
+uint8_t* BTM_ReadLocalFeatures(void);
+
+/*******************************************************************************
+ *
+ * Function         BTM_RegisterForDeviceStatusNotif
+ *
+ * Description      This function is called to register for device status
+ *                  change notifications.
+ *
+ * Returns          pointer to previous caller's callback function or NULL if
+ *                  first registration.
+ *
+ ******************************************************************************/
+tBTM_DEV_STATUS_CB* BTM_RegisterForDeviceStatusNotif(tBTM_DEV_STATUS_CB* p_cb);
+
+/*******************************************************************************
+ *
+ * Function         BTM_RegisterForVSEvents
+ *
+ * Description      This function is called to register/deregister for vendor
+ *                  specific HCI events.
+ *
+ *                  If is_register=true, then the function will be registered;
+ *                  otherwise the function will be deregistered.
+ *
+ * Returns          BTM_SUCCESS if successful,
+ *                  BTM_BUSY if maximum number of callbacks have already been
+ *                           registered.
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_RegisterForVSEvents(tBTM_VS_EVT_CB* p_cb, bool is_register);
+
+/*******************************************************************************
+ *
+ * Function         BTM_VendorSpecificCommand
+ *
+ * Description      Send a vendor specific HCI command to the controller.
+ *
+ ******************************************************************************/
+void BTM_VendorSpecificCommand(uint16_t opcode, uint8_t param_len,
+                               uint8_t* p_param_buf, tBTM_VSC_CMPL_CB* p_cb);
+
+/*******************************************************************************
+ *
+ * Function         BTM_AllocateSCN
+ *
+ * Description      Look through the Server Channel Numbers for a free one to be
+ *                  used with an RFCOMM connection.
+ *
+ * Returns          Allocated SCN number or 0 if none.
+ *
+ ******************************************************************************/
+uint8_t BTM_AllocateSCN(void);
+
+/*******************************************************************************
+ *
+ * Function         BTM_TryAllocateSCN
+ *
+ * Description      Try to allocate a fixed server channel
+ *
+ * Returns          Returns true if server channel was available
+ *
+ ******************************************************************************/
+bool BTM_TryAllocateSCN(uint8_t scn);
+
+/*******************************************************************************
+ *
+ * Function         BTM_FreeSCN
+ *
+ * Description      Free the specified SCN.
+ *
+ * Returns          true if successful, false if SCN is not in use or invalid
+ *
+ ******************************************************************************/
+bool BTM_FreeSCN(uint8_t scn);
+
+/*******************************************************************************
+ *
+ * Function         BTM_SetTraceLevel
+ *
+ * Description      This function sets the trace level for BTM.  If called with
+ *                  a value of 0xFF, it simply returns the current trace level.
+ *
+ * Returns          The new or current trace level
+ *
+ ******************************************************************************/
+uint8_t BTM_SetTraceLevel(uint8_t new_level);
+
+/*******************************************************************************
+ *
+ * Function         BTM_WritePageTimeout
+ *
+ * Description      Send HCI Wite Page Timeout.
+ *
+ ******************************************************************************/
+void BTM_WritePageTimeout(uint16_t timeout);
+
+/*******************************************************************************
+ *
+ * Function         BTM_WriteVoiceSettings
+ *
+ * Description      Send HCI Write Voice Settings command.
+ *                  See hcidefs.h for settings bitmask values.
+ *
+ ******************************************************************************/
+void BTM_WriteVoiceSettings(uint16_t settings);
+
+/*******************************************************************************
+ *
+ * Function         BTM_EnableTestMode
+ *
+ * Description      Send HCI the enable device under test command.
+ *
+ *                  Note: Controller can only be taken out of this mode by
+ *                      resetting the controller.
+ *
+ * Returns
+ *      BTM_SUCCESS         Command sent.
+ *      BTM_NO_RESOURCES    If out of resources to send the command.
+ *
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_EnableTestMode(void);
+
+/*******************************************************************************
+ *
+ * Function         BTM_ReadRemoteVersion
+ *
+ * Description      This function is called to read a remote device's version
+ *
+ * Returns          BTM_SUCCESS if successful, otherwise an error
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_ReadRemoteVersion(const RawAddress& addr, uint8_t* lmp_version,
+                                  uint16_t* manufacturer,
+                                  uint16_t* lmp_sub_version);
+
+/*******************************************************************************
+ *
+ * Function         BTM_ReadRemoteFeatures
+ *
+ * Description      This function is called to read a remote device's
+ *                  supported features mask (features mask located at page 0)
+ *
+ *                  Note: The size of device features mask page is
+ *                  BTM_FEATURE_BYTES_PER_PAGE bytes.
+ *
+ * Returns          pointer to the remote supported features mask
+ *
+ ******************************************************************************/
+uint8_t* BTM_ReadRemoteFeatures(const RawAddress& addr);
+
+/*****************************************************************************
+ *  ACL CHANNEL MANAGEMENT FUNCTIONS
+ ****************************************************************************/
+/*******************************************************************************
+ *
+ * Function         BTM_SetLinkPolicy
+ *
+ * Description      Create and send HCI "Write Policy Set" command
+ *
+ * Returns          BTM_CMD_STARTED if successfully initiated, otherwise error
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_SetLinkPolicy(const RawAddress& remote_bda, uint16_t* settings);
+
+/*******************************************************************************
+ *
+ * Function         BTM_SetDefaultLinkPolicy
+ *
+ * Description      Set the default value for HCI "Write Policy Set" command
+ *                  to use when an ACL link is created.
+ *
+ * Returns          void
+ *
+ ******************************************************************************/
+void BTM_SetDefaultLinkPolicy(uint16_t settings);
+
+/*******************************************************************************
+ *
+ * Function         BTM_SetDefaultLinkSuperTout
+ *
+ * Description      Set the default value for HCI "Write Link Supervision
+ *                  Timeout" command to use when an ACL link is created.
+ *
+ * Returns          void
+ *
+ ******************************************************************************/
+void BTM_SetDefaultLinkSuperTout(uint16_t timeout);
+
+/*******************************************************************************
+ *
+ * Function         BTM_SetLinkSuperTout
+ *
+ * Description      Create and send HCI "Write Link Supervision Timeout" command
+ *
+ * Returns          BTM_CMD_STARTED if successfully initiated, otherwise error
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_SetLinkSuperTout(const RawAddress& remote_bda,
+                                 uint16_t timeout);
+/*******************************************************************************
+ *
+ * Function         BTM_GetLinkSuperTout
+ *
+ * Description      Read the link supervision timeout value of the connection
+ *
+ * Returns          status of the operation
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_GetLinkSuperTout(const RawAddress& remote_bda,
+                                 uint16_t* p_timeout);
+
+/*******************************************************************************
+ *
+ * Function         BTM_IsAclConnectionUp
+ *
+ * Description      This function is called to check if an ACL connection exists
+ *                  to a specific remote BD Address.
+ *
+ * Returns          true if connection is up, else false.
+ *
+ ******************************************************************************/
+bool BTM_IsAclConnectionUp(const RawAddress& remote_bda,
+                           tBT_TRANSPORT transport);
+
+/*******************************************************************************
+ *
+ * Function         BTM_GetRole
+ *
+ * Description      This function is called to get the role of the local device
+ *                  for the ACL connection with the specified remote device
+ *
+ * Returns          BTM_SUCCESS if connection exists.
+ *                  BTM_UNKNOWN_ADDR if no active link with bd addr specified
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_GetRole(const RawAddress& remote_bd_addr, uint8_t* p_role);
+
+/*******************************************************************************
+ *
+ * Function         BTM_SwitchRole
+ *
+ * Description      This function is called to switch role between master and
+ *                  slave.  If role is already set it will do nothing.  If the
+ *                  command was initiated, the callback function is called upon
+ *                  completion.
+ *
+ * Returns          BTM_SUCCESS if already in specified role.
+ *                  BTM_CMD_STARTED if command issued to controller.
+ *                  BTM_NO_RESOURCES if memory couldn't be allocated to issue
+ *                                   the command
+ *                  BTM_UNKNOWN_ADDR if no active link with bd addr specified
+ *                  BTM_MODE_UNSUPPORTED if the local device does not support
+ *                                       role switching
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_SwitchRole(const RawAddress& remote_bd_addr, uint8_t new_role,
+                           tBTM_CMPL_CB* p_cb);
+
+/*******************************************************************************
+ *
+ * Function         BTM_ReadRSSI
+ *
+ * Description      This function is called to read the link policy settings.
+ *                  The address of link policy results are returned in the
+ *                  callback. (tBTM_RSSI_RESULT)
+ *
+ * Returns          BTM_CMD_STARTED if command issued to controller.
+ *                  BTM_NO_RESOURCES if memory couldn't be allocated to issue
+ *                                   the command
+ *                  BTM_UNKNOWN_ADDR if no active link with bd addr specified
+ *                  BTM_BUSY if command is already in progress
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_ReadRSSI(const RawAddress& remote_bda, tBTM_CMPL_CB* p_cb);
+
+/*******************************************************************************
+ *
+ * Function         BTM_ReadFailedContactCounter
+ *
+ * Description      This function is called to read the failed contact counter.
+ *                  The result is returned in the callback.
+ *                  (tBTM_FAILED_CONTACT_COUNTER_RESULT)
+ *
+ * Returns          BTM_CMD_STARTED if command issued to controller.
+ *                  BTM_NO_RESOURCES if memory couldn't be allocated to issue
+ *                                   the command
+ *                  BTM_UNKNOWN_ADDR if no active link with bd addr specified
+ *                  BTM_BUSY if command is already in progress
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_ReadFailedContactCounter(const RawAddress& remote_bda,
+                                         tBTM_CMPL_CB* p_cb);
+
+/*******************************************************************************
+ *
+ * Function         BTM_ReadAutomaticFlushTimeout
+ *
+ * Description      This function is called to read the automatic flush timeout.
+ *                  The result is returned in the callback.
+ *                  (tBTM_AUTOMATIC_FLUSH_TIMEOUT_RESULT)
+ *
+ * Returns          BTM_CMD_STARTED if command issued to controller.
+ *                  BTM_NO_RESOURCES if memory couldn't be allocated to issue
+ *                                   the command
+ *                  BTM_UNKNOWN_ADDR if no active link with bd addr specified
+ *                  BTM_BUSY if command is already in progress
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_ReadAutomaticFlushTimeout(const RawAddress& remote_bda,
+                                          tBTM_CMPL_CB* p_cb);
+
+/*******************************************************************************
+ *
+ * Function         BTM_ReadTxPower
+ *
+ * Description      This function is called to read the current connection
+ *                  TX power of the connection. The TX power level results
+ *                  are returned in the callback.
+ *                  (tBTM_RSSI_RESULT)
+ *
+ * Returns          BTM_CMD_STARTED if command issued to controller.
+ *                  BTM_NO_RESOURCES if memory couldn't be allocated to issue
+ *                                   the command
+ *                  BTM_UNKNOWN_ADDR if no active link with bd addr specified
+ *                  BTM_BUSY if command is already in progress
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_ReadTxPower(const RawAddress& remote_bda,
+                            tBT_TRANSPORT transport, tBTM_CMPL_CB* p_cb);
+
+/*******************************************************************************
+ *
+ * Function         BTM_RegBusyLevelNotif
+ *
+ * Description      This function is called to register a callback to receive
+ *                  busy level change events.
+ *
+ * Returns          BTM_SUCCESS if successfully registered, otherwise error
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_RegBusyLevelNotif(tBTM_BL_CHANGE_CB* p_cb, uint8_t* p_level,
+                                  tBTM_BL_EVENT_MASK evt_mask);
+
+/*******************************************************************************
+ *
+ * Function         BTM_GetNumAclLinks
+ *
+ * Description      This function is called to count the number of
+ *                  ACL links that are active.
+ *
+ * Returns          uint16_t Number of active ACL links
+ *
+ ******************************************************************************/
+uint16_t BTM_GetNumAclLinks(void);
+
+/*****************************************************************************
+ *  (e)SCO CHANNEL MANAGEMENT FUNCTIONS
+ ****************************************************************************/
+/*******************************************************************************
+ *
+ * Function         BTM_CreateSco
+ *
+ * Description      This function is called to create an SCO connection. If the
+ *                  "is_orig" flag is true, the connection will be originated,
+ *                  otherwise BTM will wait for the other side to connect.
+ *
+ * Returns          BTM_UNKNOWN_ADDR if the ACL connection is not up
+ *                  BTM_BUSY         if another SCO being set up to
+ *                                   the same BD address
+ *                  BTM_NO_RESOURCES if the max SCO limit has been reached
+ *                  BTM_CMD_STARTED  if the connection establishment is started.
+ *                                   In this case, "*p_sco_inx" is filled in
+ *                                   with the sco index used for the connection.
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_CreateSco(const RawAddress* remote_bda, bool is_orig,
+                          uint16_t pkt_types, uint16_t* p_sco_inx,
+                          tBTM_SCO_CB* p_conn_cb, tBTM_SCO_CB* p_disc_cb);
+
+/*******************************************************************************
+ *
+ * Function         BTM_RemoveSco
+ *
+ * Description      This function is called to remove a specific SCO connection.
+ *
+ * Returns          BTM_CMD_STARTED if successfully initiated, otherwise error
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_RemoveSco(uint16_t sco_inx);
+
+/*******************************************************************************
+ *
+ * Function         BTM_ReadScoBdAddr
+ *
+ * Description      This function is read the remote BD Address for a specific
+ *                  SCO connection,
+ *
+ * Returns          pointer to BD address or NULL if not known
+ *
+ ******************************************************************************/
+const RawAddress* BTM_ReadScoBdAddr(uint16_t sco_inx);
+
+/*******************************************************************************
+ *
+ * Function         BTM_SetEScoMode
+ *
+ * Description      This function sets up the negotiated parameters for SCO or
+ *                  eSCO, and sets as the default mode used for calls to
+ *                  BTM_CreateSco.  It can be called only when there are no
+ *                  active (e)SCO links.
+ *
+ * Returns          BTM_SUCCESS if the successful.
+ *                  BTM_BUSY if there are one or more active (e)SCO links.
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_SetEScoMode(enh_esco_params_t* p_parms);
+
+/*******************************************************************************
+ *
+ * Function         BTM_RegForEScoEvts
+ *
+ * Description      This function registers a SCO event callback with the
+ *                  specified instance.  It should be used to received
+ *                  connection indication events and change of link parameter
+ *                  events.
+ *
+ * Returns          BTM_SUCCESS if the successful.
+ *                  BTM_ILLEGAL_VALUE if there is an illegal sco_inx
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_RegForEScoEvts(uint16_t sco_inx, tBTM_ESCO_CBACK* p_esco_cback);
+
+/*******************************************************************************
+ *
+ * Function         BTM_ChangeEScoLinkParms
+ *
+ * Description      This function requests renegotiation of the parameters on
+ *                  the current eSCO Link.  If any of the changes are accepted
+ *                  by the controllers, the BTM_ESCO_CHG_EVT event is sent in
+ *                  the tBTM_ESCO_CBACK function with the current settings of
+ *                  the link. The callback is registered through the call to
+ *                  BTM_SetEScoMode.
+ *
+ *
+ * Returns          BTM_CMD_STARTED if command is successfully initiated.
+ *                  BTM_ILLEGAL_VALUE if no connection for specified sco_inx.
+ *                  BTM_NO_RESOURCES - not enough resources to initiate command.
+ *                  BTM_MODE_UNSUPPORTED if local controller does not support
+ *                      1.2 specification.
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_ChangeEScoLinkParms(uint16_t sco_inx,
+                                    tBTM_CHG_ESCO_PARAMS* p_parms);
+
+/*******************************************************************************
+ *
+ * Function         BTM_EScoConnRsp
+ *
+ * Description      This function is called upon receipt of an (e)SCO connection
+ *                  request event (BTM_ESCO_CONN_REQ_EVT) to accept or reject
+ *                  the request. Parameters used to negotiate eSCO links.
+ *                  If p_parms is NULL, then values set through BTM_SetEScoMode
+ *                  are used.
+ *                  If the link type of the incoming request is SCO, then only
+ *                  the tx_bw, max_latency, content format, and packet_types are
+ *                  valid.  The hci_status parameter should be
+ *                  ([0x0] to accept, [0x0d..0x0f] to reject)
+ *
+ *
+ * Returns          void
+ *
+ ******************************************************************************/
+void BTM_EScoConnRsp(uint16_t sco_inx, uint8_t hci_status,
+                     enh_esco_params_t* p_parms);
+
+/*******************************************************************************
+ *
+ * Function         BTM_GetNumScoLinks
+ *
+ * Description      This function returns the number of active SCO links.
+ *
+ * Returns          uint8_t
+ *
+ ******************************************************************************/
+uint8_t BTM_GetNumScoLinks(void);
+
+/*****************************************************************************
+ *  SECURITY MANAGEMENT FUNCTIONS
+ ****************************************************************************/
+/*******************************************************************************
+ *
+ * Function         BTM_SecRegister
+ *
+ * Description      Application manager calls this function to register for
+ *                  security services.  There can be one and only one
+ *                  application saving link keys.  BTM allows only first
+ *                  registration.
+ *
+ * Returns          true if registered OK, else false
+ *
+ ******************************************************************************/
+bool BTM_SecRegister(const tBTM_APPL_INFO* p_cb_info);
+
+/*******************************************************************************
+ *
+ * Function         BTM_SecAddRmtNameNotifyCallback
+ *
+ * Description      Profiles can register to be notified when name of the
+ *                  remote device is resolved (up to
+ *                  BTM_SEC_MAX_RMT_NAME_CALLBACKS).
+ *
+ * Returns          true if registered OK, else false
+ *
+ ******************************************************************************/
+bool BTM_SecAddRmtNameNotifyCallback(tBTM_RMT_NAME_CALLBACK* p_callback);
+
+/*******************************************************************************
+ *
+ * Function         BTM_SecDeleteRmtNameNotifyCallback
+ *
+ * Description      A profile can deregister notification when a new Link Key
+ *                  is generated per connection.
+ *
+ * Returns          true if OK, else false
+ *
+ ******************************************************************************/
+bool BTM_SecDeleteRmtNameNotifyCallback(tBTM_RMT_NAME_CALLBACK* p_callback);
+
+/*******************************************************************************
+ *
+ * Function         BTM_GetSecurityFlags
+ *
+ * Description      Get security flags for the device
+ *
+ * Returns          bool    true or false is device found
+ *
+ ******************************************************************************/
+bool BTM_GetSecurityFlags(const RawAddress& bd_addr, uint8_t* p_sec_flags);
+
+/*******************************************************************************
+ *
+ * Function         BTM_GetSecurityFlagsByTransport
+ *
+ * Description      Get security flags for the device on a particular transport
+ *
+ * Parameters      bd_addr: BD address of remote device
+ *                  p_sec_flags : Out parameter to be filled with security
+ *                                flags for the connection
+ *                  transport :  Physical transport of the connection
+ *                               (BR/EDR or LE)
+ *
+ * Returns          bool    true or false is device found
+ *
+ ******************************************************************************/
+bool BTM_GetSecurityFlagsByTransport(const RawAddress& bd_addr,
+                                     uint8_t* p_sec_flags,
+                                     tBT_TRANSPORT transport);
+
+/*******************************************************************************
+ *
+ * Function         BTM_ReadTrustedMask
+ *
+ * Description      Get trusted mask for the device
+ *
+ * Returns          NULL, if the device record is not found.
+ *                  otherwise, the trusted mask
+ *
+ ******************************************************************************/
+uint32_t* BTM_ReadTrustedMask(const RawAddress& bd_addr);
+
+/*******************************************************************************
+ *
+ * Function         BTM_SetPinType
+ *
+ * Description      Set PIN type for the device.
+ *
+ * Returns          void
+ *
+ ******************************************************************************/
+void BTM_SetPinType(uint8_t pin_type, PIN_CODE pin_code, uint8_t pin_code_len);
+
+/*******************************************************************************
+ *
+ * Function         BTM_SetPairableMode
+ *
+ * Description      Enable or disable pairing
+ *
+ * Parameters       allow_pairing - (true or false) whether or not the device
+ *                      allows pairing.
+ *                  connect_only_paired - (true or false) whether or not to
+ *                      only allow paired devices to connect.
+ *
+ * Returns          void
+ *
+ ******************************************************************************/
+void BTM_SetPairableMode(bool allow_pairing, bool connect_only_paired);
+
+/*******************************************************************************
+ *
+ * Function         BTM_SetSecurityLevel
+ *
+ * Description      Register service security level with Security Manager.  Each
+ *                  service must register its requirements regardless of the
+ *                  security level that is used.  This API is called once for
+ *                  originators and again for acceptors of connections.
+ *
+ * Returns          true if registered OK, else false
+ *
+ ******************************************************************************/
+bool BTM_SetSecurityLevel(bool is_originator, const char* p_name,
+                          uint8_t service_id, uint16_t sec_level, uint16_t psm,
+                          uint32_t mx_proto_id, uint32_t mx_chan_id);
+
+/*******************************************************************************
+ *
+ * Function         BTM_SetOutService
+ *
+ * Description      This function is called to set the service for
+ *                  outgoing connection.
+ *
+ * Returns          void
+ *
+ ******************************************************************************/
+void BTM_SetOutService(const RawAddress& bd_addr, uint8_t service_id,
+                       uint32_t mx_chan_id);
+
+/*******************************************************************************
+ *
+ * Function         BTM_SecClrService
+ *
+ * Description      Removes specified service record(s) from the security
+ *                  database. All service records with the specified name are
+ *                  removed. Typically used only by devices with limited RAM
+ *                  so that it can reuse an old security service record.
+ *
+ * Returns          Number of records that were freed.
+ *
+ ******************************************************************************/
+uint8_t BTM_SecClrService(uint8_t service_id);
+
+/*******************************************************************************
+ *
+ * Function         BTM_SecAddDevice
+ *
+ * Description      Add/modify device.  This function will be normally called
+ *                  during host startup to restore all required information
+ *                  stored in the NVRAM.
+ *                  dev_class, bd_name, link_key, and features are NULL if
+ *                  unknown
+ *
+ * Returns          true if added OK, else false
+ *
+ ******************************************************************************/
+bool BTM_SecAddDevice(const RawAddress& bd_addr, DEV_CLASS dev_class,
+                      BD_NAME bd_name, uint8_t* features,
+                      uint32_t trusted_mask[], LinkKey* link_key,
+                      uint8_t key_type, tBTM_IO_CAP io_cap, uint8_t pin_length);
+
+/** Free resources associated with the device associated with |bd_addr| address.
+ *
+ * *** WARNING ***
+ * tBTM_SEC_DEV_REC associated with bd_addr becomes invalid after this function
+ * is called, also any of it's fields. i.e. if you use p_dev_rec->bd_addr, it is
+ * no longer valid!
+ * *** WARNING ***
+ *
+ * Returns true if removed OK, false if not found or ACL link is active.
+ */
+bool BTM_SecDeleteDevice(const RawAddress& bd_addr);
+
+/*******************************************************************************
+ *
+ * Function         BTM_SecClearSecurityFlags
+ *
+ * Description      Reset the security flags (mark as not-paired) for a given
+ *                  remove device.
+ *
+ ******************************************************************************/
+void BTM_SecClearSecurityFlags(const RawAddress& bd_addr);
+
+/*******************************************************************************
+ *
+ * Function         BTM_SecGetDeviceLinkKeyType
+ *
+ * Description      This function is called to obtain link key type for the
+ *                  device.
+ *                  it returns BTM_SUCCESS if link key is available, or
+ *                  BTM_UNKNOWN_ADDR if Security Manager does not know about
+ *                  the device or device record does not contain link key info
+ *
+ * Returns          BTM_LKEY_TYPE_IGNORE if link key is unknown, link type
+ *                  otherwise.
+ *
+ ******************************************************************************/
+tBTM_LINK_KEY_TYPE BTM_SecGetDeviceLinkKeyType(const RawAddress& bd_addr);
+
+/*******************************************************************************
+ *
+ * Function         BTM_PINCodeReply
+ *
+ * Description      This function is called after Security Manager submitted
+ *                  PIN code request to the UI.
+ *
+ * Parameters:      bd_addr      - Address of the device for which PIN was
+ *                                 requested
+ *                  res          - result of the operation BTM_SUCCESS if
+ *                                 success
+ *                  pin_len      - length in bytes of the PIN Code
+ *                  p_pin        - pointer to array with the PIN Code
+ *                  trusted_mask - bitwise OR of trusted services
+ *                                 (array of uint32_t)
+ *
+ * Returns          void
+ *
+ ******************************************************************************/
+void BTM_PINCodeReply(const RawAddress& bd_addr, uint8_t res, uint8_t pin_len,
+                      uint8_t* p_pin, uint32_t trusted_mask[]);
+
+/*******************************************************************************
+ *
+ * Function         BTM_SecBond
+ *
+ * Description      Perform bonding by designated transport
+ *
+ * Parameters:      bd_addr      - Address of the device to bond
+ *                  addr_type    - address type for LE transport
+ *                  pin_len      - length in bytes of the PIN Code
+ *                  p_pin        - pointer to array with the PIN Code
+ *                  trusted_mask - bitwise OR of trusted services
+ *                                 (array of uint32_t)
+ *                  transport :  Physical transport to use for bonding
+ *                               (BR/EDR or LE)
+ *
+ * Returns          BTM_CMD_STARTED if successfully initiated, otherwise error
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_SecBond(const RawAddress& bd_addr, tBLE_ADDR_TYPE addr_type,
+                        tBT_TRANSPORT transport, uint8_t pin_len,
+                        uint8_t* p_pin, uint32_t trusted_mask[]);
+
+/*******************************************************************************
+ *
+ * Function         BTM_SecBondCancel
+ *
+ * Description      This function is called to cancel ongoing bonding process
+ *                  with peer device.
+ *
+ * Returns          BTM_CMD_STARTED if successfully initiated, otherwise error
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_SecBondCancel(const RawAddress& bd_addr);
+
+/*******************************************************************************
+ *
+ * Function         BTM_SetEncryption
+ *
+ * Description      This function is called to ensure that connection is
+ *                  encrypted.  Should be called only on an open connection.
+ *                  Typically only needed for connections that first want to
+ *                  bring up unencrypted links, then later encrypt them.
+ *
+ * Parameters:      bd_addr       - Address of the peer device
+ *                  transport     - Link transport
+ *                  p_callback    - Pointer to callback function called if
+ *                                  this function returns PENDING after required
+ *                                  procedures are completed.  Can be set to
+ *                                  NULL if status is not desired.
+ *                  p_ref_data    - pointer to any data the caller wishes to
+ *                                  receive in the callback function upon
+ *                                  completion.
+ *                                  can be set to NULL if not used.
+ *                  sec_act       - LE security action, unused for BR/EDR
+ *
+ * Returns          BTM_SUCCESS   - already encrypted
+ *                  BTM_PENDING   - command will be returned in the callback
+ *                  BTM_WRONG_MODE- connection not up.
+ *                  BTM_BUSY      - security procedures are currently active
+ *                  BTM_MODE_UNSUPPORTED - if security manager not linked in.
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_SetEncryption(const RawAddress& bd_addr,
+                              tBT_TRANSPORT transport,
+                              tBTM_SEC_CBACK* p_callback, void* p_ref_data,
+                              tBTM_BLE_SEC_ACT sec_act);
+
+/*******************************************************************************
+ *
+ * Function         BTM_ConfirmReqReply
+ *
+ * Description      This function is called to confirm the numeric value for
+ *                  Simple Pairing in response to BTM_SP_CFM_REQ_EVT
+ *
+ * Parameters:      res           - result of the operation BTM_SUCCESS if
+ *                                  success
+ *                  bd_addr       - Address of the peer device
+ *
+ ******************************************************************************/
+void BTM_ConfirmReqReply(tBTM_STATUS res, const RawAddress& bd_addr);
+
+/*******************************************************************************
+ *
+ * Function         BTM_PasskeyReqReply
+ *
+ * Description      This function is called to provide the passkey for
+ *                  Simple Pairing in response to BTM_SP_KEY_REQ_EVT
+ *
+ * Parameters:      res           - result of the operation BTM_SUCCESS if
+ *                                  success
+ *                  bd_addr       - Address of the peer device
+ *                  passkey       - numeric value in the range of
+ *                                  0 - 999999(0xF423F).
+ *
+ ******************************************************************************/
+void BTM_PasskeyReqReply(tBTM_STATUS res, const RawAddress& bd_addr,
+                         uint32_t passkey);
+
+/*******************************************************************************
+ *
+ * Function         BTM_SendKeypressNotif
+ *
+ * Description      This function is used during the passkey entry model
+ *                  by a device with KeyboardOnly IO capabilities
+ *                  (very likely to be a HID Device).
+ *                  It is called by a HID Device to inform the remote device
+ *                  when a key has been entered or erased.
+ *
+ * Parameters:      bd_addr - Address of the peer device
+ *                  type - notification type
+ *
+ ******************************************************************************/
+void BTM_SendKeypressNotif(const RawAddress& bd_addr, tBTM_SP_KEY_TYPE type);
+
+/*******************************************************************************
+ *
+ * Function         BTM_IoCapRsp
+ *
+ * Description      This function is called in response to BTM_SP_IO_REQ_EVT
+ *                  When the event data io_req.oob_data is set to
+ *                  BTM_OOB_UNKNOWN by the tBTM_SP_CALLBACK implementation, this
+ *                  function is called to provide the actual response
+ *
+ * Parameters:      bd_addr - Address of the peer device
+ *                  io_cap  - The IO capability of local device.
+ *                  oob     - BTM_OOB_NONE or BTM_OOB_PRESENT.
+ *                  auth_req- MITM protection required or not.
+ *
+ ******************************************************************************/
+void BTM_IoCapRsp(const RawAddress& bd_addr, tBTM_IO_CAP io_cap,
+                  tBTM_OOB_DATA oob, tBTM_AUTH_REQ auth_req);
+
+/*******************************************************************************
+ *
+ * Function         BTM_ReadLocalOobData
+ *
+ * Description      This function is called to read the local OOB data from
+ *                  LM
+ *
+ ******************************************************************************/
+void BTM_ReadLocalOobData(void);
+
+/*******************************************************************************
+ *
+ * Function         BTM_RemoteOobDataReply
+ *
+ * Description      This function is called to provide the remote OOB data for
+ *                  Simple Pairing in response to BTM_SP_RMT_OOB_EVT
+ *
+ * Parameters:      bd_addr     - Address of the peer device
+ *                  c           - simple pairing Hash C.
+ *                  r           - simple pairing Randomizer  C.
+ *
+ ******************************************************************************/
+void BTM_RemoteOobDataReply(tBTM_STATUS res, const RawAddress& bd_addr,
+                            const Octet16& c, const Octet16& r);
+
+/*******************************************************************************
+ *
+ * Function         BTM_BothEndsSupportSecureConnections
+ *
+ * Description      This function is called to check if both the local device
+ *                  and the peer device specified by bd_addr support BR/EDR
+ *                  Secure Connections.
+ *
+ * Parameters:      bd_addr - address of the peer
+ *
+ * Returns          true if BR/EDR Secure Connections are supported by both
+ *                  local and the remote device.
+ *                  else false.
+ *
+ ******************************************************************************/
+bool BTM_BothEndsSupportSecureConnections(const RawAddress& bd_addr);
+
+/*******************************************************************************
+ *
+ * Function         BTM_PeerSupportsSecureConnections
+ *
+ * Description      This function is called to check if the peer supports
+ *                  BR/EDR Secure Connections.
+ *
+ * Parameters:      bd_addr - address of the peer
+ *
+ * Returns          true if BR/EDR Secure Connections are supported by the peer,
+ *                  else false.
+ *
+ ******************************************************************************/
+bool BTM_PeerSupportsSecureConnections(const RawAddress& bd_addr);
+
+/*******************************************************************************
+ *
+ * Function         BTM_SecReadDevName
+ *
+ * Description      Looks for the device name in the security database for the
+ *                  specified BD address.
+ *
+ * Returns          Pointer to the name or NULL
+ *
+ ******************************************************************************/
+char* BTM_SecReadDevName(const RawAddress& bd_addr);
+
+/*****************************************************************************
+ *  POWER MANAGEMENT FUNCTIONS
+ ****************************************************************************/
+/*******************************************************************************
+ *
+ * Function         BTM_PmRegister
+ *
+ * Description      register or deregister with power manager
+ *
+ * Returns          BTM_SUCCESS if successful,
+ *                  BTM_NO_RESOURCES if no room to hold registration
+ *                  BTM_ILLEGAL_VALUE
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_PmRegister(uint8_t mask, uint8_t* p_pm_id,
+                           tBTM_PM_STATUS_CBACK* p_cb);
+
+/*******************************************************************************
+ *
+ * Function         BTM_SetPowerMode
+ *
+ * Description      store the mode in control block or
+ *                  alter ACL connection behavior.
+ *
+ * Returns          BTM_SUCCESS if successful,
+ *                  BTM_UNKNOWN_ADDR if bd addr is not active or bad
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_SetPowerMode(uint8_t pm_id, const RawAddress& remote_bda,
+                             const tBTM_PM_PWR_MD* p_mode);
+
+/*******************************************************************************
+ *
+ * Function         BTM_ReadPowerMode
+ *
+ * Description      This returns the current mode for a specific
+ *                  ACL connection.
+ *
+ * Input Param      remote_bda - device address of desired ACL connection
+ *
+ * Output Param     p_mode - address where the current mode is copied into.
+ *                          BTM_ACL_MODE_NORMAL
+ *                          BTM_ACL_MODE_HOLD
+ *                          BTM_ACL_MODE_SNIFF
+ *                          BTM_ACL_MODE_PARK
+ *                          (valid only if return code is BTM_SUCCESS)
+ *
+ * Returns          BTM_SUCCESS if successful,
+ *                  BTM_UNKNOWN_ADDR if bd addr is not active or bad
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_ReadPowerMode(const RawAddress& remote_bda,
+                              tBTM_PM_MODE* p_mode);
+
+/*******************************************************************************
+ *
+ * Function         BTM_SetSsrParams
+ *
+ * Description      This sends the given SSR parameters for the given ACL
+ *                  connection if it is in ACTIVE mode.
+ *
+ * Input Param      remote_bda - device address of desired ACL connection
+ *                  max_lat    - maximum latency (in 0.625ms)(0-0xFFFE)
+ *                  min_rmt_to - minimum remote timeout
+ *                  min_loc_to - minimum local timeout
+ *
+ *
+ * Returns          BTM_SUCCESS if the HCI command is issued successful,
+ *                  BTM_UNKNOWN_ADDR if bd addr is not active or bad
+ *                  BTM_CMD_STORED if the command is stored
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_SetSsrParams(const RawAddress& remote_bda, uint16_t max_lat,
+                             uint16_t min_rmt_to, uint16_t min_loc_to);
+
+/*******************************************************************************
+ *
+ * Function         BTM_GetHCIConnHandle
+ *
+ * Description      This function is called to get the handle for an ACL
+ *                  connection to a specific remote BD Address.
+ *
+ * Returns          the handle of the connection, or 0xFFFF if none.
+ *
+ ******************************************************************************/
+uint16_t BTM_GetHCIConnHandle(const RawAddress& remote_bda,
+                              tBT_TRANSPORT transport);
+
+/*******************************************************************************
+ *
+ * Function         BTM_DeleteStoredLinkKey
+ *
+ * Description      This function is called to delete link key for the specified
+ *                  device addresses from the NVRAM storage attached to the
+ *                  Bluetooth controller.
+ *
+ * Parameters:      bd_addr      - Addresses of the devices
+ *                  p_cb         - Call back function to be called to return
+ *                                 the results
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_DeleteStoredLinkKey(const RawAddress* bd_addr,
+                                    tBTM_CMPL_CB* p_cb);
+
+tBTM_CONTRL_STATE BTM_PM_ReadControllerState(void);
+
+/**
+ *
+ * BLE API
+ */
+/*******************************************************************************
+ *
+ * Function         BTM_BleObtainVendorCapabilities
+ *
+ * Description      This function is called to obatin vendor capabilties
+ *
+ * Parameters       p_cmn_vsc_cb - Returns the vednor capabilities
+ *
+ * Returns          void
+ *
+ ******************************************************************************/
+void BTM_BleObtainVendorCapabilities(tBTM_BLE_VSC_CB* p_cmn_vsc_cb);
+
+/**
+ * This function is called to set scan parameters. |cb| is called with operation
+ * status
+ **/
+void BTM_BleSetScanParams(uint32_t scan_interval, uint32_t scan_window,
+                          tBLE_SCAN_MODE scan_type,
+                          base::Callback<void(uint8_t)> cb);
+
+/*******************************************************************************
+ *
+ * Function         BTM_BleGetVendorCapabilities
+ *
+ * Description      This function reads local LE features
+ *
+ * Parameters       p_cmn_vsc_cb : Locala LE capability structure
+ *
+ * Returns          void
+ *
+ ******************************************************************************/
+void BTM_BleGetVendorCapabilities(tBTM_BLE_VSC_CB* p_cmn_vsc_cb);
+
+/*******************************************************************************
+ *
+ * Function         BTM_BleSetStorageConfig
+ *
+ * Description      This function is called to setup storage configuration and
+ *                  setup callbacks.
+ *
+ * Parameters       uint8_t batch_scan_full_max -Batch scan full maximum
+ *                  uint8_t batch_scan_trunc_max - Batch scan truncated value
+ *maximum uint8_t batch_scan_notify_threshold - Threshold value cb - Setup
+ *callback tBTM_BLE_SCAN_THRESHOLD_CBACK *p_thres_cback -Threshold callback void
+ **p_ref - Reference value
+ *
+ *
+ ******************************************************************************/
+void BTM_BleSetStorageConfig(uint8_t batch_scan_full_max,
+                             uint8_t batch_scan_trunc_max,
+                             uint8_t batch_scan_notify_threshold,
+                             base::Callback<void(uint8_t /* status */)> cb,
+                             tBTM_BLE_SCAN_THRESHOLD_CBACK* p_thres_cback,
+                             tBTM_BLE_REF_VALUE ref_value);
+
+/* This function is called to enable batch scan */
+void BTM_BleEnableBatchScan(tBTM_BLE_BATCH_SCAN_MODE scan_mode,
+                            uint32_t scan_interval, uint32_t scan_window,
+                            tBTM_BLE_DISCARD_RULE discard_rule,
+                            tBLE_ADDR_TYPE addr_type,
+                            base::Callback<void(uint8_t /* status */)> cb);
+
+/* This function is called to disable batch scanning */
+void BTM_BleDisableBatchScan(base::Callback<void(uint8_t /* status */)> cb);
+
+/* This function is called to read batch scan reports */
+void BTM_BleReadScanReports(tBLE_SCAN_MODE scan_mode,
+                            tBTM_BLE_SCAN_REP_CBACK cb);
+
+/* This function is called to setup the callback for tracking */
+void BTM_BleTrackAdvertiser(tBTM_BLE_TRACK_ADV_CBACK* p_track_cback,
+                            tBTM_BLE_REF_VALUE ref_value);
+
+/*******************************************************************************
+ *
+ * Function         BTM_BleWriteScanRsp
+ *
+ * Description      This function is called to write LE scan response.
+ *
+ * Parameters:      p_scan_rsp: scan response.
+ *
+ * Returns          status
+ *
+ ******************************************************************************/
+void BTM_BleWriteScanRsp(uint8_t* data, uint8_t length,
+                         tBTM_BLE_ADV_DATA_CMPL_CBACK* p_adv_data_cback);
+
+/******************************************************************************
+ *
+ * Function         BTM_BleReadControllerFeatures
+ *
+ * Description      Reads BLE specific controller features
+ *
+ * Parameters:      tBTM_BLE_CTRL_FEATURES_CBACK : Callback to notify when
+ *                  features are read
+ *
+ * Returns          void
+ *
+ ******************************************************************************/
+void BTM_BleReadControllerFeatures(tBTM_BLE_CTRL_FEATURES_CBACK* p_vsc_cback);
+
+/*******************************************************************************
+ *
+ * Function         BTM__BLEReadDiscoverability
+ *
+ * Description      This function is called to read the current LE
+ *                  discoverability mode of the device.
+ *
+ * Returns          BTM_BLE_NON_DISCOVERABLE ,BTM_BLE_LIMITED_DISCOVERABLE or
+ *                     BTM_BLE_GENRAL_DISCOVERABLE
+ *
+ ******************************************************************************/
+uint16_t BTM_BleReadDiscoverability();
+
+/*******************************************************************************
+ *
+ * Function         BTM__BLEReadConnectability
+ *
+ * Description      This function is called to read the current LE
+ *                  connectibility mode of the device.
+ *
+ * Returns          BTM_BLE_NON_CONNECTABLE or BTM_BLE_CONNECTABLE
+ *
+ ******************************************************************************/
+uint16_t BTM_BleReadConnectability();
+
+/*******************************************************************************
+ *
+ * Function         BTM_BleConfigPrivacy
+ *
+ * Description      This function is called to enable or disable the privacy in
+ *                  the local device.
+ *
+ * Parameters       enable: true to enable it; false to disable it.
+ *
+ * Returns          bool    privacy mode set success; otherwise failed.
+ *
+ ******************************************************************************/
+bool BTM_BleConfigPrivacy(bool enable);
+
+/*******************************************************************************
+ *
+ * Function         BTM_BleLocalPrivacyEnabled
+ *
+ * Description        Checks if local device supports private address
+ *
+ * Returns          Return true if local privacy is enabled else false
+ *
+ ******************************************************************************/
+bool BTM_BleLocalPrivacyEnabled(void);
+
+/*******************************************************************************
+ *
+ * Function         BTM_BleEnableMixedPrivacyMode
+ *
+ * Description      This function is called to enabled Mixed mode if privacy 1.2
+ *                  is applicable in controller.
+ *
+ * Parameters       mixed_on:  mixed mode to be used or not.
+ *
+ * Returns          void
+ *
+ ******************************************************************************/
+void BTM_BleEnableMixedPrivacyMode(bool mixed_on);
+
+/*******************************************************************************
+ *
+ * Function         BTM_BleSetConnectableMode
+ *
+ * Description      This function is called to set BLE connectable mode for a
+ *                  peripheral device.
+ *
+ * Parameters       connectable_mode:  directed connectable mode, or
+ *                                     non-directed. It can be
+ *                                     BTM_BLE_CONNECT_EVT,
+ *                                     BTM_BLE_CONNECT_DIR_EVT or
+ *                                     BTM_BLE_CONNECT_LO_DUTY_DIR_EVT
+ *
+ * Returns          BTM_ILLEGAL_VALUE if controller does not support BLE.
+ *                  BTM_SUCCESS is status set successfully; otherwise failure.
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_BleSetConnectableMode(tBTM_BLE_CONN_MODE connectable_mode);
+
+/*******************************************************************************
+ *
+ * Function         BTM_BleTurnOnPrivacyOnRemote
+ *
+ * Description      This function is called to enable or disable the privacy on
+ *                  the remote device.
+ *
+ * Parameters       bd_addr: remote device address.
+ *                  privacy_on: true to enable it; false to disable it.
+ *
+ * Returns          void
+ *
+ ******************************************************************************/
+void BTM_BleTurnOnPrivacyOnRemote(const RawAddress& bd_addr, bool privacy_on);
+
+/*******************************************************************************
+ *
+ * Function         BTM_BleStackEnable
+ *
+ * Description      Enable/Disable BLE functionality on stack regardless of
+ *                  controller capability.
+ *
+ * Parameters:      enable: true to enable, false to disable.
+ *
+ * Returns          true if added OK, else false
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_BleStackEnable(bool enable);
+
+/**
+ * This functions are called to configure the adv data payload filter condition
+ */
+/*******************************************************************************
+ *
+ * Function         BTM_BleGetEnergyInfo
+ *
+ * Description      This function obtains the energy info
+ *
+ * Parameters       p_ener_cback - Callback pointer
+ *
+ * Returns          status
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_BleGetEnergyInfo(tBTM_BLE_ENERGY_INFO_CBACK* p_ener_cback);
+
+/*******************************************************************************
+ *
+ * Function         BTM_SecBond
+ *
+ * Description      This function is called to perform bonding with peer device.
+ *                  If the connection is already up, but not secure, pairing
+ *                  is attempted.  If already paired BTM_SUCCESS is returned.
+ *
+ * Parameters:      bd_addr      - Address of the device to bond
+ *                  addr_type    - address type for LE transport
+ *                  transport    - doing SSP over BR/EDR or SMP over LE
+ *                  pin_len      - length in bytes of the PIN Code
+ *                  p_pin        - pointer to array with the PIN Code
+ *                  trusted_mask - bitwise OR of trusted services
+ *                                 (array of uint32_t)
+ *
+ *  Note: After 2.1 parameters are not used and preserved here not to change API
+ ******************************************************************************/
+tBTM_STATUS BTM_SecBond(const RawAddress& bd_addr, tBLE_ADDR_TYPE addr_type,
+                        tBT_TRANSPORT transport, uint8_t pin_len,
+                        uint8_t* p_pin, uint32_t trusted_mask[]);
+
+/*******************************************************************************
+ *
+ * Function         BTM_SecRegister
+ *
+ * Description      Application manager calls this function to register for
+ *                  security services.  There can be one and only one
+ *                  application saving link keys.  BTM allows only first
+ *                  registration.
+ *
+ * Returns          true if registered OK, else false
+ *
+ ******************************************************************************/
+bool BTM_SecRegister(const tBTM_APPL_INFO* p_cb_info);
+
+/** Free resources associated with the device associated with |bd_addr| address.
+ *
+ * *** WARNING ***
+ * tBTM_SEC_DEV_REC associated with bd_addr becomes invalid after this function
+ * is called, also any of it's fields. i.e. if you use p_dev_rec->bd_addr, it is
+ * no longer valid!
+ * *** WARNING ***
+ *
+ * Returns true if removed OK, false if not found or ACL link is active.
+ */
+bool BTM_SecDeleteDevice(const RawAddress& bd_addr);
+
+}  // namespace shim
+}  // namespace bluetooth
diff --git a/main/shim/config.cc b/main/shim/config.cc
new file mode 100644
index 0000000..ac8fa37
--- /dev/null
+++ b/main/shim/config.cc
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "bt_shim_storage"
+
+#include <cstdint>
+#include <cstring>
+#include <future>
+#include <memory>
+
+#include "btif/include/btif_config.h"
+#include "main/shim/config.h"
+#include "main/shim/entry.h"
+
+#include "storage/legacy.h"
+
+using ::bluetooth::shim::GetStorage;
+
+namespace bluetooth {
+namespace shim {
+
+std::string checksum_read(const char* filename) {
+  CHECK(filename != nullptr);
+
+  std::promise<std::string> promise;
+  auto future = promise.get_future();
+  GetStorage()->ChecksumRead(
+      std::string(filename),
+      common::BindOnce(
+          [](std::promise<std::string>* promise, std::string,
+             std::string hash_value) { promise->set_value(hash_value); },
+          &promise),
+      bluetooth::shim::GetGdShimHandler());
+  return future.get();
+}
+
+bool checksum_save(const std::string& checksum, const std::string& filename) {
+  std::promise<bool> promise;
+  auto future = promise.get_future();
+  GetStorage()->ChecksumWrite(
+      filename, checksum,
+      common::BindOnce([](std::promise<bool>* promise, std::string,
+                          bool success) { promise->set_value(success); },
+                       &promise),
+      bluetooth::shim::GetGdShimHandler());
+  return future.get();
+}
+
+std::unique_ptr<config_t> config_new(const char* filename) {
+  CHECK(filename != nullptr);
+
+  std::promise<std::unique_ptr<config_t>> promise;
+  auto future = promise.get_future();
+  GetStorage()->ConfigRead(
+      std::string(filename),
+      common::BindOnce(
+          [](std::promise<std::unique_ptr<config_t>>* promise, std::string,
+             std::unique_ptr<config_t> config) {
+            promise->set_value(std::move(config));
+          },
+          &promise),
+      bluetooth::shim::GetGdShimHandler());
+  return future.get();
+}
+
+bool config_save(const config_t& config, const std::string& filename) {
+  std::promise<bool> promise;
+  auto future = promise.get_future();
+  GetStorage()->ConfigWrite(
+      filename, config,
+      common::BindOnce([](std::promise<bool>* promise, std::string,
+                          bool success) { promise->set_value(success); },
+                       &promise),
+      bluetooth::shim::GetGdShimHandler());
+  return future.get();
+}
+
+}  // namespace shim
+}  // namespace bluetooth
+
+namespace {
+const storage_config_t interface = {
+    bluetooth::shim::checksum_read,
+    bluetooth::shim::checksum_save,
+    config_get_bool,
+    config_get_int,
+    config_get_string,
+    config_get_uint64,
+    config_has_key,
+    config_has_section,
+    bluetooth::shim::config_new,
+    config_new_clone,
+    config_new_empty,
+    config_remove_key,
+    config_remove_section,
+    bluetooth::shim::config_save,
+    config_set_bool,
+    config_set_int,
+    config_set_string,
+    config_set_uint64,
+};
+}
+
+const storage_config_t* bluetooth::shim::storage_config_get_interface() {
+  return &interface;
+}
diff --git a/main/shim/config.h b/main/shim/config.h
new file mode 100644
index 0000000..abbbf70
--- /dev/null
+++ b/main/shim/config.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "btif/include/btif_config.h"
+
+namespace bluetooth {
+namespace shim {
+
+const storage_config_t* storage_config_get_interface();
+
+}  // namespace shim
+}  // namespace bluetooth
diff --git a/main/shim/controller.cc b/main/shim/controller.cc
new file mode 100644
index 0000000..cd39b84
--- /dev/null
+++ b/main/shim/controller.cc
@@ -0,0 +1,345 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "bt_shim_controller"
+
+#include "main/shim/controller.h"
+#include "btcore/include/module.h"
+#include "main/shim/entry.h"
+#include "main/shim/shim.h"
+#include "osi/include/future.h"
+#include "osi/include/log.h"
+
+#include "hci/controller.h"
+
+using ::bluetooth::shim::GetController;
+
+constexpr uint8_t kPageZero = 0;
+constexpr uint8_t kPageOne = 1;
+constexpr uint8_t kPageTwo = 2;
+constexpr uint8_t kMaxFeaturePage = 3;
+
+constexpr int kMaxSupportedCodecs = 8;  // MAX_LOCAL_SUPPORTED_CODECS_SIZE
+
+constexpr uint8_t kPhyLe1M = 0x01;
+
+/**
+ * Interesting commands supported by controller
+ */
+constexpr int kReadRemoteExtendedFeatures = 0x41c;
+constexpr int kEnhancedSetupSynchronousConnection = 0x428;
+constexpr int kEnhancedAcceptSynchronousConnection = 0x429;
+constexpr int kLeSetPrivacyMode = 0x204e;
+
+constexpr int kHciDataPreambleSize = 4;  // #define HCI_DATA_PREAMBLE_SIZE 4
+
+// Module lifecycle functions
+static future_t* start_up(void);
+static future_t* shut_down(void);
+
+EXPORT_SYMBOL extern const module_t gd_controller_module = {
+    .name = GD_CONTROLLER_MODULE,
+    .init = nullptr,
+    .start_up = start_up,
+    .shut_down = shut_down,
+    .clean_up = nullptr,
+    .dependencies = {GD_SHIM_MODULE, nullptr}};
+
+struct {
+  bool ready;
+  uint64_t feature[kMaxFeaturePage];
+  uint64_t le_feature[kMaxFeaturePage];
+  RawAddress raw_address;
+  bt_version_t bt_version;
+  uint8_t local_supported_codecs[kMaxSupportedCodecs];
+  uint8_t number_of_local_supported_codecs;
+  uint64_t le_supported_states;
+  uint8_t phy;
+} data_;
+
+static future_t* start_up(void) {
+  LOG_INFO(LOG_TAG, "%s Starting up", __func__);
+  data_.ready = true;
+
+  std::string string_address =
+      GetController()->GetControllerMacAddress().ToString();
+  RawAddress::FromString(string_address, data_.raw_address);
+
+  data_.le_supported_states =
+      bluetooth::shim::GetController()->GetControllerLeSupportedStates();
+
+  LOG_INFO(LOG_TAG, "Mac address:%s", string_address.c_str());
+
+  data_.phy = kPhyLe1M;
+
+  return future_new_immediate(FUTURE_SUCCESS);
+}
+
+static future_t* shut_down(void) {
+  data_.ready = false;
+  return future_new_immediate(FUTURE_SUCCESS);
+}
+
+/**
+ * Module methods
+ */
+#define BIT(x) (0x1ULL << (x))
+
+static bool get_is_ready(void) { return data_.ready; }
+
+static const RawAddress* get_address(void) { return &data_.raw_address; }
+
+static const bt_version_t* get_bt_version(void) { return &data_.bt_version; }
+
+static const bt_device_features_t* get_features_classic(int index) {
+  CHECK(index >= 0 && index < kMaxFeaturePage);
+  data_.feature[index] =
+      bluetooth::shim::GetController()->GetControllerLocalExtendedFeatures(
+          index);
+  return (const bt_device_features_t*)&data_.feature[index];
+}
+
+static uint8_t get_last_features_classic_index(void) {
+  return bluetooth::shim::GetController()
+      ->GetControllerLocalExtendedFeaturesMaxPageNumber();
+}
+
+static uint8_t* get_local_supported_codecs(uint8_t* number_of_codecs) {
+  CHECK(number_of_codecs != nullptr);
+  if (data_.number_of_local_supported_codecs != 0) {
+    *number_of_codecs = data_.number_of_local_supported_codecs;
+    return data_.local_supported_codecs;
+  }
+  return (uint8_t*)nullptr;
+}
+
+static const bt_device_features_t* get_features_ble(void) {
+  return (const bt_device_features_t*)&data_.le_feature[0];
+}
+
+static const uint8_t* get_ble_supported_states(void) {
+  return (const uint8_t*)&data_.le_supported_states;
+}
+
+static bool supports_simple_pairing(void) {
+  return GetController()->GetControllerLocalExtendedFeatures(kPageOne) &
+         BIT(51);
+}
+
+static bool supports_secure_connections(void) {
+  return GetController()->GetControllerLocalExtendedFeatures(kPageTwo) & BIT(8);
+}
+
+static bool supports_simultaneous_le_bredr(void) {
+  return GetController()->GetControllerLocalExtendedFeatures(kPageZero) &
+         BIT(49);
+}
+
+static bool supports_reading_remote_extended_features(void) {
+  return GetController()->IsSupported(
+      (bluetooth::hci::OpCode)kReadRemoteExtendedFeatures);
+}
+
+static bool supports_interlaced_inquiry_scan(void) {
+  return GetController()->GetControllerLocalExtendedFeatures(kPageZero) &
+         BIT(28);
+}
+
+static bool supports_rssi_with_inquiry_results(void) {
+  return GetController()->GetControllerLocalExtendedFeatures(kPageZero) &
+         BIT(28);
+}
+
+static bool supports_extended_inquiry_response(void) {
+  return GetController()->GetControllerLocalExtendedFeatures(kPageZero) &
+         BIT(48);
+}
+
+static bool supports_master_slave_role_switch(void) {
+  return GetController()->GetControllerLocalExtendedFeatures(kPageZero) &
+         BIT(5);
+}
+
+static bool supports_enhanced_setup_synchronous_connection(void) {
+  return GetController()->IsSupported(
+      (bluetooth::hci::OpCode)kEnhancedSetupSynchronousConnection);
+}
+
+static bool supports_enhanced_accept_synchronous_connection(void) {
+  return GetController()->IsSupported(
+      (bluetooth::hci::OpCode)kEnhancedAcceptSynchronousConnection);
+}
+
+static bool supports_ble(void) {
+  return GetController()->GetControllerLocalExtendedFeatures(kPageOne) & BIT(1);
+}
+
+static bool supports_ble_privacy(void) {
+  return GetController()->GetControllerLeLocalSupportedFeatures() & BIT(6);
+}
+
+static bool supports_ble_set_privacy_mode() {
+  return GetController()->IsSupported(
+      (bluetooth::hci::OpCode)kLeSetPrivacyMode);
+}
+
+static bool supports_ble_packet_extension(void) {
+  return GetController()->GetControllerLeLocalSupportedFeatures() & BIT(5);
+}
+
+static bool supports_ble_connection_parameters_request(void) {
+  return GetController()->GetControllerLeLocalSupportedFeatures() & BIT(2);
+}
+
+static bool supports_ble_2m_phy(void) {
+  return GetController()->GetControllerLeLocalSupportedFeatures() & BIT(8);
+}
+
+static bool supports_ble_coded_phy(void) {
+  return GetController()->GetControllerLeLocalSupportedFeatures() & BIT(11);
+}
+
+static bool supports_ble_extended_advertising(void) {
+  return GetController()->GetControllerLeLocalSupportedFeatures() & BIT(12);
+}
+
+static bool supports_ble_periodic_advertising(void) {
+  return GetController()->GetControllerLeLocalSupportedFeatures() & BIT(13);
+}
+
+static uint16_t get_acl_data_size_classic(void) {
+  return GetController()->GetControllerAclPacketLength();
+}
+
+static uint16_t get_acl_data_size_ble(void) {
+  ::bluetooth::hci::LeBufferSize le_buffer_size =
+      GetController()->GetControllerLeBufferSize();
+  return le_buffer_size.le_data_packet_length_;
+}
+
+static uint16_t get_acl_packet_size_classic(void) {
+  return get_acl_data_size_classic() + kHciDataPreambleSize;
+}
+
+static uint16_t get_acl_packet_size_ble(void) {
+  return get_acl_data_size_ble() + kHciDataPreambleSize;
+}
+
+static uint16_t get_ble_suggested_default_data_length(void) {
+  LOG_WARN(LOG_TAG, "%s TODO Unimplemented", __func__);
+  return 0;
+}
+
+static uint16_t get_ble_maximum_tx_data_length(void) {
+  ::bluetooth::hci::LeMaximumDataLength le_maximum_data_length =
+      GetController()->GetControllerLeMaximumDataLength();
+  return le_maximum_data_length.supported_max_tx_octets_;
+}
+
+static uint16_t get_ble_maxium_advertising_data_length(void) {
+  LOG_WARN(LOG_TAG, "%s TODO Unimplemented", __func__);
+  return 0;
+}
+
+static uint8_t get_ble_number_of_supported_advertising_sets(void) {
+  return GetController()->GetControllerLeNumberOfSupportedAdverisingSets();
+}
+
+static uint16_t get_acl_buffer_count_classic(void) {
+  return GetController()->GetControllerNumAclPacketBuffers();
+}
+
+static uint8_t get_acl_buffer_count_ble(void) {
+  LOG_WARN(LOG_TAG, "%s TODO Unimplemented", __func__);
+  return 0;
+}
+
+static uint8_t get_ble_white_list_size(void) {
+  LOG_WARN(LOG_TAG, "%s TODO Unimplemented", __func__);
+  return 0;
+}
+
+static uint8_t get_ble_resolving_list_max_size(void) {
+  LOG_WARN(LOG_TAG, "%s TODO Unimplemented", __func__);
+  return 0;
+}
+
+static void set_ble_resolving_list_max_size(int resolving_list_max_size) {
+  LOG_WARN(LOG_TAG, "%s TODO Unimplemented", __func__);
+}
+
+static uint8_t get_le_all_initiating_phys() { return data_.phy; }
+
+static const controller_t interface = {
+    get_is_ready,
+
+    get_address,
+    get_bt_version,
+
+    get_features_classic,
+    get_last_features_classic_index,
+
+    get_features_ble,
+    get_ble_supported_states,
+
+    supports_simple_pairing,
+    supports_secure_connections,
+    supports_simultaneous_le_bredr,
+    supports_reading_remote_extended_features,
+    supports_interlaced_inquiry_scan,
+    supports_rssi_with_inquiry_results,
+    supports_extended_inquiry_response,
+    supports_master_slave_role_switch,
+    supports_enhanced_setup_synchronous_connection,
+    supports_enhanced_accept_synchronous_connection,
+
+    supports_ble,
+    supports_ble_packet_extension,
+    supports_ble_connection_parameters_request,
+    supports_ble_privacy,
+    supports_ble_set_privacy_mode,
+    supports_ble_2m_phy,
+    supports_ble_coded_phy,
+    supports_ble_extended_advertising,
+    supports_ble_periodic_advertising,
+
+    get_acl_data_size_classic,
+    get_acl_data_size_ble,
+
+    get_acl_packet_size_classic,
+    get_acl_packet_size_ble,
+    get_ble_suggested_default_data_length,
+    get_ble_maximum_tx_data_length,
+    get_ble_maxium_advertising_data_length,
+    get_ble_number_of_supported_advertising_sets,
+
+    get_acl_buffer_count_classic,
+    get_acl_buffer_count_ble,
+
+    get_ble_white_list_size,
+
+    get_ble_resolving_list_max_size,
+    set_ble_resolving_list_max_size,
+    get_local_supported_codecs,
+    get_le_all_initiating_phys};
+
+const controller_t* bluetooth::shim::controller_get_interface() {
+  static bool loaded = false;
+  if (!loaded) {
+    loaded = true;
+  }
+  return &interface;
+}
diff --git a/main/shim/controller.h b/main/shim/controller.h
new file mode 100644
index 0000000..6b77982
--- /dev/null
+++ b/main/shim/controller.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "device/include/controller.h"
+
+static const char GD_CONTROLLER_MODULE[] = "gd_controller_module";
+
+namespace bluetooth {
+namespace shim {
+
+const controller_t* controller_get_interface();
+
+}  // namespace shim
+}  // namespace bluetooth
diff --git a/main/shim/dumpsys.cc b/main/shim/dumpsys.cc
new file mode 100644
index 0000000..7f7b10e
--- /dev/null
+++ b/main/shim/dumpsys.cc
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "bt_shim_storage"
+
+#include "main/shim/dumpsys.h"
+#include "main/shim/entry.h"
+#include "main/shim/shim.h"
+
+#include "shim/dumpsys.h"
+
+namespace {
+constexpr char kModuleName[] = "shim::legacy::dumpsys";
+static std::unordered_map<const void*, bluetooth::shim::DumpsysFunction>*
+    dumpsys_functions_;
+}  // namespace
+
+void bluetooth::shim::RegisterDumpsysFunction(const void* token,
+                                              DumpsysFunction func) {
+  dumpsys_functions_ =
+      new std::unordered_map<const void*, bluetooth::shim::DumpsysFunction>();
+  CHECK(dumpsys_functions_->find(token) == dumpsys_functions_->end());
+  dumpsys_functions_->insert({token, func});
+}
+
+void bluetooth::shim::UnregisterDumpsysFunction(const void* token) {
+  CHECK(dumpsys_functions_->find(token) != dumpsys_functions_->end());
+  dumpsys_functions_->erase(token);
+}
+
+void bluetooth::shim::Dump(int fd) {
+  dprintf(fd, "%s Dumping shim legacy targets:%zd\n", kModuleName,
+          dumpsys_functions_->size());
+  for (auto& dumpsys : *dumpsys_functions_) {
+    dumpsys.second(fd);
+  }
+  if (bluetooth::shim::is_gd_stack_started_up()) {
+    bluetooth::shim::GetDumpsys()->Dump(fd);
+  } else {
+    dprintf(fd, "%s gd stack has not started up\n", kModuleName);
+  }
+}
diff --git a/main/shim/dumpsys.h b/main/shim/dumpsys.h
new file mode 100644
index 0000000..91c4ea7
--- /dev/null
+++ b/main/shim/dumpsys.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <functional>
+#include <list>
+
+namespace bluetooth {
+namespace shim {
+
+using DumpsysFunction = std::function<void(int fd)>;
+
+/**
+ * Entrypoint from legacy stack to provide dumpsys functionality
+ * for both the legacy shim and the Gabeldorsche stack.
+ */
+void Dump(int fd);
+
+/**
+ * Dumpsys access for legacy shim modules.
+ */
+void RegisterDumpsysFunction(const void* token, DumpsysFunction func);
+void UnregisterDumpsysFunction(const void* token);
+
+}  // namespace shim
+}  // namespace bluetooth
diff --git a/main/shim/entry.cc b/main/shim/entry.cc
new file mode 100644
index 0000000..7bc5f0f
--- /dev/null
+++ b/main/shim/entry.cc
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "main/shim/entry.h"
+#include "osi/include/future.h"
+#include "osi/include/log.h"
+
+#include "hci/controller.h"
+#include "hci/hci_layer.h"
+#include "hci/le_advertising_manager.h"
+#include "hci/le_scanning_manager.h"
+#include "main/shim/btm.h"
+#include "neighbor/connectability.h"
+#include "neighbor/discoverability.h"
+#include "neighbor/inquiry.h"
+#include "neighbor/name.h"
+#include "neighbor/page.h"
+#include "os/handler.h"
+#include "security/security_module.h"
+#include "shim/dumpsys.h"
+#include "shim/l2cap.h"
+#include "shim/stack.h"
+#include "stack_manager.h"
+#include "storage/legacy.h"
+
+using bluetooth::shim::GetGabeldorscheStack;
+
+extern bluetooth::shim::Btm shim_btm;
+
+future_t* bluetooth::shim::StartGabeldorscheStack() {
+  GetGabeldorscheStack()->Start();
+  shim_btm.RegisterInquiryCallbacks();
+  return (future_t*)nullptr;
+}
+
+future_t* bluetooth::shim::StopGabeldorscheStack() {
+  GetGabeldorscheStack()->Stop();
+  return (future_t*)nullptr;
+}
+
+bluetooth::os::Handler* bluetooth::shim::GetGdShimHandler() {
+  return bluetooth::shim::GetDumpsys()->GetGdShimHandler();
+}
+
+bluetooth::hci::LeAdvertisingManager* bluetooth::shim::GetAdvertising() {
+  return GetGabeldorscheStack()
+      ->GetStackManager()
+      ->GetInstance<bluetooth::hci::LeAdvertisingManager>();
+}
+
+bluetooth::hci::Controller* bluetooth::shim::GetController() {
+  return GetGabeldorscheStack()
+      ->GetStackManager()
+      ->GetInstance<bluetooth::hci::Controller>();
+}
+
+bluetooth::neighbor::ConnectabilityModule*
+bluetooth::shim::GetConnectability() {
+  return GetGabeldorscheStack()
+      ->GetStackManager()
+      ->GetInstance<bluetooth::neighbor::ConnectabilityModule>();
+}
+
+bluetooth::neighbor::DiscoverabilityModule*
+bluetooth::shim::GetDiscoverability() {
+  return GetGabeldorscheStack()
+      ->GetStackManager()
+      ->GetInstance<bluetooth::neighbor::DiscoverabilityModule>();
+}
+
+bluetooth::shim::Dumpsys* bluetooth::shim::GetDumpsys() {
+  return GetGabeldorscheStack()
+      ->GetStackManager()
+      ->GetInstance<bluetooth::shim::Dumpsys>();
+}
+
+bluetooth::neighbor::InquiryModule* bluetooth::shim::GetInquiry() {
+  return GetGabeldorscheStack()
+      ->GetStackManager()
+      ->GetInstance<bluetooth::neighbor::InquiryModule>();
+}
+
+bluetooth::hci::HciLayer* bluetooth::shim::GetHciLayer() {
+  return GetGabeldorscheStack()
+      ->GetStackManager()
+      ->GetInstance<bluetooth::hci::HciLayer>();
+}
+
+bluetooth::shim::L2cap* bluetooth::shim::GetL2cap() {
+  return GetGabeldorscheStack()
+      ->GetStackManager()
+      ->GetInstance<bluetooth::shim::L2cap>();
+}
+
+bluetooth::neighbor::NameModule* bluetooth::shim::GetName() {
+  return GetGabeldorscheStack()
+      ->GetStackManager()
+      ->GetInstance<bluetooth::neighbor::NameModule>();
+}
+
+bluetooth::neighbor::PageModule* bluetooth::shim::GetPage() {
+  return GetGabeldorscheStack()
+      ->GetStackManager()
+      ->GetInstance<bluetooth::neighbor::PageModule>();
+}
+
+bluetooth::hci::LeScanningManager* bluetooth::shim::GetScanning() {
+  return GetGabeldorscheStack()
+      ->GetStackManager()
+      ->GetInstance<bluetooth::hci::LeScanningManager>();
+}
+
+bluetooth::security::SecurityModule* bluetooth::shim::GetSecurityModule() {
+  return GetGabeldorscheStack()
+      ->GetStackManager()
+      ->GetInstance<bluetooth::security::SecurityModule>();
+}
+
+bluetooth::storage::LegacyModule* bluetooth::shim::GetStorage() {
+  return GetGabeldorscheStack()
+      ->GetStackManager()
+      ->GetInstance<bluetooth::storage::LegacyModule>();
+}
diff --git a/main/shim/entry.h b/main/shim/entry.h
new file mode 100644
index 0000000..28cac16
--- /dev/null
+++ b/main/shim/entry.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+/**
+ * Entrypoints called into Gabeldorsche from legacy stack
+ *
+ * Any marshalling/unmarshalling, data transformation of APIs to
+ * or from the Gabeldorsche stack may be placed here.
+ *
+ * The idea is to effectively provide a binary interface to prevent cross
+ * contamination of data structures and the like between the stacks.
+ *
+ * **ABSOLUTELY** No reference to Gabeldorsche stack other than well defined
+ * interfaces may be made here
+ */
+
+#include "gd/shim/only_include_this_file_into_legacy_stack___ever.h"
+#include "osi/include/future.h"
+
+namespace bluetooth {
+namespace os {
+class Handler;
+}
+namespace neighbor {
+class ConnectabilityModule;
+class DiscoverabilityModule;
+class InquiryModule;
+class NameModule;
+class PageModule;
+}
+namespace hci {
+class Controller;
+class HciLayer;
+class LeAdvertisingManager;
+class LeScanningManager;
+}
+
+namespace security {
+class SecurityModule;
+}
+namespace storage {
+class LegacyModule;
+}
+
+namespace shim {
+future_t* StartGabeldorscheStack();
+future_t* StopGabeldorscheStack();
+
+/* This returns a handler that might be used in shim to receive callbacks from
+ * within the stack. */
+os::Handler* GetGdShimHandler();
+hci::LeAdvertisingManager* GetAdvertising();
+bluetooth::hci::Controller* GetController();
+neighbor::DiscoverabilityModule* GetDiscoverability();
+neighbor::ConnectabilityModule* GetConnectability();
+Dumpsys* GetDumpsys();
+neighbor::InquiryModule* GetInquiry();
+hci::HciLayer* GetHciLayer();
+L2cap* GetL2cap();
+neighbor::NameModule* GetName();
+neighbor::PageModule* GetPage();
+hci::LeScanningManager* GetScanning();
+bluetooth::security::SecurityModule* GetSecurityModule();
+storage::LegacyModule* GetStorage();
+
+}  // namespace shim
+}  // namespace bluetooth
diff --git a/main/shim/hci_layer.cc b/main/shim/hci_layer.cc
new file mode 100644
index 0000000..c7acdbd
--- /dev/null
+++ b/main/shim/hci_layer.cc
@@ -0,0 +1,203 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "bt_shim_hci"
+
+#include <base/bind.h>
+#include <frameworks/base/core/proto/android/bluetooth/hci/enums.pb.h>
+#include <algorithm>
+#include <cstdint>
+
+#include "btcore/include/module.h"
+#include "hci/hci_layer.h"
+#include "main/shim/hci_layer.h"
+#include "main/shim/shim.h"
+#include "osi/include/allocator.h"
+#include "osi/include/future.h"
+#include "packet/raw_builder.h"
+#include "stack/include/bt_types.h"
+
+/**
+ * Callback data wrapped as opaque token bundled with the command
+ * transmit request to the Gd layer.
+ *
+ * Upon completion a token for a corresponding command transmit.
+ * request is returned from the Gd layer.
+ */
+using CommandCallbackData = struct {
+  void* context;
+};
+
+constexpr size_t kBtHdrSize = sizeof(BT_HDR);
+constexpr size_t kCommandLengthSize = sizeof(uint8_t);
+constexpr size_t kCommandOpcodeSize = sizeof(uint16_t);
+
+static hci_t interface;
+static base::Callback<void(const base::Location&, BT_HDR*)> send_data_upwards;
+
+namespace {
+bool IsCommandStatusOpcode(bluetooth::hci::OpCode op_code) {
+  switch (op_code) {
+    case bluetooth::hci::OpCode::INQUIRY:
+    case bluetooth::hci::OpCode::CREATE_CONNECTION:
+    case bluetooth::hci::OpCode::DISCONNECT:
+    case bluetooth::hci::OpCode::ACCEPT_CONNECTION_REQUEST:
+    case bluetooth::hci::OpCode::REJECT_CONNECTION_REQUEST:
+    case bluetooth::hci::OpCode::CHANGE_CONNECTION_PACKET_TYPE:
+    case bluetooth::hci::OpCode::AUTHENTICATION_REQUESTED:
+    case bluetooth::hci::OpCode::SET_CONNECTION_ENCRYPTION:
+    case bluetooth::hci::OpCode::CHANGE_CONNECTION_LINK_KEY:
+    case bluetooth::hci::OpCode::MASTER_LINK_KEY:
+    case bluetooth::hci::OpCode::REMOTE_NAME_REQUEST:
+    case bluetooth::hci::OpCode::READ_REMOTE_SUPPORTED_FEATURES:
+    case bluetooth::hci::OpCode::READ_REMOTE_EXTENDED_FEATURES:
+    case bluetooth::hci::OpCode::READ_REMOTE_VERSION_INFORMATION:
+    case bluetooth::hci::OpCode::READ_CLOCK_OFFSET:
+    case bluetooth::hci::OpCode::SETUP_SYNCHRONOUS_CONNECTION:
+    case bluetooth::hci::OpCode::ACCEPT_SYNCHRONOUS_CONNECTION:
+    case bluetooth::hci::OpCode::REJECT_SYNCHRONOUS_CONNECTION:
+    case bluetooth::hci::OpCode::ENHANCED_SETUP_SYNCHRONOUS_CONNECTION:
+    case bluetooth::hci::OpCode::ENHANCED_ACCEPT_SYNCHRONOUS_CONNECTION:
+    case bluetooth::hci::OpCode::HOLD_MODE:
+    case bluetooth::hci::OpCode::SNIFF_MODE:
+    case bluetooth::hci::OpCode::EXIT_SNIFF_MODE:
+    case bluetooth::hci::OpCode::QOS_SETUP:
+    case bluetooth::hci::OpCode::SWITCH_ROLE:
+    case bluetooth::hci::OpCode::FLOW_SPECIFICATION:
+    case bluetooth::hci::OpCode::REFRESH_ENCRYPTION_KEY:
+    case bluetooth::hci::OpCode::LE_CREATE_CONNECTION:
+    case bluetooth::hci::OpCode::LE_CONNECTION_UPDATE:
+    case bluetooth::hci::OpCode::LE_READ_REMOTE_FEATURES:
+    case bluetooth::hci::OpCode::LE_READ_LOCAL_P_256_PUBLIC_KEY_COMMAND:
+    case bluetooth::hci::OpCode::LE_GENERATE_DHKEY_COMMAND:
+    case bluetooth::hci::OpCode::LE_SET_PHY:
+    case bluetooth::hci::OpCode::LE_EXTENDED_CREATE_CONNECTION:
+    case bluetooth::hci::OpCode::LE_PERIODIC_ADVERTISING_CREATE_SYNC:
+      return true;
+    default:
+      return false;
+  }
+}
+
+std::unique_ptr<bluetooth::packet::RawBuilder> MakeUniquePacket(
+    const uint8_t* data, size_t len) {
+  bluetooth::packet::RawBuilder builder;
+  std::vector<uint8_t> bytes(data, data + len);
+
+  auto payload = std::make_unique<bluetooth::packet::RawBuilder>();
+  payload->AddOctets(bytes);
+
+  return payload;
+}
+}  // namespace
+
+static future_t* hci_module_shut_down(void);
+static future_t* hci_module_start_up(void);
+
+EXPORT_SYMBOL extern const module_t gd_hci_module = {
+    .name = GD_HCI_MODULE,
+    .init = nullptr,
+    .start_up = hci_module_start_up,
+    .shut_down = hci_module_shut_down,
+    .clean_up = nullptr,
+    .dependencies = {GD_SHIM_MODULE, nullptr}};
+
+static future_t* hci_module_start_up(void) {
+  return nullptr;
+}
+
+static future_t* hci_module_shut_down(void) {
+  return nullptr;
+}
+
+static void set_data_cb(
+    base::Callback<void(const base::Location&, BT_HDR*)> send_data_cb) {
+  send_data_upwards = std::move(send_data_cb);
+}
+
+void OnTransmitPacketCommandComplete(command_complete_cb complete_callback,
+                                     void* context,
+                                     bluetooth::hci::CommandCompleteView view) {
+  std::vector<const uint8_t> data(view.begin(), view.end());
+
+  BT_HDR* response = static_cast<BT_HDR*>(osi_calloc(data.size() + kBtHdrSize));
+  std::copy(data.begin(), data.end(), response->data);
+  response->len = data.size();
+
+  complete_callback(response, context);
+}
+
+void OnTransmitPacketStatus(command_status_cb status_callback, void* context,
+                            bluetooth::hci::CommandStatusView view) {
+  std::vector<const uint8_t> data(view.begin(), view.end());
+
+  BT_HDR* response = static_cast<BT_HDR*>(osi_calloc(data.size() + kBtHdrSize));
+  std::copy(data.begin(), data.end(), response->data);
+  response->len = data.size();
+
+  uint8_t status = static_cast<uint8_t>(view.GetStatus());
+  status_callback(status, response, context);
+}
+
+using bluetooth::common::BindOnce;
+using bluetooth::common::Unretained;
+
+static void transmit_command(BT_HDR* command,
+                             command_complete_cb complete_callback,
+                             command_status_cb status_callback, void* context) {
+  CHECK(command != nullptr);
+  uint8_t* data = command->data + command->offset;
+  size_t len = command->len;
+  CHECK(len >= (kCommandOpcodeSize + kCommandLengthSize));
+
+  // little endian command opcode
+  uint16_t command_op_code = (data[1] << 8 | data[0]);
+  // Gd stack API requires opcode specification and calculates length, so
+  // no need to provide opcode or length here.
+  data += (kCommandOpcodeSize + kCommandLengthSize);
+  len -= (kCommandOpcodeSize + kCommandLengthSize);
+
+  const bluetooth::hci::OpCode op_code =
+      static_cast<const bluetooth::hci::OpCode>(command_op_code);
+
+  auto payload = MakeUniquePacket(data, len);
+  auto packet =
+      bluetooth::hci::CommandPacketBuilder::Create(op_code, std::move(payload));
+
+  if (IsCommandStatusOpcode(op_code)) {
+    bluetooth::shim::GetHciLayer()->EnqueueCommand(
+        std::move(packet),
+        BindOnce(OnTransmitPacketStatus, status_callback, context),
+        bluetooth::shim::GetGdShimHandler());
+  } else {
+    bluetooth::shim::GetHciLayer()->EnqueueCommand(
+        std::move(packet),
+        BindOnce(OnTransmitPacketCommandComplete, complete_callback, context),
+        bluetooth::shim::GetGdShimHandler());
+  }
+}
+
+const hci_t* bluetooth::shim::hci_layer_get_interface() {
+  static bool loaded = false;
+  if (!loaded) {
+    loaded = true;
+    interface.set_data_cb = set_data_cb;
+    interface.transmit_command = transmit_command;
+    interface.transmit_command_futured = nullptr;
+    interface.transmit_downward = nullptr;
+  }
+  return &interface;
+}
diff --git a/main/shim/hci_layer.h b/main/shim/hci_layer.h
new file mode 100644
index 0000000..6b0bbcc
--- /dev/null
+++ b/main/shim/hci_layer.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Gd shim layer to legacy hci layer stack
+ */
+#pragma once
+
+#include "hci/include/hci_layer.h"
+
+static const char GD_HCI_MODULE[] = "gd_hci_module";
+
+namespace bluetooth {
+namespace shim {
+
+const hci_t* hci_layer_get_interface();
+
+}  // namespace shim
+}  // namespace bluetooth
diff --git a/main/shim/helpers.h b/main/shim/helpers.h
new file mode 100644
index 0000000..5beb09f
--- /dev/null
+++ b/main/shim/helpers.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "hci/address_with_type.h"
+
+namespace bluetooth {
+
+hci::AddressWithType ToAddressWithType(const RawAddress& legacy_address,
+                                       tBLE_ADDR_TYPE legacy_type) {
+  // Address and RawAddress are binary equivalent;
+  hci::Address address(legacy_address.address);
+
+  hci::AddressType type;
+  if (legacy_type == BLE_ADDR_PUBLIC)
+    type = hci::AddressType::PUBLIC_DEVICE_ADDRESS;
+  else if (legacy_type == BLE_ADDR_RANDOM)
+    type = hci::AddressType::RANDOM_DEVICE_ADDRESS;
+  else if (legacy_type == BLE_ADDR_PUBLIC_ID)
+    type = hci::AddressType::PUBLIC_IDENTITY_ADDRESS;
+  else if (legacy_type == BLE_ADDR_RANDOM_ID)
+    type = hci::AddressType::RANDOM_IDENTITY_ADDRESS;
+  else {
+    LOG_ALWAYS_FATAL("Bad address type");
+    return hci::AddressWithType{address,
+                                hci::AddressType::PUBLIC_DEVICE_ADDRESS};
+  }
+
+  return hci::AddressWithType{address, type};
+}
+}  // namespace bluetooth
\ No newline at end of file
diff --git a/main/shim/l2c_api.cc b/main/shim/l2c_api.cc
new file mode 100644
index 0000000..e333e9c
--- /dev/null
+++ b/main/shim/l2c_api.cc
@@ -0,0 +1,307 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "bt_shim_l2cap"
+
+#include "main/shim/l2c_api.h"
+#include "main/shim/l2cap.h"
+#include "main/shim/shim.h"
+#include "osi/include/log.h"
+
+static bluetooth::shim::legacy::L2cap shim_l2cap;
+
+/**
+ * Classic Service Registration APIs
+ */
+uint16_t bluetooth::shim::L2CA_Register(uint16_t client_psm,
+                                        tL2CAP_APPL_INFO* callbacks,
+                                        bool enable_snoop,
+                                        tL2CAP_ERTM_INFO* p_ertm_info) {
+  CHECK(callbacks != nullptr);
+
+  if (L2C_INVALID_PSM(client_psm)) {
+    LOG_ERROR(LOG_TAG, "%s Invalid classic psm:%hd", __func__, client_psm);
+    return 0;
+  }
+
+  if ((callbacks->pL2CA_ConfigCfm_Cb == nullptr) ||
+      (callbacks->pL2CA_ConfigInd_Cb == nullptr) ||
+      (callbacks->pL2CA_DataInd_Cb == nullptr) ||
+      (callbacks->pL2CA_DisconnectInd_Cb == nullptr)) {
+    LOG_ERROR(LOG_TAG, "%s Invalid classic callbacks psm:%hd", __func__,
+              client_psm);
+    return 0;
+  }
+
+  /**
+   * Check if this is a registration for an outgoing-only connection.
+   */
+  bool is_outgoing_connection_only = callbacks->pL2CA_ConnectInd_Cb == nullptr;
+  uint16_t psm = shim_l2cap.ConvertClientToRealPsm(client_psm,
+                                                   is_outgoing_connection_only);
+
+  if (shim_l2cap.Classic().IsPsmRegistered(psm)) {
+    LOG_ERROR(LOG_TAG, "%s Already registered classic client_psm:%hd psm:%hd",
+              __func__, client_psm, psm);
+    return 0;
+  }
+  LOG_INFO(LOG_TAG, "%s classic client_psm:%hd psm:%hd", __func__, client_psm,
+           psm);
+
+  return shim_l2cap.RegisterService(psm, callbacks, enable_snoop, p_ertm_info);
+}
+
+void bluetooth::shim::L2CA_Deregister(uint16_t client_psm) {
+  if (L2C_INVALID_PSM(client_psm)) {
+    LOG_ERROR(LOG_TAG, "%s Invalid classic client_psm:%hd", __func__,
+              client_psm);
+    return;
+  }
+  uint16_t psm = shim_l2cap.ConvertClientToRealPsm(client_psm);
+
+  shim_l2cap.UnregisterService(psm);
+  shim_l2cap.RemoveClientPsm(psm);
+}
+
+uint16_t bluetooth::shim::L2CA_AllocatePSM(void) {
+  return shim_l2cap.GetNextDynamicClassicPsm();
+}
+
+uint16_t bluetooth::shim::L2CA_AllocateLePSM(void) {
+  return shim_l2cap.GetNextDynamicLePsm();
+}
+
+void bluetooth::shim::L2CA_FreeLePSM(uint16_t psm) {
+  if (!shim_l2cap.Le().IsPsmRegistered(psm)) {
+    LOG_ERROR(LOG_TAG, "%s Not previously registered le psm:%hd", __func__,
+              psm);
+    return;
+  }
+  shim_l2cap.Le().UnregisterPsm(psm);
+}
+
+/**
+ * Classic Connection Oriented Channel APIS
+ */
+uint16_t bluetooth::shim::L2CA_ErtmConnectReq(uint16_t psm,
+                                              const RawAddress& raw_address,
+                                              tL2CAP_ERTM_INFO* p_ertm_info) {
+  return shim_l2cap.CreateConnection(psm, raw_address);
+}
+
+uint16_t bluetooth::shim::L2CA_ConnectReq(uint16_t psm,
+                                          const RawAddress& raw_address) {
+  return shim_l2cap.CreateConnection(psm, raw_address);
+}
+
+bool bluetooth::shim::L2CA_ErtmConnectRsp(const RawAddress& p_bd_addr,
+                                          uint8_t id, uint16_t lcid,
+                                          uint16_t result, uint16_t status,
+                                          tL2CAP_ERTM_INFO* p_ertm_info) {
+  return shim_l2cap.ConnectResponse(p_bd_addr, id, lcid, result, status,
+                                    p_ertm_info);
+}
+
+bool bluetooth::shim::L2CA_ConnectRsp(const RawAddress& p_bd_addr, uint8_t id,
+                                      uint16_t lcid, uint16_t result,
+                                      uint16_t status) {
+  return bluetooth::shim::L2CA_ErtmConnectRsp(p_bd_addr, id, lcid, result,
+                                              status, nullptr);
+}
+
+bool bluetooth::shim::L2CA_ConfigReq(uint16_t cid, tL2CAP_CFG_INFO* cfg_info) {
+  return shim_l2cap.ConfigRequest(cid, cfg_info);
+}
+
+bool bluetooth::shim::L2CA_ConfigRsp(uint16_t cid, tL2CAP_CFG_INFO* cfg_info) {
+  return shim_l2cap.ConfigResponse(cid, cfg_info);
+}
+
+bool bluetooth::shim::L2CA_DisconnectReq(uint16_t cid) {
+  return shim_l2cap.DisconnectRequest(cid);
+}
+
+bool bluetooth::shim::L2CA_DisconnectRsp(uint16_t cid) {
+  return shim_l2cap.DisconnectResponse(cid);
+}
+
+/**
+ * Le Connection Oriented Channel APIs
+ */
+uint16_t bluetooth::shim::L2CA_RegisterLECoc(uint16_t psm,
+                                             tL2CAP_APPL_INFO* callbacks) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s psm:%hd callbacks:%p", __func__, psm,
+           callbacks);
+  return 0;
+}
+
+void bluetooth::shim::L2CA_DeregisterLECoc(uint16_t psm) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s psm:%hd", __func__, psm);
+}
+
+uint16_t bluetooth::shim::L2CA_ConnectLECocReq(uint16_t psm,
+                                               const RawAddress& p_bd_addr,
+                                               tL2CAP_LE_CFG_INFO* p_cfg) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s psm:%hd addr:%s p_cfg:%p", __func__, psm,
+           p_bd_addr.ToString().c_str(), p_cfg);
+  return 0;
+}
+
+bool bluetooth::shim::L2CA_ConnectLECocRsp(const RawAddress& p_bd_addr,
+                                           uint8_t id, uint16_t lcid,
+                                           uint16_t result, uint16_t status,
+                                           tL2CAP_LE_CFG_INFO* p_cfg) {
+  LOG_INFO(LOG_TAG,
+           "UNIMPLEMENTED %s addr:%s id:%hhd lcid:%hd result:%hd status:%hd "
+           "p_cfg:%p",
+           __func__, p_bd_addr.ToString().c_str(), id, lcid, result, status,
+           p_cfg);
+  return false;
+}
+
+bool bluetooth::shim::L2CA_GetPeerLECocConfig(uint16_t lcid,
+                                              tL2CAP_LE_CFG_INFO* peer_cfg) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s lcid:%hd peer_cfg:%p", __func__, lcid,
+           peer_cfg);
+  return false;
+}
+
+/**
+ * Channel Data Writes
+ */
+bool bluetooth::shim::L2CA_SetConnectionCallbacks(
+    uint16_t cid, const tL2CAP_APPL_INFO* callbacks) {
+  LOG_INFO(LOG_TAG, "Unsupported API %s", __func__);
+  return false;
+}
+
+uint8_t bluetooth::shim::L2CA_DataWrite(uint16_t cid, BT_HDR* p_data) {
+  bool write_success = shim_l2cap.Write(cid, p_data);
+  return write_success ? L2CAP_DW_SUCCESS : L2CAP_DW_FAILED;
+}
+
+/**
+ * L2cap Layer APIs
+ */
+uint8_t bluetooth::shim::L2CA_SetDesireRole(uint8_t new_role) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  return 0;
+}
+
+/**
+ * Link APIs
+ */
+bool bluetooth::shim::L2CA_SetIdleTimeoutByBdAddr(const RawAddress& bd_addr,
+                                                  uint16_t timeout,
+                                                  tBT_TRANSPORT transport) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  return false;
+}
+
+bool bluetooth::shim::L2CA_SetAclPriority(const RawAddress& bd_addr,
+                                          uint8_t priority) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  return false;
+}
+
+bool bluetooth::shim::L2CA_SetFlushTimeout(const RawAddress& bd_addr,
+                                           uint16_t flush_tout) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  return false;
+}
+
+bool bluetooth::shim::L2CA_GetPeerFeatures(const RawAddress& bd_addr,
+                                           uint32_t* p_ext_feat,
+                                           uint8_t* p_chnl_mask) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  return false;
+}
+
+/**
+ * Fixed Channel APIs. Note: Classic fixed channel (connectionless and BR SMP)
+ * is not supported
+ */
+bool bluetooth::shim::L2CA_RegisterFixedChannel(uint16_t fixed_cid,
+                                                tL2CAP_FIXED_CHNL_REG* p_freg) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  return false;
+}
+
+bool bluetooth::shim::L2CA_ConnectFixedChnl(uint16_t fixed_cid,
+                                            const RawAddress& rem_bda) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  return false;
+}
+
+bool bluetooth::shim::L2CA_ConnectFixedChnl(uint16_t fixed_cid,
+                                            const RawAddress& rem_bda,
+                                            uint8_t initiating_phys) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  return false;
+}
+
+uint16_t bluetooth::shim::L2CA_SendFixedChnlData(uint16_t fixed_cid,
+                                                 const RawAddress& rem_bda,
+                                                 BT_HDR* p_buf) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  return 0;
+}
+
+bool bluetooth::shim::L2CA_RemoveFixedChnl(uint16_t fixed_cid,
+                                           const RawAddress& rem_bda) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  return false;
+}
+
+/**
+ * Channel hygiene APIs
+ */
+bool bluetooth::shim::L2CA_GetIdentifiers(uint16_t lcid, uint16_t* rcid,
+                                          uint16_t* handle) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  return false;
+}
+
+bool bluetooth::shim::L2CA_SetIdleTimeout(uint16_t cid, uint16_t timeout,
+                                          bool is_global) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  return false;
+}
+
+bool bluetooth::shim::L2CA_SetTxPriority(uint16_t cid,
+                                         tL2CAP_CHNL_PRIORITY priority) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  return false;
+}
+
+bool bluetooth::shim::L2CA_SetFixedChannelTout(const RawAddress& rem_bda,
+                                               uint16_t fixed_cid,
+                                               uint16_t idle_tout) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  return false;
+}
+
+bool bluetooth::shim::L2CA_SetChnlFlushability(uint16_t cid,
+                                               bool is_flushable) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  return false;
+}
+
+uint16_t bluetooth::shim::L2CA_FlushChannel(uint16_t lcid,
+                                            uint16_t num_to_flush) {
+  LOG_INFO(LOG_TAG, "UNIMPLEMENTED %s", __func__);
+  return 0;
+}
diff --git a/main/shim/l2c_api.h b/main/shim/l2c_api.h
new file mode 100644
index 0000000..fc45560
--- /dev/null
+++ b/main/shim/l2c_api.h
@@ -0,0 +1,620 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <set>
+#include <unordered_map>
+
+#include "stack/include/l2c_api.h"
+#include "stack/l2cap/l2c_int.h"
+
+namespace bluetooth {
+namespace shim {
+
+/*******************************************************************************
+ *
+ * Function         L2CA_Register
+ *
+ * Description      Other layers call this function to register for L2CAP
+ *                  services.
+ *
+ * Returns          PSM to use or zero if error. Typically, the PSM returned
+ *                  is the same as was passed in, but for an outgoing-only
+ *                  connection to a dynamic PSM, a "virtual" PSM is returned
+ *                  and should be used in the calls to L2CA_ConnectReq() and
+ *                  BTM_SetSecurityLevel().
+ *
+ ******************************************************************************/
+uint16_t L2CA_Register(uint16_t psm, tL2CAP_APPL_INFO* p_cb_info,
+                       bool enable_snoop, tL2CAP_ERTM_INFO* p_ertm_info);
+
+/*******************************************************************************
+ *
+ * Function         L2CA_Deregister
+ *
+ * Description      Other layers call this function to deregister for L2CAP
+ *                  services.
+ *
+ * Returns          void
+ *
+ ******************************************************************************/
+void L2CA_Deregister(uint16_t psm);
+
+/*******************************************************************************
+ *
+ * Function         L2CA_AllocatePSM
+ *
+ * Description      Other layers call this function to find an unused PSM for
+ *                  L2CAP services.
+ *
+ * Returns          PSM to use.
+ *
+ ******************************************************************************/
+uint16_t L2CA_AllocatePSM(void);
+
+/*******************************************************************************
+ *
+ * Function         L2CA_AllocateLePSM
+ *
+ * Description      Other layers call this function to find an unused LE PSM for
+ *                  L2CAP services.
+ *
+ * Returns          LE_PSM to use if success. Otherwise returns 0.
+ *
+ ******************************************************************************/
+uint16_t L2CA_AllocateLePSM(void);
+
+/*******************************************************************************
+ *
+ * Function         L2CA_FreeLePSM
+ *
+ * Description      Free an assigned LE PSM.
+ *
+ * Returns          void
+ *
+ ******************************************************************************/
+void L2CA_FreeLePSM(uint16_t psm);
+
+/*******************************************************************************
+ *
+ * Function         L2CA_ConnectReq
+ *
+ * Description      Higher layers call this function to create an L2CAP
+ *                  connection.
+ *                  Note that the connection is not established at this time,
+ *                  but connection establishment gets started. The callback
+ *                  will be invoked when connection establishes or fails.
+ *
+ * Returns          the CID of the connection, or 0 if it failed to start
+ *
+ ******************************************************************************/
+uint16_t L2CA_ConnectReq(uint16_t psm, const RawAddress& p_bd_addr);
+
+/*******************************************************************************
+ *
+ * Function         L2CA_ConnectRsp
+ *
+ * Description      Higher layers call this function to accept an incoming
+ *                  L2CAP connection, for which they had gotten an connect
+ *                  indication callback.
+ *
+ * Returns          true for success, false for failure
+ *
+ ******************************************************************************/
+bool L2CA_ConnectRsp(const RawAddress& p_bd_addr, uint8_t id, uint16_t lcid,
+                     uint16_t result, uint16_t status);
+
+/*******************************************************************************
+ *
+ * Function         L2CA_ErtmConnectReq
+ *
+ * Description      Higher layers call this function to create an L2CAP
+ *                  connection that needs to use Enhanced Retransmission Mode.
+ *                  Note that the connection is not established at this time,
+ *                  but connection establishment gets started. The callback
+ *                  will be invoked when connection establishes or fails.
+ *
+ * Returns          the CID of the connection, or 0 if it failed to start
+ *
+ ******************************************************************************/
+uint16_t L2CA_ErtmConnectReq(uint16_t psm, const RawAddress& p_bd_addr,
+                             tL2CAP_ERTM_INFO* p_ertm_info);
+
+/*******************************************************************************
+ *
+ * Function         L2CA_RegisterLECoc
+ *
+ * Description      Other layers call this function to register for L2CAP
+ *                  Connection Oriented Channel.
+ *
+ * Returns          PSM to use or zero if error. Typically, the PSM returned
+ *                  is the same as was passed in, but for an outgoing-only
+ *                  connection to a dynamic PSM, a "virtual" PSM is returned
+ *                  and should be used in the calls to L2CA_ConnectLECocReq()
+ *                  and BTM_SetSecurityLevel().
+ *
+ ******************************************************************************/
+uint16_t L2CA_RegisterLECoc(uint16_t psm, tL2CAP_APPL_INFO* p_cb_info);
+
+/*******************************************************************************
+ *
+ * Function         L2CA_DeregisterLECoc
+ *
+ * Description      Other layers call this function to deregister for L2CAP
+ *                  Connection Oriented Channel.
+ *
+ * Returns          void
+ *
+ ******************************************************************************/
+void L2CA_DeregisterLECoc(uint16_t psm);
+
+/*******************************************************************************
+ *
+ * Function         L2CA_ConnectLECocReq
+ *
+ * Description      Higher layers call this function to create an L2CAP LE COC.
+ *                  Note that the connection is not established at this time,
+ *                  but connection establishment gets started. The callback
+ *                  will be invoked when connection establishes or fails.
+ *
+ * Returns          the CID of the connection, or 0 if it failed to start
+ *
+ ******************************************************************************/
+uint16_t L2CA_ConnectLECocReq(uint16_t psm, const RawAddress& p_bd_addr,
+                              tL2CAP_LE_CFG_INFO* p_cfg);
+
+/*******************************************************************************
+ *
+ * Function         L2CA_ConnectLECocRsp
+ *
+ * Description      Higher layers call this function to accept an incoming
+ *                  L2CAP LE COC connection, for which they had gotten a connect
+ *                  indication callback.
+ *
+ * Returns          true for success, false for failure
+ *
+ ******************************************************************************/
+bool L2CA_ConnectLECocRsp(const RawAddress& p_bd_addr, uint8_t id,
+                          uint16_t lcid, uint16_t result, uint16_t status,
+                          tL2CAP_LE_CFG_INFO* p_cfg);
+
+/*******************************************************************************
+ *
+ *  Function         L2CA_GetPeerLECocConfig
+ *
+ *  Description      Get peers configuration for LE Connection Oriented Channel.
+ *
+ *  Return value:    true if peer is connected
+ *
+ ******************************************************************************/
+bool L2CA_GetPeerLECocConfig(uint16_t lcid, tL2CAP_LE_CFG_INFO* peer_cfg);
+
+// This function sets the callback routines for the L2CAP connection referred to
+// by |local_cid|. The callback routines can only be modified for outgoing
+// connections established by |L2CA_ConnectReq| or accepted incoming
+// connections. |callbacks| must not be NULL. This function returns true if the
+// callbacks could be updated, false if not (e.g. |local_cid| was not found).
+bool L2CA_SetConnectionCallbacks(uint16_t local_cid,
+                                 const tL2CAP_APPL_INFO* callbacks);
+
+/*******************************************************************************
+ *
+ * Function         L2CA_ErtmConnectRsp
+ *
+ * Description      Higher layers call this function to accept an incoming
+ *                  L2CAP connection, for which they had gotten an connect
+ *                  indication callback, and for which the higher layer wants
+ *                  to use Enhanced Retransmission Mode.
+ *
+ * Returns          true for success, false for failure
+ *
+ ******************************************************************************/
+bool L2CA_ErtmConnectRsp(const RawAddress& p_bd_addr, uint8_t id, uint16_t lcid,
+                         uint16_t result, uint16_t status,
+                         tL2CAP_ERTM_INFO* p_ertm_info);
+
+/*******************************************************************************
+ *
+ * Function         L2CA_ConfigReq
+ *
+ * Description      Higher layers call this function to send configuration.
+ *
+ * Returns          true if configuration sent, else false
+ *
+ ******************************************************************************/
+bool L2CA_ConfigReq(uint16_t cid, tL2CAP_CFG_INFO* p_cfg);
+
+/*******************************************************************************
+ *
+ * Function         L2CA_ConfigRsp
+ *
+ * Description      Higher layers call this function to send a configuration
+ *                  response.
+ *
+ * Returns          true if configuration response sent, else false
+ *
+ ******************************************************************************/
+bool L2CA_ConfigRsp(uint16_t cid, tL2CAP_CFG_INFO* p_cfg);
+
+/*******************************************************************************
+ *
+ * Function         L2CA_DisconnectReq
+ *
+ * Description      Higher layers call this function to disconnect a channel.
+ *
+ * Returns          true if disconnect sent, else false
+ *
+ ******************************************************************************/
+bool L2CA_DisconnectReq(uint16_t cid);
+
+/*******************************************************************************
+ *
+ * Function         L2CA_DisconnectRsp
+ *
+ * Description      Higher layers call this function to acknowledge the
+ *                  disconnection of a channel.
+ *
+ * Returns          void
+ *
+ ******************************************************************************/
+bool L2CA_DisconnectRsp(uint16_t cid);
+
+/*******************************************************************************
+ *
+ * Function         L2CA_DataWrite
+ *
+ * Description      Higher layers call this function to write data.
+ *
+ * Returns          L2CAP_DW_SUCCESS, if data accepted, else false
+ *                  L2CAP_DW_CONGESTED, if data accepted and the channel is
+ *                                      congested
+ *                  L2CAP_DW_FAILED, if error
+ *
+ ******************************************************************************/
+uint8_t L2CA_DataWrite(uint16_t cid, BT_HDR* p_data);
+
+// Given a local channel identifier, |lcid|, this function returns the bound
+// remote channel identifier, |rcid|, and the ACL link handle, |handle|. If
+// |lcid| is not known or is invalid, this function returns false and does not
+// modify the values pointed at by |rcid| and |handle|. |rcid| and |handle| may
+// be NULL.
+bool L2CA_GetIdentifiers(uint16_t lcid, uint16_t* rcid, uint16_t* handle);
+
+/*******************************************************************************
+ *
+ * Function         L2CA_SetIdleTimeout
+ *
+ * Description      Higher layers call this function to set the idle timeout for
+ *                  a connection, or for all future connections. The "idle
+ *                  timeout" is the amount of time that a connection can remain
+ *                  up with no L2CAP channels on it. A timeout of zero means
+ *                  that the connection will be torn down immediately when the
+ *                  last channel is removed. A timeout of 0xFFFF means no
+ *                  timeout. Values are in seconds.
+ *
+ * Returns          true if command succeeded, false if failed
+ *
+ ******************************************************************************/
+bool L2CA_SetIdleTimeout(uint16_t cid, uint16_t timeout, bool is_global);
+
+/*******************************************************************************
+ *
+ * Function         L2CA_SetIdleTimeoutByBdAddr
+ *
+ * Description      Higher layers call this function to set the idle timeout for
+ *                  a connection. The "idle timeout" is the amount of time that
+ *                  a connection can remain up with no L2CAP channels on it.
+ *                  A timeout of zero means that the connection will be torn
+ *                  down immediately when the last channel is removed.
+ *                  A timeout of 0xFFFF means no timeout. Values are in seconds.
+ *                  A bd_addr is the remote BD address. If bd_addr =
+ *                  RawAddress::kAny, then the idle timeouts for all active
+ *                  l2cap links will be changed.
+ *
+ * Returns          true if command succeeded, false if failed
+ *
+ * NOTE             This timeout applies to all logical channels active on the
+ *                  ACL link.
+ ******************************************************************************/
+bool L2CA_SetIdleTimeoutByBdAddr(const RawAddress& bd_addr, uint16_t timeout,
+                                 tBT_TRANSPORT transport);
+
+/*******************************************************************************
+ *
+ * Function         L2CA_SetTraceLevel
+ *
+ * Description      This function sets the trace level for L2CAP. If called with
+ *                  a value of 0xFF, it simply reads the current trace level.
+ *
+ * Returns          the new (current) trace level
+ *
+ ******************************************************************************/
+uint8_t L2CA_SetTraceLevel(uint8_t trace_level);
+
+/*******************************************************************************
+ *
+ * Function     L2CA_SetDesireRole
+ *
+ * Description  This function sets the desire role for L2CAP.
+ *              If the new role is L2CAP_ROLE_ALLOW_SWITCH, allow switch on
+ *              HciCreateConnection.
+ *              If the new role is L2CAP_ROLE_DISALLOW_SWITCH, do not allow
+ *              switch on HciCreateConnection.
+ *
+ *              If the new role is a valid role (HCI_ROLE_MASTER or
+ *              HCI_ROLE_SLAVE), the desire role is set to the new value.
+ *              Otherwise, it is not changed.
+ *
+ * Returns      the new (current) role
+ *
+ ******************************************************************************/
+uint8_t L2CA_SetDesireRole(uint8_t new_role);
+
+/*******************************************************************************
+ *
+ * Function     L2CA_FlushChannel
+ *
+ * Description  This function flushes none, some or all buffers queued up
+ *              for xmission for a particular CID. If called with
+ *              L2CAP_FLUSH_CHANS_GET (0), it simply returns the number
+ *              of buffers queued for that CID L2CAP_FLUSH_CHANS_ALL (0xffff)
+ *              flushes all buffers.  All other values specifies the maximum
+ *              buffers to flush.
+ *
+ * Returns      Number of buffers left queued for that CID
+ *
+ ******************************************************************************/
+uint16_t L2CA_FlushChannel(uint16_t lcid, uint16_t num_to_flush);
+
+/*******************************************************************************
+ *
+ * Function         L2CA_SetAclPriority
+ *
+ * Description      Sets the transmission priority for an ACL channel.
+ *                  (For initial implementation only two values are valid.
+ *                  L2CAP_PRIORITY_NORMAL and L2CAP_PRIORITY_HIGH).
+ *
+ * Returns          true if a valid channel, else false
+ *
+ ******************************************************************************/
+bool L2CA_SetAclPriority(const RawAddress& bd_addr, uint8_t priority);
+
+/*******************************************************************************
+ *
+ * Function         L2CA_SetTxPriority
+ *
+ * Description      Sets the transmission priority for a channel. (FCR Mode)
+ *
+ * Returns          true if a valid channel, else false
+ *
+ ******************************************************************************/
+bool L2CA_SetTxPriority(uint16_t cid, tL2CAP_CHNL_PRIORITY priority);
+
+/*******************************************************************************
+ *
+ * Function         L2CA_SetFlushTimeout
+ *
+ * Description      This function set the automatic flush time out in Baseband
+ *                  for ACL-U packets.
+ *                  BdAddr : the remote BD address of ACL link. If it is
+ *                           BT_DB_ANY then the flush time out will be applied
+ *                           to all ACL link.
+ *                  FlushTimeout: flush time out in ms
+ *                           0x0000 : No automatic flush
+ *                           L2CAP_NO_RETRANSMISSION : No retransmission
+ *                           0x0002 - 0xFFFE : flush time out, if
+ *                                             (flush_tout * 8) + 3 / 5) <=
+ *                                             HCI_MAX_AUTOMATIC_FLUSH_TIMEOUT
+ *                                             (in 625us slot).
+ *                                    Otherwise, return false.
+ *                           L2CAP_NO_AUTOMATIC_FLUSH : No automatic flush
+ *
+ * Returns          true if command succeeded, false if failed
+ *
+ * NOTE             This flush timeout applies to all logical channels active on
+ *                  the ACL link.
+ ******************************************************************************/
+bool L2CA_SetFlushTimeout(const RawAddress& bd_addr, uint16_t flush_tout);
+
+/*******************************************************************************
+ *
+ * Function         L2CA_SetChnlFlushability
+ *
+ * Description      Higher layers call this function to set a channels
+ *                  flushability flags
+ *
+ * Returns          true if CID found, else false
+ *
+ ******************************************************************************/
+bool L2CA_SetChnlFlushability(uint16_t cid, bool is_flushable);
+
+/*******************************************************************************
+ *
+ *  Function         L2CA_GetPeerFeatures
+ *
+ *  Description      Get a peers features and fixed channel map
+ *
+ *  Parameters:      BD address of the peer
+ *                   Pointers to features and channel mask storage area
+ *
+ *  Return value:    true if peer is connected
+ *
+ ******************************************************************************/
+bool L2CA_GetPeerFeatures(const RawAddress& bd_addr, uint32_t* p_ext_feat,
+                          uint8_t* p_chnl_mask);
+
+/*******************************************************************************
+ *
+ *  Function        L2CA_RegisterFixedChannel
+ *
+ *  Description     Register a fixed channel.
+ *
+ *  Parameters:     Fixed Channel #
+ *                  Channel Callbacks and config
+ *
+ *  Return value:   true if registered OK
+ *
+ ******************************************************************************/
+bool L2CA_RegisterFixedChannel(uint16_t fixed_cid,
+                               tL2CAP_FIXED_CHNL_REG* p_freg);
+
+/*******************************************************************************
+ *
+ *  Function        L2CA_ConnectFixedChnl
+ *
+ *  Description     Connect an fixed signalling channel to a remote device.
+ *
+ *  Parameters:     Fixed CID
+ *                  BD Address of remote
+ *
+ *  Return value:   true if connection started
+ *
+ ******************************************************************************/
+bool L2CA_ConnectFixedChnl(uint16_t fixed_cid, const RawAddress& bd_addr);
+bool L2CA_ConnectFixedChnl(uint16_t fixed_cid, const RawAddress& bd_addr,
+                           uint8_t initiating_phys);
+
+/*******************************************************************************
+ *
+ *  Function        L2CA_SendFixedChnlData
+ *
+ *  Description     Write data on a fixed signalling channel.
+ *
+ *  Parameters:     Fixed CID
+ *                  BD Address of remote
+ *                  Pointer to buffer of type BT_HDR
+ *
+ * Return value     L2CAP_DW_SUCCESS, if data accepted
+ *                  L2CAP_DW_FAILED,  if error
+ *
+ ******************************************************************************/
+uint16_t L2CA_SendFixedChnlData(uint16_t fixed_cid, const RawAddress& rem_bda,
+                                BT_HDR* p_buf);
+
+/*******************************************************************************
+ *
+ *  Function        L2CA_RemoveFixedChnl
+ *
+ *  Description     Remove a fixed channel to a remote device.
+ *
+ *  Parameters:     Fixed CID
+ *                  BD Address of remote
+ *                  Idle timeout to use (or 0xFFFF if don't care)
+ *
+ *  Return value:   true if channel removed
+ *
+ ******************************************************************************/
+bool L2CA_RemoveFixedChnl(uint16_t fixed_cid, const RawAddress& rem_bda);
+
+/*******************************************************************************
+ *
+ * Function         L2CA_SetFixedChannelTout
+ *
+ * Description      Higher layers call this function to set the idle timeout for
+ *                  a fixed channel. The "idle timeout" is the amount of time
+ *                  that a connection can remain up with no L2CAP channels on
+ *                  it. A timeout of zero means that the connection will be torn
+ *                  down immediately when the last channel is removed.
+ *                  A timeout of 0xFFFF means no timeout. Values are in seconds.
+ *                  A bd_addr is the remote BD address. If bd_addr =
+ *                  RawAddress::kAny, then the idle timeouts for all active
+ *                  l2cap links will be changed.
+ *
+ * Returns          true if command succeeded, false if failed
+ *
+ ******************************************************************************/
+bool L2CA_SetFixedChannelTout(const RawAddress& rem_bda, uint16_t fixed_cid,
+                              uint16_t idle_tout);
+
+/*******************************************************************************
+ *
+ *  Function        L2CA_CancelBleConnectReq
+ *
+ *  Description     Cancel a pending connection attempt to a BLE device.
+ *
+ *  Parameters:     BD Address of remote
+ *
+ *  Return value:   true if connection was cancelled
+ *
+ ******************************************************************************/
+bool L2CA_CancelBleConnectReq(const RawAddress& rem_bda);
+
+/*******************************************************************************
+ *
+ *  Function        L2CA_UpdateBleConnParams
+ *
+ *  Description     Update BLE connection parameters.
+ *
+ *  Parameters:     BD Address of remote
+ *
+ *  Return value:   true if update started
+ *
+ ******************************************************************************/
+bool L2CA_UpdateBleConnParams(const RawAddress& rem_bdRa, uint16_t min_int,
+                              uint16_t max_int, uint16_t latency,
+                              uint16_t timeout);
+bool L2CA_UpdateBleConnParams(const RawAddress& rem_bda, uint16_t min_int,
+                              uint16_t max_int, uint16_t latency,
+                              uint16_t timeout, uint16_t min_ce_len,
+                              uint16_t max_ce_len);
+
+/*******************************************************************************
+ *
+ *  Function        L2CA_EnableUpdateBleConnParams
+ *
+ *  Description     Update BLE connection parameters.
+ *
+ *  Parameters:     BD Address of remote
+ *                  enable flag
+ *
+ *  Return value:   true if update started
+ *
+ ******************************************************************************/
+bool L2CA_EnableUpdateBleConnParams(const RawAddress& rem_bda, bool enable);
+
+/*******************************************************************************
+ *
+ * Function         L2CA_GetBleConnRole
+ *
+ * Description      This function returns the connection role.
+ *
+ * Returns          link role.
+ *
+ ******************************************************************************/
+uint8_t L2CA_GetBleConnRole(const RawAddress& bd_addr);
+
+/*******************************************************************************
+ *
+ * Function         L2CA_GetDisconnectReason
+ *
+ * Description      This function returns the disconnect reason code.
+ *
+ *  Parameters:     BD Address of remote
+ *                  Physical transport for the L2CAP connection (BR/EDR or LE)
+ *
+ * Returns          disconnect reason
+ *
+ ******************************************************************************/
+uint16_t L2CA_GetDisconnectReason(const RawAddress& remote_bda,
+                                  tBT_TRANSPORT transport);
+
+void L2CA_AdjustConnectionIntervals(uint16_t* min_interval,
+                                    uint16_t* max_interval,
+                                    uint16_t floor_interval);
+
+}  // namespace shim
+}  // namespace bluetooth
diff --git a/main/shim/l2cap.cc b/main/shim/l2cap.cc
new file mode 100644
index 0000000..3bd011c
--- /dev/null
+++ b/main/shim/l2cap.cc
@@ -0,0 +1,420 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_TAG "bt_shim_l2cap"
+
+#include <cstdint>
+
+#include "main/shim/dumpsys.h"
+#include "main/shim/entry.h"
+#include "main/shim/l2cap.h"
+#include "main/shim/shim.h"
+#include "osi/include/allocator.h"
+#include "osi/include/log.h"
+
+#include "shim/l2cap.h"
+
+namespace {
+constexpr char kModuleName[] = "shim::legacy::L2cap";
+constexpr bool kDisconnectResponseRequired = false;
+constexpr size_t kBtHdrSize = sizeof(BT_HDR);
+constexpr uint16_t kConnectionFail = 1;
+constexpr uint16_t kConnectionSuccess = 0;
+constexpr uint16_t kInvalidConnectionInterfaceDescriptor = 0;
+constexpr uint8_t kUnusedId = 0;
+constexpr uint16_t kUnusedResult = 0;
+}  // namespace
+
+bool bluetooth::shim::legacy::PsmManager::IsPsmRegistered(uint16_t psm) const {
+  return psm_to_callback_map_.find(psm) != psm_to_callback_map_.end();
+}
+
+bool bluetooth::shim::legacy::PsmManager::HasClient(uint16_t psm) const {
+  return IsPsmRegistered(psm) && psm_to_callback_map_.at(psm) != nullptr;
+}
+
+void bluetooth::shim::legacy::PsmManager::RegisterPsm(
+    uint16_t psm, const tL2CAP_APPL_INFO* callbacks) {
+  CHECK(!HasClient(psm));
+  psm_to_callback_map_[psm] = callbacks;
+}
+
+void bluetooth::shim::legacy::PsmManager::RegisterPsm(uint16_t psm) {
+  RegisterPsm(psm, nullptr);
+}
+
+void bluetooth::shim::legacy::PsmManager::UnregisterPsm(uint16_t psm) {
+  CHECK(IsPsmRegistered(psm));
+  psm_to_callback_map_.erase(psm);
+}
+
+const tL2CAP_APPL_INFO* bluetooth::shim::legacy::PsmManager::Callbacks(
+    uint16_t psm) {
+  CHECK(HasClient(psm));
+  return psm_to_callback_map_[psm];
+}
+
+bluetooth::shim::legacy::L2cap::L2cap()
+    : classic_dynamic_psm_(kInitialClassicDynamicPsm),
+      le_dynamic_psm_(kInitialLeDynamicPsm),
+      classic_virtual_psm_(kInitialClassicVirtualPsm) {
+  bluetooth::shim::RegisterDumpsysFunction(static_cast<void*>(this),
+                                           [this](int fd) { Dump(fd); });
+}
+
+bluetooth::shim::legacy::L2cap::~L2cap() {
+  bluetooth::shim::UnregisterDumpsysFunction(static_cast<void*>(this));
+}
+
+bluetooth::shim::legacy::PsmManager& bluetooth::shim::legacy::L2cap::Le() {
+  return le_;
+}
+
+bluetooth::shim::legacy::PsmManager& bluetooth::shim::legacy::L2cap::Classic() {
+  return classic_;
+}
+
+bool bluetooth::shim::legacy::L2cap::ConnectionExists(uint16_t cid) const {
+  return cid_to_psm_map_.find(cid) != cid_to_psm_map_.end();
+}
+
+uint16_t bluetooth::shim::legacy::L2cap::CidToPsm(uint16_t cid) const {
+  CHECK(ConnectionExists(cid));
+  return cid_to_psm_map_.at(cid);
+}
+
+uint16_t bluetooth::shim::legacy::L2cap::ConvertClientToRealPsm(
+    uint16_t client_psm, bool is_outgoing_only_connection) {
+  if (!is_outgoing_only_connection) {
+    return client_psm;
+  }
+  return GetNextVirtualPsm(client_psm);
+}
+
+uint16_t bluetooth::shim::legacy::L2cap::ConvertClientToRealPsm(
+    uint16_t client_psm) {
+  if (client_psm_to_real_psm_map_.find(client_psm) ==
+      client_psm_to_real_psm_map_.end()) {
+    return client_psm;
+  }
+  return client_psm_to_real_psm_map_.at(client_psm);
+}
+
+void bluetooth::shim::legacy::L2cap::RemoveClientPsm(uint16_t client_psm) {
+  if (client_psm_to_real_psm_map_.find(client_psm) !=
+      client_psm_to_real_psm_map_.end()) {
+    client_psm_to_real_psm_map_.erase(client_psm);
+  }
+}
+
+uint16_t bluetooth::shim::legacy::L2cap::GetNextVirtualPsm(uint16_t real_psm) {
+  if (real_psm < kInitialClassicDynamicPsm) {
+    return real_psm;
+  }
+
+  while (Classic().IsPsmRegistered(classic_virtual_psm_)) {
+    classic_virtual_psm_ += 2;
+    if (classic_virtual_psm_ >= kFinalClassicVirtualPsm) {
+      classic_virtual_psm_ = kInitialClassicVirtualPsm;
+    }
+  }
+  return classic_virtual_psm_;
+}
+
+uint16_t bluetooth::shim::legacy::L2cap::GetNextDynamicLePsm() {
+  while (Le().IsPsmRegistered(le_dynamic_psm_)) {
+    le_dynamic_psm_++;
+    if (le_dynamic_psm_ > kFinalLeDynamicPsm) {
+      le_dynamic_psm_ = kInitialLeDynamicPsm;
+    }
+  }
+  return le_dynamic_psm_;
+}
+
+uint16_t bluetooth::shim::legacy::L2cap::GetNextDynamicClassicPsm() {
+  while (Classic().IsPsmRegistered(classic_dynamic_psm_)) {
+    classic_dynamic_psm_ += 2;
+    if (classic_dynamic_psm_ > kFinalClassicDynamicPsm) {
+      classic_dynamic_psm_ = kInitialClassicDynamicPsm;
+    } else if (classic_dynamic_psm_ & 0x0100) {
+      /* the upper byte must be even */
+      classic_dynamic_psm_ += 0x0100;
+    }
+
+    /* if psm is in range of reserved BRCM Aware features */
+    if ((BRCM_RESERVED_PSM_START <= classic_dynamic_psm_) &&
+        (classic_dynamic_psm_ <= BRCM_RESERVED_PSM_END)) {
+      classic_dynamic_psm_ = BRCM_RESERVED_PSM_END + 2;
+    }
+  }
+  return classic_dynamic_psm_;
+}
+
+uint16_t bluetooth::shim::legacy::L2cap::RegisterService(
+    uint16_t psm, const tL2CAP_APPL_INFO* callbacks, bool enable_snoop,
+    tL2CAP_ERTM_INFO* p_ertm_info) {
+  if (Classic().IsPsmRegistered(psm)) {
+    LOG_WARN(LOG_TAG, "Service is already registered psm:%hd", psm);
+    return 0;
+  }
+  if (!enable_snoop) {
+    LOG_INFO(LOG_TAG, "Disable snooping on psm basis unsupported psm:%d", psm);
+  }
+
+  LOG_DEBUG(LOG_TAG, "Registering service on psm:%hd", psm);
+  RegisterServicePromise register_promise;
+  auto service_registered = register_promise.get_future();
+  bool use_ertm = false;
+  if (p_ertm_info != nullptr &&
+      p_ertm_info->preferred_mode == L2CAP_FCR_ERTM_MODE) {
+    use_ertm = true;
+  }
+  constexpr auto mtu = 1000;  // TODO: Let client decide
+  bluetooth::shim::GetL2cap()->RegisterService(
+      psm, use_ertm, mtu,
+      std::bind(
+          &bluetooth::shim::legacy::L2cap::OnRemoteInitiatedConnectionCreated,
+          this, std::placeholders::_1, std::placeholders::_2,
+          std::placeholders::_3),
+      std::move(register_promise));
+
+  uint16_t registered_psm = service_registered.get();
+  if (registered_psm != psm) {
+    LOG_WARN(LOG_TAG, "Unable to register psm:%hd", psm);
+  } else {
+    LOG_DEBUG(LOG_TAG, "Successfully registered psm:%hd", psm);
+    Classic().RegisterPsm(registered_psm, callbacks);
+  }
+  return registered_psm;
+}
+
+void bluetooth::shim::legacy::L2cap::UnregisterService(uint16_t psm) {
+  if (!Classic().IsPsmRegistered(psm)) {
+    LOG_WARN(LOG_TAG,
+             "Service must be registered in order to unregister psm:%hd", psm);
+    return;
+  }
+  for (auto& entry : cid_to_psm_map_) {
+    if (entry.second == psm) {
+      LOG_WARN(LOG_TAG,
+               "  Unregistering service with active channels psm:%hd cid:%hd",
+               psm, entry.first);
+    }
+  }
+
+  LOG_DEBUG(LOG_TAG, "Unregistering service on psm:%hd", psm);
+  UnregisterServicePromise unregister_promise;
+  auto service_unregistered = unregister_promise.get_future();
+  bluetooth::shim::GetL2cap()->UnregisterService(psm,
+                                                 std::move(unregister_promise));
+  service_unregistered.wait();
+  Classic().UnregisterPsm(psm);
+}
+
+uint16_t bluetooth::shim::legacy::L2cap::CreateConnection(
+    uint16_t psm, const RawAddress& raw_address) {
+  if (!Classic().IsPsmRegistered(psm)) {
+    LOG_WARN(LOG_TAG, "Service must be registered in order to connect psm:%hd",
+             psm);
+    return kInvalidConnectionInterfaceDescriptor;
+  }
+
+  CreateConnectionPromise create_promise;
+  auto created = create_promise.get_future();
+  LOG_DEBUG(LOG_TAG, "Initiating local connection to psm:%hd address:%s", psm,
+            raw_address.ToString().c_str());
+
+  bluetooth::shim::GetL2cap()->CreateConnection(
+      psm, raw_address.ToString(),
+      std::bind(
+          &bluetooth::shim::legacy::L2cap::OnLocalInitiatedConnectionCreated,
+          this, std::placeholders::_1, std::placeholders::_2,
+          std::placeholders::_3, std::placeholders::_4),
+      std::move(create_promise));
+
+  uint16_t cid = created.get();
+  if (cid == kInvalidConnectionInterfaceDescriptor) {
+    LOG_WARN(LOG_TAG,
+             "Failed to initiate connection interface to psm:%hd address:%s",
+             psm, raw_address.ToString().c_str());
+  } else {
+    LOG_DEBUG(LOG_TAG,
+              "Successfully initiated connection to psm:%hd address:%s"
+              " connection_interface_descriptor:%hd",
+              psm, raw_address.ToString().c_str(), cid);
+    CHECK(!ConnectionExists(cid));
+    cid_to_psm_map_[cid] = psm;
+  }
+  return cid;
+}
+
+void bluetooth::shim::legacy::L2cap::OnLocalInitiatedConnectionCreated(
+    std::string string_address, uint16_t psm, uint16_t cid, bool connected) {
+  if (cid_closing_set_.count(cid) == 0) {
+    if (connected) {
+      SetDownstreamCallbacks(cid);
+    } else {
+      LOG_WARN(LOG_TAG,
+               "Failed intitiating connection remote:%s psm:%hd cid:%hd",
+               string_address.c_str(), psm, cid);
+    }
+    Classic().Callbacks(psm)->pL2CA_ConnectCfm_Cb(
+        cid, connected ? (kConnectionSuccess) : (kConnectionFail));
+  } else {
+    LOG_DEBUG(LOG_TAG, "Connection Closed before presentation to upper layer");
+    if (connected) {
+      SetDownstreamCallbacks(cid);
+      bluetooth::shim::GetL2cap()->CloseConnection(cid);
+    } else {
+      LOG_DEBUG(LOG_TAG, "Connection failed after initiator closed");
+    }
+  }
+}
+
+void bluetooth::shim::legacy::L2cap::OnRemoteInitiatedConnectionCreated(
+    std::string string_address, uint16_t psm, uint16_t cid) {
+  RawAddress raw_address;
+  RawAddress::FromString(string_address, raw_address);
+
+  LOG_DEBUG(LOG_TAG,
+            "Sending connection indicator to upper stack from device:%s "
+            "psm:%hd cid:%hd",
+            string_address.c_str(), psm, cid);
+
+  CHECK(!ConnectionExists(cid));
+  cid_to_psm_map_[cid] = psm;
+  SetDownstreamCallbacks(cid);
+  Classic().Callbacks(psm)->pL2CA_ConnectInd_Cb(raw_address, cid, psm,
+                                                kUnusedId);
+}
+
+bool bluetooth::shim::legacy::L2cap::Write(uint16_t cid, BT_HDR* bt_hdr) {
+  CHECK(bt_hdr != nullptr);
+  const uint8_t* data = bt_hdr->data + bt_hdr->offset;
+  size_t len = bt_hdr->len;
+  if (!ConnectionExists(cid) || len == 0) {
+    return false;
+  }
+  LOG_DEBUG(LOG_TAG, "Writing data cid:%hd len:%zd", cid, len);
+  bluetooth::shim::GetL2cap()->Write(cid, data, len);
+  return true;
+}
+
+void bluetooth::shim::legacy::L2cap::SetDownstreamCallbacks(uint16_t cid) {
+  bluetooth::shim::GetL2cap()->SetReadDataReadyCallback(
+      cid, [this](uint16_t cid, std::vector<const uint8_t> data) {
+        LOG_DEBUG(LOG_TAG, "OnDataReady cid:%hd len:%zd", cid, data.size());
+        BT_HDR* bt_hdr =
+            static_cast<BT_HDR*>(osi_calloc(data.size() + kBtHdrSize));
+        std::copy(data.begin(), data.end(), bt_hdr->data);
+        bt_hdr->len = data.size();
+        classic_.Callbacks(CidToPsm(cid))->pL2CA_DataInd_Cb(cid, bt_hdr);
+      });
+
+  bluetooth::shim::GetL2cap()->SetConnectionClosedCallback(
+      cid, [this](uint16_t cid, int error_code) {
+        LOG_DEBUG(LOG_TAG, "OnChannel closed callback cid:%hd", cid);
+        if (!ConnectionExists(cid)) {
+          LOG_WARN(LOG_TAG, "%s Unexpected channel closure cid:%hd", __func__,
+                   cid);
+          return;
+        }
+        if (cid_closing_set_.count(cid) == 1) {
+          cid_closing_set_.erase(cid);
+          classic_.Callbacks(CidToPsm(cid))
+              ->pL2CA_DisconnectCfm_Cb(cid, kUnusedResult);
+        } else {
+          classic_.Callbacks(CidToPsm(cid))
+              ->pL2CA_DisconnectInd_Cb(cid, kDisconnectResponseRequired);
+        }
+        cid_to_psm_map_.erase(cid);
+      });
+}
+
+bool bluetooth::shim::legacy::L2cap::ConnectResponse(
+    const RawAddress& raw_address, uint8_t signal_id, uint16_t cid,
+    uint16_t result, uint16_t status, tL2CAP_ERTM_INFO* ertm_info) {
+  CHECK(ConnectionExists(cid));
+  LOG_DEBUG(LOG_TAG,
+            "%s Silently dropping client connect response as channel is "
+            "already connected",
+            __func__);
+  return true;
+}
+
+bool bluetooth::shim::legacy::L2cap::ConfigRequest(
+    uint16_t cid, const tL2CAP_CFG_INFO* config_info) {
+  LOG_INFO(LOG_TAG, "Received config request from upper layer cid:%hd", cid);
+  CHECK(ConnectionExists(cid));
+
+  bluetooth::shim::GetL2cap()->SendLoopbackResponse([this, cid]() {
+    CHECK(ConnectionExists(cid));
+    tL2CAP_CFG_INFO cfg_info{
+        .result = L2CAP_CFG_OK,
+        .mtu_present = false,
+        .qos_present = false,
+        .flush_to_present = false,
+        .fcr_present = false,
+        .fcs_present = false,
+        .ext_flow_spec_present = false,
+        .flags = 0,
+    };
+    classic_.Callbacks(CidToPsm(cid))->pL2CA_ConfigCfm_Cb(cid, &cfg_info);
+    classic_.Callbacks(CidToPsm(cid))->pL2CA_ConfigInd_Cb(cid, &cfg_info);
+  });
+  return true;
+}
+
+bool bluetooth::shim::legacy::L2cap::ConfigResponse(
+    uint16_t cid, const tL2CAP_CFG_INFO* config_info) {
+  CHECK(ConnectionExists(cid));
+  LOG_DEBUG(
+      LOG_TAG,
+      "%s Silently dropping client config response as channel is already open",
+      __func__);
+  return true;
+}
+
+bool bluetooth::shim::legacy::L2cap::DisconnectRequest(uint16_t cid) {
+  CHECK(ConnectionExists(cid));
+  if (cid_closing_set_.find(cid) != cid_closing_set_.end()) {
+    LOG_WARN(LOG_TAG, "%s Channel already in closing state cid:%hu", __func__,
+             cid);
+    return false;
+  }
+  LOG_DEBUG(LOG_TAG, "%s initiated locally cid:%hu", __func__, cid);
+  cid_closing_set_.insert(cid);
+  bluetooth::shim::GetL2cap()->CloseConnection(cid);
+  return true;
+}
+
+bool bluetooth::shim::legacy::L2cap::DisconnectResponse(uint16_t cid) {
+  LOG_DEBUG(LOG_TAG,
+            "%s Silently dropping client disconnect response as channel is "
+            "already disconnected",
+            __func__);
+  return true;
+}
+
+void bluetooth::shim::legacy::L2cap::Dump(int fd) {
+  if (cid_to_psm_map_.empty()) {
+    dprintf(fd, "%s No active l2cap channels\n", kModuleName);
+  } else {
+    for (auto& connection : cid_to_psm_map_) {
+      dprintf(fd, "%s active l2cap channel cid:%hd psm:%hd\n", kModuleName,
+              connection.first, connection.second);
+    }
+  }
+}
diff --git a/main/shim/l2cap.h b/main/shim/l2cap.h
new file mode 100644
index 0000000..b1e3755
--- /dev/null
+++ b/main/shim/l2cap.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <set>
+#include <unordered_map>
+
+#include "main/shim/dumpsys.h"
+#include "stack/include/l2c_api.h"
+
+namespace bluetooth {
+namespace shim {
+namespace legacy {
+
+static constexpr uint16_t kInitialClassicDynamicPsm = 0x1001;
+static constexpr uint16_t kFinalClassicDynamicPsm = 0xfeff;
+static constexpr uint16_t kInitialClassicVirtualPsm = kInitialClassicDynamicPsm;
+static constexpr uint16_t kFinalClassicVirtualPsm = 0x8000;
+static constexpr uint16_t kInitialLeDynamicPsm = 0x0080;
+static constexpr uint16_t kFinalLeDynamicPsm = 0x00ff;
+
+class PsmManager {
+ public:
+  bool IsPsmRegistered(uint16_t psm) const;
+  bool HasClient(uint16_t psm) const;
+  void RegisterPsm(uint16_t psm, const tL2CAP_APPL_INFO* callbacks);
+  void RegisterPsm(uint16_t psm);
+  void UnregisterPsm(uint16_t psm);
+  const tL2CAP_APPL_INFO* Callbacks(uint16_t psm);
+
+ private:
+  /**
+   * Mapping of psm to client callback.
+   *
+   * The current API allows a client may reserve a psm but not
+   * provide a callback which is reflected in a mapping of a
+   * valid psm key entry but a nullptr value.
+   *
+   * A valid client is indicated with a valid psm key entry and a
+   * non-nullptr value.
+   */
+  std::unordered_map<uint16_t, const tL2CAP_APPL_INFO*> psm_to_callback_map_;
+};
+
+class L2cap {
+ public:
+  uint16_t RegisterService(uint16_t psm, const tL2CAP_APPL_INFO* callbacks,
+                           bool enable_snoop, tL2CAP_ERTM_INFO* p_ertm_info);
+  void UnregisterService(uint16_t psm);
+
+  uint16_t CreateConnection(uint16_t psm, const RawAddress& raw_address);
+
+  bool Write(uint16_t cid, BT_HDR* bt_hdr);
+
+  void OnLocalInitiatedConnectionCreated(std::string string_address,
+                                         uint16_t psm, uint16_t cid,
+                                         bool connected);
+  void OnRemoteInitiatedConnectionCreated(std::string string_addresss,
+                                          uint16_t psm, uint16_t cid);
+
+  uint16_t GetNextDynamicClassicPsm();
+  uint16_t GetNextDynamicLePsm();
+
+  uint16_t ConvertClientToRealPsm(uint16_t psm,
+                                  bool is_outgoing_only_connection);
+  uint16_t ConvertClientToRealPsm(uint16_t psm);
+  void RemoveClientPsm(uint16_t client_psm);
+
+  // Legacy API entry points
+  bool ConnectResponse(const RawAddress& raw_address, uint8_t signal_id,
+                       uint16_t cid, uint16_t result, uint16_t status,
+                       tL2CAP_ERTM_INFO* ertm_info);
+  bool ConfigRequest(uint16_t cid, const tL2CAP_CFG_INFO* config_info);
+  bool ConfigResponse(uint16_t cid, const tL2CAP_CFG_INFO* config_info);
+  bool DisconnectRequest(uint16_t cid);
+  bool DisconnectResponse(uint16_t cid);
+
+  L2cap();
+  ~L2cap();
+
+  PsmManager& Classic();
+  PsmManager& Le();
+
+ private:
+  uint16_t GetNextVirtualPsm(uint16_t real_psm);
+  void SetDownstreamCallbacks(uint16_t cid);
+
+  void Dump(int fd);
+
+  PsmManager classic_;
+  PsmManager le_;
+
+  bool ConnectionExists(uint16_t cid) const;
+  uint16_t CidToPsm(uint16_t cid) const;
+
+  uint16_t classic_dynamic_psm_;
+  uint16_t le_dynamic_psm_;
+  uint16_t classic_virtual_psm_;
+
+  std::unordered_map<uint16_t,
+                     std::function<void(std::function<void(uint16_t c)>)>>
+      cid_to_postable_map_;
+  std::set<uint16_t> cid_closing_set_;
+
+  std::unordered_map<uint16_t, uint16_t> cid_to_psm_map_;
+  std::unordered_map<uint16_t, uint16_t> client_psm_to_real_psm_map_;
+};
+
+}  // namespace legacy
+}  // namespace shim
+}  // namespace bluetooth
diff --git a/main/shim/shim.cc b/main/shim/shim.cc
new file mode 100644
index 0000000..fe08aad
--- /dev/null
+++ b/main/shim/shim.cc
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cstdint>
+
+#define LOG_TAG "bt_shim"
+
+#include "common/message_loop_thread.h"
+#include "main/shim/entry.h"
+#include "main/shim/shim.h"
+#include "osi/include/log.h"
+#include "osi/include/properties.h"
+
+static const char* kPropertyKey = "bluetooth.gd.enabled";
+
+static bluetooth::common::MessageLoopThread bt_shim_thread("bt_shim_thread");
+
+static bool gd_shim_enabled_ = false;
+static bool gd_shim_property_checked_ = false;
+static bool gd_stack_started_up_ = false;
+
+future_t* ShimModuleStartUp() {
+  bt_shim_thread.StartUp();
+  CHECK(bt_shim_thread.IsRunning())
+      << "Unable to start bt shim message loop thread.";
+  module_start_up(get_module(GD_SHIM_BTM_MODULE));
+  bluetooth::shim::StartGabeldorscheStack();
+  gd_stack_started_up_ = true;
+  return kReturnImmediate;
+}
+
+future_t* ShimModuleShutDown() {
+  gd_stack_started_up_ = false;
+  bluetooth::shim::StopGabeldorscheStack();
+  module_shut_down(get_module(GD_SHIM_BTM_MODULE));
+  bt_shim_thread.ShutDown();
+  return kReturnImmediate;
+}
+
+EXPORT_SYMBOL extern const module_t gd_shim_module = {
+    .name = GD_SHIM_MODULE,
+    .init = kUnusedModuleApi,
+    .start_up = ShimModuleStartUp,
+    .shut_down = ShimModuleShutDown,
+    .clean_up = kUnusedModuleApi,
+    .dependencies = {kUnusedModuleDependencies}};
+
+void bluetooth::shim::Post(base::OnceClosure task) {
+  bt_shim_thread.DoInThread(FROM_HERE, std::move(task));
+}
+
+bool bluetooth::shim::is_gd_shim_enabled() {
+  if (!gd_shim_property_checked_) {
+    gd_shim_property_checked_ = true;
+    gd_shim_enabled_ = osi_property_get_bool(kPropertyKey, false);
+  }
+  return gd_shim_enabled_;
+}
+
+bool bluetooth::shim::is_gd_stack_started_up() { return gd_stack_started_up_; }
diff --git a/main/shim/shim.h b/main/shim/shim.h
new file mode 100644
index 0000000..6b64ddb
--- /dev/null
+++ b/main/shim/shim.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+/**
+ * Gabeldorsche related legacy-only-stack-side expansion and support code.
+ */
+#include "base/bind.h"
+#include "btcore/include/module.h"
+#include "main/shim/entry.h"
+#include "osi/include/future.h"
+
+static const char GD_SHIM_MODULE[] = "gd_shim_module";
+static const char GD_SHIM_BTM_MODULE[] = "gd_shim_btm_module";
+
+constexpr future_t* kReturnImmediate = nullptr;
+constexpr module_lifecycle_fn kUnusedModuleApi = nullptr;
+constexpr char* kUnusedModuleDependencies = nullptr;
+
+namespace bluetooth {
+namespace shim {
+
+/**
+ * Checks if the bluetooth stack is running in legacy or gd mode.
+ *
+ * This check is used throughout the legacy stack to determine which
+ * methods, classes or functions to invoke.  The default (false) mode
+ * is the legacy mode which runs the original legacy bluetooth stack.
+ * When enabled (true) the core portion of the gd stack is invoked
+ * at key points to execute equivalent functionality using the
+ * gd core components.
+ *
+ * @return true if using gd shim core, false if using legacy.
+ */
+bool is_gd_shim_enabled();
+
+/**
+ * Checks if the bluetooth gd stack has been started up.
+ *
+ * @return true if bluetooth gd stack is started, false otherwise.
+ */
+bool is_gd_stack_started_up();
+
+/**
+ * Posts a task on the shim message queue.
+ *
+ * @param task Task to be posted onto the message queue.
+ */
+void Post(base::OnceClosure task);
+
+}  // namespace shim
+}  // namespace bluetooth
diff --git a/main/shim/timer.cc b/main/shim/timer.cc
new file mode 100644
index 0000000..7e21deb
--- /dev/null
+++ b/main/shim/timer.cc
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "bt_shim_timer"
+
+#include <base/bind.h>
+#include <cstdint>
+#include <functional>
+
+#include "main/shim/shim.h"
+#include "main/shim/timer.h"
+#include "osi/include/alarm.h"
+#include "osi/include/log.h"
+
+static void timer_timeout(void* data) {
+  CHECK(data != nullptr);
+  bluetooth::shim::Timer* timeout = static_cast<bluetooth::shim::Timer*>(data);
+  bluetooth::shim::Post(
+      base::Bind(&bluetooth::shim::Timer::Pop, base::Unretained(timeout)));
+}
+
+void bluetooth::shim::Timer::Set(uint64_t duration_ms,
+                                 std::function<void()> func) {
+  CHECK(duration_ms != 0);
+  callback_ = func;
+  alarm_set_on_mloop(timer_, duration_ms, timer_timeout, (void*)this);
+}
+
+void bluetooth::shim::Timer::Cancel() {
+  alarm_cancel(timer_);
+  callback_ = {};
+}
+
+bool bluetooth::shim::Timer::IsActive() { return callback_ != nullptr; }
+
+bluetooth::shim::Timer::Timer(const char* name) {
+  timer_ = alarm_new(name);
+  CHECK(timer_ != nullptr);
+}
+
+bluetooth::shim::Timer::~Timer() { alarm_free(timer_); }
+
+void bluetooth::shim::Timer::Pop(Timer* timer) {
+  timer->callback_();
+  timer->callback_ = {};
+}
diff --git a/main/shim/timer.h b/main/shim/timer.h
new file mode 100644
index 0000000..16d35c4
--- /dev/null
+++ b/main/shim/timer.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <functional>
+#include "osi/include/alarm.h"
+
+namespace bluetooth {
+namespace shim {
+
+class Timer {
+ public:
+  /**
+   * Set this timer using the osi timer alarm functionality.
+   *
+   * The alarm duration *must not* be zero.  The timer is set
+   * on the bluetooth main message loop thread.
+   *
+   * @param duration_ms Duration in milliseconds (>0) before alarm pops
+   * @param func Function to execute upon alarm pop.
+   */
+  void Set(uint64_t duration_ms, std::function<void()> func);
+
+  /**
+   * Cancel this previously set timer.
+   *
+   * The associated function call will *not* be executed.
+   */
+  void Cancel();
+
+  /**
+   * Determine if a given timer has been set or not.
+   *
+   * @return |true| if timer has been set, |false| otherwise.
+   */
+  bool IsActive();
+
+  /**
+   * @param name Arbitrary name passed to the osi module.
+   */
+  Timer(const char* name);
+  ~Timer();
+
+  /**
+   * Pop this timer.
+   *
+   * Called from an internal trampoline timeout global function registered
+   * with osi alarm.  This trampoline function will then post
+   * the execution of the callback function onto the shim thread.
+   */
+  static void Pop(Timer* timer);
+
+ private:
+  std::function<void()> callback_{};
+  alarm_t* timer_{nullptr};
+};
+
+}  // namespace shim
+}  // namespace bluetooth
diff --git a/osi/Android.bp b/osi/Android.bp
index e6c95d0..3f652f4 100644
--- a/osi/Android.bp
+++ b/osi/Android.bp
@@ -6,7 +6,7 @@
         "system/bt/internal_include",
         "system/bt/utils/include",
         "system/bt/stack/include",
-    ]
+    ],
 }
 
 // Static libraries required by other modules
@@ -19,7 +19,7 @@
     ],
     host_supported: true,
     shared: {
-        enabled: false
+        enabled: false,
     },
 }
 
@@ -31,7 +31,7 @@
     ],
     host_supported: true,
     shared: {
-        enabled: false
+        enabled: false,
     },
 }
 
@@ -122,6 +122,7 @@
         "libbt-protos-lite",
         "libgmock",
         "libosi",
+        "libc++fs",
     ],
     target: {
         linux_glibc: {
diff --git a/osi/include/config.h b/osi/include/config.h
index 47c3a3e..a74314d 100644
--- a/osi/include/config.h
+++ b/osi/include/config.h
@@ -33,10 +33,15 @@
 struct section_t {
   std::string name;
   std::list<entry_t> entries;
+  void Set(std::string key, std::string value);
+  std::list<entry_t>::iterator Find(const std::string& key);
+  bool Has(const std::string& key);
 };
 
 struct config_t {
   std::list<section_t> sections;
+  std::list<section_t>::iterator Find(const std::string& section);
+  bool Has(const std::string& section);
 };
 
 // Creates a new config object with no entries (i.e. not backed by a file).
diff --git a/osi/src/config.cc b/osi/src/config.cc
index de7e6e6..29d6206 100644
--- a/osi/src/config.cc
+++ b/osi/src/config.cc
@@ -30,11 +30,41 @@
 #include <string.h>
 #include <sys/stat.h>
 #include <unistd.h>
+
 #include <sstream>
 #include <type_traits>
 
-// Empty definition; this type is aliased to list_node_t.
-struct config_section_iter_t {};
+void section_t::Set(std::string key, std::string value) {
+  for (entry_t& entry : entries) {
+    if (entry.key == key) {
+      entry.value = value;
+      return;
+    }
+  }
+  // add a new key to the section
+  entries.emplace_back(
+      entry_t{.key = std::move(key), .value = std::move(value)});
+}
+
+std::list<entry_t>::iterator section_t::Find(const std::string& key) {
+  return std::find_if(
+      entries.begin(), entries.end(),
+      [&key](const entry_t& entry) { return entry.key == key; });
+}
+
+bool section_t::Has(const std::string& key) {
+  return Find(key) != entries.end();
+}
+
+std::list<section_t>::iterator config_t::Find(const std::string& section) {
+  return std::find_if(
+      sections.begin(), sections.end(),
+      [&section](const section_t& sec) { return sec.name == section; });
+}
+
+bool config_t::Has(const std::string& key) {
+  return Find(key) != sections.end();
+}
 
 static bool config_parse(FILE* fp, config_t* config);
 
diff --git a/osi/test/alarm_test.cc b/osi/test/alarm_test.cc
index dad54ef..fb55dc7 100644
--- a/osi/test/alarm_test.cc
+++ b/osi/test/alarm_test.cc
@@ -46,7 +46,7 @@
 
 class AlarmTest : public AlarmTestHarness {
  protected:
-  virtual void SetUp() {
+  void SetUp() override {
     AlarmTestHarness::SetUp();
     cb_counter = 0;
     cb_misordered_counter = 0;
@@ -54,7 +54,7 @@
     semaphore = semaphore_new(0);
   }
 
-  virtual void TearDown() {
+  void TearDown() override {
     semaphore_free(semaphore);
     AlarmTestHarness::TearDown();
   }
diff --git a/osi/test/config_test.cc b/osi/test/config_test.cc
index 7cf4db8..e16cd8a 100644
--- a/osi/test/config_test.cc
+++ b/osi/test/config_test.cc
@@ -1,13 +1,33 @@
-#include <base/files/file_util.h>
-#include <gtest/gtest.h>
-
-#include "AllocationTestHarness.h"
+/*
+ *  Copyright 2020 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
 
 #include "osi/include/config.h"
 
-static const char CONFIG_FILE[] = "/data/local/tmp/config_test.conf";
+#include <base/files/file_util.h>
+#include <gtest/gtest.h>
+
+#include <filesystem>
+
+#include "AllocationTestHarness.h"
+
+static const std::filesystem::path kConfigFile =
+    std::filesystem::temp_directory_path() / "config_test.conf";
+static const char* CONFIG_FILE = kConfigFile.c_str();
 static const char CONFIG_FILE_CONTENT[] =
-    "                                                                                    \n\
+    "                                                                                \n\
 first_key=value                                                                      \n\
                                                                                      \n\
 # Device ID (DID) configuration                                                      \n\
@@ -52,14 +72,68 @@
 
 class ConfigTest : public AllocationTestHarness {
  protected:
-  virtual void SetUp() {
+  void SetUp() override {
     AllocationTestHarness::SetUp();
     FILE* fp = fopen(CONFIG_FILE, "wt");
-    fwrite(CONFIG_FILE_CONTENT, 1, sizeof(CONFIG_FILE_CONTENT), fp);
-    fclose(fp);
+    ASSERT_NE(fp, nullptr);
+    ASSERT_EQ(fwrite(CONFIG_FILE_CONTENT, 1, sizeof(CONFIG_FILE_CONTENT), fp),
+              sizeof(CONFIG_FILE_CONTENT));
+    ASSERT_EQ(fclose(fp), 0);
+  }
+
+  void TearDown() override {
+    EXPECT_TRUE(std::filesystem::remove(kConfigFile));
+    AllocationTestHarness::TearDown();
   }
 };
 
+TEST_F(ConfigTest, config_find) {
+  std::unique_ptr<config_t> config = config_new(CONFIG_FILE);
+  ASSERT_NE(config, nullptr);
+  EXPECT_TRUE(config->Has("DID"));
+  auto section_iter = config->Find("DID");
+  ASSERT_NE(section_iter, config->sections.end());
+  EXPECT_FALSE(config->Has("random"));
+  EXPECT_EQ(config->Find("random"), config->sections.end());
+}
+
+TEST_F(ConfigTest, section_find) {
+  std::unique_ptr<config_t> config = config_new(CONFIG_FILE);
+  ASSERT_NE(config, nullptr);
+  EXPECT_TRUE(config->Has("DID"));
+  auto section_iter = config->Find("DID");
+  ASSERT_NE(section_iter, config->sections.end());
+  EXPECT_EQ(section_iter->name, "DID");
+  EXPECT_TRUE(section_iter->Has("version"));
+  auto entry_iter = section_iter->Find("version");
+  ASSERT_NE(entry_iter, section_iter->entries.end());
+  EXPECT_EQ(entry_iter->key, "version");
+  EXPECT_EQ(entry_iter->value, "0x1436");
+  EXPECT_EQ(section_iter->Find("random"), section_iter->entries.end());
+  EXPECT_FALSE(section_iter->Has("random"));
+}
+
+TEST_F(ConfigTest, section_set) {
+  std::unique_ptr<config_t> config = config_new(CONFIG_FILE);
+  ASSERT_NE(config, nullptr);
+  EXPECT_TRUE(config->Has("DID"));
+  auto section_iter = config->Find("DID");
+  ASSERT_NE(section_iter, config->sections.end());
+  EXPECT_EQ(section_iter->name, "DID");
+  EXPECT_FALSE(section_iter->Has("random"));
+  section_iter->Set("random", "foo");
+  EXPECT_TRUE(section_iter->Has("random"));
+  auto entry_iter = section_iter->Find("random");
+  ASSERT_NE(entry_iter, section_iter->entries.end());
+  EXPECT_EQ(entry_iter->key, "random");
+  EXPECT_EQ(entry_iter->value, "foo");
+  section_iter->Set("random", "bar");
+  EXPECT_EQ(entry_iter->value, "bar");
+  entry_iter = section_iter->Find("random");
+  ASSERT_NE(entry_iter, section_iter->entries.end());
+  EXPECT_EQ(entry_iter->value, "bar");
+}
+
 TEST_F(ConfigTest, config_new_empty) {
   std::unique_ptr<config_t> config = config_new_empty();
   EXPECT_TRUE(config.get() != NULL);
@@ -176,22 +250,28 @@
 }
 
 TEST_F(ConfigTest, checksum_read) {
-  std::string filename = "/data/misc/bluedroid/test.checksum";
+  auto tmp_dir = std::filesystem::temp_directory_path();
+  auto filename = tmp_dir / "test.checksum";
   std::string checksum = "0x1234";
-  base::FilePath file_path(filename);
+  base::FilePath file_path(filename.string());
 
   EXPECT_EQ(base::WriteFile(file_path, checksum.data(), checksum.size()),
             (int)checksum.size());
 
   EXPECT_EQ(checksum_read(filename.c_str()), checksum.c_str());
+
+  EXPECT_TRUE(std::filesystem::remove(filename));
 }
 
 TEST_F(ConfigTest, checksum_save) {
-  std::string filename = "/data/misc/bluedroid/test.checksum";
+  auto tmp_dir = std::filesystem::temp_directory_path();
+  auto filename = tmp_dir / "test.checksum";
   std::string checksum = "0x1234";
-  base::FilePath file_path(filename);
+  base::FilePath file_path(filename.string());
 
   EXPECT_TRUE(checksum_save(checksum, filename));
 
   EXPECT_TRUE(base::PathExists(file_path));
+
+  EXPECT_TRUE(std::filesystem::remove(filename));
 }
diff --git a/osi/test/hash_map_utils_test.cc b/osi/test/hash_map_utils_test.cc
index 1e312d4..d5df2d7 100644
--- a/osi/test/hash_map_utils_test.cc
+++ b/osi/test/hash_map_utils_test.cc
@@ -27,8 +27,8 @@
 
 class HashMapUtilsTest : public AllocationTestHarness {
  protected:
-  virtual void SetUp() { AllocationTestHarness::SetUp(); }
-  virtual void TearDown() {
+  void SetUp() override { AllocationTestHarness::SetUp(); }
+  void TearDown() override {
     map.clear();
     AllocationTestHarness::TearDown();
   }
diff --git a/osi/test/wakelock_test.cc b/osi/test/wakelock_test.cc
index 0e027be..a1dfc05 100644
--- a/osi/test/wakelock_test.cc
+++ b/osi/test/wakelock_test.cc
@@ -45,7 +45,7 @@
 
 class WakelockTest : public AllocationTestHarness {
  protected:
-  virtual void SetUp() {
+  void SetUp() override {
     AllocationTestHarness::SetUp();
 
 // TODO (jamuraa): maybe use base::CreateNewTempDirectory instead?
@@ -69,7 +69,7 @@
     creat(unlock_path_.c_str(), S_IRWXU);
   }
 
-  virtual void TearDown() {
+  void TearDown() override {
     is_wake_lock_acquired = false;
     wakelock_cleanup();
     wakelock_set_os_callouts(NULL);
diff --git a/profile/avrcp/Android.bp b/profile/avrcp/Android.bp
index d452cb0..515f513 100644
--- a/profile/avrcp/Android.bp
+++ b/profile/avrcp/Android.bp
@@ -2,7 +2,7 @@
     name: "avrcp-target-service",
     defaults: [
         "fluoride_defaults",
-        "clang_file_coverage"
+        "clang_file_coverage",
     ],
     host_supported: true,
     include_dirs: [
@@ -62,3 +62,35 @@
 
     cflags: ["-DBUILDCFG"],
 }
+
+cc_fuzz {
+    name: "avrcp_device_fuzz",
+    host_supported: true,
+    defaults: [
+        "fluoride_defaults_fuzzable",
+    ],
+    srcs: [
+        "tests/avrcp_device_fuzz/avrcp_device_fuzz.cc",
+    ],
+    include_dirs: [
+        "system/bt",
+        "system/bt/packet/tests",
+        "system/bt/btcore/include",
+        "system/bt/internal_include",
+        "system/bt/stack/include",
+    ],
+    static_libs: [
+        "avrcp-target-service",
+        "lib-bt-packets",
+        "libbase",
+        "libchrome",
+        "libcutils",
+        "libevent",
+        "liblog",
+        "libstatslog",
+    ],
+    header_libs: ["libbluetooth_headers"],
+    corpus: [
+        "tests/avrcp_device_fuzz/corpus/*",
+    ],
+}
diff --git a/profile/avrcp/connection_handler.cc b/profile/avrcp/connection_handler.cc
index 686f89b..af8fb57 100644
--- a/profile/avrcp/connection_handler.cc
+++ b/profile/avrcp/connection_handler.cc
@@ -132,7 +132,7 @@
     return;
   };
 
-  return SdpLookup(bdaddr, base::Bind(connection_lambda, this, bdaddr));
+  return SdpLookup(bdaddr, base::Bind(connection_lambda, this, bdaddr), false);
 }
 
 bool ConnectionHandler::DisconnectDevice(const RawAddress& bdaddr) {
@@ -155,7 +155,8 @@
   return list;
 }
 
-bool ConnectionHandler::SdpLookup(const RawAddress& bdaddr, SdpCallback cb) {
+bool ConnectionHandler::SdpLookup(const RawAddress& bdaddr, SdpCallback cb,
+                                  bool retry) {
   LOG(INFO) << __PRETTY_FUNCTION__;
 
   tAVRC_SDP_DB_PARAMS db_params;
@@ -172,11 +173,11 @@
   db_params.p_db = disc_db;
   db_params.p_attrs = attr_list;
 
-  return avrc_->FindService(
-             UUID_SERVCLASS_AV_REMOTE_CONTROL, bdaddr, &db_params,
-             base::Bind(&ConnectionHandler::SdpCb,
-                        weak_ptr_factory_.GetWeakPtr(), bdaddr, cb, disc_db)) ==
-         AVRC_SUCCESS;
+  return avrc_->FindService(UUID_SERVCLASS_AV_REMOTE_CONTROL, bdaddr,
+                            &db_params,
+                            base::Bind(&ConnectionHandler::SdpCb,
+                                       weak_ptr_factory_.GetWeakPtr(), bdaddr,
+                                       cb, disc_db, retry)) == AVRC_SUCCESS;
 }
 
 bool ConnectionHandler::AvrcpConnect(bool initiator, const RawAddress& bdaddr) {
@@ -342,7 +343,7 @@
         }
       };
 
-      SdpLookup(*peer_addr, base::Bind(sdp_lambda, this, handle));
+      SdpLookup(*peer_addr, base::Bind(sdp_lambda, this, handle), false);
 
       avrc_->OpenBrowse(handle, AVCT_ACP);
       AvrcpConnect(false, RawAddress::kAny);
@@ -406,10 +407,15 @@
 }
 
 void ConnectionHandler::SdpCb(const RawAddress& bdaddr, SdpCallback cb,
-                              tSDP_DISCOVERY_DB* disc_db, uint16_t status) {
+                              tSDP_DISCOVERY_DB* disc_db, bool retry,
+                              uint16_t status) {
   LOG(INFO) << __PRETTY_FUNCTION__ << ": SDP lookup callback received";
 
-  if (status != AVRC_SUCCESS) {
+  if (status == SDP_CONN_FAILED and !retry) {
+    LOG(WARNING) << __PRETTY_FUNCTION__ << ": SDP Failure retry again";
+    SdpLookup(bdaddr, cb, true);
+    return;
+  } else if (status != AVRC_SUCCESS) {
     LOG(ERROR) << __PRETTY_FUNCTION__
                << ": SDP Failure: status = " << (unsigned int)status;
     cb.Run(status, 0, 0);
@@ -534,6 +540,11 @@
   // doesn't need to be processed. In the future, this is the only place sending
   // the packet so none of these layer specific fields will be used.
   pkt->event = 0xFFFF;
+  /* Handle for AVRCP fragment */
+  uint16_t op_code = (uint16_t)(::bluetooth::Packet::Specialize<Packet>(packet)->GetOpcode());
+  if (!browse && (op_code == (uint16_t)(Opcode::VENDOR))) {
+    pkt->event = op_code;
+  }
 
   // TODO (apanicke): This layer specific stuff can go away once we move over
   // to the new service.
diff --git a/profile/avrcp/connection_handler.h b/profile/avrcp/connection_handler.h
index e22cb6a..d5f0a27 100644
--- a/profile/avrcp/connection_handler.h
+++ b/profile/avrcp/connection_handler.h
@@ -135,9 +135,9 @@
 
   using SdpCallback = base::Callback<void(uint16_t status, uint16_t version,
                                           uint16_t features)>;
-  virtual bool SdpLookup(const RawAddress& bdaddr, SdpCallback cb);
+  virtual bool SdpLookup(const RawAddress& bdaddr, SdpCallback cb, bool retry);
   void SdpCb(const RawAddress& bdaddr, SdpCallback cb,
-             tSDP_DISCOVERY_DB* disc_db, uint16_t status);
+             tSDP_DISCOVERY_DB* disc_db, bool retry, uint16_t status);
 
   virtual bool AvrcpConnect(bool initiator, const RawAddress& bdaddr);
 
diff --git a/profile/avrcp/device.cc b/profile/avrcp/device.cc
index 264eaf0..a7e0e9a 100644
--- a/profile/avrcp/device.cc
+++ b/profile/avrcp/device.cc
@@ -995,9 +995,16 @@
   DEVICE_VLOG(2) << __func__ << ": media_id=\"" << media_id << "\"";
 
   SongInfo info;
-  for (const auto& temp : song_list) {
-    if (temp.media_id == media_id) {
-      info = temp;
+  if (song_list.size() == 1) {
+    DEVICE_VLOG(2)
+        << __func__
+        << " Send out the only song in the queue as now playing song.";
+    info = song_list.front();
+  } else {
+    for (const auto& temp : song_list) {
+      if (temp.media_id == media_id) {
+        info = temp;
+      }
     }
   }
 
diff --git a/profile/avrcp/tests/avrcp_connection_handler_test.cc b/profile/avrcp/tests/avrcp_connection_handler_test.cc
index 4f542f6..5a811b2 100644
--- a/profile/avrcp/tests/avrcp_connection_handler_test.cc
+++ b/profile/avrcp/tests/avrcp_connection_handler_test.cc
@@ -25,12 +25,13 @@
 #include "connection_handler.h"
 
 using ::testing::_;
+using ::testing::DoAll;
+using ::testing::MockFunction;
+using ::testing::NiceMock;
 using ::testing::Return;
 using ::testing::SaveArg;
 using ::testing::SaveArgPointee;
 using ::testing::SetArgPointee;
-using ::testing::MockFunction;
-using ::testing::NiceMock;
 using ::testing::StrictMock;
 
 namespace bluetooth {
@@ -55,7 +56,7 @@
         .p_next_attr = nullptr,
         .attr_id = 0,
         .attr_len_type = 0,
-        .attr_value.v.u16 = 0,
+        .attr_value = {.v = {.u16 = 0}},
     };
 
     if (browsing) fake_features.attr_value.v.u16 |= AVRC_SUPF_CT_BROWSE;
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/avrcp_device_fuzz.cc b/profile/avrcp/tests/avrcp_device_fuzz/avrcp_device_fuzz.cc
new file mode 100644
index 0000000..24a794d
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/avrcp_device_fuzz.cc
@@ -0,0 +1,86 @@
+#include <cstddef>
+#include <cstdint>
+
+#include "avrcp_packet.h"
+#include "device.h"
+#include "packet_test_helper.h"
+#include "stack_config.h"
+
+namespace bluetooth {
+namespace avrcp {
+class FakeMediaInterface : public MediaInterface {
+ public:
+  virtual void SendKeyEvent(uint8_t key, KeyState state) {}
+  using SongInfoCallback = base::Callback<void(SongInfo)>;
+  virtual void GetSongInfo(SongInfoCallback info_cb) {}
+  using PlayStatusCallback = base::Callback<void(PlayStatus)>;
+  virtual void GetPlayStatus(PlayStatusCallback status_cb) {}
+  using NowPlayingCallback =
+      base::Callback<void(std::string, std::vector<SongInfo>)>;
+  virtual void GetNowPlayingList(NowPlayingCallback now_playing_cb) {}
+  using MediaListCallback =
+      base::Callback<void(uint16_t curr_player, std::vector<MediaPlayerInfo>)>;
+  virtual void GetMediaPlayerList(MediaListCallback list_cb) {}
+  using FolderItemsCallback = base::Callback<void(std::vector<ListItem>)>;
+  virtual void GetFolderItems(uint16_t player_id, std::string media_id,
+                              FolderItemsCallback folder_cb) {}
+  using SetBrowsedPlayerCallback = base::Callback<void(
+      bool success, std::string root_id, uint32_t num_items)>;
+  virtual void SetBrowsedPlayer(uint16_t player_id,
+                                SetBrowsedPlayerCallback browse_cb) {}
+  virtual void PlayItem(uint16_t player_id, bool now_playing,
+                        std::string media_id) {}
+  virtual void SetActiveDevice(const RawAddress& address) {}
+  virtual void RegisterUpdateCallback(MediaCallbacks* callback) {}
+  virtual void UnregisterUpdateCallback(MediaCallbacks* callback) {}
+};
+
+class FakeVolumeInterface : public VolumeInterface {
+ public:
+  virtual void DeviceConnected(const RawAddress& bdaddr) {}
+  virtual void DeviceConnected(const RawAddress& bdaddr, VolumeChangedCb cb) {}
+  virtual void DeviceDisconnected(const RawAddress& bdaddr) {}
+  virtual void SetVolume(int8_t volume) {}
+};
+
+class FakeA2dpInterface : public A2dpInterface {
+ public:
+  virtual RawAddress active_peer() { return RawAddress(); }
+  virtual bool is_peer_in_silence_mode(const RawAddress& peer_address) {
+    return false;
+  }
+};
+
+bool get_pts_avrcp_test(void) { return false; }
+
+const stack_config_t interface = {
+    nullptr, get_pts_avrcp_test, nullptr, nullptr, nullptr, nullptr, nullptr,
+    nullptr};
+
+void Callback(uint8_t, bool, std::unique_ptr<::bluetooth::PacketBuilder>) {}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) {
+  FakeMediaInterface fmi;
+  FakeVolumeInterface fvi;
+  FakeA2dpInterface fai;
+
+  std::vector<uint8_t> Packet(Data, Data + Size);
+  Device device(RawAddress::kAny, true,
+                base::Bind([](uint8_t, bool,
+                              std::unique_ptr<::bluetooth::PacketBuilder>) {}),
+                0xFFFF, 0xFFFF);
+  device.RegisterInterfaces(&fmi, &fai, &fvi);
+
+  auto browse_request = TestPacketType<BrowsePacket>::Make(Packet);
+  device.BrowseMessageReceived(1, browse_request);
+
+  auto avrcp_request = TestPacketType<avrcp::Packet>::Make(Packet);
+  device.MessageReceived(1, avrcp_request);
+  return 0;
+}
+}  // namespace avrcp
+}  // namespace bluetooth
+
+const stack_config_t* stack_config_get_interface(void) {
+  return &bluetooth::avrcp::interface;
+}
\ No newline at end of file
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/change_path_error_response b/profile/avrcp/tests/avrcp_device_fuzz/corpus/change_path_error_response
new file mode 100644
index 0000000..f5f982c
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/change_path_error_response
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/change_path_request b/profile/avrcp/tests/avrcp_device_fuzz/corpus/change_path_request
new file mode 100644
index 0000000..581327a
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/change_path_request
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/change_path_response b/profile/avrcp/tests/avrcp_device_fuzz/corpus/change_path_response
new file mode 100644
index 0000000..10c7576
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/change_path_response
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/change_path_up_request b/profile/avrcp/tests/avrcp_device_fuzz/corpus/change_path_up_request
new file mode 100644
index 0000000..a222657
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/change_path_up_request
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/changed_play_pos_notification b/profile/avrcp/tests/avrcp_device_fuzz/corpus/changed_play_pos_notification
new file mode 100644
index 0000000..6ee7661
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/changed_play_pos_notification
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/changed_volume_changed_notification b/profile/avrcp/tests/avrcp_device_fuzz/corpus/changed_volume_changed_notification
new file mode 100644
index 0000000..31d50fb
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/changed_volume_changed_notification
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/general_reject_invalid_command_packet b/profile/avrcp/tests/avrcp_device_fuzz/corpus/general_reject_invalid_command_packet
new file mode 100644
index 0000000..83fa427
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/general_reject_invalid_command_packet
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_capabilities_request b/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_capabilities_request
new file mode 100644
index 0000000..3968c03
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_capabilities_request
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_capabilities_request_company_id b/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_capabilities_request_company_id
new file mode 100644
index 0000000..f3966ed
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_capabilities_request_company_id
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_capabilities_request_unknown b/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_capabilities_request_unknown
new file mode 100644
index 0000000..eada547
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_capabilities_request_unknown
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_capabilities_response_company_id b/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_capabilities_response_company_id
new file mode 100644
index 0000000..21889ca
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_capabilities_response_company_id
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_capabilities_response_events_supported b/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_capabilities_response_events_supported
new file mode 100644
index 0000000..d2cf8a8
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_capabilities_response_events_supported
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_element_attributes_request_full b/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_element_attributes_request_full
new file mode 100644
index 0000000..d79c1d3
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_element_attributes_request_full
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_element_attributes_request_partial b/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_element_attributes_request_partial
new file mode 100644
index 0000000..c37c9b6
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_element_attributes_request_partial
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_elements_attributes_response_full b/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_elements_attributes_response_full
new file mode 100644
index 0000000..2e63feb
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_elements_attributes_response_full
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_folder_items_error_response b/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_folder_items_error_response
new file mode 100644
index 0000000..a09361b
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_folder_items_error_response
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_folder_items_folder_response b/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_folder_items_folder_response
new file mode 100644
index 0000000..f58889a
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_folder_items_folder_response
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_folder_items_media_player_response b/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_folder_items_media_player_response
new file mode 100644
index 0000000..ed25c8c
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_folder_items_media_player_response
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_folder_items_request b/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_folder_items_request
new file mode 100644
index 0000000..098fa05
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_folder_items_request
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_folder_items_request_no_attrs b/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_folder_items_request_no_attrs
new file mode 100644
index 0000000..7e27ff9
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_folder_items_request_no_attrs
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_folder_items_request_now_playing b/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_folder_items_request_now_playing
new file mode 100644
index 0000000..9eaa355
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_folder_items_request_now_playing
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_folder_items_request_title b/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_folder_items_request_title
new file mode 100644
index 0000000..0108fc7
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_folder_items_request_title
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_folder_items_request_vfs b/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_folder_items_request_vfs
new file mode 100644
index 0000000..e8cc867
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_folder_items_request_vfs
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_folder_items_song_response b/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_folder_items_song_response
new file mode 100644
index 0000000..f7472e0
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_folder_items_song_response
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_item_attributes_request_all_attributes b/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_item_attributes_request_all_attributes
new file mode 100644
index 0000000..7d706b8
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_item_attributes_request_all_attributes
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_item_attributes_request_all_attributes_invalid b/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_item_attributes_request_all_attributes_invalid
new file mode 100644
index 0000000..d55d0c4
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_item_attributes_request_all_attributes_invalid
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_item_attributes_song_response b/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_item_attributes_song_response
new file mode 100644
index 0000000..3553664
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_item_attributes_song_response
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_play_status_request b/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_play_status_request
new file mode 100644
index 0000000..2311a47
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_play_status_request
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_play_status_response b/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_play_status_response
new file mode 100644
index 0000000..240136f
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_play_status_response
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_total_number_of_items_request_media_players b/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_total_number_of_items_request_media_players
new file mode 100644
index 0000000..a5b85b7
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_total_number_of_items_request_media_players
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_total_number_of_items_request_now_playing b/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_total_number_of_items_request_now_playing
new file mode 100644
index 0000000..a4a6654
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_total_number_of_items_request_now_playing
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_total_number_of_items_request_vfs b/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_total_number_of_items_request_vfs
new file mode 100644
index 0000000..0f9ad75
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_total_number_of_items_request_vfs
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_total_number_of_items_response b/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_total_number_of_items_response
new file mode 100644
index 0000000..742da96
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/get_total_number_of_items_response
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/interim_addressed_player_notification b/profile/avrcp/tests/avrcp_device_fuzz/corpus/interim_addressed_player_notification
new file mode 100644
index 0000000..2f6044b
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/interim_addressed_player_notification
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/interim_available_players_notification b/profile/avrcp/tests/avrcp_device_fuzz/corpus/interim_available_players_notification
new file mode 100644
index 0000000..b643cf4
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/interim_available_players_notification
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/interim_now_playing_notification b/profile/avrcp/tests/avrcp_device_fuzz/corpus/interim_now_playing_notification
new file mode 100644
index 0000000..ea6675e
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/interim_now_playing_notification
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/interim_play_status_notification b/profile/avrcp/tests/avrcp_device_fuzz/corpus/interim_play_status_notification
new file mode 100644
index 0000000..0fb233d
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/interim_play_status_notification
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/interim_track_changed_notification b/profile/avrcp/tests/avrcp_device_fuzz/corpus/interim_track_changed_notification
new file mode 100644
index 0000000..b23cf6e
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/interim_track_changed_notification
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/interim_uids_notificaiton b/profile/avrcp/tests/avrcp_device_fuzz/corpus/interim_uids_notificaiton
new file mode 100644
index 0000000..cac9b4a
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/interim_uids_notificaiton
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/interim_volume_changed_notification b/profile/avrcp/tests/avrcp_device_fuzz/corpus/interim_volume_changed_notification
new file mode 100644
index 0000000..101bae6
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/interim_volume_changed_notification
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/pass_through_command_play_pushed b/profile/avrcp/tests/avrcp_device_fuzz/corpus/pass_through_command_play_pushed
new file mode 100644
index 0000000..9e002d2
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/pass_through_command_play_pushed
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/pass_through_command_play_released b/profile/avrcp/tests/avrcp_device_fuzz/corpus/pass_through_command_play_released
new file mode 100644
index 0000000..5c100e8
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/pass_through_command_play_released
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/play_item_request b/profile/avrcp/tests/avrcp_device_fuzz/corpus/play_item_request
new file mode 100644
index 0000000..538a7c0
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/play_item_request
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/play_item_response b/profile/avrcp/tests/avrcp_device_fuzz/corpus/play_item_response
new file mode 100644
index 0000000..1180bcc
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/play_item_response
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/register_notification_invalid b/profile/avrcp/tests/avrcp_device_fuzz/corpus/register_notification_invalid
new file mode 100644
index 0000000..2a213f0
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/register_notification_invalid
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/register_play_status_notification b/profile/avrcp/tests/avrcp_device_fuzz/corpus/register_play_status_notification
new file mode 100644
index 0000000..fa40de4
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/register_play_status_notification
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/register_volume_changed_notification b/profile/avrcp/tests/avrcp_device_fuzz/corpus/register_volume_changed_notification
new file mode 100644
index 0000000..3e7209b
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/register_volume_changed_notification
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/reject_player_app_settings_response b/profile/avrcp/tests/avrcp_device_fuzz/corpus/reject_player_app_settings_response
new file mode 100644
index 0000000..82cc047
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/reject_player_app_settings_response
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/rejected_volume_changed_notification b/profile/avrcp/tests/avrcp_device_fuzz/corpus/rejected_volume_changed_notification
new file mode 100644
index 0000000..5119a2d
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/rejected_volume_changed_notification
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/set_absolute_volume_request b/profile/avrcp/tests/avrcp_device_fuzz/corpus/set_absolute_volume_request
new file mode 100644
index 0000000..5403b84
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/set_absolute_volume_request
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/set_absolute_volume_response b/profile/avrcp/tests/avrcp_device_fuzz/corpus/set_absolute_volume_response
new file mode 100644
index 0000000..b30cde2
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/set_absolute_volume_response
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/set_addressed_player_id_1_request b/profile/avrcp/tests/avrcp_device_fuzz/corpus/set_addressed_player_id_1_request
new file mode 100644
index 0000000..ebe224e
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/set_addressed_player_id_1_request
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/set_addressed_player_request b/profile/avrcp/tests/avrcp_device_fuzz/corpus/set_addressed_player_request
new file mode 100644
index 0000000..a38cfb7
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/set_addressed_player_request
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/set_addressed_player_response b/profile/avrcp/tests/avrcp_device_fuzz/corpus/set_addressed_player_response
new file mode 100644
index 0000000..4230524
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/set_addressed_player_response
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/set_browsed_player_request b/profile/avrcp/tests/avrcp_device_fuzz/corpus/set_browsed_player_request
new file mode 100644
index 0000000..667137b
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/set_browsed_player_request
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/set_browsed_player_response b/profile/avrcp/tests/avrcp_device_fuzz/corpus/set_browsed_player_response
new file mode 100644
index 0000000..c3a3c93
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/set_browsed_player_response
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/short_browse_packet b/profile/avrcp/tests/avrcp_device_fuzz/corpus/short_browse_packet
new file mode 100644
index 0000000..b866ef0
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/short_browse_packet
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/short_change_path_request b/profile/avrcp/tests/avrcp_device_fuzz/corpus/short_change_path_request
new file mode 100644
index 0000000..365d337
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/short_change_path_request
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/short_get_capabilities_request b/profile/avrcp/tests/avrcp_device_fuzz/corpus/short_get_capabilities_request
new file mode 100644
index 0000000..c7c5b33
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/short_get_capabilities_request
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/short_get_element_attributes_request b/profile/avrcp/tests/avrcp_device_fuzz/corpus/short_get_element_attributes_request
new file mode 100644
index 0000000..102d788
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/short_get_element_attributes_request
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/short_get_folder_items_request b/profile/avrcp/tests/avrcp_device_fuzz/corpus/short_get_folder_items_request
new file mode 100644
index 0000000..4ab0aa8
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/short_get_folder_items_request
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/short_get_item_attributes_request b/profile/avrcp/tests/avrcp_device_fuzz/corpus/short_get_item_attributes_request
new file mode 100644
index 0000000..7467555
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/short_get_item_attributes_request
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/short_get_total_number_of_items_request b/profile/avrcp/tests/avrcp_device_fuzz/corpus/short_get_total_number_of_items_request
new file mode 100644
index 0000000..bc634e2
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/short_get_total_number_of_items_request
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/short_play_item_request b/profile/avrcp/tests/avrcp_device_fuzz/corpus/short_play_item_request
new file mode 100644
index 0000000..a138dc4
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/short_play_item_request
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/short_set_addressed_player_request b/profile/avrcp/tests/avrcp_device_fuzz/corpus/short_set_addressed_player_request
new file mode 100644
index 0000000..4872d6b
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/short_set_addressed_player_request
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_fuzz/corpus/short_vendor_packet b/profile/avrcp/tests/avrcp_device_fuzz/corpus/short_vendor_packet
new file mode 100644
index 0000000..0c2a66e
--- /dev/null
+++ b/profile/avrcp/tests/avrcp_device_fuzz/corpus/short_vendor_packet
Binary files differ
diff --git a/profile/avrcp/tests/avrcp_device_test.cc b/profile/avrcp/tests/avrcp_device_test.cc
index c608da6..d221e1b 100644
--- a/profile/avrcp/tests/avrcp_device_test.cc
+++ b/profile/avrcp/tests/avrcp_device_test.cc
@@ -57,7 +57,7 @@
 // Add more tests to increase code coverage.
 class AvrcpDeviceTest : public ::testing::Test {
  public:
-  virtual void SetUp() override {
+  void SetUp() override {
     // NOTE: We use a wrapper lambda for the MockFunction in order to
     // add a const qualifier to the response. Otherwise the MockFunction
     // type doesn't match the callback type and a compiler error occurs.
@@ -71,7 +71,7 @@
     test_device = new Device(RawAddress::kAny, true, cb, 0xFFFF, 0xFFFF);
   }
 
-  virtual void TearDown() override {
+  void TearDown() override {
     delete test_device;
     Mock::VerifyAndClear(&response_cb);
   }
diff --git a/profile/sdp/Android.bp b/profile/sdp/Android.bp
index 9254337..5abd77b 100644
--- a/profile/sdp/Android.bp
+++ b/profile/sdp/Android.bp
@@ -1,40 +1,40 @@
 cc_library_static {
-  name: "sdp_service",
-  defaults: [
-    "fluoride_defaults",
-    "clang_file_coverage"
-  ],
-  host_supported: true,
-  include_dirs: [
-    "system/bt/",
-  ],
-  srcs: [
-    "common/data_element_reader.cc",
-  ],
-  static_libs: [
-    "lib-bt-packets",
-    "libbluetooth-types",
-  ],
+    name: "sdp_service",
+    defaults: [
+        "fluoride_defaults",
+        "clang_file_coverage",
+    ],
+    host_supported: true,
+    include_dirs: [
+        "system/bt/",
+    ],
+    srcs: [
+        "common/data_element_reader.cc",
+    ],
+    static_libs: [
+        "lib-bt-packets",
+        "libbluetooth-types",
+    ],
 }
 
 cc_test {
-  name: "bluetooth_test_sdp",
-  test_suites: ["general-tests"],
-  defaults: [
-    "fluoride_defaults",
-    "clang_coverage_bin",
-  ],
-  host_supported: true,
-  include_dirs: [
-    "system/bt/",
-  ],
-  srcs: [
-    "common/test/data_element_reader_test.cc",
-  ],
-  static_libs: [
-    "libgmock",
-    "sdp_service",
-    "lib-bt-packets",
-    "libbluetooth-types",
-  ],
+    name: "bluetooth_test_sdp",
+    test_suites: ["general-tests"],
+    defaults: [
+        "fluoride_defaults",
+        "clang_coverage_bin",
+    ],
+    host_supported: true,
+    include_dirs: [
+        "system/bt/",
+    ],
+    srcs: [
+        "common/test/data_element_reader_test.cc",
+    ],
+    static_libs: [
+        "libgmock",
+        "sdp_service",
+        "lib-bt-packets",
+        "libbluetooth-types",
+    ],
 }
diff --git a/proto/Android.bp b/proto/Android.bp
index 0f97c72..ccd2b3b 100644
--- a/proto/Android.bp
+++ b/proto/Android.bp
@@ -4,7 +4,10 @@
     proto: {
         type: "lite",
     },
-    srcs: ["bluetooth/metrics/bluetooth.proto"],
+    srcs: [
+        "bluetooth/metrics/bluetooth.proto",
+        "bluetooth/bluetoothKeystore/keystore.proto",
+    ],
 }
 
 cc_library_static {
@@ -14,6 +17,8 @@
         export_proto_headers: true,
         type: "lite",
     },
-    srcs: ["bluetooth/metrics/bluetooth.proto"],
+    srcs: [
+        "bluetooth/metrics/bluetooth.proto",
+        "bluetooth/bluetoothKeystore/keystore.proto",
+    ],
 }
-
diff --git a/proto/bluetooth/bluetoothKeystore/keystore.proto b/proto/bluetooth/bluetoothKeystore/keystore.proto
new file mode 100644
index 0000000..fd4416d
--- /dev/null
+++ b/proto/bluetooth/bluetoothKeystore/keystore.proto
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+// C++ namespace: bluetooth::metrics::BluetoothMetricsProto
+package bluetooth.keystore.BluetoothKeystoreProto;
+
+option java_package = "com.android.bluetooth";
+option java_outer_classname = "BluetoothKeystoreProto";
+
+// Holds encrypted, authenticated data.
+message EncryptedData {
+  // The initialization vector used during encryption.
+  optional bytes init_vector = 1;
+  // MAC of (init_vector + encrypted_data).
+  optional bytes authentication_data = 2;
+  optional bytes encrypted_data = 3;
+}
diff --git a/service/Android.bp b/service/Android.bp
index d57119a..2760b5b 100644
--- a/service/Android.bp
+++ b/service/Android.bp
@@ -8,7 +8,7 @@
     include_dirs: [
         "system/bt",
     ],
-    header_libs: [ "libbluetooth_headers" ],
+    header_libs: ["libbluetooth_headers"],
 }
 
 // Source variables
@@ -63,6 +63,7 @@
     "hal/fake_bluetooth_gatt_interface.cc",
     "hal/fake_bluetooth_interface.cc",
     "test/a2dp_sink_unittest.cc",
+    "test/a2dp_source_unittest.cc",
     "test/adapter_unittest.cc",
     "test/advertise_data_unittest.cc",
     "test/fake_hal_util.cc",
@@ -80,9 +81,9 @@
     name: "bluetoothtbd",
     defaults: ["fluoride_service_defaults"],
     srcs: btserviceBinderDaemonSrc +
-    btserviceLinuxSrc +
-    btserviceDaemonSrc +
-    ["main.cc"],
+        btserviceLinuxSrc +
+        btserviceDaemonSrc +
+        ["main.cc"],
     static_libs: [
         "libbluetooth-binder-common",
         "libbtcore",
@@ -108,9 +109,9 @@
     test_suites: ["device-tests"],
     defaults: ["fluoride_service_defaults"],
     srcs: btserviceBaseTestSrc +
-    btserviceDaemonSrc + [
-        "test/main.cc",
-    ],
+        btserviceDaemonSrc + [
+            "test/main.cc",
+        ],
     aidl: {
         include_dirs: [
             "system/bt/service/common",
diff --git a/service/a2dp_source.cc b/service/a2dp_source.cc
index 9e77007..d39f202 100644
--- a/service/a2dp_source.cc
+++ b/service/a2dp_source.cc
@@ -199,6 +199,13 @@
                              codecs_selectable_capabilities);
 }
 
+bool A2dpSource::MandatoryCodecPreferredCallback(BluetoothAvInterface* iface,
+                                                 const RawAddress& bd_addr) {
+  LockGuard lock(delegate_mutex_);
+  // Do nothing. Optional codecs are preferred by default.
+  return false;
+}
+
 // A2dpSourceFactory implementation
 // ========================================================
 A2dpSourceFactory::A2dpSourceFactory() = default;
diff --git a/service/a2dp_source.h b/service/a2dp_source.h
index f4e3ab3..53da6bd 100644
--- a/service/a2dp_source.h
+++ b/service/a2dp_source.h
@@ -82,6 +82,8 @@
       const std::vector<btav_a2dp_codec_config_t> codecs_local_capabilities,
       const std::vector<btav_a2dp_codec_config_t>
           codecs_selectable_capabilities) override;
+  bool MandatoryCodecPreferredCallback(hal::BluetoothAvInterface* iface,
+                                       const RawAddress& bd_addr) override;
 
   // For |GetAppIdentifier|.
   const Uuid app_identifier_;
diff --git a/service/common/Android.bp b/service/common/Android.bp
index fa63cf9..e3bde74 100644
--- a/service/common/Android.bp
+++ b/service/common/Android.bp
@@ -7,7 +7,7 @@
         "-fvisibility=default",
     ],
     host_supported: true,
-    header_libs: [ "libbluetooth_headers" ],
+    header_libs: ["libbluetooth_headers"],
     srcs: [
         "bluetooth/a2dp_codec_config.cc",
         "bluetooth/adapter_state.cc",
@@ -40,7 +40,7 @@
         /* we export all classes, so change default visibility, instead of having EXPORT_SYMBOL on each class*/
         "-fvisibility=default",
     ],
-    header_libs: [ "libbluetooth_headers" ],
+    header_libs: ["libbluetooth_headers"],
     srcs: [
         "android/bluetooth/IBluetooth.aidl",
         "android/bluetooth/IBluetoothA2dpSink.aidl",
@@ -94,5 +94,5 @@
     ],
     static_libs: [
         "libbluetooth-types",
-    ]
+    ],
 }
diff --git a/service/gatt_server.cc b/service/gatt_server.cc
index f46927d..f911f54 100644
--- a/service/gatt_server.cc
+++ b/service/gatt_server.cc
@@ -18,7 +18,6 @@
 
 #include <base/logging.h>
 
-#include "osi/include/log.h"
 #include "service/logging_helpers.h"
 #include "stack/include/bt_types.h"
 
@@ -69,18 +68,20 @@
 
   std::vector<btgatt_db_element_t> svc;
 
-  svc.push_back({.type = (service.primary() ? BTGATT_DB_PRIMARY_SERVICE
-                                            : BTGATT_DB_SECONDARY_SERVICE),
-                 .uuid = service.uuid()});
+  svc.push_back({
+      .uuid = service.uuid(),
+      .type = (service.primary() ? BTGATT_DB_PRIMARY_SERVICE
+                                 : BTGATT_DB_SECONDARY_SERVICE),
+  });
 
   for (const auto& characteristic : service.characteristics()) {
-    svc.push_back({.type = BTGATT_DB_CHARACTERISTIC,
-                   .uuid = characteristic.uuid(),
+    svc.push_back({.uuid = characteristic.uuid(),
+                   .type = BTGATT_DB_CHARACTERISTIC,
                    .properties = characteristic.properties(),
                    .permissions = characteristic.permissions()});
     for (const auto& descriptor : characteristic.descriptors())
-      svc.push_back({.type = BTGATT_DB_DESCRIPTOR,
-                     .uuid = descriptor.uuid(),
+      svc.push_back({.uuid = descriptor.uuid(),
+                     .type = BTGATT_DB_DESCRIPTOR,
                      .permissions = descriptor.permissions()});
   }
 
@@ -118,7 +119,6 @@
   }
 
   if (offset < 0) {
-    android_errorWriteLog(0x534e4554, "143231677");
     LOG(ERROR) << "Offset is less than 0 offset: " << offset;
     return false;
   }
diff --git a/service/gatt_server_old.cc b/service/gatt_server_old.cc
index b583d93..cfc394c 100644
--- a/service/gatt_server_old.cc
+++ b/service/gatt_server_old.cc
@@ -136,8 +136,10 @@
 
   g_internal->server_if = server_if;
 
-  pending_svc_decl.push_back(
-      {.type = BTGATT_DB_PRIMARY_SERVICE, .uuid = app_uuid});
+  pending_svc_decl.push_back({
+      .uuid = app_uuid,
+      .type = BTGATT_DB_PRIMARY_SERVICE,
+  });
 }
 
 void ServiceAddedCallback(int status, int server_if,
@@ -502,8 +504,8 @@
 bt_status_t ServerInternals::AddCharacteristic(const Uuid& uuid,
                                                uint8_t properties,
                                                uint16_t permissions) {
-  pending_svc_decl.push_back({.type = BTGATT_DB_CHARACTERISTIC,
-                              .uuid = uuid,
+  pending_svc_decl.push_back({.uuid = uuid,
+                              .type = BTGATT_DB_CHARACTERISTIC,
                               .properties = properties,
                               .permissions = permissions});
   return BT_STATUS_SUCCESS;
diff --git a/service/hal/bluetooth_av_interface.cc b/service/hal/bluetooth_av_interface.cc
index 68f5bd0..4700cce 100644
--- a/service/hal/bluetooth_av_interface.cc
+++ b/service/hal/bluetooth_av_interface.cc
@@ -46,11 +46,11 @@
 base::ObserverList<BluetoothAvInterface::A2dpSinkObserver>*
 GetA2dpSinkObservers();
 
-#define VERIFY_INTERFACE_OR_RETURN()                                   \
+#define VERIFY_INTERFACE_OR_RETURN(...)                                \
   do {                                                                 \
     if (!g_interface) {                                                \
       LOG(WARNING) << "Callback received while |g_interface| is NULL"; \
-      return;                                                          \
+      return __VA_ARGS__;                                              \
     }                                                                  \
   } while (0)
 
@@ -88,6 +88,17 @@
   }
 }
 
+bool SourceMandatoryCodecPreferredCallback(const RawAddress& bd_addr) {
+  VERIFY_INTERFACE_OR_RETURN(false);
+  // The mandatory codec is preferred only when all observers disable their
+  // optional codecs.
+  for (auto& observer : *GetA2dpSourceObservers()) {
+    if (!observer.MandatoryCodecPreferredCallback(g_interface, bd_addr))
+      return false;
+  }
+  return true;
+}
+
 void SinkConnectionStateCallback(const RawAddress& bd_addr,
                                  btav_connection_state_t state) {
   std::shared_lock<shared_mutex_impl> lock(g_instance_lock);
@@ -121,6 +132,7 @@
     .connection_state_cb = SourceConnectionStateCallback,
     .audio_state_cb = SourceAudioStateCallback,
     .audio_config_cb = SourceAudioConfigCallback,
+    .mandatory_codec_preferred_cb = SourceMandatoryCodecPreferredCallback,
 };
 
 btav_sink_callbacks_t av_sink_callbacks = {
@@ -146,9 +158,11 @@
 
     // Right now we only support one connected audio device.
     int max_connected_audio_devices = 1;
+    std::vector<btav_a2dp_codec_config_t> offloading_preference(0);
     if (hal_source_iface_->init(
             &av_source_callbacks, max_connected_audio_devices,
-            std::move(codec_priorities)) != BT_STATUS_SUCCESS) {
+            std::move(codec_priorities),
+            std::move(offloading_preference)) != BT_STATUS_SUCCESS) {
       LOG(ERROR) << "Failed to initialize HAL A2DP source interface";
       return false;
     }
@@ -295,6 +309,12 @@
   // Do nothing.
 }
 
+bool BluetoothAvInterface::A2dpSourceObserver::MandatoryCodecPreferredCallback(
+    BluetoothAvInterface* iface, const RawAddress& bd_addr) {
+  // Do nothing.
+  return false;
+}
+
 void BluetoothAvInterface::A2dpSinkObserver::ConnectionStateCallback(
     BluetoothAvInterface* iface, const RawAddress& bd_addr,
     btav_connection_state_t state) {
diff --git a/service/hal/bluetooth_av_interface.h b/service/hal/bluetooth_av_interface.h
index 9346143..eda6476 100644
--- a/service/hal/bluetooth_av_interface.h
+++ b/service/hal/bluetooth_av_interface.h
@@ -41,6 +41,8 @@
         const std::vector<btav_a2dp_codec_config_t> codecs_local_capabilities,
         const std::vector<btav_a2dp_codec_config_t>
             codecs_selectable_capabilities);
+    virtual bool MandatoryCodecPreferredCallback(BluetoothAvInterface* iface,
+                                                 const RawAddress& bd_addr);
 
    protected:
     virtual ~A2dpSourceObserver() = default;
diff --git a/service/hal/bluetooth_interface.cc b/service/hal/bluetooth_interface.cc
index b87ee8c..42eeb00 100644
--- a/service/hal/bluetooth_interface.cc
+++ b/service/hal/bluetooth_interface.cc
@@ -254,7 +254,7 @@
 
     // Initialize the Bluetooth interface. Set up the adapter (Bluetooth DM) API
     // callbacks.
-    status = hal_iface_->init(&bt_callbacks, false, false, false);
+    status = hal_iface_->init(&bt_callbacks, false, false, 0, false);
     if (status != BT_STATUS_SUCCESS) {
       LOG(ERROR) << "Failed to initialize Bluetooth stack";
       return false;
diff --git a/service/hal/fake_bluetooth_av_interface.cc b/service/hal/fake_bluetooth_av_interface.cc
index 39a6331..01c8ebe 100644
--- a/service/hal/fake_bluetooth_av_interface.cc
+++ b/service/hal/fake_bluetooth_av_interface.cc
@@ -23,19 +23,30 @@
 // The global test handler instances. We have to have globals since the HAL
 // interface methods all have to be global and their signatures don't allow us
 // to pass in user_data.
+std::shared_ptr<FakeBluetoothAvInterface::TestA2dpSourceHandler>
+    g_a2dp_source_handler{};
 std::shared_ptr<FakeBluetoothAvInterface::TestA2dpSinkHandler>
-    g_a2dp_sink_handler;
+    g_a2dp_sink_handler{};
 
-bt_status_t FakeInit(btav_sink_callbacks_t* callbacks) {
+bt_status_t FakeSourceInit(
+    btav_source_callbacks_t* callbacks, int max_connected_audio_devices,
+    const std::vector<btav_a2dp_codec_config_t>& codec_priorities,
+    const std::vector<btav_a2dp_codec_config_t>& offloading_preference) {
+  return BT_STATUS_SUCCESS;
+}
+
+bt_status_t FakeSinkInit(btav_sink_callbacks_t* callbacks) {
   return BT_STATUS_SUCCESS;
 }
 
 bt_status_t FakeConnect(const RawAddress& bd_addr) {
+  if (g_a2dp_source_handler) return g_a2dp_source_handler->Connect(bd_addr);
   if (g_a2dp_sink_handler) return g_a2dp_sink_handler->Connect(bd_addr);
   return BT_STATUS_FAIL;
 }
 
 bt_status_t FakeDisconnect(const RawAddress& bd_addr) {
+  if (g_a2dp_source_handler) return g_a2dp_source_handler->Disconnect(bd_addr);
   if (g_a2dp_sink_handler) return g_a2dp_sink_handler->Disconnect(bd_addr);
   return BT_STATUS_FAIL;
 }
@@ -53,26 +64,36 @@
 
 btav_source_interface_t fake_a2dp_source_interface = {
     .size = sizeof(btav_source_interface_t),
-    .init = nullptr,
-    .connect = nullptr,
-    .disconnect = nullptr,
+    .init = FakeSourceInit,
+    .connect = FakeConnect,
+    .disconnect = FakeDisconnect,
+    .set_silence_device = nullptr,
+    .set_active_device = nullptr,
     .config_codec = nullptr,
-    .cleanup = nullptr,
+    .cleanup = FakeCleanup,
 };
 
 btav_sink_interface_t fake_a2dp_sink_interface = {
     .size = sizeof(btav_sink_interface_t),
-    .init = FakeInit,
+    .init = FakeSinkInit,
     .connect = FakeConnect,
     .disconnect = FakeDisconnect,
     .cleanup = FakeCleanup,
     .set_audio_focus_state = FakeSetAudioFocusState,
     .set_audio_track_gain = FakeSetAudioTrackGain,
+    .set_active_device = nullptr,
 };
 
 }  // namespace
 
 FakeBluetoothAvInterface::FakeBluetoothAvInterface(
+    std::shared_ptr<TestA2dpSourceHandler> a2dp_source_handler) {
+  CHECK(!g_a2dp_source_handler);
+
+  if (a2dp_source_handler) g_a2dp_source_handler = a2dp_source_handler;
+}
+
+FakeBluetoothAvInterface::FakeBluetoothAvInterface(
     std::shared_ptr<TestA2dpSinkHandler> a2dp_sink_handler) {
   CHECK(!g_a2dp_sink_handler);
 
@@ -80,21 +101,50 @@
 }
 
 FakeBluetoothAvInterface::~FakeBluetoothAvInterface() {
-  g_a2dp_sink_handler = nullptr;
+  g_a2dp_source_handler = {};
+  g_a2dp_sink_handler = {};
 }
 
 void FakeBluetoothAvInterface::NotifyConnectionState(
     const RawAddress& bda, btav_connection_state_t state) {
+  for (auto& observer : a2dp_source_observers_) {
+    observer.ConnectionStateCallback(this, bda, state);
+  }
   for (auto& observer : a2dp_sink_observers_) {
     observer.ConnectionStateCallback(this, bda, state);
   }
 }
 void FakeBluetoothAvInterface::NotifyAudioState(const RawAddress& bda,
                                                 btav_audio_state_t state) {
+  for (auto& observer : a2dp_source_observers_) {
+    observer.AudioStateCallback(this, bda, state);
+  }
   for (auto& observer : a2dp_sink_observers_) {
     observer.AudioStateCallback(this, bda, state);
   }
 }
+void FakeBluetoothAvInterface::NotifyAudioConfig(
+    const RawAddress& bda, const btav_a2dp_codec_config_t& codec_config,
+    const std::vector<btav_a2dp_codec_config_t> codecs_local_capabilities,
+    const std::vector<btav_a2dp_codec_config_t>
+        codecs_selectable_capabilities) {
+  for (auto& observer : a2dp_source_observers_) {
+    observer.AudioConfigCallback(this, bda, codec_config,
+                                 codecs_local_capabilities,
+                                 codecs_selectable_capabilities);
+  }
+}
+bool FakeBluetoothAvInterface::QueryMandatoryCodecPreferred(
+    const RawAddress& bda) {
+  // The mandatory codec is preferred only when all observers disable their
+  // optional codecs.
+  for (auto& observer : a2dp_source_observers_) {
+    if (!observer.MandatoryCodecPreferredCallback(this, bda)) {
+      return false;
+    }
+  }
+  return true;
+}
 void FakeBluetoothAvInterface::NotifyAudioConfig(const RawAddress& bda,
                                                  uint32_t sample_rate,
                                                  uint8_t channel_count) {
diff --git a/service/hal/fake_bluetooth_av_interface.h b/service/hal/fake_bluetooth_av_interface.h
index c979434..34aeac8 100644
--- a/service/hal/fake_bluetooth_av_interface.h
+++ b/service/hal/fake_bluetooth_av_interface.h
@@ -26,9 +26,16 @@
 
 class FakeBluetoothAvInterface : public BluetoothAvInterface {
  public:
-  // Handles HAL Bluetooth A2DP sink API calls for testing. Test code can
-  // provide a fake or mock implementation of this and all calls will be routed
-  // to it.
+  // Handles HAL Bluetooth A2DP API calls for testing. Test code can provide a
+  // fake or mock implementation of this and all calls will be routed to it.
+  class TestA2dpSourceHandler {
+   public:
+    virtual bt_status_t Connect(RawAddress bda) = 0;
+    virtual bt_status_t Disconnect(RawAddress bda) = 0;
+
+   protected:
+    virtual ~TestA2dpSourceHandler() = default;
+  };
   class TestA2dpSinkHandler {
    public:
     virtual bt_status_t Connect(RawAddress bda) = 0;
@@ -44,16 +51,26 @@
   // provide their own handlers or simply pass "nullptr" for the default
   // behavior in which BT_STATUS_FAIL will be returned from all calls.
   FakeBluetoothAvInterface(
+      std::shared_ptr<TestA2dpSourceHandler> a2dp_source_handler);
+  FakeBluetoothAvInterface(
       std::shared_ptr<TestA2dpSinkHandler> a2dp_sink_handler);
   ~FakeBluetoothAvInterface();
 
   // The methods below can be used to notify observers with certain events and
   // given parameters.
 
-  // A2DP sink callbacks
+  // A2DP common callbacks
   void NotifyConnectionState(const RawAddress& bda,
                              btav_connection_state_t state);
   void NotifyAudioState(const RawAddress& bda, btav_audio_state_t state);
+  // A2DP source callbacks
+  void NotifyAudioConfig(
+      const RawAddress& bda, const btav_a2dp_codec_config_t& codec_config,
+      const std::vector<btav_a2dp_codec_config_t> codecs_local_capabilities,
+      const std::vector<btav_a2dp_codec_config_t>
+          codecs_selectable_capabilities);
+  bool QueryMandatoryCodecPreferred(const RawAddress& bda);
+  // A2DP sink callbacks
   void NotifyAudioConfig(const RawAddress& bda, uint32_t sample_rate,
                          uint8_t channel_count);
 
@@ -73,7 +90,6 @@
  private:
   base::ObserverList<A2dpSourceObserver> a2dp_source_observers_;
   base::ObserverList<A2dpSinkObserver> a2dp_sink_observers_;
-  std::shared_ptr<TestA2dpSinkHandler> scanner_handler_;
 
   DISALLOW_COPY_AND_ASSIGN(FakeBluetoothAvInterface);
 };
diff --git a/service/hal/fake_bluetooth_interface.cc b/service/hal/fake_bluetooth_interface.cc
index f1d03ad..4af8bc6 100644
--- a/service/hal/fake_bluetooth_interface.cc
+++ b/service/hal/fake_bluetooth_interface.cc
@@ -75,6 +75,7 @@
     nullptr, /* interop_database_add */
     nullptr, /* get_avrcp_service */
     nullptr, /* obfuscate_address */
+    nullptr, /* get_metric_id */
 };
 
 }  // namespace
diff --git a/service/test/a2dp_source_unittest.cc b/service/test/a2dp_source_unittest.cc
new file mode 100644
index 0000000..5fb1961
--- /dev/null
+++ b/service/test/a2dp_source_unittest.cc
@@ -0,0 +1,249 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "service/a2dp_source.h"
+#include "service/hal/fake_bluetooth_av_interface.h"
+
+using ::testing::_;
+using ::testing::Return;
+
+namespace bluetooth {
+namespace {
+
+class MockA2dpSourceHandler
+    : public hal::FakeBluetoothAvInterface::TestA2dpSourceHandler {
+ public:
+  MockA2dpSourceHandler() = default;
+  ~MockA2dpSourceHandler() override = default;
+
+  MOCK_METHOD1(Connect, bt_status_t(RawAddress));
+  MOCK_METHOD1(Disconnect, bt_status_t(RawAddress));
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockA2dpSourceHandler);
+};
+
+class TestDelegate : public A2dpSource::Delegate {
+ public:
+  TestDelegate() = default;
+  ~TestDelegate() override = default;
+
+  struct RequestData {
+    std::string device_address;
+    int state = -1;
+    int count = 0;
+  };
+
+  // A2dpSource::Delegate implementation:
+  void OnConnectionState(const std::string& device_address,
+                         int state) override {
+    ++connection_state_.count;
+    connection_state_.device_address = device_address;
+    connection_state_.state = state;
+  }
+  void OnAudioState(const std::string& device_address, int state) override {
+    ++audio_state_.count;
+    audio_state_.device_address = device_address;
+    audio_state_.state = state;
+  }
+  void OnAudioConfig(
+      const std::string& device_address, A2dpCodecConfig codec_config,
+      const std::vector<A2dpCodecConfig>& codecs_local_capabilities,
+      const std::vector<A2dpCodecConfig>& codecs_selectable_capabilities)
+      override {
+    ++audio_config_.count;
+    audio_config_.device_address = device_address;
+  }
+
+  const RequestData& connection_state() const { return connection_state_; }
+  const RequestData& audio_state() const { return audio_state_; }
+  const RequestData& audio_config() const { return audio_config_; }
+
+ private:
+  RequestData connection_state_;
+  RequestData audio_state_;
+  RequestData audio_config_;
+};
+
+class A2dpSourceTest : public ::testing::Test {
+ public:
+  A2dpSourceTest() = default;
+  ~A2dpSourceTest() override = default;
+
+  void SetUp() override {
+    mock_handler_.reset(new MockA2dpSourceHandler());
+    fake_hal_av_iface_ = new hal::FakeBluetoothAvInterface(mock_handler_);
+    hal::BluetoothAvInterface::InitializeForTesting(fake_hal_av_iface_);
+    factory_.reset(new A2dpSourceFactory());
+  }
+
+  void TearDown() override {
+    factory_.reset();
+    hal::BluetoothAvInterface::CleanUp();
+  }
+
+ protected:
+  hal::FakeBluetoothAvInterface* fake_hal_av_iface_;
+  std::shared_ptr<MockA2dpSourceHandler> mock_handler_;
+  std::unique_ptr<A2dpSourceFactory> factory_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(A2dpSourceTest);
+};
+
+class A2dpSourcePostRegisterTest : public A2dpSourceTest {
+ public:
+  A2dpSourcePostRegisterTest() = default;
+  ~A2dpSourcePostRegisterTest() override = default;
+
+  void SetUp() override {
+    A2dpSourceTest::SetUp();
+    Uuid uuid = Uuid::GetRandom();
+    auto callback = [&](BLEStatus status, const Uuid& in_uuid,
+                        std::unique_ptr<BluetoothInstance> in_client) {
+      CHECK(in_uuid == uuid);
+      CHECK(in_client.get());
+      CHECK(status == BLE_STATUS_SUCCESS);
+
+      a2dp_source_ = std::unique_ptr<A2dpSource>(
+          static_cast<A2dpSource*>(in_client.release()));
+    };
+
+    factory_->RegisterInstance(uuid, callback);
+  }
+
+  void TearDown() override {
+    a2dp_source_ = nullptr;
+    A2dpSourceTest::TearDown();
+  }
+
+ protected:
+  void Connect(const std::string& addr) {
+    RawAddress hal_addr;
+    ASSERT_TRUE(RawAddress::FromString(addr, hal_addr));
+
+    EXPECT_CALL(*mock_handler_, Connect(hal_addr))
+        .WillOnce(Return(BT_STATUS_SUCCESS));
+
+    EXPECT_TRUE(a2dp_source_->Connect(addr));
+  }
+
+  void Disconnect(const std::string& addr) {
+    RawAddress hal_addr;
+    ASSERT_TRUE(RawAddress::FromString(addr, hal_addr));
+
+    EXPECT_CALL(*mock_handler_, Disconnect(hal_addr))
+        .WillOnce(Return(BT_STATUS_SUCCESS));
+
+    EXPECT_TRUE(a2dp_source_->Disconnect(addr));
+  }
+
+  std::unique_ptr<A2dpSource> a2dp_source_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(A2dpSourcePostRegisterTest);
+};
+
+TEST_F(A2dpSourceTest, RegisterA2dpSource) {
+  // These will be asynchronously populate with a result when the callback
+  // executes.
+  BLEStatus status = BLE_STATUS_SUCCESS;
+  Uuid cb_uuid;
+  std::unique_ptr<A2dpSource> a2dp_source;
+  int callback_count = 0;
+
+  auto callback = [&](BLEStatus in_status, const Uuid& uuid,
+                      std::unique_ptr<BluetoothInstance> in_a2dp_source) {
+    status = in_status;
+    cb_uuid = uuid;
+    a2dp_source = std::unique_ptr<A2dpSource>(
+        static_cast<A2dpSource*>(in_a2dp_source.release()));
+    callback_count++;
+  };
+
+  Uuid uuid0 = Uuid::GetRandom();
+
+  // This should always succeed.
+  EXPECT_TRUE(factory_->RegisterInstance(uuid0, callback));
+  EXPECT_EQ(1, callback_count);
+
+  testing::Mock::VerifyAndClearExpectations(mock_handler_.get());
+
+  ASSERT_TRUE(a2dp_source.get() !=
+              nullptr);  // Assert to terminate in case of error
+  EXPECT_EQ(BLE_STATUS_SUCCESS, status);
+  EXPECT_EQ(bluetooth::A2dpSource::kSingletonInstanceId,
+            a2dp_source->GetInstanceId());
+  EXPECT_EQ(uuid0, a2dp_source->GetAppIdentifier());
+  EXPECT_EQ(uuid0, cb_uuid);
+
+  testing::Mock::VerifyAndClearExpectations(mock_handler_.get());
+}
+
+TEST_F(A2dpSourcePostRegisterTest, Connect) {
+  static const char kTestAddr[] = "AA:BB:CC:DD:EE:FF";
+  Connect(kTestAddr);
+  Disconnect(kTestAddr);
+}
+
+TEST_F(A2dpSourcePostRegisterTest, CallbackTest) {
+  static const char kTestAddr[] = "AA:BB:CC:DD:EE:FF";
+  RawAddress hal_addr;
+  ASSERT_TRUE(RawAddress::FromString(kTestAddr, hal_addr));
+
+  TestDelegate delegate;
+  a2dp_source_->SetDelegate(&delegate);
+  Connect(kTestAddr);
+
+  // OnConnectionState
+  const int kConnectionState = 2;
+  EXPECT_EQ(0, delegate.connection_state().count);
+  fake_hal_av_iface_->NotifyConnectionState(
+      hal_addr, static_cast<btav_connection_state_t>(kConnectionState));
+  EXPECT_EQ(1, delegate.connection_state().count);
+  EXPECT_EQ(kTestAddr, delegate.connection_state().device_address);
+  EXPECT_EQ(kConnectionState, delegate.connection_state().state);
+
+  // OnAudioState
+  const int kAudioState = 1;
+  EXPECT_EQ(0, delegate.audio_state().count);
+  fake_hal_av_iface_->NotifyAudioState(
+      hal_addr, static_cast<btav_audio_state_t>(kAudioState));
+  EXPECT_EQ(1, delegate.audio_state().count);
+  EXPECT_EQ(kTestAddr, delegate.audio_state().device_address);
+  EXPECT_EQ(kAudioState, delegate.audio_state().state);
+
+  // OnAudioConfig
+  const btav_a2dp_codec_config_t codec_config{};
+  const std::vector<btav_a2dp_codec_config_t> codecs_local_capabilities(0);
+  const std::vector<btav_a2dp_codec_config_t> codecs_selectable_capabilities(0);
+  EXPECT_EQ(0, delegate.audio_config().count);
+  fake_hal_av_iface_->NotifyAudioConfig(hal_addr, codec_config,
+                                        codecs_local_capabilities,
+                                        codecs_selectable_capabilities);
+  EXPECT_EQ(1, delegate.audio_config().count);
+  EXPECT_EQ(kTestAddr, delegate.audio_config().device_address);
+
+  fake_hal_av_iface_->QueryMandatoryCodecPreferred(hal_addr);
+
+  Disconnect(kTestAddr);
+}
+
+}  // namespace
+}  // namespace bluetooth
diff --git a/service/test/gatt_server_unittest.cc b/service/test/gatt_server_unittest.cc
index 7d7fec2..e19742d 100644
--- a/service/test/gatt_server_unittest.cc
+++ b/service/test/gatt_server_unittest.cc
@@ -267,14 +267,14 @@
     desc_handle_ = 0x0004;
 
     std::vector<btgatt_db_element_t> service_with_handles = {
-        {.type = BTGATT_DB_PRIMARY_SERVICE,
-         .uuid = uuid0,
+        {.uuid = uuid0,
+         .type = BTGATT_DB_PRIMARY_SERVICE,
          .attribute_handle = srvc_handle_},
-        {.type = BTGATT_DB_CHARACTERISTIC,
-         .uuid = uuid1,
+        {.uuid = uuid1,
+         .type = BTGATT_DB_CHARACTERISTIC,
          .attribute_handle = char_handle_},
-        {.type = BTGATT_DB_DESCRIPTOR,
-         .uuid = uuid2,
+        {.uuid = uuid2,
+         .type = BTGATT_DB_DESCRIPTOR,
          .attribute_handle = desc_handle_},
     };
 
diff --git a/service/test/low_energy_client_unittest.cc b/service/test/low_energy_client_unittest.cc
index 3bf566c..ce3c7c0 100644
--- a/service/test/low_energy_client_unittest.cc
+++ b/service/test/low_energy_client_unittest.cc
@@ -58,13 +58,13 @@
   int connection_state_count() const { return connection_state_count_; }
 
   void OnConnectionState(LowEnergyClient* client, int status,
-                         const char* address, bool connected) {
+                         const char* address, bool connected) override {
     ASSERT_TRUE(client);
     connection_state_count_++;
   }
 
   void OnMtuChanged(LowEnergyClient* client, int status, const char* address,
-                    int mtu) {
+                    int mtu) override {
     ASSERT_TRUE(client);
     last_mtu_ = mtu;
   }
diff --git a/service/test/low_energy_scanner_unittest.cc b/service/test/low_energy_scanner_unittest.cc
index c8b16f3..9a7875d 100644
--- a/service/test/low_energy_scanner_unittest.cc
+++ b/service/test/low_energy_scanner_unittest.cc
@@ -72,12 +72,12 @@
   MOCK_METHOD1(StopSync, void(uint16_t));
 
   void ScanFilterAdd(int filter_index, std::vector<ApcfCommand> filters,
-                     FilterConfigCallback cb){};
+                     FilterConfigCallback cb) override{};
 
   void ScanFilterParamSetup(
       uint8_t client_if, uint8_t action, uint8_t filt_index,
       std::unique_ptr<btgatt_filt_param_setup_t> filt_param,
-      FilterParamSetupCallback cb) {
+      FilterParamSetupCallback cb) override {
     ScanFilterParamSetupImpl(client_if, action, filt_index, filt_param.get(),
                              std::move(cb));
   }
@@ -92,7 +92,8 @@
   int scan_result_count() const { return scan_result_count_; }
   const ScanResult& last_scan_result() const { return last_scan_result_; }
 
-  void OnScanResult(LowEnergyScanner* scanner, const ScanResult& scan_result) {
+  void OnScanResult(LowEnergyScanner* scanner,
+                    const ScanResult& scan_result) override {
     ASSERT_TRUE(scanner);
     scan_result_count_++;
     last_scan_result_ = scan_result;
diff --git a/stack/Android.bp b/stack/Android.bp
index c468423..2f3d12c 100644
--- a/stack/Android.bp
+++ b/stack/Android.bp
@@ -46,7 +46,6 @@
         "system/bt/bta/sys",
         "system/bt/utils/include",
     ],
-    cflags: ["-Wno-implicit-fallthrough"],
     srcs: crypto_toolbox_srcs + [
         "a2dp/a2dp_aac.cc",
         "a2dp/a2dp_aac_decoder.cc",
@@ -139,7 +138,6 @@
         "l2cap/l2c_link.cc",
         "l2cap/l2c_main.cc",
         "l2cap/l2c_utils.cc",
-        "l2cap/l2cap_client.cc",
         "pan/pan_api.cc",
         "pan/pan_main.cc",
         "pan/pan_utils.cc",
@@ -203,23 +201,45 @@
         "test/stack_avrcp_test.cc",
     ],
     shared_libs: [
-        "libcrypto",
+        "android.hardware.bluetooth@1.0",
+        "android.hardware.bluetooth@1.1",
+        "android.hardware.bluetooth.a2dp@1.0",
+        "android.hardware.bluetooth.audio@2.0",
+        "libaaudio",
+        "libcutils",
+        "libdl",
+        "libfmq",
         "libhidlbase",
         "liblog",
+        "libprocessgroup",
         "libprotobuf-cpp-lite",
-        "libcutils",
         "libutils",
+        "libtinyxml2",
+        "libz",
+        "libcrypto",
+        "android.hardware.keymaster@4.0",
+        "android.hardware.keymaster@3.0",
+        "libkeymaster4support",
+        "libkeystore_aidl",
+        "libkeystore_binder",
+        "libkeystore_parcelables",
     ],
     static_libs: [
+        "libbt-audio-hal-interface",
+        "libbtcore",
         "libbt-bta",
         "libbt-stack",
         "libbt-common",
         "libbt-sbc-decoder",
         "libbt-sbc-encoder",
+        "libbt-utils",
+        "libbtif",
         "libFraunhoferAAC",
-        "libbtdevice",
         "libbt-hci",
+        "libbtdevice",
+        "libg722codec",
         "libosi",
+        "libudrv-uipc",
         "libbt-protos-lite",
     ],
     whole_static_libs: [
@@ -449,6 +469,41 @@
 }
 
 cc_test {
+    name: "net_test_stack_gatt_native",
+    defaults: ["fluoride_defaults"],
+    test_suites: ["device-tests"],
+    host_supported: true,
+    include_dirs: [
+        "system/bt",
+        "system/bt/stack/include",
+        "system/bt/stack/l2cap",
+        "system/bt/stack/btm",
+        "system/bt/utils/include",
+    ],
+    srcs: [
+        "test/gatt/gatt_sr_test.cc",
+        "gatt/gatt_utils.cc",
+    ],
+    shared_libs: [
+        "libcutils",
+        "libprotobuf-cpp-lite",
+        "libcrypto",
+    ],
+    static_libs: [
+        "liblog",
+        "libosi",
+        "libbt-common",
+        "libbt-protos-lite",
+        "libosi-AllocationTestHarness",
+    ],
+    sanitize: {
+        address: true,
+        cfi: true,
+        misc_undefined: ["bounds"],
+    },
+}
+
+cc_test {
     name: "net_test_stack_a2dp_native",
     defaults: ["fluoride_defaults"],
     test_suites: ["device-tests"],
diff --git a/stack/BUILD.gn b/stack/BUILD.gn
index 87dc726..8b39594 100644
--- a/stack/BUILD.gn
+++ b/stack/BUILD.gn
@@ -122,7 +122,6 @@
     "l2cap/l2c_link.cc",
     "l2cap/l2c_main.cc",
     "l2cap/l2c_utils.cc",
-    "l2cap/l2cap_client.cc",
     "pan/pan_api.cc",
     "pan/pan_main.cc",
     "pan/pan_utils.cc",
diff --git a/stack/a2dp/a2dp_aac.cc b/stack/a2dp/a2dp_aac.cc
index df641be..a7b7218 100644
--- a/stack/a2dp/a2dp_aac.cc
+++ b/stack/a2dp/a2dp_aac.cc
@@ -102,8 +102,12 @@
 };
 
 static const tA2DP_DECODER_INTERFACE a2dp_decoder_interface_aac = {
-    a2dp_aac_decoder_init, a2dp_aac_decoder_cleanup,
+    a2dp_aac_decoder_init,
+    a2dp_aac_decoder_cleanup,
     a2dp_aac_decoder_decode_packet,
+    nullptr,  // decoder_start
+    nullptr,  // decoder_suspend
+    nullptr,  // decoder_configure
 };
 
 UNUSED_ATTR static tA2DP_STATUS A2DP_CodecInfoMatchesCapabilityAac(
diff --git a/stack/a2dp/a2dp_aac_encoder.cc b/stack/a2dp/a2dp_aac_encoder.cc
index f6c68a6..665bb04 100644
--- a/stack/a2dp/a2dp_aac_encoder.cc
+++ b/stack/a2dp/a2dp_aac_encoder.cc
@@ -19,6 +19,7 @@
 #include "a2dp_aac_encoder.h"
 
 #include <inttypes.h>
+#include <math.h>
 #include <stdio.h>
 #include <string.h>
 
@@ -56,8 +57,8 @@
 
 typedef struct {
   uint32_t counter;
-  uint32_t bytes_per_tick; /* pcm bytes read each media task tick */
-  uint64_t last_frame_us;
+  uint32_t bytes_per_tick;              // pcm bytes read each media task tick
+  uint64_t last_frame_timestamp_100ns;  // values in 1/10 microseconds
 } tA2DP_AAC_FEEDING_STATE;
 
 typedef struct {
@@ -93,6 +94,8 @@
   a2dp_aac_encoder_stats_t stats;
 } tA2DP_AAC_ENCODER_CB;
 
+static uint32_t a2dp_aac_encoder_interval_ms = A2DP_AAC_ENCODER_INTERVAL_MS;
+
 static tA2DP_AAC_ENCODER_CB a2dp_aac_encoder_cb;
 
 static void a2dp_aac_encoder_update(uint16_t peer_mtu,
@@ -218,7 +221,6 @@
   LOG_DEBUG(LOG_TAG, "%s: sample_rate=%u bits_per_sample=%u channel_count=%u",
             __func__, p_feeding_params->sample_rate,
             p_feeding_params->bits_per_sample, p_feeding_params->channel_count);
-  a2dp_aac_feeding_reset();
 
   // The codec parameters
   p_encoder_params->sample_rate =
@@ -451,6 +453,9 @@
             __func__, p_encoder_params->frame_length,
             p_encoder_params->input_channels_n,
             p_encoder_params->max_encoded_buffer_bytes);
+
+  // After encoder params ready, reset the feeding state and its interval.
+  a2dp_aac_feeding_reset();
 }
 
 void a2dp_aac_encoder_cleanup(void) {
@@ -460,6 +465,23 @@
 }
 
 void a2dp_aac_feeding_reset(void) {
+  auto frame_length = a2dp_aac_encoder_cb.aac_encoder_params.frame_length;
+  auto sample_rate = a2dp_aac_encoder_cb.feeding_params.sample_rate;
+  if (frame_length == 0 || sample_rate == 0) {
+    LOG_WARN(LOG_TAG, "%s: AAC encoder is not configured", __func__);
+    a2dp_aac_encoder_interval_ms = A2DP_AAC_ENCODER_INTERVAL_MS;
+  } else {
+    // PCM data size per AAC frame (bits)
+    // = aac_encoder_params.frame_length * feeding_params.bits_per_sample
+    //   * feeding_params.channel_count
+    // = feeding_params.sample_rate * feeding_params.bits_per_sample
+    //   * feeding_params.channel_count * (T_interval_ms / 1000);
+    // Here we use the nearest integer not greater than the value.
+    a2dp_aac_encoder_interval_ms = frame_length * 1000 / sample_rate;
+    if (a2dp_aac_encoder_interval_ms < A2DP_AAC_ENCODER_INTERVAL_MS)
+      a2dp_aac_encoder_interval_ms = A2DP_AAC_ENCODER_INTERVAL_MS;
+  }
+
   /* By default, just clear the entire state */
   memset(&a2dp_aac_encoder_cb.aac_feeding_state, 0,
          sizeof(a2dp_aac_encoder_cb.aac_feeding_state));
@@ -468,11 +490,12 @@
       (a2dp_aac_encoder_cb.feeding_params.sample_rate *
        a2dp_aac_encoder_cb.feeding_params.bits_per_sample / 8 *
        a2dp_aac_encoder_cb.feeding_params.channel_count *
-       A2DP_AAC_ENCODER_INTERVAL_MS) /
+       a2dp_aac_encoder_interval_ms) /
       1000;
 
-  LOG_DEBUG(LOG_TAG, "%s: PCM bytes per tick %u", __func__,
-            a2dp_aac_encoder_cb.aac_feeding_state.bytes_per_tick);
+  LOG_INFO(LOG_TAG, "%s: PCM bytes %u per tick %u ms", __func__,
+           a2dp_aac_encoder_cb.aac_feeding_state.bytes_per_tick,
+           a2dp_aac_encoder_interval_ms);
 }
 
 void a2dp_aac_feeding_flush(void) {
@@ -480,7 +503,7 @@
 }
 
 uint64_t a2dp_aac_get_encoder_interval_ms(void) {
-  return A2DP_AAC_ENCODER_INTERVAL_MS;
+  return a2dp_aac_encoder_interval_ms;
 }
 
 void a2dp_aac_send_frames(uint64_t timestamp_us) {
@@ -515,16 +538,30 @@
   LOG_VERBOSE(LOG_TAG, "%s: pcm_bytes_per_frame %u", __func__,
               pcm_bytes_per_frame);
 
-  uint32_t us_this_tick = A2DP_AAC_ENCODER_INTERVAL_MS * 1000;
-  uint64_t now_us = timestamp_us;
-  if (a2dp_aac_encoder_cb.aac_feeding_state.last_frame_us != 0)
-    us_this_tick =
-        (now_us - a2dp_aac_encoder_cb.aac_feeding_state.last_frame_us);
-  a2dp_aac_encoder_cb.aac_feeding_state.last_frame_us = now_us;
+  uint32_t hecto_ns_this_tick = a2dp_aac_encoder_interval_ms * 10000;
+  uint64_t* last_100ns =
+      &a2dp_aac_encoder_cb.aac_feeding_state.last_frame_timestamp_100ns;
+  uint64_t now_100ns = timestamp_us * 10;
+  if (*last_100ns != 0) {
+    hecto_ns_this_tick = (now_100ns - *last_100ns);
+  }
+  *last_100ns = now_100ns;
 
-  a2dp_aac_encoder_cb.aac_feeding_state.counter +=
-      a2dp_aac_encoder_cb.aac_feeding_state.bytes_per_tick * us_this_tick /
-      (A2DP_AAC_ENCODER_INTERVAL_MS * 1000);
+  uint32_t bytes_this_tick =
+      a2dp_aac_encoder_cb.aac_feeding_state.bytes_per_tick *
+      hecto_ns_this_tick / (a2dp_aac_encoder_interval_ms * 10000);
+  a2dp_aac_encoder_cb.aac_feeding_state.counter += bytes_this_tick;
+  // Without this erratum, there was a three microseocnd shift per tick which
+  // would cause one frame mismatched after every 180 seconds
+  uint32_t erratum_100ns =
+      ceil(1.0f * bytes_this_tick * a2dp_aac_encoder_interval_ms * 10000 /
+           a2dp_aac_encoder_cb.aac_feeding_state.bytes_per_tick);
+  if (erratum_100ns < hecto_ns_this_tick) {
+    LOG_VERBOSE(LOG_TAG,
+                "%s: hecto_ns_this_tick=%d, bytes=%d, erratum_100ns=%d",
+                __func__, hecto_ns_this_tick, bytes_this_tick, erratum_100ns);
+    *last_100ns -= hecto_ns_this_tick - erratum_100ns;
+  }
 
   result = a2dp_aac_encoder_cb.aac_feeding_state.counter / pcm_bytes_per_frame;
   a2dp_aac_encoder_cb.aac_feeding_state.counter -= result * pcm_bytes_per_frame;
diff --git a/stack/a2dp/a2dp_sbc.cc b/stack/a2dp/a2dp_sbc.cc
index 4c48993..388419b 100644
--- a/stack/a2dp/a2dp_sbc.cc
+++ b/stack/a2dp/a2dp_sbc.cc
@@ -103,8 +103,12 @@
 };
 
 static const tA2DP_DECODER_INTERFACE a2dp_decoder_interface_sbc = {
-    a2dp_sbc_decoder_init, a2dp_sbc_decoder_cleanup,
+    a2dp_sbc_decoder_init,
+    a2dp_sbc_decoder_cleanup,
     a2dp_sbc_decoder_decode_packet,
+    nullptr,  // decoder_start
+    nullptr,  // decoder_suspend
+    nullptr,  // decoder_configure
 };
 
 static tA2DP_STATUS A2DP_CodecInfoMatchesCapabilitySbc(
diff --git a/stack/a2dp/a2dp_sbc_encoder.cc b/stack/a2dp/a2dp_sbc_encoder.cc
index aac7e6d..5773462 100644
--- a/stack/a2dp/a2dp_sbc_encoder.cc
+++ b/stack/a2dp/a2dp_sbc_encoder.cc
@@ -22,6 +22,7 @@
 #include "a2dp_sbc_encoder.h"
 
 #include <limits.h>
+#include <math.h>
 #include <stdio.h>
 #include <string.h>
 
@@ -71,8 +72,8 @@
   int32_t aa_feed_counter;
   int32_t aa_feed_residue;
   uint32_t counter;
-  uint32_t bytes_per_tick; /* pcm bytes read each media task tick */
-  uint64_t last_frame_us;
+  uint32_t bytes_per_tick;              // pcm bytes read each media task tick
+  uint64_t last_frame_timestamp_100ns;  // values in 1/10 microseconds
 } tA2DP_SBC_FEEDING_STATE;
 
 typedef struct {
@@ -433,15 +434,30 @@
   LOG_VERBOSE(LOG_TAG, "%s: pcm_bytes_per_frame %u", __func__,
               pcm_bytes_per_frame);
 
-  uint32_t us_this_tick = A2DP_SBC_ENCODER_INTERVAL_MS * 1000;
-  uint64_t now_us = timestamp_us;
-  if (a2dp_sbc_encoder_cb.feeding_state.last_frame_us != 0)
-    us_this_tick = (now_us - a2dp_sbc_encoder_cb.feeding_state.last_frame_us);
-  a2dp_sbc_encoder_cb.feeding_state.last_frame_us = now_us;
+  uint32_t hecto_ns_this_tick = A2DP_SBC_ENCODER_INTERVAL_MS * 10000;
+  uint64_t* last_100ns =
+      &a2dp_sbc_encoder_cb.feeding_state.last_frame_timestamp_100ns;
+  uint64_t now_100ns = timestamp_us * 10;
+  if (*last_100ns != 0) {
+    hecto_ns_this_tick = (now_100ns - *last_100ns);
+  }
+  *last_100ns = now_100ns;
 
-  a2dp_sbc_encoder_cb.feeding_state.counter +=
-      a2dp_sbc_encoder_cb.feeding_state.bytes_per_tick * us_this_tick /
-      (A2DP_SBC_ENCODER_INTERVAL_MS * 1000);
+  uint32_t bytes_this_tick = a2dp_sbc_encoder_cb.feeding_state.bytes_per_tick *
+                             hecto_ns_this_tick /
+                             (A2DP_SBC_ENCODER_INTERVAL_MS * 10000);
+  a2dp_sbc_encoder_cb.feeding_state.counter += bytes_this_tick;
+  // Without this erratum, there was a three microseocnd shift per tick which
+  // would cause one SBC frame mismatched after every 20 seconds
+  uint32_t erratum_100ns =
+      ceil(1.0f * A2DP_SBC_ENCODER_INTERVAL_MS * 10000 * bytes_this_tick /
+           a2dp_sbc_encoder_cb.feeding_state.bytes_per_tick);
+  if (erratum_100ns < hecto_ns_this_tick) {
+    LOG_VERBOSE(LOG_TAG,
+                "%s: hecto_ns_this_tick=%d, bytes=%d, erratum_100ns=%d",
+                __func__, hecto_ns_this_tick, bytes_this_tick, erratum_100ns);
+    *last_100ns -= hecto_ns_this_tick - erratum_100ns;
+  }
 
   /* Calculate the number of frames pending for this media tick */
   projected_nof =
diff --git a/stack/a2dp/a2dp_vendor_ldac.cc b/stack/a2dp/a2dp_vendor_ldac.cc
index 4949e74..0a1017c 100644
--- a/stack/a2dp/a2dp_vendor_ldac.cc
+++ b/stack/a2dp/a2dp_vendor_ldac.cc
@@ -69,7 +69,8 @@
     (A2DP_LDAC_SAMPLING_FREQ_44100 | A2DP_LDAC_SAMPLING_FREQ_48000 |
      A2DP_LDAC_SAMPLING_FREQ_88200 | A2DP_LDAC_SAMPLING_FREQ_96000),
     // channelMode
-    (A2DP_LDAC_CHANNEL_MODE_DUAL | A2DP_LDAC_CHANNEL_MODE_STEREO),
+    (A2DP_LDAC_CHANNEL_MODE_MONO | A2DP_LDAC_CHANNEL_MODE_DUAL |
+     A2DP_LDAC_CHANNEL_MODE_STEREO),
     // bits_per_sample
     (BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16 | BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24 |
      BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32)};
@@ -93,9 +94,9 @@
     a2dp_vendor_ldac_set_transmit_queue_length};
 
 static const tA2DP_DECODER_INTERFACE a2dp_decoder_interface_ldac = {
-    a2dp_vendor_ldac_decoder_init,
-    a2dp_vendor_ldac_decoder_cleanup,
-    a2dp_vendor_ldac_decoder_decode_packet,
+    a2dp_vendor_ldac_decoder_init,          a2dp_vendor_ldac_decoder_cleanup,
+    a2dp_vendor_ldac_decoder_decode_packet, a2dp_vendor_ldac_decoder_start,
+    a2dp_vendor_ldac_decoder_suspend,       a2dp_vendor_ldac_decoder_configure,
 };
 
 UNUSED_ATTR static tA2DP_STATUS A2DP_CodecInfoMatchesCapabilityLdac(
diff --git a/stack/a2dp/a2dp_vendor_ldac_decoder.cc b/stack/a2dp/a2dp_vendor_ldac_decoder.cc
index d49337a..feace36 100644
--- a/stack/a2dp/a2dp_vendor_ldac_decoder.cc
+++ b/stack/a2dp/a2dp_vendor_ldac_decoder.cc
@@ -24,10 +24,12 @@
 #endif
 #include <dlfcn.h>
 #include <inttypes.h>
+#include <pthread.h>
 #include <stdio.h>
 #include <string.h>
 
 #include <ldacBT.h>
+#include <ldacBT_bco_for_fluoride.h>
 
 #include "a2dp_vendor.h"
 #include "a2dp_vendor_ldac.h"
@@ -40,50 +42,40 @@
 //
 
 //
-// The LDAC decoder shared library, and the functions to use
+// The LDAC BCO shared library, and the functions to use
 //
-static const char* LDAC_DECODER_LIB_NAME = "libldacBT_dec.so";
-static void* ldac_decoder_lib_handle = NULL;
+static const char* LDAC_BCO_LIB_NAME = "libldacBT_bco.so";
+static void* ldac_bco_lib_handle = NULL;
 
-static const char* LDAC_GET_HANDLE_NAME = "ldacBT_get_handle";
-typedef HANDLE_LDAC_BT (*tLDAC_GET_HANDLE)(void);
+static const char* LDAC_BCO_INIT_NAME = "ldac_BCO_init";
+typedef HANDLE_LDAC_BCO (*tLDAC_BCO_INIT)(
+    decoded_data_callback_t decode_callback);
 
-static const char* LDAC_FREE_HANDLE_NAME = "ldacBT_free_handle";
-typedef void (*tLDAC_FREE_HANDLE)(HANDLE_LDAC_BT hLdacParam);
+static const char* LDAC_BCO_CLEANUP_NAME = "ldac_BCO_cleanup";
+typedef int32_t (*tLDAC_BCO_CLEANUP)(HANDLE_LDAC_BCO hLdacBco);
 
-static const char* LDAC_CLOSE_HANDLE_NAME = "ldacBT_close_handle";
-typedef void (*tLDAC_CLOSE_HANDLE)(HANDLE_LDAC_BT hLdacParam);
+static const char* LDAC_BCO_DECODE_PACKET_NAME = "ldac_BCO_decode_packet";
+typedef int32_t (*tLDAC_BCO_DECODE_PACKET)(HANDLE_LDAC_BCO hLdacBco, void* data,
+                                           int32_t length);
 
-static const char* LDAC_GET_VERSION_NAME = "ldacBT_get_version";
-typedef int (*tLDAC_GET_VERSION)(void);
+static const char* LDAC_BCO_START_NAME = "ldac_BCO_start";
+typedef int32_t (*tLDAC_BCO_START)(HANDLE_LDAC_BCO hLdacBco);
 
-static const char* LDAC_GET_BITRATE_NAME = "ldacBT_get_bitrate";
-typedef int (*tLDAC_GET_BITRATE)(HANDLE_LDAC_BT hLdacParam);
+static const char* LDAC_BCO_SUSPEND_NAME = "ldac_BCO_suspend";
+typedef int32_t (*tLDAC_BCO_SUSPEND)(HANDLE_LDAC_BCO hLdacBco);
 
-static const char* LDAC_GET_SAMPLING_FREQ_NAME = "ldacBT_get_sampling_freq";
-typedef int (*tLDAC_GET_SAMPLING_FREQ)(HANDLE_LDAC_BT hLdacParam);
+static const char* LDAC_BCO_CONFIGURE_NAME = "ldac_BCO_configure";
+typedef int32_t (*tLDAC_BCO_CONFIGURE)(HANDLE_LDAC_BCO hLdacBco,
+                                       int32_t sample_rate,
+                                       int32_t bits_per_sample,
+                                       int32_t channel_mode);
 
-static const char* LDAC_INIT_HANDLE_DECODE_NAME = "ldacBT_init_handle_decode";
-typedef int (*tLDAC_INIT_HANDLE_DECODE)(HANDLE_LDAC_BT hLdacParam, int cm,
-                                        int sf, int var0, int var1, int var2);
-
-static const char* LDAC_DECODE_NAME = "ldacBT_decode";
-typedef int (*tLDAC_DECODE)(HANDLE_LDAC_BT hLdacBt, unsigned char* p_bs,
-                            unsigned char* p_pcm, LDACBT_SMPL_FMT_T fmt,
-                            int bs_bytes, int* used_bytes, int* wrote_bytes);
-
-static const char* LDAC_GET_ERROR_CODE_NAME = "ldacBT_get_error_code";
-typedef int (*tLDAC_GET_ERROR_CODE)(HANDLE_LDAC_BT hLdacParam);
-
-static tLDAC_GET_HANDLE ldac_get_handle_func;
-static tLDAC_FREE_HANDLE ldac_free_handle_func;
-static tLDAC_CLOSE_HANDLE ldac_close_handle_func;
-static tLDAC_GET_VERSION ldac_get_version_func;
-static tLDAC_GET_BITRATE ldac_get_bitrate_func;
-static tLDAC_GET_SAMPLING_FREQ ldac_get_sampling_freq_func;
-static tLDAC_INIT_HANDLE_DECODE ldac_init_handle_decode_func;
-static tLDAC_DECODE ldac_decode_func;
-static tLDAC_GET_ERROR_CODE ldac_get_error_code_func;
+static tLDAC_BCO_INIT ldac_BCO_init_func;
+static tLDAC_BCO_CLEANUP ldac_BCO_cleanup_func;
+static tLDAC_BCO_DECODE_PACKET ldac_BCO_decode_packet_func;
+static tLDAC_BCO_START ldac_BCO_start_func;
+static tLDAC_BCO_SUSPEND ldac_BCO_suspend_func;
+static tLDAC_BCO_CONFIGURE ldac_BCO_configure_func;
 
 // offset
 #if (BTA_AV_CO_CP_SCMS_T == TRUE)
@@ -101,13 +93,14 @@
 } tA2DP_LDAC_DECODER_PARAMS;
 
 typedef struct {
+  pthread_mutex_t mutex;
   bool use_SCMS_T;
   bool is_peer_edr;          // True if the peer device supports EDR
   bool peer_supports_3mbps;  // True if the peer device supports 3Mbps EDR
   uint16_t peer_mtu;         // MTU of the A2DP peer
   uint32_t timestamp;        // Timestamp for the A2DP frames
 
-  HANDLE_LDAC_BT ldac_handle;
+  HANDLE_LDAC_BCO ldac_handle_bco;
   bool has_ldac_handle;  // True if ldac_handle is valid
   unsigned char* decode_buf;
   decoded_data_callback_t decode_callback;
@@ -116,7 +109,7 @@
 static tA2DP_LDAC_DECODER_CB a2dp_ldac_decoder_cb;
 
 static void* load_func(const char* func_name) {
-  void* func_ptr = dlsym(ldac_decoder_lib_handle, func_name);
+  void* func_ptr = dlsym(ldac_bco_lib_handle, func_name);
   if (func_ptr == NULL) {
     LOG_ERROR(LOG_TAG,
               "%s: cannot find function '%s' in the decoder library: %s",
@@ -128,163 +121,159 @@
 }
 
 bool A2DP_VendorLoadDecoderLdac(void) {
-  if (ldac_decoder_lib_handle != NULL) return true;  // Already loaded
+  if (ldac_bco_lib_handle != NULL) return true;  // Already loaded
 
   // Initialize the control block
   memset(&a2dp_ldac_decoder_cb, 0, sizeof(a2dp_ldac_decoder_cb));
 
+  pthread_mutex_init(&(a2dp_ldac_decoder_cb.mutex), NULL);
+
   // Open the decoder library
-  ldac_decoder_lib_handle = dlopen(LDAC_DECODER_LIB_NAME, RTLD_NOW);
-  if (ldac_decoder_lib_handle == NULL) {
+  ldac_bco_lib_handle = dlopen(LDAC_BCO_LIB_NAME, RTLD_NOW);
+  if (ldac_bco_lib_handle == NULL) {
     LOG_ERROR(LOG_TAG, "%s: cannot open LDAC decoder library %s: %s", __func__,
-              LDAC_DECODER_LIB_NAME, dlerror());
+              LDAC_BCO_LIB_NAME, dlerror());
     return false;
   }
 
   // Load all functions
-  ldac_get_handle_func = (tLDAC_GET_HANDLE)load_func(LDAC_GET_HANDLE_NAME);
-  if (ldac_get_handle_func == NULL) return false;
-  ldac_free_handle_func = (tLDAC_FREE_HANDLE)load_func(LDAC_FREE_HANDLE_NAME);
-  if (ldac_free_handle_func == NULL) return false;
-  ldac_close_handle_func =
-      (tLDAC_CLOSE_HANDLE)load_func(LDAC_CLOSE_HANDLE_NAME);
-  if (ldac_close_handle_func == NULL) return false;
-  ldac_get_version_func = (tLDAC_GET_VERSION)load_func(LDAC_GET_VERSION_NAME);
-  if (ldac_get_version_func == NULL) return false;
-  ldac_get_bitrate_func = (tLDAC_GET_BITRATE)load_func(LDAC_GET_BITRATE_NAME);
-  if (ldac_get_bitrate_func == NULL) return false;
-  ldac_get_sampling_freq_func =
-      (tLDAC_GET_SAMPLING_FREQ)load_func(LDAC_GET_SAMPLING_FREQ_NAME);
-  if (ldac_get_sampling_freq_func == NULL) return false;
-  ldac_init_handle_decode_func =
-      (tLDAC_INIT_HANDLE_DECODE)load_func(LDAC_INIT_HANDLE_DECODE_NAME);
-  if (ldac_init_handle_decode_func == NULL) return false;
-  ldac_decode_func = (tLDAC_DECODE)load_func(LDAC_DECODE_NAME);
-  if (ldac_decode_func == NULL) return false;
-  ldac_get_error_code_func =
-      (tLDAC_GET_ERROR_CODE)load_func(LDAC_GET_ERROR_CODE_NAME);
-  if (ldac_get_error_code_func == NULL) return false;
+  ldac_BCO_init_func = (tLDAC_BCO_INIT)load_func(LDAC_BCO_INIT_NAME);
+  if (ldac_BCO_init_func == NULL) return false;
+
+  ldac_BCO_cleanup_func = (tLDAC_BCO_CLEANUP)load_func(LDAC_BCO_CLEANUP_NAME);
+  if (ldac_BCO_cleanup_func == NULL) return false;
+
+  ldac_BCO_decode_packet_func =
+      (tLDAC_BCO_DECODE_PACKET)load_func(LDAC_BCO_DECODE_PACKET_NAME);
+  if (ldac_BCO_decode_packet_func == NULL) return false;
+
+  ldac_BCO_start_func = (tLDAC_BCO_START)load_func(LDAC_BCO_START_NAME);
+  if (ldac_BCO_start_func == NULL) return false;
+
+  ldac_BCO_suspend_func = (tLDAC_BCO_SUSPEND)load_func(LDAC_BCO_SUSPEND_NAME);
+  if (ldac_BCO_suspend_func == NULL) return false;
+
+  ldac_BCO_configure_func =
+      (tLDAC_BCO_CONFIGURE)load_func(LDAC_BCO_CONFIGURE_NAME);
+  if (ldac_BCO_configure_func == NULL) return false;
 
   return true;
 }
 
 void A2DP_VendorUnloadDecoderLdac(void) {
   // Cleanup any LDAC-related state
-  if (a2dp_ldac_decoder_cb.has_ldac_handle && ldac_free_handle_func != NULL)
-    ldac_free_handle_func(a2dp_ldac_decoder_cb.ldac_handle);
+  if (a2dp_ldac_decoder_cb.has_ldac_handle && ldac_BCO_cleanup_func != NULL)
+    ldac_BCO_cleanup_func(a2dp_ldac_decoder_cb.ldac_handle_bco);
+  pthread_mutex_destroy(&(a2dp_ldac_decoder_cb.mutex));
   memset(&a2dp_ldac_decoder_cb, 0, sizeof(a2dp_ldac_decoder_cb));
 
-  ldac_get_handle_func = NULL;
-  ldac_free_handle_func = NULL;
-  ldac_close_handle_func = NULL;
-  ldac_get_version_func = NULL;
-  ldac_get_bitrate_func = NULL;
-  ldac_get_sampling_freq_func = NULL;
-  ldac_init_handle_decode_func = NULL;
-  ldac_decode_func = NULL;
-  ldac_get_error_code_func = NULL;
+  ldac_BCO_init_func = NULL;
+  ldac_BCO_cleanup_func = NULL;
+  ldac_BCO_decode_packet_func = NULL;
+  ldac_BCO_start_func = NULL;
+  ldac_BCO_suspend_func = NULL;
+  ldac_BCO_configure_func = NULL;
 
-  if (ldac_decoder_lib_handle != NULL) {
-    dlclose(ldac_decoder_lib_handle);
-    ldac_decoder_lib_handle = NULL;
+  if (ldac_bco_lib_handle != NULL) {
+    dlclose(ldac_bco_lib_handle);
+    ldac_bco_lib_handle = NULL;
   }
 }
 
 bool a2dp_vendor_ldac_decoder_init(decoded_data_callback_t decode_callback) {
+  pthread_mutex_lock(&(a2dp_ldac_decoder_cb.mutex));
+
   if (a2dp_ldac_decoder_cb.has_ldac_handle)
-    ldac_free_handle_func(a2dp_ldac_decoder_cb.ldac_handle);
-  memset(&a2dp_ldac_decoder_cb, 0, sizeof(a2dp_ldac_decoder_cb));
+    ldac_BCO_cleanup_func(a2dp_ldac_decoder_cb.ldac_handle_bco);
 
-  a2dp_vendor_ldac_decoder_cleanup();
+  a2dp_ldac_decoder_cb.ldac_handle_bco = ldac_BCO_init_func(decode_callback);
+  a2dp_ldac_decoder_cb.has_ldac_handle =
+      (a2dp_ldac_decoder_cb.ldac_handle_bco != NULL);
 
-  a2dp_ldac_decoder_cb.ldac_handle = ldac_get_handle_func();
-  a2dp_ldac_decoder_cb.has_ldac_handle = true;
-  a2dp_ldac_decoder_cb.decode_buf = static_cast<unsigned char*>(
-      osi_malloc(sizeof(a2dp_ldac_decoder_cb.decode_buf[0]) * LDACBT_MAX_LSU *
-                 LDAC_PRCNCH * sizeof(int)));
-  a2dp_ldac_decoder_cb.decode_callback = decode_callback;
-
-  // initialize
-  ldac_init_handle_decode_func(a2dp_ldac_decoder_cb.ldac_handle,
-                               LDACBT_CHANNEL_MODE_STEREO, 96000, 0, 0, 0);
+  pthread_mutex_unlock(&(a2dp_ldac_decoder_cb.mutex));
   return true;
 }
 
 void a2dp_vendor_ldac_decoder_cleanup(void) {
+  pthread_mutex_lock(&(a2dp_ldac_decoder_cb.mutex));
   if (a2dp_ldac_decoder_cb.has_ldac_handle)
-    ldac_free_handle_func(a2dp_ldac_decoder_cb.ldac_handle);
-  memset(&a2dp_ldac_decoder_cb, 0, sizeof(a2dp_ldac_decoder_cb));
+    ldac_BCO_cleanup_func(a2dp_ldac_decoder_cb.ldac_handle_bco);
+  a2dp_ldac_decoder_cb.ldac_handle_bco = NULL;
+  pthread_mutex_unlock(&(a2dp_ldac_decoder_cb.mutex));
 }
 
 bool a2dp_vendor_ldac_decoder_decode_packet(BT_HDR* p_buf) {
   if (p_buf == nullptr) {
-    LOG_ERROR(LOG_TAG, "%s Dropping packet with nullptr", __func__);
+    LOG(ERROR) << __func__ << "Dropping packet with nullptr";
     return false;
   }
+
   unsigned char* pBuffer =
       reinterpret_cast<unsigned char*>(p_buf->data + p_buf->offset);
   //  unsigned int bufferSize = p_buf->len;
   unsigned int bytesValid = p_buf->len;
-  int err;
+
   if (bytesValid == 0) {
-    LOG_WARN(LOG_TAG, "%s Dropping packet with zero length", __func__);
+    LOG(WARNING) << __func__ << "Dropping packet with zero length";
     return false;
   }
 
+  pthread_mutex_lock(&(a2dp_ldac_decoder_cb.mutex));
+
   LDACBT_SMPL_FMT_T fmt;
-  int bs_bytes, used_bytes, wrote_bytes, frame_number;
+  int bs_bytes, frame_number;
 
   fmt = LDACBT_SMPL_FMT_S32;
   frame_number = (int)pBuffer[0];
-  pBuffer++;
-  bs_bytes = (int)bytesValid - 1;
+  bs_bytes = (int)bytesValid;
   bytesValid -= 1;
-  LOG_VERBOSE(LOG_TAG, "%s:INPUT size : %d, frame : %d", __func__, bs_bytes,
-              frame_number);
+  LOG_DEBUG(LOG_TAG, "%s:INPUT size : %d, frame : %d", __func__, bs_bytes,
+            frame_number);
 
-  while (bytesValid > 0) {
-#if 0
-    err = ldacDecoder_Fill(a2dp_ldac_decoder_cb.ldac_handle,
-                               &pBuffer, &bufferSize, &bytesValid);
-    if (err != LDACBT_ERR_NONE) {
-      LOG_ERROR(LOG_TAG, "%s: ldacDecoder_Fill failed: 0x%x", __func__,
-                static_cast<unsigned>(err));
-      return false;
-    }
-#endif
-    while (true) {
-      // Todo : implement LDAC Buffer Control Operation instead of
-      // ldac_decode_func().
-      err = ldac_decode_func(a2dp_ldac_decoder_cb.ldac_handle, pBuffer,
-                             a2dp_ldac_decoder_cb.decode_buf, fmt, bs_bytes,
-                             &used_bytes, &wrote_bytes);
-      //      if (err == LDAC_DEC_NOT_ENOUGH_FRAMES) {
-      //        break;
-      //      }
-      if (LDACBT_ERROR(err)) {
-        err = ldac_get_error_code_func(a2dp_ldac_decoder_cb.ldac_handle);
-        LOG_ERROR(LOG_TAG, "%s: ldacDecoder_DecodeFrame failed: %d:%d:%d",
-                  __func__, LDACBT_API_ERR(err), LDACBT_HANDLE_ERR(err),
-                  LDACBT_BLOCK_ERR(err));
-        if (LDACBT_FATAL(err)) {
-          break;
-        }
-      }
+  if (a2dp_ldac_decoder_cb.has_ldac_handle)
+    ldac_BCO_decode_packet_func(a2dp_ldac_decoder_cb.ldac_handle_bco, pBuffer,
+                                bs_bytes);
 
-      if (wrote_bytes > 0) {
-        size_t frame_len = (size_t)wrote_bytes;
-        a2dp_ldac_decoder_cb.decode_callback(
-            reinterpret_cast<uint8_t*>(a2dp_ldac_decoder_cb.decode_buf),
-            frame_len);
-      }
-      pBuffer += used_bytes;
-      bs_bytes -= used_bytes;
-      if (bs_bytes <= 1) {
-        bytesValid = 0;
-        break;
-      }
-    }
+  pthread_mutex_unlock(&(a2dp_ldac_decoder_cb.mutex));
+  return true;
+}
+
+void a2dp_vendor_ldac_decoder_start(void) {
+  pthread_mutex_lock(&(a2dp_ldac_decoder_cb.mutex));
+  LOG_DEBUG(LOG_TAG, "%s", __func__);
+  if (a2dp_ldac_decoder_cb.has_ldac_handle)
+    ldac_BCO_start_func(a2dp_ldac_decoder_cb.ldac_handle_bco);
+  pthread_mutex_unlock(&(a2dp_ldac_decoder_cb.mutex));
+}
+
+void a2dp_vendor_ldac_decoder_suspend(void) {
+  pthread_mutex_lock(&(a2dp_ldac_decoder_cb.mutex));
+  LOG_DEBUG(LOG_TAG, "%s", __func__);
+  if (a2dp_ldac_decoder_cb.has_ldac_handle)
+    ldac_BCO_suspend_func(a2dp_ldac_decoder_cb.ldac_handle_bco);
+  pthread_mutex_unlock(&(a2dp_ldac_decoder_cb.mutex));
+}
+
+void a2dp_vendor_ldac_decoder_configure(const uint8_t* p_codec_info) {
+  int32_t sample_rate;
+  int32_t bits_per_sample;
+  int32_t channel_mode;
+
+  if (p_codec_info == NULL) {
+    LOG_ERROR(LOG_TAG, "%s: p_codec_info is NULL", __func__);
+    return;
   }
 
-  return true;
+  pthread_mutex_lock(&(a2dp_ldac_decoder_cb.mutex));
+  sample_rate = A2DP_VendorGetTrackSampleRateLdac(p_codec_info);
+  bits_per_sample = A2DP_VendorGetTrackBitsPerSampleLdac(p_codec_info);
+  channel_mode = A2DP_VendorGetChannelModeCodeLdac(p_codec_info);
+
+  LOG_DEBUG(LOG_TAG, "%s , sample_rate=%d, bits_per_sample=%d, channel_mode=%d",
+            __func__, sample_rate, bits_per_sample, channel_mode);
+
+  if (a2dp_ldac_decoder_cb.has_ldac_handle)
+    ldac_BCO_configure_func(a2dp_ldac_decoder_cb.ldac_handle_bco, sample_rate,
+                            bits_per_sample, channel_mode);
+  pthread_mutex_unlock(&(a2dp_ldac_decoder_cb.mutex));
 }
diff --git a/stack/avct/avct_api.cc b/stack/avct/avct_api.cc
index 51e2daf..68dfec2 100644
--- a/stack/avct/avct_api.cc
+++ b/stack/avct/avct_api.cc
@@ -57,7 +57,7 @@
 
   /* register PSM with L2CAP */
   L2CA_Register(AVCT_PSM, (tL2CAP_APPL_INFO*)&avct_l2c_appl,
-                true /* enable_snoop */);
+                true /* enable_snoop */, nullptr);
 
   /* set security level */
   BTM_SetSecurityLevel(true, "", BTM_SEC_SERVICE_AVCTP, sec_mask, AVCT_PSM, 0,
@@ -69,8 +69,15 @@
   memset(&avct_cb, 0, sizeof(tAVCT_CB));
 
   /* Include the browsing channel which uses eFCR */
+  tL2CAP_ERTM_INFO ertm_info;
+  ertm_info.preferred_mode = avct_l2c_br_fcr_opts_def.mode;
+  ertm_info.allowed_modes = L2CAP_FCR_CHAN_OPT_ERTM;
+  ertm_info.user_rx_buf_size = BT_DEFAULT_BUFFER_SIZE;
+  ertm_info.user_tx_buf_size = BT_DEFAULT_BUFFER_SIZE;
+  ertm_info.fcr_rx_buf_size = BT_DEFAULT_BUFFER_SIZE;
+  ertm_info.fcr_tx_buf_size = BT_DEFAULT_BUFFER_SIZE;
   L2CA_Register(AVCT_BR_PSM, (tL2CAP_APPL_INFO*)&avct_l2c_br_appl,
-                true /*enable_snoop*/);
+                true /*enable_snoop*/, &ertm_info);
 
   /* AVCTP browsing channel uses the same security service as AVCTP control
    * channel */
diff --git a/stack/avct/avct_bcb_act.cc b/stack/avct/avct_bcb_act.cc
index 616976e..aca7349 100644
--- a/stack/avct/avct_bcb_act.cc
+++ b/stack/avct/avct_bcb_act.cc
@@ -106,7 +106,7 @@
   BTM_SetOutService(p_lcb->peer_addr, BTM_SEC_SERVICE_AVCTP_BROWSE, 0);
 
   /* Set the FCR options: Browsing channel mandates ERTM */
-  ertm_info.preferred_mode = avct_l2c_br_fcr_opts_def.mode;
+  ertm_info.preferred_mode = L2CAP_FCR_ERTM_MODE;
   ertm_info.allowed_modes = L2CAP_FCR_CHAN_OPT_ERTM;
   ertm_info.user_rx_buf_size = BT_DEFAULT_BUFFER_SIZE;
   ertm_info.user_tx_buf_size = BT_DEFAULT_BUFFER_SIZE;
diff --git a/stack/avdt/avdt_api.cc b/stack/avdt/avdt_api.cc
index 9350c31..8a1079f 100644
--- a/stack/avdt/avdt_api.cc
+++ b/stack/avdt/avdt_api.cc
@@ -91,7 +91,7 @@
 void AVDT_Register(AvdtpRcb* p_reg, tAVDT_CTRL_CBACK* p_cback) {
   /* register PSM with L2CAP */
   L2CA_Register(AVDT_PSM, (tL2CAP_APPL_INFO*)&avdt_l2c_appl,
-                true /* enable_snoop */);
+                true /* enable_snoop */, nullptr);
 
   /* set security level */
   BTM_SetSecurityLevel(true, "", BTM_SEC_SERVICE_AVDTP, p_reg->sec_mask,
@@ -141,13 +141,14 @@
 }
 
 void AVDT_AbortReq(uint8_t handle) {
-  AVDT_TRACE_WARNING("%s: handle=%d", __func__, handle);
+  AVDT_TRACE_WARNING("%s: avdt_handle=%d", __func__, handle);
 
   AvdtpScb* p_scb = avdt_scb_by_hdl(handle);
   if (p_scb != NULL) {
     avdt_scb_event(p_scb, AVDT_SCB_API_ABORT_REQ_EVT, NULL);
   } else {
-    AVDT_TRACE_ERROR("%s Improper SCB, can not abort the stream", __func__);
+    AVDT_TRACE_ERROR("%s Improper avdp_handle=%d, can not abort the stream",
+                     __func__, handle);
   }
 }
 
@@ -187,8 +188,10 @@
     }
   }
 
-  AVDT_TRACE_DEBUG("%s: result=%d handle=%d scb_index=%d", __func__, result,
-                   *p_handle, avdtp_stream_config.scb_index);
+  if (result != AVDT_SUCCESS) {
+    AVDT_TRACE_ERROR("%s: result=%d peer_id=%d scb_index=%d", __func__, result,
+                     peer_id, avdtp_stream_config.scb_index);
+  }
 
   return result;
 }
@@ -211,7 +214,7 @@
   uint16_t result = AVDT_SUCCESS;
   AvdtpScb* p_scb;
 
-  AVDT_TRACE_DEBUG("%s: handle=%d", __func__, handle);
+  AVDT_TRACE_DEBUG("%s: avdt_handle=%d", __func__, handle);
 
   /* look up scb */
   p_scb = avdt_scb_by_hdl(handle);
@@ -222,7 +225,9 @@
     avdt_scb_event(p_scb, AVDT_SCB_API_REMOVE_EVT, NULL);
   }
 
-  AVDT_TRACE_DEBUG("%s: result=%d", __func__, result);
+  if (result != AVDT_SUCCESS) {
+    AVDT_TRACE_ERROR("%s: result=%d avdt_handle=%d", __func__, result, handle);
+  }
 
   return result;
 }
@@ -286,8 +291,10 @@
     }
   }
 
-  AVDT_TRACE_DEBUG("%s: result=%d", __func__, result);
-
+  if (result != AVDT_SUCCESS) {
+    AVDT_TRACE_ERROR("%s: result=%d address=%s", __func__, result,
+                     bd_addr.ToString().c_str());
+  }
   return result;
 }
 
@@ -337,8 +344,10 @@
     }
   }
 
-  AVDT_TRACE_DEBUG("%s: result=%d", __func__, result);
-
+  if (result != AVDT_SUCCESS) {
+    AVDT_TRACE_ERROR("%s: result=%d address=%s", __func__, result,
+                     bd_addr.ToString().c_str());
+  }
   return result;
 }
 
@@ -384,8 +393,10 @@
   getcap.p_cback = p_cback;
   result = avdt_get_cap_req(bd_addr, channel_index, &getcap);
 
-  AVDT_TRACE_DEBUG("%s: result=%d", __func__, result);
-
+  if (result != AVDT_SUCCESS) {
+    AVDT_TRACE_ERROR("%s: result=%d address=%s", __func__, result,
+                     bd_addr.ToString().c_str());
+  }
   return result;
 }
 
@@ -405,8 +416,8 @@
   uint16_t result = AVDT_SUCCESS;
   tAVDT_SCB_EVT evt;
 
-  AVDT_TRACE_DEBUG("%s: handle=%d ceid=%d delay=%d", __func__, handle, seid,
-                   delay);
+  AVDT_TRACE_DEBUG("%s: avdt_handle=%d seid=%d delay=%d", __func__, handle,
+                   seid, delay);
 
   /* map handle to scb */
   p_scb = avdt_scb_by_hdl(handle);
@@ -420,8 +431,10 @@
     avdt_scb_event(p_scb, AVDT_SCB_API_DELAY_RPT_REQ_EVT, &evt);
   }
 
-  AVDT_TRACE_DEBUG("%s: result=%d", __func__, result);
-
+  if (result != AVDT_SUCCESS) {
+    AVDT_TRACE_ERROR("%s: result=%d avdt_handle=%d seid=%d", __func__, result,
+                     handle, seid);
+  }
   return result;
 }
 
@@ -447,7 +460,8 @@
   uint16_t result = AVDT_SUCCESS;
   tAVDT_SCB_EVT evt;
 
-  AVDT_TRACE_DEBUG("%s: handle=%d seid=%d", __func__, handle, seid);
+  AVDT_TRACE_API("%s: address=%s avdt_handle=%d seid=%d", __func__,
+                 bd_addr.ToString().c_str(), handle, seid);
 
   /* verify SEID */
   if ((seid < AVDT_SEID_MIN) || (seid > AVDT_SEID_MAX)) {
@@ -482,10 +496,11 @@
     evt.msg.config_cmd.int_seid = handle;
     evt.msg.config_cmd.p_cfg = p_cfg;
     avdt_scb_event(p_scb, AVDT_SCB_API_SETCONFIG_REQ_EVT, &evt);
+  } else {
+    AVDT_TRACE_ERROR("%s: result=%d address=%s avdt_handle=%d", __func__,
+                     result, bd_addr.ToString().c_str(), handle);
   }
 
-  AVDT_TRACE_DEBUG("%s: result=%d", __func__, result);
-
   return result;
 }
 
@@ -508,7 +523,7 @@
   uint16_t result = AVDT_SUCCESS;
   uint8_t event_code;
 
-  AVDT_TRACE_DEBUG("%s: handle=%d label=%d error_code=0x%x category=%d",
+  AVDT_TRACE_DEBUG("%s: avdt_handle=%d label=%d error_code=0x%x category=%d",
                    __func__, handle, label, error_code, category);
 
   /* map handle to scb */
@@ -535,8 +550,9 @@
     avdt_scb_event(p_scb, event_code, &evt);
   }
 
-  AVDT_TRACE_DEBUG("%s: result=%d", __func__, result);
-
+  if (result != AVDT_SUCCESS) {
+    AVDT_TRACE_ERROR("%s: result=%d avdt_handle=%d", __func__, result, handle);
+  }
   return result;
 }
 
@@ -587,8 +603,16 @@
     }
   }
 
-  AVDT_TRACE_DEBUG("%s: result=%d", __func__, result);
-
+  if (result != AVDT_SUCCESS) {
+    if ((num_handles == 0) || (num_handles > AVDT_NUM_SEPS)) {
+      AVDT_TRACE_ERROR("%s: result=%d num_handles=%d invalid", __func__, result,
+                       num_handles);
+    } else {
+      AVDT_TRACE_ERROR(
+          "%s: result=%d avdt_handle=%d", __func__, result,
+          (i < num_handles ? p_handles[i] : p_handles[num_handles - 1]));
+    }
+  }
   return result;
 }
 
@@ -639,8 +663,16 @@
     }
   }
 
-  AVDT_TRACE_DEBUG("%s: result=%d", __func__, result);
-
+  if (result != AVDT_SUCCESS) {
+    if ((num_handles == 0) || (num_handles > AVDT_NUM_SEPS)) {
+      AVDT_TRACE_ERROR("%s: result=%d num_handles=%d invalid", __func__, result,
+                       num_handles);
+    } else {
+      AVDT_TRACE_ERROR(
+          "%s: result=%d avdt_handle=%d", __func__, result,
+          (i < num_handles ? p_handles[i] : p_handles[num_handles - 1]));
+    }
+  }
   return result;
 }
 
@@ -662,7 +694,7 @@
   AvdtpScb* p_scb;
   uint16_t result = AVDT_SUCCESS;
 
-  AVDT_TRACE_DEBUG("%s: handle=%d", __func__, handle);
+  AVDT_TRACE_API("%s: avdt_handle=%d", __func__, handle);
 
   /* map handle to scb */
   p_scb = avdt_scb_by_hdl(handle);
@@ -674,8 +706,9 @@
     avdt_scb_event(p_scb, AVDT_SCB_API_CLOSE_REQ_EVT, NULL);
   }
 
-  AVDT_TRACE_DEBUG("%s: result=%d", __func__, result);
-
+  if (result != AVDT_SUCCESS) {
+    AVDT_TRACE_ERROR("%s: result=%d avdt_handle=%d", __func__, result, handle);
+  }
   return result;
 }
 
@@ -701,7 +734,7 @@
   uint16_t result = AVDT_SUCCESS;
   tAVDT_SCB_EVT evt;
 
-  AVDT_TRACE_DEBUG("%s: handle=%d", __func__, handle);
+  AVDT_TRACE_DEBUG("%s: avdt_handle=%d", __func__, handle);
 
   /* map handle to scb */
   p_scb = avdt_scb_by_hdl(handle);
@@ -716,8 +749,9 @@
     avdt_scb_event(p_scb, AVDT_SCB_API_RECONFIG_REQ_EVT, &evt);
   }
 
-  AVDT_TRACE_DEBUG("%s: result=%d", __func__, result);
-
+  if (result != AVDT_SUCCESS) {
+    AVDT_TRACE_ERROR("%s: result=%d avdt_handle=%d", __func__, result, handle);
+  }
   return result;
 }
 
@@ -739,7 +773,7 @@
   tAVDT_SCB_EVT evt;
   uint16_t result = AVDT_SUCCESS;
 
-  AVDT_TRACE_DEBUG("%s: handle=%d label=%d error_code=0x%x category=%d",
+  AVDT_TRACE_DEBUG("%s: avdt_handle=%d label=%d error_code=0x%x category=%d",
                    __func__, handle, label, error_code, category);
 
   /* map handle to scb */
@@ -755,8 +789,9 @@
     avdt_scb_event(p_scb, AVDT_SCB_API_RECONFIG_RSP_EVT, &evt);
   }
 
-  AVDT_TRACE_DEBUG("%s: result=%d", __func__, result);
-
+  if (result != AVDT_SUCCESS) {
+    AVDT_TRACE_ERROR("%s: result=%d avdt_handle=%d", __func__, result, handle);
+  }
   return result;
 }
 
@@ -779,7 +814,7 @@
   uint16_t result = AVDT_SUCCESS;
   tAVDT_SCB_EVT evt;
 
-  AVDT_TRACE_DEBUG("%s: handle=%d len=%d", __func__, handle, len);
+  AVDT_TRACE_DEBUG("%s: avdt_handle=%d len=%d", __func__, handle, len);
 
   /* map handle to scb */
   p_scb = avdt_scb_by_hdl(handle);
@@ -793,8 +828,9 @@
     avdt_scb_event(p_scb, AVDT_SCB_API_SECURITY_REQ_EVT, &evt);
   }
 
-  AVDT_TRACE_DEBUG("%s: result=%d", __func__, result);
-
+  if (result != AVDT_SUCCESS) {
+    AVDT_TRACE_ERROR("%s: result=%d avdt_handle=%d", __func__, result, handle);
+  }
   return result;
 }
 
@@ -818,8 +854,8 @@
   uint16_t result = AVDT_SUCCESS;
   tAVDT_SCB_EVT evt;
 
-  AVDT_TRACE_DEBUG("%s: handle=%d label=%d error_code=0x%x len=%d", __func__,
-                   handle, label, error_code, len);
+  AVDT_TRACE_DEBUG("%s: avdt_handle=%d label=%d error_code=0x%x len=%d",
+                   __func__, handle, label, error_code, len);
 
   /* map handle to scb */
   p_scb = avdt_scb_by_hdl(handle);
@@ -835,8 +871,9 @@
     avdt_scb_event(p_scb, AVDT_SCB_API_SECURITY_RSP_EVT, &evt);
   }
 
-  AVDT_TRACE_DEBUG("%s: result=%d", __func__, result);
-
+  if (result != AVDT_SUCCESS) {
+    AVDT_TRACE_ERROR("%s: result=%d avdt_handle=%d", __func__, result, handle);
+  }
   return result;
 }
 
@@ -883,8 +920,8 @@
   tAVDT_SCB_EVT evt;
   uint16_t result = AVDT_SUCCESS;
 
-  AVDT_TRACE_DEBUG("%s: handle=%d timestamp=%d m_pt=0x%x opt=0x%x", __func__,
-                   handle, time_stamp, m_pt, opt);
+  AVDT_TRACE_DEBUG("%s: avdt_handle=%d timestamp=%d m_pt=0x%x opt=0x%x",
+                   __func__, handle, time_stamp, m_pt, opt);
 
   /* map handle to scb */
   p_scb = avdt_scb_by_hdl(handle);
@@ -898,8 +935,7 @@
     avdt_scb_event(p_scb, AVDT_SCB_API_WRITE_REQ_EVT, &evt);
   }
 
-  AVDT_TRACE_DEBUG("%s: result=%d", __func__, result);
-
+  AVDT_TRACE_DEBUG("%s: result=%d avdt_handle=%d", __func__, result, handle);
   return result;
 }
 
@@ -1024,11 +1060,11 @@
     /* send event to ccb */
     evt.disconnect.p_cback = p_cback;
     avdt_ccb_event(p_ccb, AVDT_CCB_API_DISCONNECT_REQ_EVT, &evt);
+  } else {
+    AVDT_TRACE_ERROR("%s: address=%s result=%d", __func__,
+                     bd_addr.ToString().c_str(), result);
   }
 
-  AVDT_TRACE_DEBUG("%s: address=%s result=%d", __func__,
-                   bd_addr.ToString().c_str(), result);
-
   return result;
 }
 
@@ -1109,7 +1145,7 @@
   uint32_t ssrc;
   uint16_t len;
 
-  AVDT_TRACE_DEBUG("%s: handle=%d type=%d", __func__, handle, type);
+  AVDT_TRACE_DEBUG("%s: avdt_handle=%d type=%d", __func__, handle, type);
 
   /* map handle to scb && verify parameters */
   if (((p_scb = avdt_scb_by_hdl(handle)) != NULL) && (p_scb->p_ccb != NULL) &&
@@ -1180,7 +1216,10 @@
     }
   }
 
-  AVDT_TRACE_DEBUG("%s: result=%d", __func__, result);
+  if (result != AVDT_SUCCESS) {
+    AVDT_TRACE_WARNING("%s: result=%d avdt_handle=%d", __func__, result,
+                       handle);
+  }
 
   return result;
 }
@@ -1252,7 +1291,7 @@
       dprintf(fd, "      SEP type: 0x%x\n", scb.stream_config.tsep);
       dprintf(fd, "      Media type: 0x%x\n", scb.stream_config.media_type);
       dprintf(fd, "      MTU: %d\n", scb.stream_config.mtu);
-      dprintf(fd, "      SCB handle: %d\n", scb.ScbHandle());
+      dprintf(fd, "      AVDT SCB handle: %d\n", scb.ScbHandle());
       dprintf(fd, "      SCB index: %d\n", scb.stream_config.scb_index);
       dprintf(fd, "      Configured codec: %s\n",
               A2DP_CodecName(scb.curr_cfg.codec_info));
diff --git a/stack/avrc/avrc_api.cc b/stack/avrc/avrc_api.cc
index e2f109a..62b7fe4 100644
--- a/stack/avrc/avrc_api.cc
+++ b/stack/avrc/avrc_api.cc
@@ -32,6 +32,7 @@
 #include "btu.h"
 #include "osi/include/fixed_queue.h"
 #include "osi/include/osi.h"
+#include "osi/include/properties.h"
 
 /*****************************************************************************
  *  Global data
@@ -1113,27 +1114,32 @@
 
   AVRC_TRACE_DEBUG("%s handle = %u label = %u ctype = %u len = %d", __func__,
                    handle, label, ctype, p_pkt->len);
-
+  /* Handle for AVRCP fragment */
+  bool is_new_avrcp = osi_property_get_bool("persist.bluetooth.enablenewavrcp", true);
   if (ctype >= AVRC_RSP_NOT_IMPL) cr = AVCT_RSP;
 
   if (p_pkt->event == AVRC_OP_VENDOR) {
-    /* add AVRCP Vendor Dependent headers */
-    p_start = ((uint8_t*)(p_pkt + 1) + p_pkt->offset);
-    p_pkt->offset -= AVRC_VENDOR_HDR_SIZE;
-    p_pkt->len += AVRC_VENDOR_HDR_SIZE;
-    p_data = (uint8_t*)(p_pkt + 1) + p_pkt->offset;
-    *p_data++ = (ctype & AVRC_CTYPE_MASK);
-    *p_data++ = (AVRC_SUB_PANEL << AVRC_SUBTYPE_SHIFT);
-    *p_data++ = AVRC_OP_VENDOR;
-    AVRC_CO_ID_TO_BE_STREAM(p_data, AVRC_CO_METADATA);
+    if (is_new_avrcp) {
+      p_start = (uint8_t*)(p_pkt + 1) + p_pkt->offset + AVRC_VENDOR_HDR_SIZE;
+    } else {
+      /* add AVRCP Vendor Dependent headers */
+      p_start = ((uint8_t*)(p_pkt + 1) + p_pkt->offset);
+      p_pkt->offset -= AVRC_VENDOR_HDR_SIZE;
+      p_pkt->len += AVRC_VENDOR_HDR_SIZE;
+      p_data = (uint8_t*)(p_pkt + 1) + p_pkt->offset;
+      *p_data++ = (ctype & AVRC_CTYPE_MASK);
+      *p_data++ = (AVRC_SUB_PANEL << AVRC_SUBTYPE_SHIFT);
+      *p_data++ = AVRC_OP_VENDOR;
+      AVRC_CO_ID_TO_BE_STREAM(p_data, AVRC_CO_METADATA);
 
-    /* Check if this is a AVRC_PDU_REQUEST_CONTINUATION_RSP */
-    if (cr == AVCT_CMD) {
-      msg_mask |= AVRC_MSG_MASK_IS_VENDOR_CMD;
+      /* Check if this is a AVRC_PDU_REQUEST_CONTINUATION_RSP */
+      if (cr == AVCT_CMD) {
+        msg_mask |= AVRC_MSG_MASK_IS_VENDOR_CMD;
 
-      if ((*p_start == AVRC_PDU_REQUEST_CONTINUATION_RSP) ||
-          (*p_start == AVRC_PDU_ABORT_CONTINUATION_RSP)) {
-        msg_mask |= AVRC_MSG_MASK_IS_CONTINUATION_RSP;
+        if ((*p_start == AVRC_PDU_REQUEST_CONTINUATION_RSP) ||
+            (*p_start == AVRC_PDU_ABORT_CONTINUATION_RSP)) {
+          msg_mask |= AVRC_MSG_MASK_IS_CONTINUATION_RSP;
+        }
       }
     }
   } else if (p_pkt->event == AVRC_OP_PASS_THRU) {
diff --git a/stack/avrc/avrc_bld_ct.cc b/stack/avrc/avrc_bld_ct.cc
index afb1784..384ae60 100644
--- a/stack/avrc/avrc_bld_ct.cc
+++ b/stack/avrc/avrc_bld_ct.cc
@@ -431,7 +431,24 @@
   p_pkt->len = (p_data - p_start);
   return AVRC_STS_NO_ERROR;
 }
-
+static tAVRC_STS avrc_bld_get_item_attributes_cmd(
+    BT_HDR* p_pkt, const tAVRC_GET_ATTRS_CMD* cmd) {
+  AVRC_TRACE_API("%s", __func__);
+  uint8_t* p_start = (uint8_t*)(p_pkt + 1) + p_pkt->offset;
+  /* This is where the PDU specific for AVRC starts
+   * AVRCP Spec 1.4 section 22.19 */
+  uint8_t* p_data = p_start + 1; /* pdu */
+  UINT16_TO_BE_STREAM(p_data, 12 + 4 * cmd->attr_count);
+  UINT8_TO_BE_STREAM(p_data, cmd->scope);
+  uint64_t uid;
+  memcpy(&uid, cmd->uid, 8);
+  UINT64_TO_BE_STREAM(p_data, uid);
+  UINT16_TO_BE_STREAM(p_data, cmd->uid_counter);
+  UINT8_TO_BE_STREAM(p_data, cmd->attr_count);
+  ARRAY_TO_BE_STREAM(p_data, cmd->p_attr_list, 4 * cmd->attr_count);
+  p_pkt->len = (p_data - p_start);
+  return AVRC_STS_NO_ERROR;
+}
 /*******************************************************************************
  *
  * Function         avrc_bld_set_browsed_player_cmd
@@ -645,6 +662,9 @@
     case AVRC_PDU_CHANGE_PATH:
       status = avrc_bld_change_folder_cmd(p_pkt, &(p_cmd->chg_path));
       break;
+    case AVRC_PDU_GET_ITEM_ATTRIBUTES:
+      status = avrc_bld_get_item_attributes_cmd(p_pkt, &(p_cmd->get_attrs));
+      break;
     case AVRC_PDU_SET_BROWSED_PLAYER:
       status = avrc_bld_set_browsed_player_cmd(p_pkt, &(p_cmd->br_player));
       break;
diff --git a/stack/avrc/avrc_bld_tg.cc b/stack/avrc/avrc_bld_tg.cc
index b28c9e2..1dac160 100644
--- a/stack/avrc/avrc_bld_tg.cc
+++ b/stack/avrc/avrc_bld_tg.cc
@@ -1338,8 +1338,7 @@
     case AVRC_OP_VENDOR:
       /* reserved 0, packet_type 0 */
       UINT8_TO_BE_STREAM(p_data, 0);
-    /* continue to the next "case to add length */
-
+      [[fallthrough]];
     case AVRC_OP_BROWSE:
       /* add fixed lenth - 0 */
       UINT16_TO_BE_STREAM(p_data, 0);
diff --git a/stack/avrc/avrc_pars_ct.cc b/stack/avrc/avrc_pars_ct.cc
index 39ed921..7b64b86 100644
--- a/stack/avrc/avrc_pars_ct.cc
+++ b/stack/avrc/avrc_pars_ct.cc
@@ -425,6 +425,30 @@
       break;
     }
 
+    case AVRC_PDU_GET_ITEM_ATTRIBUTES: {
+      tAVRC_GET_ATTRS_RSP* get_attr_rsp = &(p_rsp->get_attrs);
+      get_attr_rsp->pdu = pdu;
+      BE_STREAM_TO_UINT8(get_attr_rsp->status, p)
+      BE_STREAM_TO_UINT8(get_attr_rsp->num_attrs, p);
+      get_attr_rsp->p_attrs = (tAVRC_ATTR_ENTRY*)osi_malloc(
+          get_attr_rsp->num_attrs * sizeof(tAVRC_ATTR_ENTRY));
+      for (int i = 0; i < get_attr_rsp->num_attrs; i++) {
+        tAVRC_ATTR_ENTRY* attr_entry = &(get_attr_rsp->p_attrs[i]);
+        BE_STREAM_TO_UINT32(attr_entry->attr_id, p);
+        BE_STREAM_TO_UINT16(attr_entry->name.charset_id, p);
+        BE_STREAM_TO_UINT16(attr_entry->name.str_len, p);
+        min_len += attr_entry->name.str_len;
+        if (pkt_len < min_len) goto browse_length_error;
+        attr_entry->name.p_str =
+            (uint8_t*)osi_malloc(attr_entry->name.str_len * sizeof(uint8_t));
+        BE_STREAM_TO_ARRAY(p, attr_entry->name.p_str, attr_entry->name.str_len);
+        AVRC_TRACE_DEBUG("%s media attr id %d cs %d name len %d", __func__,
+                         attr_entry->attr_id, attr_entry->name.charset_id,
+                         attr_entry->name.str_len);
+      }
+
+      break;
+    }
     case AVRC_PDU_SET_BROWSED_PLAYER: {
       tAVRC_SET_BR_PLAYER_RSP* set_br_pl_rsp = &(p_rsp->br_player);
       /* Copyback the PDU */
diff --git a/stack/avrc/avrc_pars_tg.cc b/stack/avrc/avrc_pars_tg.cc
index d15f3e2..5a81d0d 100644
--- a/stack/avrc/avrc_pars_tg.cc
+++ b/stack/avrc/avrc_pars_tg.cc
@@ -71,6 +71,8 @@
       break;
     }
     case AVRC_PDU_REGISTER_NOTIFICATION: /* 0x31 */
+      if (len < 5) return AVRC_STS_INTERNAL_ERR;
+
       BE_STREAM_TO_UINT8(p_result->reg_notif.event_id, p);
       BE_STREAM_TO_UINT32(p_result->reg_notif.param, p);
       break;
diff --git a/stack/bnep/bnep_main.cc b/stack/bnep/bnep_main.cc
index 265ec79..99124cf 100644
--- a/stack/bnep/bnep_main.cc
+++ b/stack/bnep/bnep_main.cc
@@ -94,8 +94,8 @@
   bnep_cb.reg_info.pL2CA_CongestionStatus_Cb = bnep_congestion_ind;
 
   /* Now, register with L2CAP */
-  if (!L2CA_Register(BT_PSM_BNEP, &bnep_cb.reg_info,
-                     false /* enable_snoop */)) {
+  if (!L2CA_Register(BT_PSM_BNEP, &bnep_cb.reg_info, false /* enable_snoop */,
+                     nullptr)) {
     BNEP_TRACE_ERROR("BNEP - Registration failed");
     return BNEP_SECURITY_FAIL;
   }
diff --git a/stack/btm/ble_advertiser_hci_interface.cc b/stack/btm/ble_advertiser_hci_interface.cc
index dddf8d4..fbf8d27 100644
--- a/stack/btm/ble_advertiser_hci_interface.cc
+++ b/stack/btm/ble_advertiser_hci_interface.cc
@@ -97,7 +97,7 @@
 class BleAdvertiserVscHciInterfaceImpl : public BleAdvertiserHciInterface {
   void SendAdvCmd(const base::Location& posted_from, uint8_t param_len,
                   uint8_t* param_buf, status_cb command_complete) {
-    btu_hcif_send_cmd_with_cb(posted_from, HCI_BLE_MULTI_ADV_OCF, param_buf,
+    btu_hcif_send_cmd_with_cb(posted_from, HCI_BLE_MULTI_ADV, param_buf,
                               param_len,
                               base::Bind(&btm_ble_multi_adv_vsc_cmpl_cback,
                                          param_buf[0], command_complete));
diff --git a/stack/btm/btm_acl.cc b/stack/btm/btm_acl.cc
index 6daf2d2..3c8a6a6 100644
--- a/stack/btm/btm_acl.cc
+++ b/stack/btm/btm_acl.cc
@@ -1770,65 +1770,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_ReadRemoteExtendedFeatures
- *
- * Returns          pointer to the remote extended features mask (8 bytes)
- *                  or NULL if bad page
- *
- ******************************************************************************/
-uint8_t* BTM_ReadRemoteExtendedFeatures(const RawAddress& addr,
-                                        uint8_t page_number) {
-  tACL_CONN* p = btm_bda_to_acl(addr, BT_TRANSPORT_BR_EDR);
-  BTM_TRACE_DEBUG("BTM_ReadRemoteExtendedFeatures");
-  if (p == NULL) {
-    return (NULL);
-  }
-
-  if (page_number > HCI_EXT_FEATURES_PAGE_MAX) {
-    BTM_TRACE_ERROR("Warning: BTM_ReadRemoteExtendedFeatures page %d unknown",
-                    page_number);
-    return NULL;
-  }
-
-  return (p->peer_lmp_feature_pages[page_number]);
-}
-
-/*******************************************************************************
- *
- * Function         BTM_ReadNumberRemoteFeaturesPages
- *
- * Returns          number of features pages read from the remote device.
- *
- ******************************************************************************/
-uint8_t BTM_ReadNumberRemoteFeaturesPages(const RawAddress& addr) {
-  tACL_CONN* p = btm_bda_to_acl(addr, BT_TRANSPORT_BR_EDR);
-  BTM_TRACE_DEBUG("BTM_ReadNumberRemoteFeaturesPages");
-  if (p == NULL) {
-    return (0);
-  }
-
-  return (p->num_read_pages);
-}
-
-/*******************************************************************************
- *
- * Function         BTM_ReadAllRemoteFeatures
- *
- * Returns          pointer to all features of the remote (24 bytes).
- *
- ******************************************************************************/
-uint8_t* BTM_ReadAllRemoteFeatures(const RawAddress& addr) {
-  tACL_CONN* p = btm_bda_to_acl(addr, BT_TRANSPORT_BR_EDR);
-  BTM_TRACE_DEBUG("BTM_ReadAllRemoteFeatures");
-  if (p == NULL) {
-    return (NULL);
-  }
-
-  return (p->peer_lmp_feature_pages[0]);
-}
-
-/*******************************************************************************
- *
  * Function         BTM_RegBusyLevelNotif
  *
  * Description      This function is called to register a callback to receive
@@ -1856,40 +1797,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_SetQoS
- *
- * Description      This function is called to setup QoS
- *
- * Returns          status of the operation
- *
- ******************************************************************************/
-tBTM_STATUS BTM_SetQoS(const RawAddress& bd, FLOW_SPEC* p_flow,
-                       tBTM_CMPL_CB* p_cb) {
-  tACL_CONN* p = &btm_cb.acl_db[0];
-
-  VLOG(2) << __func__ << " BdAddr: " << bd;
-
-  /* If someone already waiting on the version, do not allow another */
-  if (btm_cb.devcb.p_qos_setup_cmpl_cb) return (BTM_BUSY);
-
-  p = btm_bda_to_acl(bd, BT_TRANSPORT_BR_EDR);
-  if (p != NULL) {
-    btm_cb.devcb.p_qos_setup_cmpl_cb = p_cb;
-    alarm_set_on_mloop(btm_cb.devcb.qos_setup_timer, BTM_DEV_REPLY_TIMEOUT_MS,
-                       btm_qos_setup_timeout, NULL);
-
-    btsnd_hcic_qos_setup(p->hci_handle, p_flow->qos_flags, p_flow->service_type,
-                         p_flow->token_rate, p_flow->peak_bandwidth,
-                         p_flow->latency, p_flow->delay_variation);
-    return (BTM_CMD_STARTED);
-  }
-
-  /* If here, no BD Addr found */
-  return (BTM_UNKNOWN_ADDR);
-}
-
-/*******************************************************************************
- *
  * Function         btm_qos_setup_timeout
  *
  * Description      Callback when QoS setup times out.
@@ -2061,39 +1968,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_ReadLinkQuality
- *
- * Description      This function is called to read the link qulaity.
- *                  The value of the link quality is returned in the callback.
- *                  (tBTM_LINK_QUALITY_RESULT)
- *
- * Returns          BTM_CMD_STARTED if successfully initiated or error code
- *
- ******************************************************************************/
-tBTM_STATUS BTM_ReadLinkQuality(const RawAddress& remote_bda,
-                                tBTM_CMPL_CB* p_cb) {
-  VLOG(2) << __func__ << ": RemBdAddr: " << remote_bda;
-
-  /* If someone already waiting on the version, do not allow another */
-  if (btm_cb.devcb.p_link_qual_cmpl_cb) return (BTM_BUSY);
-
-  tACL_CONN* p = btm_bda_to_acl(remote_bda, BT_TRANSPORT_BR_EDR);
-  if (p != (tACL_CONN*)NULL) {
-    btm_cb.devcb.p_link_qual_cmpl_cb = p_cb;
-    alarm_set_on_mloop(btm_cb.devcb.read_link_quality_timer,
-                       BTM_DEV_REPLY_TIMEOUT_MS, btm_read_link_quality_timeout,
-                       NULL);
-
-    btsnd_hcic_get_link_quality(p->hci_handle);
-    return (BTM_CMD_STARTED);
-  }
-
-  /* If here, no BD Addr found */
-  return (BTM_UNKNOWN_ADDR);
-}
-
-/*******************************************************************************
- *
  * Function         BTM_ReadTxPower
  *
  * Description      This function is called to read the current
@@ -2629,7 +2503,7 @@
   } else {
     if (!BTM_ACL_IS_CONNECTED(bda)) {
       VLOG(1) << "connecting_bda: " << btm_cb.connecting_bda;
-      if (btm_cb.paging && bda != btm_cb.connecting_bda) {
+      if (btm_cb.paging && bda == btm_cb.connecting_bda) {
         fixed_queue_enqueue(btm_cb.page_queue, p);
       } else {
         p_dev_rec = btm_find_or_alloc_dev(bda);
diff --git a/stack/btm/btm_ble.cc b/stack/btm/btm_ble.cc
index b1f4119..06a597a 100644
--- a/stack/btm/btm_ble.cc
+++ b/stack/btm/btm_ble.cc
@@ -39,8 +39,10 @@
 #include "gap_api.h"
 #include "gatt_api.h"
 #include "hcimsgs.h"
-#include "log/log.h"
 #include "l2c_int.h"
+#include "log/log.h"
+#include "main/shim/btm_api.h"
+#include "main/shim/shim.h"
 #include "osi/include/log.h"
 #include "osi/include/osi.h"
 #include "stack/crypto_toolbox/crypto_toolbox.h"
@@ -69,6 +71,11 @@
  ******************************************************************************/
 bool BTM_SecAddBleDevice(const RawAddress& bd_addr, BD_NAME bd_name,
                          tBT_DEVICE_TYPE dev_type, tBLE_ADDR_TYPE addr_type) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::BTM_SecAddBleDevice(bd_addr, bd_name, dev_type,
+                                                addr_type);
+  }
+
   BTM_TRACE_DEBUG("%s: dev_type=0x%x", __func__, dev_type);
 
   tBTM_SEC_DEV_REC* p_dev_rec = btm_find_dev(bd_addr);
@@ -130,6 +137,10 @@
  ******************************************************************************/
 bool BTM_SecAddBleKey(const RawAddress& bd_addr, tBTM_LE_KEY_VALUE* p_le_key,
                       tBTM_LE_KEY_TYPE key_type) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::BTM_SecAddBleKey(bd_addr, p_le_key, key_type);
+  }
+
   tBTM_SEC_DEV_REC* p_dev_rec;
   BTM_TRACE_DEBUG("BTM_SecAddBleKey");
   p_dev_rec = btm_find_dev(bd_addr);
@@ -170,6 +181,10 @@
  *
  ******************************************************************************/
 void BTM_BleLoadLocalKeys(uint8_t key_type, tBTM_BLE_LOCAL_KEYS* p_key) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::BTM_BleLoadLocalKeys(key_type, p_key);
+  }
+
   tBTM_DEVCB* p_devcb = &btm_cb.devcb;
   BTM_TRACE_DEBUG("%s", __func__);
   if (p_key != NULL) {
@@ -192,14 +207,27 @@
 
 /** Returns local device encryption root (ER) */
 const Octet16& BTM_GetDeviceEncRoot() {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::BTM_GetDeviceEncRoot();
+  }
   return btm_cb.devcb.ble_encryption_key_value;
 }
 
 /** Returns local device identity root (IR). */
-const Octet16& BTM_GetDeviceIDRoot() { return btm_cb.devcb.id_keys.irk; }
+const Octet16& BTM_GetDeviceIDRoot() {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::BTM_GetDeviceIDRoot();
+  }
+  return btm_cb.devcb.id_keys.irk;
+}
 
 /** Return local device DHK. */
-const Octet16& BTM_GetDeviceDHK() { return btm_cb.devcb.id_keys.dhk; }
+const Octet16& BTM_GetDeviceDHK() {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::BTM_GetDeviceDHK();
+  }
+  return btm_cb.devcb.id_keys.dhk;
+}
 
 /*******************************************************************************
  *
@@ -214,6 +242,10 @@
 void BTM_ReadConnectionAddr(const RawAddress& remote_bda,
                             RawAddress& local_conn_addr,
                             tBLE_ADDR_TYPE* p_addr_type) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::BTM_ReadConnectionAddr(remote_bda, local_conn_addr,
+                                                   p_addr_type);
+  }
   tACL_CONN* p_acl = btm_bda_to_acl(remote_bda, BT_TRANSPORT_LE);
 
   if (p_acl == NULL) {
@@ -238,6 +270,9 @@
  *
  ******************************************************************************/
 bool BTM_IsBleConnection(uint16_t conn_handle) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::BTM_IsBleConnection(conn_handle);
+  }
   uint8_t xx;
   tACL_CONN* p;
 
@@ -268,6 +303,10 @@
 bool BTM_ReadRemoteConnectionAddr(const RawAddress& pseudo_addr,
                                   RawAddress& conn_addr,
                                   tBLE_ADDR_TYPE* p_addr_type) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::BTM_ReadRemoteConnectionAddr(pseudo_addr, conn_addr,
+                                                         p_addr_type);
+  }
   bool st = true;
 #if (BLE_PRIVACY_SPT == TRUE)
   tACL_CONN* p = btm_bda_to_acl(pseudo_addr, BT_TRANSPORT_LE);
@@ -306,6 +345,9 @@
  *
  ******************************************************************************/
 void BTM_SecurityGrant(const RawAddress& bd_addr, uint8_t res) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::BTM_SecurityGrant(bd_addr, res);
+  }
   tSMP_STATUS res_smp =
       (res == BTM_SUCCESS) ? SMP_SUCCESS : SMP_REPEATED_ATTEMPTS;
   BTM_TRACE_DEBUG("BTM_SecurityGrant");
@@ -330,6 +372,9 @@
  ******************************************************************************/
 void BTM_BlePasskeyReply(const RawAddress& bd_addr, uint8_t res,
                          uint32_t passkey) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::BTM_BlePasskeyReply(bd_addr, res, passkey);
+  }
   tBTM_SEC_DEV_REC* p_dev_rec = btm_find_dev(bd_addr);
   tSMP_STATUS res_smp =
       (res == BTM_SUCCESS) ? SMP_SUCCESS : SMP_PASSKEY_ENTRY_FAIL;
@@ -357,6 +402,9 @@
  *
  ******************************************************************************/
 void BTM_BleConfirmReply(const RawAddress& bd_addr, uint8_t res) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::BTM_BleConfirmReply(bd_addr, res);
+  }
   tBTM_SEC_DEV_REC* p_dev_rec = btm_find_dev(bd_addr);
   tSMP_STATUS res_smp =
       (res == BTM_SUCCESS) ? SMP_SUCCESS : SMP_PASSKEY_ENTRY_FAIL;
@@ -388,6 +436,9 @@
  ******************************************************************************/
 void BTM_BleOobDataReply(const RawAddress& bd_addr, uint8_t res, uint8_t len,
                          uint8_t* p_data) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::BTM_BleOobDataReply(bd_addr, res, len, p_data);
+  }
   tSMP_STATUS res_smp = (res == BTM_SUCCESS) ? SMP_SUCCESS : SMP_OOB_FAIL;
   tBTM_SEC_DEV_REC* p_dev_rec = btm_find_dev(bd_addr);
 
@@ -417,6 +468,10 @@
  ******************************************************************************/
 void BTM_BleSecureConnectionOobDataReply(const RawAddress& bd_addr,
                                          uint8_t* p_c, uint8_t* p_r) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::BTM_BleSecureConnectionOobDataReply(bd_addr, p_c,
+                                                                p_r);
+  }
   tBTM_SEC_DEV_REC* p_dev_rec = btm_find_dev(bd_addr);
 
   BTM_TRACE_DEBUG("%s:", __func__);
@@ -453,6 +508,10 @@
  *
  ******************************************************************************/
 void BTM_BleSetConnScanParams(uint32_t scan_interval, uint32_t scan_window) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::BTM_BleSetConnScanParams(scan_interval,
+                                                     scan_window);
+  }
   tBTM_BLE_CB* p_ble_cb = &btm_cb.ble_ctr_cb;
   bool new_param = false;
 
@@ -498,6 +557,10 @@
 void BTM_BleSetPrefConnParams(const RawAddress& bd_addr, uint16_t min_conn_int,
                               uint16_t max_conn_int, uint16_t slave_latency,
                               uint16_t supervision_tout) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::BTM_BleSetPrefConnParams(
+        bd_addr, min_conn_int, max_conn_int, slave_latency, supervision_tout);
+  }
   tBTM_SEC_DEV_REC* p_dev_rec = btm_find_dev(bd_addr);
 
   BTM_TRACE_API(
@@ -617,6 +680,10 @@
  ******************************************************************************/
 bool BTM_ReadConnectedTransportAddress(RawAddress* remote_bda,
                                        tBT_TRANSPORT transport) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::BTM_ReadConnectedTransportAddress(remote_bda,
+                                                              transport);
+  }
   tBTM_SEC_DEV_REC* p_dev_rec = btm_find_dev(*remote_bda);
 
   /* if no device can be located, return */
@@ -655,6 +722,9 @@
  *
  ******************************************************************************/
 void BTM_BleReceiverTest(uint8_t rx_freq, tBTM_CMPL_CB* p_cmd_cmpl_cback) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::BTM_BleReceiverTest(rx_freq, p_cmd_cmpl_cback);
+  }
   btm_cb.devcb.p_le_test_cmd_cmpl_cb = p_cmd_cmpl_cback;
 
   btsnd_hcic_ble_receiver_test(rx_freq);
@@ -676,6 +746,10 @@
 void BTM_BleTransmitterTest(uint8_t tx_freq, uint8_t test_data_len,
                             uint8_t packet_payload,
                             tBTM_CMPL_CB* p_cmd_cmpl_cback) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::BTM_BleTransmitterTest(
+        tx_freq, test_data_len, packet_payload, p_cmd_cmpl_cback);
+  }
   btm_cb.devcb.p_le_test_cmd_cmpl_cb = p_cmd_cmpl_cback;
   btsnd_hcic_ble_transmitter_test(tx_freq, test_data_len, packet_payload);
 }
@@ -691,6 +765,9 @@
  *
  ******************************************************************************/
 void BTM_BleTestEnd(tBTM_CMPL_CB* p_cmd_cmpl_cback) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::BTM_BleTestEnd(p_cmd_cmpl_cback);
+  }
   btm_cb.devcb.p_le_test_cmd_cmpl_cb = p_cmd_cmpl_cback;
 
   btsnd_hcic_ble_test_end();
@@ -720,6 +797,9 @@
  *
  ******************************************************************************/
 bool BTM_UseLeLink(const RawAddress& bd_addr) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::BTM_UseLeLink(bd_addr);
+  }
   tACL_CONN* p;
   tBT_DEVICE_TYPE dev_type;
   tBLE_ADDR_TYPE addr_type;
@@ -751,6 +831,9 @@
  ******************************************************************************/
 tBTM_STATUS BTM_SetBleDataLength(const RawAddress& bd_addr,
                                  uint16_t tx_pdu_length) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::BTM_SetBleDataLength(bd_addr, tx_pdu_length);
+  }
   tACL_CONN* p_acl = btm_bda_to_acl(bd_addr, BT_TRANSPORT_LE);
   uint16_t tx_time = BTM_BLE_DATA_TX_TIME_MAX_LEGACY;
 
@@ -819,6 +902,9 @@
 void BTM_BleReadPhy(
     const RawAddress& bd_addr,
     base::Callback<void(uint8_t tx_phy, uint8_t rx_phy, uint8_t status)> cb) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::BTM_BleReadPhy(bd_addr, cb);
+  }
   BTM_TRACE_DEBUG("%s", __func__);
 
   tACL_CONN* p_acl = btm_bda_to_acl(bd_addr, BT_TRANSPORT_LE);
@@ -866,6 +952,9 @@
  ******************************************************************************/
 tBTM_STATUS BTM_BleSetDefaultPhy(uint8_t all_phys, uint8_t tx_phys,
                                  uint8_t rx_phys) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::BTM_BleSetDefaultPhy(all_phys, tx_phys, rx_phys);
+  }
   BTM_TRACE_DEBUG("%s: all_phys = 0x%02x, tx_phys = 0x%02x, rx_phys = 0x%02x",
                   __func__, all_phys, tx_phys, rx_phys);
 
@@ -905,6 +994,10 @@
  ******************************************************************************/
 void BTM_BleSetPhy(const RawAddress& bd_addr, uint8_t tx_phys, uint8_t rx_phys,
                    uint16_t phy_options) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::BTM_BleSetPhy(bd_addr, tx_phys, rx_phys,
+                                          phy_options);
+  }
   tACL_CONN* p_acl = btm_bda_to_acl(bd_addr, BT_TRANSPORT_LE);
 
   if (p_acl == NULL) {
@@ -1670,7 +1763,9 @@
   if (p_dev_rec->p_callback && enc_cback) {
     if (encr_enable)
       btm_sec_dev_rec_cback_event(p_dev_rec, BTM_SUCCESS, true);
-    else if (p_dev_rec->role_master)
+    else if (p_dev_rec->sec_flags & ~BTM_SEC_LE_LINK_KEY_KNOWN) {
+      btm_sec_dev_rec_cback_event(p_dev_rec, BTM_FAILED_ON_SECURITY, true);
+    } else if (p_dev_rec->role_master)
       btm_sec_dev_rec_cback_event(p_dev_rec, BTM_ERR_PROCESSING, true);
   }
   /* to notify GATT to send data if any request is pending */
@@ -2036,6 +2131,10 @@
  ******************************************************************************/
 bool BTM_BleDataSignature(const RawAddress& bd_addr, uint8_t* p_text,
                           uint16_t len, BLE_SIGNATURE signature) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::BTM_BleDataSignature(bd_addr, p_text, len,
+                                                 signature);
+  }
   tBTM_SEC_DEV_REC* p_rec = btm_find_dev(bd_addr);
 
   BTM_TRACE_DEBUG("%s", __func__);
@@ -2094,6 +2193,10 @@
  ******************************************************************************/
 bool BTM_BleVerifySignature(const RawAddress& bd_addr, uint8_t* p_orig,
                             uint16_t len, uint32_t counter, uint8_t* p_comp) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::BTM_BleVerifySignature(bd_addr, p_orig, len,
+                                                   counter, p_comp);
+  }
   bool verified = false;
   tBTM_SEC_DEV_REC* p_rec = btm_find_dev(bd_addr);
   uint8_t p_mac[BTM_CMAC_TLEN_SIZE];
@@ -2131,6 +2234,10 @@
 bool BTM_GetLeSecurityState(const RawAddress& bd_addr,
                             uint8_t* p_le_dev_sec_flags,
                             uint8_t* p_le_key_size) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::BTM_GetLeSecurityState(bd_addr, p_le_dev_sec_flags,
+                                                   p_le_key_size);
+  }
   tBTM_SEC_DEV_REC* p_dev_rec;
   uint16_t dev_rec_sec_flags;
 
@@ -2187,6 +2294,9 @@
  *
  ******************************************************************************/
 bool BTM_BleSecurityProcedureIsRunning(const RawAddress& bd_addr) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::BTM_BleSecurityProcedureIsRunning(bd_addr);
+  }
   tBTM_SEC_DEV_REC* p_dev_rec = btm_find_dev(bd_addr);
 
   if (p_dev_rec == NULL) {
@@ -2211,6 +2321,9 @@
  *
  ******************************************************************************/
 extern uint8_t BTM_BleGetSupportedKeySize(const RawAddress& bd_addr) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::BTM_BleGetSupportedKeySize(bd_addr);
+  }
 #if (L2CAP_LE_COC_INCLUDED == TRUE)
   tBTM_SEC_DEV_REC* p_dev_rec = btm_find_dev(bd_addr);
   tBTM_LE_EVT_DATA btm_le_evt_data;
diff --git a/stack/btm/btm_ble_addr.cc b/stack/btm/btm_ble_addr.cc
index 000066e..678dd76 100644
--- a/stack/btm/btm_ble_addr.cc
+++ b/stack/btm/btm_ble_addr.cc
@@ -72,7 +72,7 @@
   p_cb->own_addr_type = BLE_ADDR_RANDOM;
 
   /* start a periodical timer to refresh random addr */
-  uint64_t interval_ms = BTM_BLE_PRIVATE_ADDR_INT_MS;
+  uint64_t interval_ms = btm_get_next_private_addrress_interval_ms();
 #if (BTM_BLE_CONFORMANCE_TESTING == TRUE)
   interval_ms = btm_cb.ble_ctr_cb.rpa_tout * 1000;
 #endif
@@ -93,6 +93,14 @@
       std::move(cb)));
 }
 
+uint64_t btm_get_next_private_addrress_interval_ms() {
+  /* 7 minutes minimum, 15 minutes maximum for random address refreshing */
+  const uint64_t interval_min_ms = (7 * 60 * 1000);
+  const uint64_t interval_random_part_max_ms = (8 * 60 * 1000);
+
+  return interval_min_ms + std::rand() % interval_random_part_max_ms;
+}
+
 /*******************************************************************************
  *
  * Function         btm_gen_non_resolve_paddr_cmpl
diff --git a/stack/btm/btm_ble_adv_filter.cc b/stack/btm/btm_ble_adv_filter.cc
index f69f1a2..708e7c2 100644
--- a/stack/btm/btm_ble_adv_filter.cc
+++ b/stack/btm/btm_ble_adv_filter.cc
@@ -316,7 +316,7 @@
 
   /* send local name filter */
   btu_hcif_send_cmd_with_cb(
-      FROM_HERE, HCI_BLE_ADV_FILTER_OCF, param, len,
+      FROM_HERE, HCI_BLE_ADV_FILTER, param, len,
       base::Bind(&btm_flt_update_cb, BTM_BLE_META_PF_LOCAL_NAME, cb));
 
   memset(&btm_ble_adv_filt_cb.cur_filter_target, 0, sizeof(tBLE_BD_ADDR));
@@ -379,7 +379,7 @@
   }
 
   btu_hcif_send_cmd_with_cb(
-      FROM_HERE, HCI_BLE_ADV_FILTER_OCF, param, len,
+      FROM_HERE, HCI_BLE_ADV_FILTER, param, len,
       base::Bind(&btm_flt_update_cb, BTM_BLE_META_PF_MANU_DATA, cb));
 
   memset(&btm_ble_adv_filt_cb.cur_filter_target, 0, sizeof(tBLE_BD_ADDR));
@@ -417,7 +417,7 @@
   }
 
   btu_hcif_send_cmd_with_cb(
-      FROM_HERE, HCI_BLE_ADV_FILTER_OCF, param, len,
+      FROM_HERE, HCI_BLE_ADV_FILTER, param, len,
       base::Bind(&btm_flt_update_cb, BTM_BLE_META_PF_SRVC_DATA, cb));
 
   memset(&btm_ble_adv_filt_cb.cur_filter_target, 0, sizeof(tBLE_BD_ADDR));
@@ -520,7 +520,7 @@
 
   /* send address filter */
   btu_hcif_send_cmd_with_cb(
-      FROM_HERE, HCI_BLE_ADV_FILTER_OCF, param, len,
+      FROM_HERE, HCI_BLE_ADV_FILTER, param, len,
       base::Bind(&btm_flt_update_cb, BTM_BLE_META_PF_ADDR, cb));
 
   memset(&btm_ble_adv_filt_cb.cur_filter_target, 0, sizeof(tBLE_BD_ADDR));
@@ -591,7 +591,7 @@
   }
 
   /* send UUID filter update */
-  btu_hcif_send_cmd_with_cb(FROM_HERE, HCI_BLE_ADV_FILTER_OCF, param, len,
+  btu_hcif_send_cmd_with_cb(FROM_HERE, HCI_BLE_ADV_FILTER, param, len,
                             base::Bind(&btm_flt_update_cb, evt_type, cb));
   memset(&btm_ble_adv_filt_cb.cur_filter_target, 0, sizeof(tBLE_BD_ADDR));
 }
@@ -716,7 +716,7 @@
   UINT8_TO_STREAM(p, BTM_BLE_PF_LOGIC_OR);
 
   btu_hcif_send_cmd_with_cb(
-      FROM_HERE, HCI_BLE_ADV_FILTER_OCF, param, len,
+      FROM_HERE, HCI_BLE_ADV_FILTER, param, len,
       base::Bind(&btm_flt_update_cb, BTM_BLE_META_PF_FEAT_SEL, cb));
 
   memset(&btm_ble_adv_filt_cb.cur_filter_target, 0, sizeof(tBLE_BD_ADDR));
@@ -801,7 +801,7 @@
             BTM_BLE_ADV_FILT_TRACK_NUM;
 
     btu_hcif_send_cmd_with_cb(
-        FROM_HERE, HCI_BLE_ADV_FILTER_OCF, param, len,
+        FROM_HERE, HCI_BLE_ADV_FILTER, param, len,
         base::Bind(&btm_flt_update_cb, BTM_BLE_META_PF_FEAT_SEL, cb));
   } else if (BTM_BLE_SCAN_COND_DELETE == action) {
     /* select feature based on control block settings */
@@ -811,7 +811,7 @@
     UINT8_TO_STREAM(p, filt_index);
 
     btu_hcif_send_cmd_with_cb(
-        FROM_HERE, HCI_BLE_ADV_FILTER_OCF, param,
+        FROM_HERE, HCI_BLE_ADV_FILTER, param,
         (uint8_t)(BTM_BLE_ADV_FILT_META_HDR_LENGTH),
         base::Bind(&btm_flt_update_cb, BTM_BLE_META_PF_FEAT_SEL, cb));
   } else if (BTM_BLE_SCAN_COND_CLEAR == action) {
@@ -823,7 +823,7 @@
     UINT8_TO_STREAM(p, BTM_BLE_SCAN_COND_CLEAR);
 
     btu_hcif_send_cmd_with_cb(
-        FROM_HERE, HCI_BLE_ADV_FILTER_OCF, param,
+        FROM_HERE, HCI_BLE_ADV_FILTER, param,
         (uint8_t)(BTM_BLE_ADV_FILT_META_HDR_LENGTH - 1),
         base::Bind(&btm_flt_update_cb, BTM_BLE_META_PF_FEAT_SEL, cb));
   }
@@ -874,7 +874,7 @@
   UINT8_TO_STREAM(p, BTM_BLE_META_PF_ENABLE);
   UINT8_TO_STREAM(p, enable);
 
-  btu_hcif_send_cmd_with_cb(FROM_HERE, HCI_BLE_ADV_FILTER_OCF, param,
+  btu_hcif_send_cmd_with_cb(FROM_HERE, HCI_BLE_ADV_FILTER, param,
                             BTM_BLE_PCF_ENABLE_LEN,
                             base::Bind(&enable_cmpl_cback, p_stat_cback));
 }
diff --git a/stack/btm/btm_ble_batchscan.cc b/stack/btm/btm_ble_batchscan.cc
index db82829..3d89358 100644
--- a/stack/btm/btm_ble_batchscan.cc
+++ b/stack/btm/btm_ble_batchscan.cc
@@ -242,7 +242,7 @@
   UINT8_TO_STREAM(pp, BTM_BLE_BATCH_SCAN_READ_RESULTS);
   UINT8_TO_STREAM(pp, scan_mode);
 
-  btu_hcif_send_cmd_with_cb(FROM_HERE, HCI_BLE_BATCH_SCAN_OCF, param, len, cb);
+  btu_hcif_send_cmd_with_cb(FROM_HERE, HCI_BLE_BATCH_SCAN, param, len, cb);
 }
 
 /* read reports. data is accumulated in |data_all|, number of records is
@@ -313,7 +313,7 @@
   UINT8_TO_STREAM(pp, batch_scan_trunc_max);
   UINT8_TO_STREAM(pp, batch_scan_notify_threshold);
 
-  btu_hcif_send_cmd_with_cb(FROM_HERE, HCI_BLE_BATCH_SCAN_OCF, param, len, cb);
+  btu_hcif_send_cmd_with_cb(FROM_HERE, HCI_BLE_BATCH_SCAN, param, len, cb);
 }
 
 /* This function writes the batch scan params in controller */
@@ -338,7 +338,7 @@
   UINT8_TO_STREAM(p, addr_type);
   UINT8_TO_STREAM(p, discard_rule);
 
-  btu_hcif_send_cmd_with_cb(FROM_HERE, HCI_BLE_BATCH_SCAN_OCF, param, len, cb);
+  btu_hcif_send_cmd_with_cb(FROM_HERE, HCI_BLE_BATCH_SCAN, param, len, cb);
 }
 
 /* This function enables the customer specific feature in controller */
@@ -351,7 +351,7 @@
   UINT8_TO_STREAM(p, BTM_BLE_BATCH_SCAN_ENB_DISAB_CUST_FEATURE);
   UINT8_TO_STREAM(p, 0x01 /* enable */);
 
-  btu_hcif_send_cmd_with_cb(FROM_HERE, HCI_BLE_BATCH_SCAN_OCF, param, len, cb);
+  btu_hcif_send_cmd_with_cb(FROM_HERE, HCI_BLE_BATCH_SCAN, param, len, cb);
 }
 
 }  // namespace
diff --git a/stack/btm/btm_ble_bgconn.cc b/stack/btm/btm_ble_bgconn.cc
index 209f49f..dbeb103 100644
--- a/stack/btm/btm_ble_bgconn.cc
+++ b/stack/btm/btm_ble_bgconn.cc
@@ -311,6 +311,14 @@
   BTM_TRACE_DEBUG("%s white_list_size = %d", __func__, white_list_size);
 }
 
+uint8_t BTM_GetWhiteListSize() {
+  const controller_t* controller = controller_get_interface();
+  if (!controller->supports_ble()) {
+    return 0;
+  }
+  return controller->get_ble_white_list_size();
+}
+
 bool BTM_SetLeConnectionModeToFast() {
   VLOG(2) << __func__;
   tBTM_BLE_CB* p_cb = &btm_cb.ble_ctr_cb;
diff --git a/stack/btm/btm_ble_bgconn.h b/stack/btm/btm_ble_bgconn.h
index 45ab2ec..407f513 100644
--- a/stack/btm/btm_ble_bgconn.h
+++ b/stack/btm/btm_ble_bgconn.h
@@ -25,6 +25,9 @@
 /** Removes the device from white list */
 extern void BTM_WhiteListRemove(const RawAddress& address);
 
+/** Get max white list size supports of the Bluetooth controller */
+extern uint8_t BTM_GetWhiteListSize();
+
 /** Clear the whitelist, end any pending whitelist connections */
 extern void BTM_WhiteListClear();
 
@@ -37,4 +40,4 @@
 /* Use slow scan window/interval for LE connection establishment.
  * This does not send any requests to controller, instead it changes the
  * parameters that will be used after next add/remove request */
-extern void BTM_SetLeConnectionModeToSlow();
\ No newline at end of file
+extern void BTM_SetLeConnectionModeToSlow();
diff --git a/stack/btm/btm_ble_cont_energy.cc b/stack/btm/btm_ble_cont_energy.cc
index 4e87308..23fe709 100644
--- a/stack/btm/btm_ble_cont_energy.cc
+++ b/stack/btm/btm_ble_cont_energy.cc
@@ -93,7 +93,7 @@
   }
 
   ble_energy_info_cb.p_ener_cback = p_ener_cback;
-  BTM_VendorSpecificCommand(HCI_BLE_ENERGY_INFO_OCF, 0, NULL,
+  BTM_VendorSpecificCommand(HCI_BLE_ENERGY_INFO, 0, NULL,
                             btm_ble_cont_energy_cmpl_cback);
   return BTM_CMD_STARTED;
 }
diff --git a/stack/btm/btm_ble_gap.cc b/stack/btm/btm_ble_gap.cc
index 05cac15..37cfeb5 100644
--- a/stack/btm/btm_ble_gap.cc
+++ b/stack/btm/btm_ble_gap.cc
@@ -49,6 +49,10 @@
 #include "gattdefs.h"
 #include "l2c_int.h"
 #include "osi/include/log.h"
+#include "common/time_util.h"
+
+#include "main/shim/btm_api.h"
+#include "main/shim/shim.h"
 
 #define BTM_BLE_NAME_SHORT 0x01
 #define BTM_BLE_NAME_CMPL 0x02
@@ -147,11 +151,12 @@
  *  Local functions
  ******************************************************************************/
 static void btm_ble_update_adv_flag(uint8_t flag);
-static void btm_ble_process_adv_pkt_cont(
-    uint16_t evt_type, uint8_t addr_type, const RawAddress& bda,
-    uint8_t primary_phy, uint8_t secondary_phy, uint8_t advertising_sid,
-    int8_t tx_power, int8_t rssi, uint16_t periodic_adv_int, uint8_t data_len,
-    uint8_t* data);
+void btm_ble_process_adv_pkt_cont(uint16_t evt_type, uint8_t addr_type,
+                                  const RawAddress& bda, uint8_t primary_phy,
+                                  uint8_t secondary_phy,
+                                  uint8_t advertising_sid, int8_t tx_power,
+                                  int8_t rssi, uint16_t periodic_adv_int,
+                                  uint8_t data_len, uint8_t* data);
 static uint8_t btm_set_conn_mode_adv_init_addr(tBTM_BLE_INQ_CB* p_cb,
                                                RawAddress& p_peer_addr_ptr,
                                                tBLE_ADDR_TYPE* p_peer_addr_type,
@@ -359,6 +364,10 @@
  * Return           void
  ******************************************************************************/
 void BTM_BleUpdateAdvFilterPolicy(tBTM_BLE_AFP adv_policy) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::BTM_BleUpdateAdvFilterPolicy(adv_policy);
+  }
+
   tBTM_BLE_INQ_CB* p_cb = &btm_cb.ble_ctr_cb.inq_var;
   tBLE_ADDR_TYPE init_addr_type = BLE_ADDR_PUBLIC;
   RawAddress adv_address = RawAddress::kEmpty;
@@ -406,6 +415,11 @@
 tBTM_STATUS BTM_BleObserve(bool start, uint8_t duration,
                            tBTM_INQ_RESULTS_CB* p_results_cb,
                            tBTM_CMPL_CB* p_cmpl_cb) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::BTM_BleObserve(start, duration, p_results_cb,
+                                           p_cmpl_cb);
+  }
+
   tBTM_BLE_INQ_CB* p_inq = &btm_cb.ble_ctr_cb.inq_var;
   tBTM_STATUS status = BTM_WRONG_MODE;
 
@@ -475,7 +489,7 @@
  *
  * Function         btm_vsc_brcm_features_complete
  *
- * Description      Command Complete callback for HCI_BLE_VENDOR_CAP_OCF
+ * Description      Command Complete callback for HCI_BLE_VENDOR_CAP
  *
  * Returns          void
  *
@@ -488,7 +502,7 @@
   BTM_TRACE_DEBUG("%s", __func__);
 
   /* Check status of command complete event */
-  CHECK(p_vcs_cplt_params->opcode == HCI_BLE_VENDOR_CAP_OCF);
+  CHECK(p_vcs_cplt_params->opcode == HCI_BLE_VENDOR_CAP);
   CHECK(p_vcs_cplt_params->param_len > 0);
 
   p = p_vcs_cplt_params->p_param_buf;
@@ -498,7 +512,7 @@
     BTM_TRACE_DEBUG("%s: Status = 0x%02x (0 is success)", __func__, status);
     return;
   }
-  CHECK(p_vcs_cplt_params->param_len > BTM_VSC_CHIP_CAPABILITY_RSP_LEN);
+  CHECK(p_vcs_cplt_params->param_len >= BTM_VSC_CHIP_CAPABILITY_RSP_LEN);
   STREAM_TO_UINT8(btm_cb.cmn_ble_vsc_cb.adv_inst_max, p);
   STREAM_TO_UINT8(btm_cb.cmn_ble_vsc_cb.rpa_offloading, p);
   STREAM_TO_UINT16(btm_cb.cmn_ble_vsc_cb.tot_scan_results_strg, p);
@@ -587,7 +601,7 @@
   BTM_TRACE_DEBUG("BTM_BleReadControllerFeatures");
 
   p_ctrl_le_feature_rd_cmpl_cback = p_vsc_cback;
-  BTM_VendorSpecificCommand(HCI_BLE_VENDOR_CAP_OCF, 0, NULL,
+  BTM_VendorSpecificCommand(HCI_BLE_VENDOR_CAP, 0, NULL,
                             btm_ble_vendor_capability_vsc_cmpl_cback);
 }
 #else
@@ -679,6 +693,9 @@
  *
  ******************************************************************************/
 extern uint8_t BTM_BleMaxMultiAdvInstanceCount(void) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::BTM_BleMaxMultiAdvInstanceCount();
+  }
   return btm_cb.cmn_ble_vsc_cb.adv_inst_max < BTM_BLE_MULTI_ADV_MAX
              ? btm_cb.cmn_ble_vsc_cb.adv_inst_max
              : BTM_BLE_MULTI_ADV_MAX;
@@ -695,6 +712,9 @@
  ******************************************************************************/
 bool BTM_BleLocalPrivacyEnabled(void) {
 #if (BLE_PRIVACY_SPT == TRUE)
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::BTM_BleLocalPrivacyEnabled();
+  }
   return (btm_cb.ble_ctr_cb.privacy_mode != BTM_PRIVACY_NONE);
 #else
   return false;
@@ -1286,6 +1306,7 @@
     /* enable IRK list */
     btm_ble_enable_resolving_list_for_platform(BTM_BLE_RL_SCAN);
 #endif
+    p_ble_cb->inq_var.scan_type = BTM_BLE_SCAN_MODE_ACTI;
     p_ble_cb->inq_var.scan_duplicate_filter = BTM_BLE_DUPLICATE_DISABLE;
     status = btm_ble_start_scan();
   } else if ((p_ble_cb->inq_var.scan_interval !=
@@ -1636,7 +1657,7 @@
   tBTM_INQUIRY_VAR_ST* p_inq = &btm_cb.btm_inq_vars;
 
   /* Save the info */
-  p_cur->inq_result_type = BTM_INQ_RESULT_BLE;
+  p_cur->inq_result_type |= BTM_INQ_RESULT_BLE;
   p_cur->ble_addr_type = addr_type;
   p_cur->rssi = rssi;
   p_cur->ble_primary_phy = primary_phy;
@@ -1873,18 +1894,23 @@
     btm_ble_process_adv_addr(bda, &addr_type);
 
     uint16_t event_type;
-    if (legacy_evt_type == 0x00) {  // ADV_IND;
-      event_type = 0x0013;
-    } else if (legacy_evt_type == 0x01) {  // ADV_DIRECT_IND;
-      event_type = 0x0015;
-    } else if (legacy_evt_type == 0x02) {  // ADV_SCAN_IND;
-      event_type = 0x0012;
-    } else if (legacy_evt_type == 0x03) {  // ADV_NONCONN_IND;
-      event_type = 0x0010;
-    } else if (legacy_evt_type == 0x04) {  // SCAN_RSP;
+    event_type = 1 << BLE_EVT_LEGACY_BIT;
+    if (legacy_evt_type == BTM_BLE_ADV_IND_EVT) {
+      event_type |= (1 << BLE_EVT_CONNECTABLE_BIT)|
+                    (1 << BLE_EVT_SCANNABLE_BIT);
+    } else if (legacy_evt_type == BTM_BLE_ADV_DIRECT_IND_EVT) {
+      event_type |= (1 << BLE_EVT_CONNECTABLE_BIT)|
+                    (1 << BLE_EVT_DIRECTED_BIT);
+    } else if (legacy_evt_type == BTM_BLE_ADV_SCAN_IND_EVT) {
+      event_type |= (1 << BLE_EVT_SCANNABLE_BIT);
+    } else if (legacy_evt_type == BTM_BLE_ADV_NONCONN_IND_EVT) {
+      event_type = (1 << BLE_EVT_LEGACY_BIT);//0x0010;
+    } else if (legacy_evt_type == BTM_BLE_SCAN_RSP_EVT) {  // SCAN_RSP;
       // We can't distinguish between "SCAN_RSP to an ADV_IND", and "SCAN_RSP to
       // an ADV_SCAN_IND", so always return "SCAN_RSP to an ADV_IND"
-      event_type = 0x001B;
+      event_type |= (1 << BLE_EVT_CONNECTABLE_BIT)|
+                    (1 << BLE_EVT_SCANNABLE_BIT)|
+                    (1 << BLE_EVT_SCAN_RESPONSE_BIT);
     } else {
       BTM_TRACE_ERROR(
           "Malformed LE Advertising Report Event - unsupported "
@@ -1904,11 +1930,12 @@
  * This function is called after random address resolution is done, and proceed
  * to process adv packet.
  */
-static void btm_ble_process_adv_pkt_cont(
-    uint16_t evt_type, uint8_t addr_type, const RawAddress& bda,
-    uint8_t primary_phy, uint8_t secondary_phy, uint8_t advertising_sid,
-    int8_t tx_power, int8_t rssi, uint16_t periodic_adv_int, uint8_t data_len,
-    uint8_t* data) {
+void btm_ble_process_adv_pkt_cont(uint16_t evt_type, uint8_t addr_type,
+                                  const RawAddress& bda, uint8_t primary_phy,
+                                  uint8_t secondary_phy,
+                                  uint8_t advertising_sid, int8_t tx_power,
+                                  int8_t rssi, uint16_t periodic_adv_int,
+                                  uint8_t data_len, uint8_t* data) {
   tBTM_INQUIRY_VAR_ST* p_inq = &btm_cb.btm_inq_vars;
   bool update = true;
 
@@ -1975,11 +2002,13 @@
     p_i = btm_inq_db_new(bda);
     if (p_i != NULL) {
       p_inq->inq_cmpl_info.num_resp++;
+      p_i->time_of_resp = bluetooth::common::time_get_os_boottime_ms();
     } else
       return;
   } else if (p_i->inq_count !=
              p_inq->inq_counter) /* first time seen in this inquiry */
   {
+    p_i->time_of_resp = bluetooth::common::time_get_os_boottime_ms();
     p_inq->inq_cmpl_info.num_resp++;
   }
 
diff --git a/stack/btm/btm_ble_int.h b/stack/btm/btm_ble_int.h
index fc1bfcf..b2702fe 100644
--- a/stack/btm/btm_ble_int.h
+++ b/stack/btm/btm_ble_int.h
@@ -139,6 +139,7 @@
 extern tBTM_SEC_DEV_REC* btm_ble_resolve_random_addr(
     const RawAddress& random_bda);
 extern void btm_gen_resolve_paddr_low(const RawAddress& address);
+extern uint64_t btm_get_next_private_addrress_interval_ms();
 
 /*  privacy function */
 #if (BLE_PRIVACY_SPT == TRUE)
diff --git a/stack/btm/btm_ble_int_types.h b/stack/btm/btm_ble_int_types.h
index d2fa554..375c2b3 100644
--- a/stack/btm/btm_ble_int_types.h
+++ b/stack/btm/btm_ble_int_types.h
@@ -119,9 +119,6 @@
 #define BTM_BLE_ISVALID_PARAM(x, min, max) \
   (((x) >= (min) && (x) <= (max)) || ((x) == BTM_BLE_CONN_PARAM_UNDEF))
 
-/* 15 minutes minimum for random address refreshing */
-#define BTM_BLE_PRIVATE_ADDR_INT_MS (15 * 60 * 1000)
-
 typedef struct {
   uint16_t discoverable_mode;
   uint16_t connectable_mode;
diff --git a/stack/btm/btm_ble_multi_adv.cc b/stack/btm/btm_ble_multi_adv.cc
index b425c09..e58d00d 100644
--- a/stack/btm/btm_ble_multi_adv.cc
+++ b/stack/btm/btm_ble_multi_adv.cc
@@ -24,6 +24,7 @@
 #include "ble_advertiser.h"
 #include "ble_advertiser_hci_interface.h"
 #include "btm_int_types.h"
+#include "stack/btm/btm_ble_int.h"
 
 #include <string.h>
 #include <queue>
@@ -170,7 +171,7 @@
                    weak_factory_.GetWeakPtr()));
   }
 
-  ~BleAdvertisingManagerImpl() { adv_inst.clear(); }
+  ~BleAdvertisingManagerImpl() override { adv_inst.clear(); }
 
   void GetOwnAddress(uint8_t inst_id, GetAddressCallback cb) override {
     cb.Run(adv_inst[inst_id].own_address_type, adv_inst[inst_id].own_address);
@@ -257,7 +258,7 @@
               p_inst->own_address = bda;
 
               alarm_set_on_mloop(p_inst->adv_raddr_timer,
-                                 BTM_BLE_PRIVATE_ADDR_INT_MS,
+                                 btm_get_next_private_addrress_interval_ms(),
                                  btm_ble_adv_raddr_timer_timeout, p_inst);
               cb.Run(p_inst->inst_id, BTM_BLE_MULTI_ADV_SUCCESS);
             },
@@ -429,7 +430,8 @@
             c->self->adv_inst[c->inst_id].tx_power = tx_power;
 
             if (c->self->adv_inst[c->inst_id].own_address_type == BLE_ADDR_PUBLIC) {
-              c->self->StartAdvertisingSetAfterAddressPart(std::move(c));
+              auto self = c->self;
+              self->StartAdvertisingSetAfterAddressPart(std::move(c));
               return;
             }
 
@@ -449,7 +451,8 @@
                   return;
                 }
 
-                c->self->StartAdvertisingSetAfterAddressPart(std::move(c));
+                auto self = c->self;
+                self->StartAdvertisingSetAfterAddressPart(std::move(c));
           }, base::Passed(&c)));
         }, base::Passed(&c)));
     }, base::Passed(&c)));
@@ -492,11 +495,11 @@
                           return;
                         }
 
+                        auto self = c->self;
                         if (c->periodic_params.enable) {
-                          c->self->StartAdvertisingSetPeriodicPart(
-                              std::move(c));
+                          self->StartAdvertisingSetPeriodicPart(std::move(c));
                         } else {
-                          c->self->StartAdvertisingSetFinish(std::move(c));
+                          self->StartAdvertisingSetFinish(std::move(c));
                         }
                       },
                       base::Passed(&c)));
@@ -550,7 +553,8 @@
                   return;
                 }
 
-                c->self->StartAdvertisingSetFinish(std::move(c));
+                auto self = c->self;
+                self->StartAdvertisingSetFinish(std::move(c));
 
               }, base::Passed(&c)));
         }, base::Passed(&c)));
@@ -788,8 +792,9 @@
     int length = moreThanOnePacket ? ADV_DATA_LEN_MAX : dataSize - offset;
     int newOffset = offset + length;
 
+    auto dataData = data.data();
     sender.Run(
-        inst_id, operation, length, data.data() + offset,
+        inst_id, operation, length, dataData + offset,
         Bind(&BleAdvertisingManagerImpl::DivideAndSendDataRecursively, false,
              inst_id, std::move(data), newOffset, std::move(done_cb), sender));
   }
diff --git a/stack/btm/btm_dev.cc b/stack/btm/btm_dev.cc
index d5e2850..0d9f737 100644
--- a/stack/btm/btm_dev.cc
+++ b/stack/btm/btm_dev.cc
@@ -36,6 +36,8 @@
 #include "hcidefs.h"
 #include "hcimsgs.h"
 #include "l2c_api.h"
+#include "main/shim/btm_api.h"
+#include "main/shim/shim.h"
 
 /*******************************************************************************
  *
@@ -166,6 +168,10 @@
  * Returns true if removed OK, false if not found or ACL link is active.
  */
 bool BTM_SecDeleteDevice(const RawAddress& bd_addr) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::BTM_SecDeleteDevice(bd_addr);
+  }
+
   if (BTM_IsAclConnectionUp(bd_addr, BT_TRANSPORT_LE) ||
       BTM_IsAclConnectionUp(bd_addr, BT_TRANSPORT_BR_EDR)) {
     BTM_TRACE_WARNING("%s FAILED: Cannot Delete when connection is active",
diff --git a/stack/btm/btm_devctl.cc b/stack/btm/btm_devctl.cc
index bc3fb56..52b66b4 100644
--- a/stack/btm/btm_devctl.cc
+++ b/stack/btm/btm_devctl.cc
@@ -43,6 +43,9 @@
 #include "stack/gatt/connection_manager.h"
 
 #include "gatt_int.h"
+#include "main/shim/btm_api.h"
+#include "main/shim/controller.h"
+#include "main/shim/shim.h"
 
 extern bluetooth::common::MessageLoopThread bt_startup_thread;
 
@@ -196,14 +199,17 @@
 
   l2c_link_processs_num_bufs(controller->get_acl_buffer_count_classic());
 
+  // setup the random number generator
+  std::srand(std::time(nullptr));
+
 #if (BLE_PRIVACY_SPT == TRUE)
   /* Set up the BLE privacy settings */
   if (controller->supports_ble() && controller->supports_ble_privacy() &&
       controller->get_ble_resolving_list_max_size() > 0) {
     btm_ble_resolving_list_init(controller->get_ble_resolving_list_max_size());
     /* set the default random private address timeout */
-    btsnd_hcic_ble_set_rand_priv_addr_timeout(BTM_BLE_PRIVATE_ADDR_INT_MS /
-                                              1000);
+    btsnd_hcic_ble_set_rand_priv_addr_timeout(
+        btm_get_next_private_addrress_interval_ms() / 1000);
   }
 #endif
 
@@ -231,8 +237,13 @@
   /* Clear the callback, so application would not hang on reset */
   btm_db_reset();
 
-  module_start_up_callbacked_wrapper(get_module(CONTROLLER_MODULE),
-                                     &bt_startup_thread, reset_complete);
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    module_start_up_callbacked_wrapper(get_module(GD_CONTROLLER_MODULE),
+                                       &bt_startup_thread, reset_complete);
+  } else {
+    module_start_up_callbacked_wrapper(get_module(CONTROLLER_MODULE),
+                                       &bt_startup_thread, reset_complete);
+  }
 }
 
 /*******************************************************************************
@@ -272,6 +283,7 @@
  ******************************************************************************/
 static void btm_decode_ext_features_page(uint8_t page_number,
                                          const uint8_t* p_features) {
+  CHECK(p_features != nullptr);
   BTM_TRACE_DEBUG("btm_decode_ext_features_page page: %d", page_number);
   switch (page_number) {
     /* Extended (Legacy) Page 0 */
@@ -892,9 +904,6 @@
   length--;
 
   if (sub_event == HCI_VSE_SUBCODE_BQR_SUB_EVT) {
-    LOG(INFO) << __func__
-              << ": BQR sub event, report length: " << std::to_string(length);
-
     if (btm_cb.p_bqr_report_receiver == nullptr) {
       LOG(WARNING) << __func__ << ": No registered report receiver.";
       return;
diff --git a/stack/btm/btm_inq.cc b/stack/btm/btm_inq.cc
index f56369b..800025c 100644
--- a/stack/btm/btm_inq.cc
+++ b/stack/btm/btm_inq.cc
@@ -43,6 +43,8 @@
 #include "btu.h"
 #include "hcidefs.h"
 #include "hcimsgs.h"
+#include "main/shim/btm_api.h"
+#include "main/shim/shim.h"
 
 using bluetooth::Uuid;
 
@@ -116,10 +118,10 @@
 static void btm_initiate_inquiry(tBTM_INQUIRY_VAR_ST* p_inq);
 static tBTM_STATUS btm_set_inq_event_filter(uint8_t filter_cond_type,
                                             tBTM_INQ_FILT_COND* p_filt_cond);
-static void btm_clr_inq_result_flt(void);
+void btm_clr_inq_result_flt(void);
 
 static uint8_t btm_convert_uuid_to_eir_service(uint16_t uuid16);
-static void btm_set_eir_uuid(uint8_t* p_eir, tBTM_INQ_RESULTS* p_results);
+void btm_set_eir_uuid(uint8_t* p_eir, tBTM_INQ_RESULTS* p_results);
 static const uint8_t* btm_eir_get_uuid_list(uint8_t* p_eir, size_t eir_len,
                                             uint8_t uuid_size,
                                             uint8_t* p_num_uuid,
@@ -143,6 +145,10 @@
  ******************************************************************************/
 tBTM_STATUS BTM_SetDiscoverability(uint16_t inq_mode, uint16_t window,
                                    uint16_t interval) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::BTM_SetDiscoverability(inq_mode, window, interval);
+  }
+
   uint8_t scan_mode = 0;
   uint16_t service_class;
   uint8_t* p_cod;
@@ -253,6 +259,10 @@
  *
  ******************************************************************************/
 tBTM_STATUS BTM_SetInquiryScanType(uint16_t scan_type) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::BTM_SetInquiryScanType(scan_type);
+  }
+
   BTM_TRACE_API("BTM_SetInquiryScanType");
   if (scan_type != BTM_SCAN_TYPE_STANDARD &&
       scan_type != BTM_SCAN_TYPE_INTERLACED)
@@ -286,6 +296,10 @@
  *
  ******************************************************************************/
 tBTM_STATUS BTM_SetPageScanType(uint16_t scan_type) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::BTM_SetPageScanType(scan_type);
+  }
+
   BTM_TRACE_API("BTM_SetPageScanType");
   if (scan_type != BTM_SCAN_TYPE_STANDARD &&
       scan_type != BTM_SCAN_TYPE_INTERLACED)
@@ -322,6 +336,10 @@
  *
  ******************************************************************************/
 tBTM_STATUS BTM_SetInquiryMode(uint8_t mode) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::BTM_SetInquiryMode(mode);
+  }
+
   const controller_t* controller = controller_get_interface();
   BTM_TRACE_API("BTM_SetInquiryMode");
   if (mode == BTM_INQ_RESULT_STANDARD) {
@@ -357,6 +375,10 @@
  *
  ******************************************************************************/
 uint16_t BTM_ReadDiscoverability(uint16_t* p_window, uint16_t* p_interval) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::BTM_ReadDiscoverability(p_window, p_interval);
+  }
+
   BTM_TRACE_API("BTM_ReadDiscoverability");
   if (p_window) *p_window = btm_cb.btm_inq_vars.inq_scan_window;
 
@@ -367,121 +389,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_SetPeriodicInquiryMode
- *
- * Description      This function is called to set the device periodic inquiry
- *                  mode. If the duration is zero, the periodic inquiry mode is
- *                  cancelled.
- *
- *                  Note: We currently do not allow concurrent inquiry and
- *                  periodic inquiry.
- *
- * Parameters:      p_inqparms - pointer to the inquiry information
- *                      mode - GENERAL or LIMITED inquiry
- *                      duration - length in 1.28 sec intervals (If '0', the
- *                                 inquiry is CANCELLED)
- *                      max_resps - maximum amount of devices to search for
- *                                  before ending the inquiry
- *                      filter_cond_type - BTM_CLR_INQUIRY_FILTER,
- *                                         BTM_FILTER_COND_DEVICE_CLASS, or
- *                                         BTM_FILTER_COND_BD_ADDR
- *                      filter_cond - value for the filter (based on
- *                                                          filter_cond_type)
- *
- *                  max_delay - maximum amount of time between successive
- *                              inquiries
- *                  min_delay - minimum amount of time between successive
- *                              inquiries
- *                  p_results_cb - callback returning pointer to results
- *                              (tBTM_INQ_RESULTS)
- *
- * Returns          BTM_CMD_STARTED if successfully started
- *                  BTM_ILLEGAL_VALUE if a bad parameter is detected
- *                  BTM_NO_RESOURCES if could not allocate a message buffer
- *                  BTM_SUCCESS - if cancelling the periodic inquiry
- *                  BTM_BUSY - if an inquiry is already active
- *                  BTM_WRONG_MODE if the device is not up.
- *
- ******************************************************************************/
-tBTM_STATUS BTM_SetPeriodicInquiryMode(tBTM_INQ_PARMS* p_inqparms,
-                                       uint16_t max_delay, uint16_t min_delay,
-                                       tBTM_INQ_RESULTS_CB* p_results_cb) {
-  tBTM_STATUS status;
-  tBTM_INQUIRY_VAR_ST* p_inq = &btm_cb.btm_inq_vars;
-
-  BTM_TRACE_API(
-      "BTM_SetPeriodicInquiryMode: mode: %d, dur: %d, rsps: %d, flt: %d, min: "
-      "%d, max: %d",
-      p_inqparms->mode, p_inqparms->duration, p_inqparms->max_resps,
-      p_inqparms->filter_cond_type, min_delay, max_delay);
-
-  /*** Make sure the device is ready ***/
-  if (!BTM_IsDeviceUp()) return (BTM_WRONG_MODE);
-
-  /* Only one active inquiry is allowed in this implementation.
-     Also do not allow an inquiry if the inquiry filter is being updated */
-  if (p_inq->inq_active || p_inq->inqfilt_active) return (BTM_BUSY);
-
-  /* If illegal parameters return false */
-  if (p_inqparms->mode != BTM_GENERAL_INQUIRY &&
-      p_inqparms->mode != BTM_LIMITED_INQUIRY)
-    return (BTM_ILLEGAL_VALUE);
-
-  /* Verify the parameters for this command */
-  if (p_inqparms->duration < BTM_MIN_INQUIRY_LEN ||
-      p_inqparms->duration > BTM_MAX_INQUIRY_LENGTH ||
-      min_delay <= p_inqparms->duration ||
-      min_delay < BTM_PER_INQ_MIN_MIN_PERIOD ||
-      min_delay > BTM_PER_INQ_MAX_MIN_PERIOD || max_delay <= min_delay ||
-      max_delay < BTM_PER_INQ_MIN_MAX_PERIOD)
-  /*       max_delay > BTM_PER_INQ_MAX_MAX_PERIOD)*/
-  /*  BTM_PER_INQ_MAX_MAX_PERIOD set to 1's in all bits. Condition resulting in
-     false always*/
-  {
-    return (BTM_ILLEGAL_VALUE);
-  }
-
-  /* Save the inquiry parameters to be used upon the completion of
-   * setting/clearing the inquiry filter */
-  p_inq->inqparms = *p_inqparms;
-  p_inq->per_min_delay = min_delay;
-  p_inq->per_max_delay = max_delay;
-  p_inq->inq_cmpl_info.num_resp = 0; /* Clear the results counter */
-  p_inq->p_inq_results_cb = p_results_cb;
-
-  p_inq->inq_active = (uint8_t)(
-      (p_inqparms->mode == BTM_LIMITED_INQUIRY)
-          ? (BTM_LIMITED_INQUIRY_ACTIVE | BTM_PERIODIC_INQUIRY_ACTIVE)
-          : (BTM_GENERAL_INQUIRY_ACTIVE | BTM_PERIODIC_INQUIRY_ACTIVE));
-
-  /* If a filter is specified, then save it for later and clear the current
-     filter.
-     The setting of the filter is done upon completion of clearing of the
-     previous
-     filter.
-  */
-  if (p_inqparms->filter_cond_type != BTM_CLR_INQUIRY_FILTER) {
-    p_inq->state = BTM_INQ_CLR_FILT_STATE;
-    p_inqparms->filter_cond_type = BTM_CLR_INQUIRY_FILTER;
-  } else /* The filter is not being used so simply clear it; the inquiry can
-            start after this operation */
-    p_inq->state = BTM_INQ_SET_FILT_STATE;
-
-  /* Before beginning the inquiry the current filter must be cleared, so
-   * initiate the command */
-  status = btm_set_inq_event_filter(p_inqparms->filter_cond_type,
-                                    &p_inqparms->filter_cond);
-  if (status != BTM_CMD_STARTED) {
-    /* If set filter command is not succesful reset the state */
-    p_inq->p_inq_results_cb = NULL;
-    p_inq->state = BTM_INQ_INACTIVE_STATE;
-  }
-
-  return (status);
-}
-
-/*******************************************************************************
- *
  * Function         BTM_CancelPeriodicInquiry
  *
  * Description      This function cancels a periodic inquiry
@@ -493,6 +400,10 @@
  *
  ******************************************************************************/
 tBTM_STATUS BTM_CancelPeriodicInquiry(void) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::BTM_CancelPeriodicInquiry();
+  }
+
   tBTM_INQUIRY_VAR_ST* p_inq = &btm_cb.btm_inq_vars;
   tBTM_STATUS status = BTM_SUCCESS;
   BTM_TRACE_API("BTM_CancelPeriodicInquiry called");
@@ -535,6 +446,10 @@
  ******************************************************************************/
 tBTM_STATUS BTM_SetConnectability(uint16_t page_mode, uint16_t window,
                                   uint16_t interval) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::BTM_SetConnectability(page_mode, window, interval);
+  }
+
   uint8_t scan_mode = 0;
   tBTM_INQUIRY_VAR_ST* p_inq = &btm_cb.btm_inq_vars;
 
@@ -609,6 +524,10 @@
  *
  ******************************************************************************/
 uint16_t BTM_ReadConnectability(uint16_t* p_window, uint16_t* p_interval) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::BTM_ReadConnectability(p_window, p_interval);
+  }
+
   BTM_TRACE_API("BTM_ReadConnectability");
   if (p_window) *p_window = btm_cb.btm_inq_vars.page_scan_window;
 
@@ -631,6 +550,10 @@
  *
  ******************************************************************************/
 uint16_t BTM_IsInquiryActive(void) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::BTM_IsInquiryActive();
+  }
+
   BTM_TRACE_API("BTM_IsInquiryActive");
 
   return (btm_cb.btm_inq_vars.inq_active);
@@ -648,6 +571,10 @@
  *
  ******************************************************************************/
 tBTM_STATUS BTM_CancelInquiry(void) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::BTM_CancelInquiry();
+  }
+
   tBTM_STATUS status = BTM_SUCCESS;
   tBTM_INQUIRY_VAR_ST* p_inq = &btm_cb.btm_inq_vars;
   BTM_TRACE_API("BTM_CancelInquiry called");
@@ -734,6 +661,11 @@
                              tBTM_CMPL_CB* p_cmpl_cb) {
   tBTM_INQUIRY_VAR_ST* p_inq = &btm_cb.btm_inq_vars;
 
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::BTM_StartInquiry(p_inqparms, p_results_cb,
+                                             p_cmpl_cb);
+  }
+
   BTM_TRACE_API("BTM_StartInquiry: mode: %d, dur: %d, rsps: %d, flt: %d",
                 p_inqparms->mode, p_inqparms->duration, p_inqparms->max_resps,
                 p_inqparms->filter_cond_type);
@@ -741,18 +673,8 @@
   /* Only one active inquiry is allowed in this implementation.
      Also do not allow an inquiry if the inquiry filter is being updated */
   if (p_inq->inq_active || p_inq->inqfilt_active) {
-    /*check if LE observe is already running*/
-    if (p_inq->scan_type == INQ_LE_OBSERVE &&
-        p_inq->p_inq_ble_results_cb != nullptr) {
-      BTM_TRACE_API("BTM_StartInquiry: LE observe in progress");
-      p_inq->scan_type = INQ_GENERAL;
-      p_inq->inq_active = BTM_INQUIRY_INACTIVE;
-      btm_cb.ble_ctr_cb.inq_var.scan_type = BTM_BLE_SCAN_MODE_NONE;
-      btm_send_hci_scan_enable(BTM_BLE_SCAN_DISABLE, BTM_BLE_DUPLICATE_ENABLE);
-    } else {
-      LOG(ERROR) << __func__ << ": BTM_BUSY";
-      return (BTM_BUSY);
-    }
+    LOG(ERROR) << __func__ << ": BTM_BUSY";
+    return (BTM_BUSY);
   } else {
     p_inq->scan_type = INQ_GENERAL;
   }
@@ -879,6 +801,11 @@
 tBTM_STATUS BTM_ReadRemoteDeviceName(const RawAddress& remote_bda,
                                      tBTM_CMPL_CB* p_cb,
                                      tBT_TRANSPORT transport) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::BTM_ReadRemoteDeviceName(remote_bda, p_cb,
+                                                     transport);
+  }
+
   VLOG(1) << __func__ << ": bd addr " << remote_bda;
   /* Use LE transport when LE is the only available option */
   if (transport == BT_TRANSPORT_LE) {
@@ -1028,29 +955,6 @@
 }
 
 /*******************************************************************************
- *
- * Function         BTM_ReadInquiryRspTxPower
- *
- * Description      This command will read the inquiry Transmit Power level used
- *                  to transmit the FHS and EIR data packets. This can be used
- *                  directly in the Tx Power Level EIR data type.
- *
- * Returns          BTM_SUCCESS if successful
- *
- ******************************************************************************/
-tBTM_STATUS BTM_ReadInquiryRspTxPower(tBTM_CMPL_CB* p_cb) {
-  if (btm_cb.devcb.p_inq_tx_power_cmpl_cb) return (BTM_BUSY);
-
-  btm_cb.devcb.p_inq_tx_power_cmpl_cb = p_cb;
-  alarm_set_on_mloop(btm_cb.devcb.read_inq_tx_power_timer,
-                     BTM_INQ_REPLY_TIMEOUT_MS, btm_read_inq_tx_power_timeout,
-                     NULL);
-
-  btsnd_hcic_read_inq_tx_power();
-  return (BTM_CMD_STARTED);
-}
-
-/*******************************************************************************
  *******************************************************************************
  *                                                                            **
  *                    BTM Internal Inquiry Functions                          **
@@ -1244,7 +1148,7 @@
  * Returns          true if found, else false (new entry)
  *
  ******************************************************************************/
-static void btm_clr_inq_result_flt(void) {
+void btm_clr_inq_result_flt(void) {
   tBTM_INQUIRY_VAR_ST* p_inq = &btm_cb.btm_inq_vars;
 
   osi_free_and_reset((void**)&p_inq->p_bd_db);
@@ -1675,7 +1579,6 @@
     }
 
     p_i = btm_inq_db_find(bda);
-
     /* Only process the num_resp is smaller than max_resps.
        If results are queued to BTU task while canceling inquiry,
        or when more than one result is in this response, > max_resp
@@ -1695,8 +1598,8 @@
 
     /* Check if this address has already been processed for this inquiry */
     if (btm_inq_find_bdaddr(bda)) {
-      /* BTM_TRACE_DEBUG("BDA seen before [%02x%02x %02x%02x %02x%02x]",
-                      bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]);*/
+      /* BTM_TRACE_DEBUG("BDA seen before %s", bda.ToString().c_str()); */
+
       /* By default suppose no update needed */
       i_rssi = (int8_t)rssi;
 
@@ -1763,7 +1666,7 @@
       if (p_i->inq_count != p_inq->inq_counter)
         p_inq->inq_cmpl_info.num_resp++; /* A new response was found */
 
-      p_cur->inq_result_type = BTM_INQ_RESULT_BR;
+      p_cur->inq_result_type |= BTM_INQ_RESULT_BR;
       if (p_i->inq_count != p_inq->inq_counter) {
         p_cur->device_type = BT_DEVICE_TYPE_BREDR;
         p_i->scan_rsp = false;
@@ -1803,9 +1706,12 @@
         p_eir_data = NULL;
 
       /* If a callback is registered, call it with the results */
-      if (p_inq_results_cb)
+      if (p_inq_results_cb) {
         (p_inq_results_cb)((tBTM_INQ_RESULTS*)p_cur, p_eir_data,
                            HCI_EXT_INQ_RESPONSE_LEN);
+      } else {
+        BTM_TRACE_DEBUG("No callback is registered");
+      }
     }
   }
 }
@@ -1866,13 +1772,6 @@
 
   p_inq->inqparms.mode &= ~(mode);
 
-  if (p_inq->scan_type == INQ_LE_OBSERVE && !p_inq->inq_active) {
-    /*end of LE observe*/
-    p_inq->p_inq_ble_results_cb = NULL;
-    p_inq->p_inq_ble_cmpl_cb = NULL;
-    p_inq->scan_type = INQ_NONE;
-  }
-
 #if (BTM_INQ_DEBUG == TRUE)
   BTM_TRACE_DEBUG(
       "btm_process_inq_complete inq_active:0x%x state:%d inqfilt_active:%d",
@@ -1919,12 +1818,6 @@
       p_inq->scan_type == INQ_GENERAL)  // this inquiry is complete
   {
     p_inq->scan_type = INQ_NONE;
-    /* check if the LE observe is pending */
-    if (p_inq->p_inq_ble_results_cb != NULL) {
-      BTM_TRACE_DEBUG("BTM Inq Compl: resuming a pending LE scan");
-      BTM_BleObserve(1, 0, p_inq->p_inq_ble_results_cb,
-                     p_inq->p_inq_ble_cmpl_cb);
-    }
   }
 #if (BTM_INQ_DEBUG == TRUE)
   BTM_TRACE_DEBUG("inq_active:0x%x state:%d inqfilt_active:%d",
@@ -1998,7 +1891,7 @@
 
       /* If the database entry exists for the device, use its clock offset */
       tINQ_DB_ENT* p_i = btm_inq_db_find(remote_bda);
-      if (p_i) {
+      if (p_i && (p_i->inq_info.results.inq_result_type & BTM_INQ_RESULT_BR)) {
         tBTM_INQ_INFO* p_cur = &p_i->inq_info;
         btsnd_hcic_rmt_name_req(
             remote_bda, p_cur->results.page_scan_rep_mode,
diff --git a/stack/btm/btm_int_types.h b/stack/btm/btm_int_types.h
index a0460b1..1cd6cfa 100644
--- a/stack/btm/btm_int_types.h
+++ b/stack/btm/btm_int_types.h
@@ -223,7 +223,7 @@
   bool scan_rsp;
 } tINQ_DB_ENT;
 
-enum { INQ_NONE, INQ_LE_OBSERVE, INQ_GENERAL };
+enum { INQ_NONE, INQ_GENERAL };
 typedef uint8_t tBTM_INQ_TYPE;
 
 typedef struct {
@@ -252,10 +252,6 @@
 
   tBTM_CMPL_CB* p_inq_cmpl_cb;
   tBTM_INQ_RESULTS_CB* p_inq_results_cb;
-  tBTM_CMPL_CB*
-      p_inq_ble_cmpl_cb; /*completion callback exclusively for LE Observe*/
-  tBTM_INQ_RESULTS_CB*
-      p_inq_ble_results_cb; /*results callback exclusively for LE observe*/
   tBTM_CMPL_CB* p_inqfilter_cmpl_cb; /* Called (if not NULL) after inquiry
                                         filter completed */
   uint32_t inq_counter; /* Counter incremented each time an inquiry completes */
diff --git a/stack/btm/btm_sco.cc b/stack/btm/btm_sco.cc
index d8d2def..b8e0c9a 100644
--- a/stack/btm/btm_sco.cc
+++ b/stack/btm/btm_sco.cc
@@ -1016,152 +1016,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_SetScoPacketTypes
- *
- * Description      This function is called to set the packet types used for
- *                  a specific SCO connection,
- *
- * Parameters       pkt_types - One or more of the following
- *                  BTM_SCO_PKT_TYPES_MASK_HV1
- *                  BTM_SCO_PKT_TYPES_MASK_HV2
- *                  BTM_SCO_PKT_TYPES_MASK_HV3
- *                  BTM_SCO_PKT_TYPES_MASK_EV3
- *                  BTM_SCO_PKT_TYPES_MASK_EV4
- *                  BTM_SCO_PKT_TYPES_MASK_EV5
- *                  BTM_SCO_PKT_TYPES_MASK_NO_2_EV3
- *                  BTM_SCO_PKT_TYPES_MASK_NO_3_EV3
- *                  BTM_SCO_PKT_TYPES_MASK_NO_2_EV5
- *                  BTM_SCO_PKT_TYPES_MASK_NO_3_EV5
- *
- *                  BTM_SCO_LINK_ALL_MASK   - enables all supported types
- *
- * Returns          status of the operation
- *
- ******************************************************************************/
-tBTM_STATUS BTM_SetScoPacketTypes(uint16_t sco_inx, uint16_t pkt_types) {
-#if (BTM_MAX_SCO_LINKS > 0)
-  tBTM_CHG_ESCO_PARAMS parms;
-  tSCO_CONN* p;
-
-  /* Validity check */
-  if (sco_inx >= BTM_MAX_SCO_LINKS) return (BTM_UNKNOWN_ADDR);
-
-  p = &btm_cb.sco_cb.sco_db[sco_inx];
-  parms.packet_types = pkt_types;
-
-  /* Keep the other parameters the same for SCO */
-  parms.max_latency_ms = p->esco.setup.max_latency_ms;
-  parms.retransmission_effort = p->esco.setup.retransmission_effort;
-
-  return (BTM_ChangeEScoLinkParms(sco_inx, &parms));
-#else
-  return (BTM_UNKNOWN_ADDR);
-#endif
-}
-
-/*******************************************************************************
- *
- * Function         BTM_ReadScoPacketTypes
- *
- * Description      This function is read the packet types used for a specific
- *                  SCO connection.
- *
- * Returns          Packet types supported for the connection
- *                  One or more of the following (bitmask):
- *                  BTM_SCO_PKT_TYPES_MASK_HV1
- *                  BTM_SCO_PKT_TYPES_MASK_HV2
- *                  BTM_SCO_PKT_TYPES_MASK_HV3
- *                  BTM_SCO_PKT_TYPES_MASK_EV3
- *                  BTM_SCO_PKT_TYPES_MASK_EV4
- *                  BTM_SCO_PKT_TYPES_MASK_EV5
- *                  BTM_SCO_PKT_TYPES_MASK_NO_2_EV3
- *                  BTM_SCO_PKT_TYPES_MASK_NO_3_EV3
- *                  BTM_SCO_PKT_TYPES_MASK_NO_2_EV5
- *                  BTM_SCO_PKT_TYPES_MASK_NO_3_EV5
- *
- ******************************************************************************/
-uint16_t BTM_ReadScoPacketTypes(uint16_t sco_inx) {
-#if (BTM_MAX_SCO_LINKS > 0)
-  tSCO_CONN* p = &btm_cb.sco_cb.sco_db[sco_inx];
-
-  /* Validity check */
-  if ((sco_inx < BTM_MAX_SCO_LINKS) && (p->state == SCO_ST_CONNECTED))
-    return (p->esco.setup.packet_types);
-  else
-    return (0);
-#else
-  return (0);
-#endif
-}
-
-/*******************************************************************************
- *
- * Function         BTM_ReadScoDiscReason
- *
- * Description      This function is returns the reason why an (e)SCO connection
- *                  has been removed. It contains the value until read, or until
- *                  another (e)SCO connection has disconnected.
- *
- * Returns          HCI reason or BTM_INVALID_SCO_DISC_REASON if not set.
- *
- ******************************************************************************/
-uint16_t BTM_ReadScoDiscReason(void) {
-  uint16_t res = btm_cb.sco_cb.sco_disc_reason;
-  btm_cb.sco_cb.sco_disc_reason = BTM_INVALID_SCO_DISC_REASON;
-  return (res);
-}
-
-/*******************************************************************************
- *
- * Function         BTM_ReadDeviceScoPacketTypes
- *
- * Description      This function is read the SCO packet types that
- *                  the device supports.
- *
- * Returns          Packet types supported by the device.
- *                  One or more of the following (bitmask):
- *                  BTM_SCO_PKT_TYPES_MASK_HV1
- *                  BTM_SCO_PKT_TYPES_MASK_HV2
- *                  BTM_SCO_PKT_TYPES_MASK_HV3
- *                  BTM_SCO_PKT_TYPES_MASK_EV3
- *                  BTM_SCO_PKT_TYPES_MASK_EV4
- *                  BTM_SCO_PKT_TYPES_MASK_EV5
- *                  BTM_SCO_PKT_TYPES_MASK_NO_2_EV3
- *                  BTM_SCO_PKT_TYPES_MASK_NO_3_EV3
- *                  BTM_SCO_PKT_TYPES_MASK_NO_2_EV5
- *                  BTM_SCO_PKT_TYPES_MASK_NO_3_EV5
- *
- ******************************************************************************/
-uint16_t BTM_ReadDeviceScoPacketTypes(void) {
-  return (btm_cb.btm_sco_pkt_types_supported);
-}
-
-/*******************************************************************************
- *
- * Function         BTM_ReadScoHandle
- *
- * Description      This function is used to read the HCI handle used for a
- *                  specific SCO connection,
- *
- * Returns          handle for the connection, or 0xFFFF if invalid SCO index.
- *
- ******************************************************************************/
-uint16_t BTM_ReadScoHandle(uint16_t sco_inx) {
-#if (BTM_MAX_SCO_LINKS > 0)
-  tSCO_CONN* p = &btm_cb.sco_cb.sco_db[sco_inx];
-
-  /* Validity check */
-  if ((sco_inx < BTM_MAX_SCO_LINKS) && (p->state == SCO_ST_CONNECTED))
-    return (p->hci_handle);
-  else
-    return (BTM_INVALID_HCI_HANDLE);
-#else
-  return (BTM_INVALID_HCI_HANDLE);
-#endif
-}
-
-/*******************************************************************************
- *
  * Function         BTM_ReadScoBdAddr
  *
  * Description      This function is read the remote BD Address for a specific
@@ -1260,52 +1114,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_ReadEScoLinkParms
- *
- * Description      This function returns the current eSCO link parameters for
- *                  the specified handle.  This can be called anytime a
- *                  connection is active, but is typically called after
- *                  receiving the SCO opened callback.
- *
- *                  Note: If called over a 1.1 controller, only the packet types
- *                        field has meaning.
- *
- * Returns          BTM_SUCCESS if returned data is valid connection.
- *                  BTM_WRONG_MODE if no connection with a peer device or bad
- *                                 sco_inx.
- *
- ******************************************************************************/
-tBTM_STATUS BTM_ReadEScoLinkParms(uint16_t sco_inx, tBTM_ESCO_DATA* p_parms) {
-#if (BTM_MAX_SCO_LINKS > 0)
-  uint8_t index;
-
-  BTM_TRACE_API("%s: -> sco_inx 0x%04x", __func__, sco_inx);
-
-  if (sco_inx < BTM_MAX_SCO_LINKS &&
-      btm_cb.sco_cb.sco_db[sco_inx].state >= SCO_ST_CONNECTED) {
-    *p_parms = btm_cb.sco_cb.sco_db[sco_inx].esco.data;
-    return (BTM_SUCCESS);
-  }
-
-  if (sco_inx == BTM_FIRST_ACTIVE_SCO_INDEX) {
-    for (index = 0; index < BTM_MAX_SCO_LINKS; index++) {
-      if (btm_cb.sco_cb.sco_db[index].state >= SCO_ST_CONNECTED) {
-        BTM_TRACE_API("%s: the first active SCO index is %d", __func__, index);
-        *p_parms = btm_cb.sco_cb.sco_db[index].esco.data;
-        return (BTM_SUCCESS);
-      }
-    }
-  }
-
-#endif
-
-  BTM_TRACE_API("BTM_ReadEScoLinkParms cannot find the SCO index!");
-  memset(p_parms, 0, sizeof(tBTM_ESCO_DATA));
-  return (BTM_WRONG_MODE);
-}
-
-/*******************************************************************************
- *
  * Function         BTM_ChangeEScoLinkParms
  *
  * Description      This function requests renegotiation of the parameters on
diff --git a/stack/btm/btm_sec.cc b/stack/btm/btm_sec.cc
index 32e4b4e..5d7d0f9 100644
--- a/stack/btm/btm_sec.cc
+++ b/stack/btm/btm_sec.cc
@@ -34,6 +34,8 @@
 #include "common/metrics.h"
 #include "common/time_util.h"
 #include "device/include/controller.h"
+#include "main/shim/btm_api.h"
+#include "main/shim/shim.h"
 #include "osi/include/log.h"
 #include "osi/include/osi.h"
 
@@ -233,6 +235,9 @@
  *
  ******************************************************************************/
 bool BTM_SecRegister(const tBTM_APPL_INFO* p_cb_info) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::BTM_SecRegister(p_cb_info);
+  }
 
   BTM_TRACE_EVENT("%s application registered", __func__);
 
@@ -259,23 +264,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_SecRegisterLinkKeyNotificationCallback
- *
- * Description      Application manager calls this function to register for
- *                  link key notification.  When there is nobody registered
- *                  we should avoid changing link key
- *
- * Returns          true if registered OK, else false
- *
- ******************************************************************************/
-bool BTM_SecRegisterLinkKeyNotificationCallback(
-    tBTM_LINK_KEY_CALLBACK* p_callback) {
-  btm_cb.api.p_link_key_callback = p_callback;
-  return true;
-}
-
-/*******************************************************************************
- *
  * Function         BTM_SecAddRmtNameNotifyCallback
  *
  * Description      Any profile can register to be notified when name of the
@@ -416,28 +404,6 @@
   btm_cb.connect_only_paired = connect_only_paired;
 }
 
-/*******************************************************************************
- *
- * Function         BTM_SetSecureConnectionsOnly
- *
- * Description      Enable or disable default treatment for Mode 4 Level 0
- *                  services
- *
- * Parameter        secure_connections_only_mode -
- *                  true means that the device should treat Mode 4 Level 0
- *                       services as services of other levels.
- *                  false means that the device should provide default
- *                        treatment for Mode 4 Level 0 services.
- *
- * Returns          void
- *
- ******************************************************************************/
-void BTM_SetSecureConnectionsOnly(bool secure_connections_only_mode) {
-  BTM_TRACE_API("%s: Mode : %u", __func__, secure_connections_only_mode);
-
-  btm_cb.devcb.secure_connections_only = secure_connections_only_mode;
-  btm_cb.security_mode = BTM_SEC_MODE_SC;
-}
 #define BTM_NO_AVAIL_SEC_SERVICES ((uint16_t)0xffff)
 
 /*******************************************************************************
@@ -1035,7 +1001,7 @@
 
 /*******************************************************************************
  *
- * Function         BTM_SecBondByTransport
+ * Function         BTM_SecBond
  *
  * Description      This function is called to perform bonding with peer device.
  *                  If the connection is already up, but not secure, pairing
@@ -1050,11 +1016,18 @@
  *
  *  Note: After 2.1 parameters are not used and preserved here not to change API
  ******************************************************************************/
-tBTM_STATUS BTM_SecBondByTransport(const RawAddress& bd_addr,
-                                   tBT_TRANSPORT transport, uint8_t pin_len,
-                                   uint8_t* p_pin, uint32_t trusted_mask[]) {
+tBTM_STATUS BTM_SecBond(const RawAddress& bd_addr, tBLE_ADDR_TYPE addr_type,
+                        tBT_TRANSPORT transport, uint8_t pin_len,
+                        uint8_t* p_pin, uint32_t trusted_mask[]) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::BTM_SecBond(bd_addr, addr_type, transport, pin_len,
+                                        p_pin, trusted_mask);
+  }
+
+  if (transport == BT_TRANSPORT_INVALID)
+    transport = BTM_UseLeLink(bd_addr) ? BT_TRANSPORT_LE : BT_TRANSPORT_BR_EDR;
+
   tBT_DEVICE_TYPE dev_type;
-  tBLE_ADDR_TYPE addr_type;
 
   BTM_ReadDevInfo(bd_addr, &dev_type, &addr_type);
   /* LE device, do SMP pairing */
@@ -1069,29 +1042,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_SecBond
- *
- * Description      This function is called to perform bonding with peer device.
- *                  If the connection is already up, but not secure, pairing
- *                  is attempted.  If already paired BTM_SUCCESS is returned.
- *
- * Parameters:      bd_addr      - Address of the device to bond
- *                  pin_len      - length in bytes of the PIN Code
- *                  p_pin        - pointer to array with the PIN Code
- *                  trusted_mask - bitwise OR of trusted services
- *                                 (array of uint32_t)
- *
- *  Note: After 2.1 parameters are not used and preserved here not to change API
- ******************************************************************************/
-tBTM_STATUS BTM_SecBond(const RawAddress& bd_addr, uint8_t pin_len,
-                        uint8_t* p_pin, uint32_t trusted_mask[]) {
-  tBT_TRANSPORT transport = BT_TRANSPORT_BR_EDR;
-  if (BTM_UseLeLink(bd_addr)) transport = BT_TRANSPORT_LE;
-  return btm_sec_bond_by_transport(bd_addr, transport, pin_len, p_pin,
-                                   trusted_mask);
-}
-/*******************************************************************************
- *
  * Function         BTM_SecBondCancel
  *
  * Description      This function is called to cancel ongoing bonding process
@@ -1102,6 +1052,10 @@
  *
  ******************************************************************************/
 tBTM_STATUS BTM_SecBondCancel(const RawAddress& bd_addr) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::BTM_SecBondCancel(bd_addr);
+  }
+
   tBTM_SEC_DEV_REC* p_dev_rec;
 
   BTM_TRACE_API("BTM_SecBondCancel()  State: %s flags:0x%x",
@@ -1170,30 +1124,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_SecGetDeviceLinkKey
- *
- * Description      This function is called to obtain link key for the device
- *                  it returns BTM_SUCCESS if link key is available, or
- *                  BTM_UNKNOWN_ADDR if Security Manager does not know about
- *                  the device or device record does not contain link key info
- *
- * Parameters:      bd_addr      - Address of the device
- *                  link_key     - Link Key is copied into this pointer
- *
- ******************************************************************************/
-tBTM_STATUS BTM_SecGetDeviceLinkKey(const RawAddress& bd_addr,
-                                    LinkKey* link_key) {
-  tBTM_SEC_DEV_REC* p_dev_rec;
-  p_dev_rec = btm_find_dev(bd_addr);
-  if ((p_dev_rec != NULL) && (p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN)) {
-    *link_key = p_dev_rec->link_key;
-    return (BTM_SUCCESS);
-  }
-  return (BTM_UNKNOWN_ADDR);
-}
-
-/*******************************************************************************
- *
  * Function         BTM_SecGetDeviceLinkKeyType
  *
  * Description      This function is called to obtain link key type for the
@@ -1589,90 +1519,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_BuildOobData
- *
- * Description      This function is called to build the OOB data payload to
- *                  be sent over OOB (non-Bluetooth) link
- *
- * Parameters:      p_data  - the location for OOB data
- *                  max_len - p_data size.
- *                  c       - simple pairing Hash C.
- *                  r       - simple pairing Randomizer  C.
- *                  name_len- 0, local device name would not be included.
- *                            otherwise, the local device name is included for
- *                            up to this specified length
- *
- * Returns          Number of bytes in p_data.
- *
- ******************************************************************************/
-uint16_t BTM_BuildOobData(uint8_t* p_data, uint16_t max_len, const Octet16& c,
-                          const Octet16& r, uint8_t name_len) {
-  uint8_t* p = p_data;
-  uint16_t len = 0;
-  uint16_t name_size;
-  uint8_t name_type = BTM_EIR_SHORTENED_LOCAL_NAME_TYPE;
-
-  if (p_data && max_len >= BTM_OOB_MANDATORY_SIZE) {
-    /* add mandatory part */
-    UINT16_TO_STREAM(p, len);
-    BDADDR_TO_STREAM(p, *controller_get_interface()->get_address());
-
-    len = BTM_OOB_MANDATORY_SIZE;
-    max_len -= len;
-
-    /* now optional part */
-
-    /* add Hash C */
-    uint16_t delta = BTM_OOB_HASH_C_SIZE + 2;
-    if (max_len >= delta) {
-      *p++ = BTM_OOB_HASH_C_SIZE + 1;
-      *p++ = BTM_EIR_OOB_SSP_HASH_C_TYPE;
-      ARRAY_TO_STREAM(p, c.data(), BTM_OOB_HASH_C_SIZE);
-      len += delta;
-      max_len -= delta;
-    }
-
-    /* add Rand R */
-    delta = BTM_OOB_RAND_R_SIZE + 2;
-    if (max_len >= delta) {
-      *p++ = BTM_OOB_RAND_R_SIZE + 1;
-      *p++ = BTM_EIR_OOB_SSP_RAND_R_TYPE;
-      ARRAY_TO_STREAM(p, r.data(), BTM_OOB_RAND_R_SIZE);
-      len += delta;
-      max_len -= delta;
-    }
-
-    /* add class of device */
-    delta = BTM_OOB_COD_SIZE + 2;
-    if (max_len >= delta) {
-      *p++ = BTM_OOB_COD_SIZE + 1;
-      *p++ = BTM_EIR_OOB_COD_TYPE;
-      DEVCLASS_TO_STREAM(p, btm_cb.devcb.dev_class);
-      len += delta;
-      max_len -= delta;
-    }
-    name_size = name_len;
-    if (name_size > strlen(btm_cb.cfg.bd_name)) {
-      name_type = BTM_EIR_COMPLETE_LOCAL_NAME_TYPE;
-      name_size = (uint16_t)strlen(btm_cb.cfg.bd_name);
-    }
-    delta = name_size + 2;
-    if (max_len >= delta) {
-      *p++ = name_size + 1;
-      *p++ = name_type;
-      ARRAY_TO_STREAM(p, btm_cb.cfg.bd_name, name_size);
-      len += delta;
-      max_len -= delta;
-    }
-    /* update len */
-    p = p_data;
-    UINT16_TO_STREAM(p, len);
-  }
-  return len;
-}
-
-/*******************************************************************************
- *
  * Function         BTM_BothEndsSupportSecureConnections
  *
  * Description      This function is called to check if both the local device
@@ -1717,64 +1563,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_ReadOobData
- *
- * Description      This function is called to parse the OOB data payload
- *                  received over OOB (non-Bluetooth) link
- *
- * Parameters:      p_data  - the location for OOB data
- *                  eir_tag - The associated EIR tag to read the data.
- *                  *p_len(output) - the length of the data with the given tag.
- *
- * Returns          the beginning of the data with the given tag.
- *                  NULL, if the tag is not found.
- *
- ******************************************************************************/
-uint8_t* BTM_ReadOobData(uint8_t* p_data, uint8_t eir_tag, uint8_t* p_len) {
-  uint8_t* p = p_data;
-  uint16_t max_len;
-  uint8_t len, type;
-  uint8_t* p_ret = NULL;
-  uint8_t ret_len = 0;
-
-  if (p_data) {
-    STREAM_TO_UINT16(max_len, p);
-    if (max_len >= BTM_OOB_MANDATORY_SIZE) {
-      if (BTM_EIR_OOB_BD_ADDR_TYPE == eir_tag) {
-        p_ret = p; /* the location for bd_addr */
-        ret_len = BTM_OOB_BD_ADDR_SIZE;
-      } else {
-        p += BD_ADDR_LEN;
-        max_len -= BTM_OOB_MANDATORY_SIZE;
-        /* now the optional data in EIR format */
-        while (max_len > 0) {
-          len = *p++; /* tag data len + 1 */
-          type = *p++;
-          if (eir_tag == type) {
-            p_ret = p;
-            ret_len = len - 1;
-            break;
-          }
-          /* the data size of this tag is len + 1 (tag data len + 2) */
-          if (max_len > len) {
-            max_len -= len;
-            max_len--;
-            len--;
-            p += len;
-          } else
-            max_len = 0;
-        }
-      }
-    }
-  }
-
-  if (p_len) *p_len = ret_len;
-
-  return p_ret;
-}
-
-/*******************************************************************************
- *
  * Function         BTM_SetOutService
  *
  * Description      This function is called to set the service for
@@ -1942,13 +1730,11 @@
   bool old_is_originator;
   tBTM_STATUS rc = BTM_SUCCESS;
   bool chk_acp_auth_done = false;
-  bool is_originator;
-  tBT_TRANSPORT transport =
+  const bool is_originator = conn_type;
+  constexpr tBT_TRANSPORT transport =
       BT_TRANSPORT_BR_EDR; /* should check PSM range in LE connection oriented
                               L2CAP connection */
 
-  is_originator = conn_type;
-
   BTM_TRACE_DEBUG("%s() is_originator:%d, 0x%x, psm=0x%04x", __func__,
                   is_originator, p_ref_data, psm);
 
@@ -3782,6 +3568,7 @@
   tBTM_PAIRING_STATE old_state = btm_cb.pairing_state;
   tBTM_SEC_DEV_REC* p_dev_rec = btm_find_dev_by_handle(handle);
   bool are_bonding = false;
+  bool was_authenticating = false;
 
   if (p_dev_rec) {
     VLOG(2) << __func__ << ": Security Manager: in state: "
@@ -3822,16 +3609,35 @@
 
   if (!p_dev_rec) return;
 
-  if ((btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) &&
-      (btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) &&
-      (p_dev_rec->bd_addr == btm_cb.pairing_bda))
-    are_bonding = true;
+  if (p_dev_rec->sec_state == BTM_SEC_STATE_AUTHENTICATING) {
+    p_dev_rec->sec_state = BTM_SEC_STATE_IDLE;
+    was_authenticating = true;
+    /* There can be a race condition, when we are starting authentication
+     * and the peer device is doing encryption.
+     * If first we receive encryption change up, then initiated
+     * authentication can not be performed.
+     * According to the spec we can not do authentication on the
+     * encrypted link, so device is correct.
+     */
+    if ((status == HCI_ERR_COMMAND_DISALLOWED) &&
+        ((p_dev_rec->sec_flags & (BTM_SEC_AUTHENTICATED | BTM_SEC_ENCRYPTED)) ==
+         (BTM_SEC_AUTHENTICATED | BTM_SEC_ENCRYPTED))) {
+      status = HCI_SUCCESS;
+    }
+    if (status == HCI_SUCCESS) {
+      p_dev_rec->sec_flags |= BTM_SEC_AUTHENTICATED;
+    }
+  }
 
   if ((btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) &&
-      (p_dev_rec->bd_addr == btm_cb.pairing_bda))
+      (p_dev_rec->bd_addr == btm_cb.pairing_bda)) {
+    if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) {
+      are_bonding = true;
+    }
     btm_sec_change_pairing_state(BTM_PAIR_STATE_IDLE);
+  }
 
-  if (p_dev_rec->sec_state != BTM_SEC_STATE_AUTHENTICATING) {
+  if (was_authenticating == false) {
     if ((btm_cb.api.p_auth_complete_callback && status != HCI_SUCCESS) &&
         (old_state != BTM_PAIR_STATE_IDLE)) {
       (*btm_cb.api.p_auth_complete_callback)(p_dev_rec->bd_addr,
@@ -3841,17 +3647,6 @@
     return;
   }
 
-  /* There can be a race condition, when we are starting authentication and
-  ** the peer device is doing encryption.
-  ** If first we receive encryption change up, then initiated authentication
-  ** can not be performed.  According to the spec we can not do authentication
-  ** on the encrypted link, so device is correct.
-  */
-  if ((status == HCI_ERR_COMMAND_DISALLOWED) &&
-      ((p_dev_rec->sec_flags & (BTM_SEC_AUTHENTICATED | BTM_SEC_ENCRYPTED)) ==
-       (BTM_SEC_AUTHENTICATED | BTM_SEC_ENCRYPTED))) {
-    status = HCI_SUCCESS;
-  }
   /* Currently we do not notify user if it is a keyboard which connects */
   /* User probably Disabled the keyboard while it was asleap.  Let her try */
   if (btm_cb.api.p_auth_complete_callback) {
@@ -3862,8 +3657,6 @@
                                              p_dev_rec->sec_bd_name, status);
   }
 
-  p_dev_rec->sec_state = BTM_SEC_STATE_IDLE;
-
   /* If this is a bonding procedure can disconnect the link now */
   if (are_bonding) {
     p_dev_rec->security_required &= ~BTM_SEC_OUT_AUTHENTICATE;
@@ -3909,8 +3702,6 @@
     return;
   }
 
-  p_dev_rec->sec_flags |= BTM_SEC_AUTHENTICATED;
-
   if (p_dev_rec->pin_code_length >= 16 ||
       p_dev_rec->link_key_type == BTM_LKEY_TYPE_AUTH_COMB ||
       p_dev_rec->link_key_type == BTM_LKEY_TYPE_AUTH_COMB_P_256) {
@@ -4563,7 +4354,8 @@
    */
   if (is_sample_ltk(p_dev_rec->ble.keys.pltk)) {
     android_errorWriteLog(0x534e4554, "128437297");
-    LOG(INFO) << __func__ << " removing bond to device that used sample LTK: " << p_dev_rec->bd_addr;
+    LOG(INFO) << __func__ << " removing bond to device that used sample LTK: "
+              << p_dev_rec->bd_addr;
 
     bta_dm_remove_device(p_dev_rec->bd_addr);
     return;
diff --git a/stack/btu/btu_hcif.cc b/stack/btu/btu_hcif.cc
index 52d5d60..2d7033d 100644
--- a/stack/btu/btu_hcif.cc
+++ b/stack/btu/btu_hcif.cc
@@ -43,6 +43,7 @@
 #include "bt_common.h"
 #include "bt_types.h"
 #include "bt_utils.h"
+#include "btif_config.h"
 #include "btm_api.h"
 #include "btm_int.h"
 #include "btu.h"
@@ -58,6 +59,7 @@
 using base::Location;
 
 extern void btm_process_cancel_complete(uint8_t status, uint8_t mode);
+extern void btm_process_inq_results2(uint8_t* p, uint8_t inq_res_mode);
 extern void btm_ble_test_command_complete(uint8_t* p);
 extern void smp_cancel_start_encryption_attempt();
 
@@ -191,6 +193,44 @@
       bluetooth::common::LogLinkLayerConnectionEvent(
           &bda, handle, android::bluetooth::DIRECTION_UNKNOWN, link_type, cmd,
           evt_code, android::bluetooth::hci::BLE_EVT_UNKNOWN, status, reason);
+
+      // Read SDP_DI manufacturer, model, HW version from config,
+      // and log them
+      int sdp_di_manufacturer_id = 0;
+      int sdp_di_model_id = 0;
+      int sdp_di_hw_version = 0;
+      int sdp_di_vendor_id_source = 0;
+      std::string bda_string = bda.ToString();
+      btif_config_get_int(bda_string, BT_CONFIG_KEY_SDP_DI_MANUFACTURER,
+                          &sdp_di_manufacturer_id);
+      btif_config_get_int(bda_string, BT_CONFIG_KEY_SDP_DI_MODEL,
+                          &sdp_di_model_id);
+      btif_config_get_int(bda_string, BT_CONFIG_KEY_SDP_DI_HW_VERSION,
+                          &sdp_di_hw_version);
+      btif_config_get_int(bda_string, BT_CONFIG_KEY_SDP_DI_VENDOR_ID_SRC,
+                          &sdp_di_vendor_id_source);
+
+      std::stringstream ss;
+      // [N - native]::SDP::[DIP - Device ID Profile]
+      ss << "N:SDP::DIP::" << loghex(sdp_di_vendor_id_source);
+      bluetooth::common::LogManufacturerInfo(
+          bda, android::bluetooth::DeviceInfoSrcEnum::DEVICE_INFO_INTERNAL,
+          ss.str(), loghex(sdp_di_manufacturer_id), loghex(sdp_di_model_id),
+          loghex(sdp_di_hw_version), "");
+
+      // Read LMP version, subversion and  manufacturer from config,
+      // and log them
+      int lmp_version = -1;
+      int lmp_subversion = -1;
+      int lmp_manufacturer_id = -1;
+      btif_config_get_int(bda_string, BT_CONFIG_KEY_REMOTE_VER_VER,
+                          &lmp_version);
+      btif_config_get_int(bda_string, BT_CONFIG_KEY_REMOTE_VER_SUBVER,
+                          &lmp_subversion);
+      btif_config_get_int(bda_string, BT_CONFIG_KEY_REMOTE_VER_MFCT,
+                          &lmp_manufacturer_id);
+      bluetooth::common::LogRemoteVersionInfo(
+          handle, status, lmp_version, lmp_manufacturer_id, lmp_subversion);
       break;
     }
     case HCI_CONNECTION_REQUEST_EVT: {
@@ -1664,6 +1704,12 @@
  ******************************************************************************/
 static void btu_hcif_hardware_error_evt(uint8_t* p) {
   HCI_TRACE_ERROR("Ctlr H/w error event - code:0x%x", *p);
+  if (hci_is_root_inflammation_event_received()) {
+    // Ignore the hardware error event here as we have already received
+    // root inflammation event earlier.
+    HCI_TRACE_ERROR(LOG_TAG, "H/w error event after root inflammation event!");
+    return;
+  }
 
   /* If anyone wants device status notifications, give him one. */
   btm_report_device_status(BTM_DEV_STATUS_DOWN);
diff --git a/stack/btu/btu_task.cc b/stack/btu/btu_task.cc
index 49c9697..06d06d1 100644
--- a/stack/btu/btu_task.cc
+++ b/stack/btu/btu_task.cc
@@ -71,6 +71,11 @@
       btu_hcif_send_cmd((uint8_t)(p_msg->event & BT_SUB_EVT_MASK), p_msg);
       break;
 
+    case BT_EVT_TO_BTU_HCI_ISO:
+      // TODO: implement handler
+      osi_free(p_msg);
+      break;
+
     default:
       osi_free(p_msg);
       break;
@@ -92,6 +97,17 @@
   return BT_STATUS_SUCCESS;
 }
 
+bt_status_t do_in_main_thread_delayed(const base::Location& from_here,
+                                      base::OnceClosure task,
+                                      const base::TimeDelta& delay) {
+  if (!get_main_message_loop()->task_runner()->PostDelayedTask(
+          from_here, std::move(task), delay)) {
+    LOG(ERROR) << __func__ << ": failed from " << from_here.ToString();
+    return BT_STATUS_FAIL;
+  }
+  return BT_STATUS_SUCCESS;
+}
+
 void btu_task_start_up(UNUSED_ATTR void* context) {
   LOG(INFO) << "Bluetooth chip preload is complete";
 
diff --git a/stack/crypto_toolbox/aes_cmac.cc b/stack/crypto_toolbox/aes_cmac.cc
index c90b80c..a5342b3 100644
--- a/stack/crypto_toolbox/aes_cmac.cc
+++ b/stack/crypto_toolbox/aes_cmac.cc
@@ -38,7 +38,7 @@
   uint16_t round;
 } tCMAC_CB;
 
-tCMAC_CB cmac_cb;
+thread_local tCMAC_CB cmac_cb;
 
 /* Rb for AES-128 as block cipher, LSB as [0] */
 Octet16 const_Rb{0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
diff --git a/stack/crypto_toolbox/crypto_toolbox.cc b/stack/crypto_toolbox/crypto_toolbox.cc
index ae33bd9..d5c8e5d 100644
--- a/stack/crypto_toolbox/crypto_toolbox.cc
+++ b/stack/crypto_toolbox/crypto_toolbox.cc
@@ -34,7 +34,7 @@
   return aes_cmac(salt, w.data(), w.size());
 }
 
-Octet16 f4(uint8_t* u, uint8_t* v, const Octet16& x, uint8_t z) {
+Octet16 f4(const uint8_t* u, const uint8_t* v, const Octet16& x, uint8_t z) {
   constexpr size_t msg_len = BT_OCTET32_LEN /* U size */ +
                              BT_OCTET32_LEN /* V size */ + 1 /* Z size */;
 
@@ -73,7 +73,7 @@
   return aes_cmac(t, msg.data(), msg.size());
 }
 
-void f5(uint8_t* w, const Octet16& n1, const Octet16& n2, uint8_t* a1,
+void f5(const uint8_t* w, const Octet16& n1, const Octet16& n2, uint8_t* a1,
         uint8_t* a2, Octet16* mac_key, Octet16* ltk) {
   DVLOG(2) << __func__ << "W=" << HexEncode(w, BT_OCTET32_LEN)
            << ", N1=" << HexEncode(n1.data(), n1.size())
@@ -123,7 +123,8 @@
   return aes_cmac(w, msg.data(), msg.size());
 }
 
-uint32_t g2(uint8_t* u, uint8_t* v, const Octet16& x, const Octet16& y) {
+uint32_t g2(const uint8_t* u, const uint8_t* v, const Octet16& x,
+            const Octet16& y) {
   constexpr size_t msg_len = BT_OCTET32_LEN /* U size */ +
                              BT_OCTET32_LEN /* V size */
                              + OCTET16_LEN /* Y size */;
diff --git a/stack/crypto_toolbox/crypto_toolbox.h b/stack/crypto_toolbox/crypto_toolbox.h
index 90d7392..b445fa2 100644
--- a/stack/crypto_toolbox/crypto_toolbox.h
+++ b/stack/crypto_toolbox/crypto_toolbox.h
@@ -23,14 +23,16 @@
 extern Octet16 aes_128(const Octet16& key, const Octet16& message);
 extern Octet16 aes_cmac(const Octet16& key, const uint8_t* message,
                         uint16_t length);
-extern Octet16 f4(uint8_t* u, uint8_t* v, const Octet16& x, uint8_t z);
-extern void f5(uint8_t* w, const Octet16& n1, const Octet16& n2, uint8_t* a1,
-               uint8_t* a2, Octet16* mac_key, Octet16* ltk);
+extern Octet16 f4(const uint8_t* u, const uint8_t* v, const Octet16& x,
+                  uint8_t z);
+extern void f5(const uint8_t* w, const Octet16& n1, const Octet16& n2,
+               uint8_t* a1, uint8_t* a2, Octet16* mac_key, Octet16* ltk);
 extern Octet16 f6(const Octet16& w, const Octet16& n1, const Octet16& n2,
                   const Octet16& r, uint8_t* iocap, uint8_t* a1, uint8_t* a2);
 extern Octet16 h6(const Octet16& w, std::array<uint8_t, 4> keyid);
 extern Octet16 h7(const Octet16& salt, const Octet16& w);
-extern uint32_t g2(uint8_t* u, uint8_t* v, const Octet16& x, const Octet16& y);
+extern uint32_t g2(const uint8_t* u, const uint8_t* v, const Octet16& x,
+                   const Octet16& y);
 extern Octet16 ltk_to_link_key(const Octet16& ltk, bool use_h7);
 extern Octet16 link_key_to_ltk(const Octet16& link_key, bool use_h7);
 
diff --git a/stack/gap/gap_ble.cc b/stack/gap/gap_ble.cc
index cce62d4..e71238f 100644
--- a/stack/gap/gap_ble.cc
+++ b/stack/gap/gap_ble.cc
@@ -411,23 +411,26 @@
   Uuid addr_res_uuid = Uuid::From16Bit(GATT_UUID_GAP_CENTRAL_ADDR_RESOL);
 
   btgatt_db_element_t service[] = {
-    {.type = BTGATT_DB_PRIMARY_SERVICE, .uuid = svc_uuid},
-    {.type = BTGATT_DB_CHARACTERISTIC,
-     .uuid = name_uuid,
+    {
+        .uuid = svc_uuid,
+        .type = BTGATT_DB_PRIMARY_SERVICE,
+    },
+    {.uuid = name_uuid,
+     .type = BTGATT_DB_CHARACTERISTIC,
      .properties = GATT_CHAR_PROP_BIT_READ,
      .permissions = GATT_PERM_READ},
-    {.type = BTGATT_DB_CHARACTERISTIC,
-     .uuid = icon_uuid,
+    {.uuid = icon_uuid,
+     .type = BTGATT_DB_CHARACTERISTIC,
      .properties = GATT_CHAR_PROP_BIT_READ,
      .permissions = GATT_PERM_READ},
-    {.type = BTGATT_DB_CHARACTERISTIC,
-     .uuid = addr_res_uuid,
+    {.uuid = addr_res_uuid,
+     .type = BTGATT_DB_CHARACTERISTIC,
      .properties = GATT_CHAR_PROP_BIT_READ,
      .permissions = GATT_PERM_READ}
 #if (BTM_PERIPHERAL_ENABLED == TRUE) /* Only needed for peripheral testing */
     ,
-    {.type = BTGATT_DB_CHARACTERISTIC,
-     .uuid = Uuid::From16Bit(GATT_UUID_GAP_PREF_CONN_PARAM),
+    {.uuid = Uuid::From16Bit(GATT_UUID_GAP_PREF_CONN_PARAM),
+     .type = BTGATT_DB_CHARACTERISTIC,
      .properties = GATT_CHAR_PROP_BIT_READ,
      .permissions = GATT_PERM_READ}
 #endif
diff --git a/stack/gap/gap_conn.cc b/stack/gap/gap_conn.cc
index 15d3ddb..1285bc5 100644
--- a/stack/gap/gap_conn.cc
+++ b/stack/gap/gap_conn.cc
@@ -233,9 +233,23 @@
   else
     conn.reg_info.pL2CA_ConnectInd_Cb = gap_connect_ind;
 
+  /* Fill in eL2CAP parameter data */
+  if (p_ccb->cfg.fcr_present) {
+    if (ertm_info == NULL) {
+      p_ccb->ertm_info.preferred_mode = p_ccb->cfg.fcr.mode;
+      p_ccb->ertm_info.user_rx_buf_size = GAP_DATA_BUF_SIZE;
+      p_ccb->ertm_info.user_tx_buf_size = GAP_DATA_BUF_SIZE;
+      p_ccb->ertm_info.fcr_rx_buf_size = L2CAP_INVALID_ERM_BUF_SIZE;
+      p_ccb->ertm_info.fcr_tx_buf_size = L2CAP_INVALID_ERM_BUF_SIZE;
+    } else {
+      p_ccb->ertm_info = *ertm_info;
+    }
+  }
+
   /* Register the PSM with L2CAP */
   if (transport == BT_TRANSPORT_BR_EDR) {
-    p_ccb->psm = L2CA_REGISTER(psm, &conn.reg_info, false /* enable_snoop */);
+    p_ccb->psm = L2CA_Register(psm, &conn.reg_info, false /* enable_snoop */,
+                               &p_ccb->ertm_info);
     if (p_ccb->psm == 0) {
       LOG(ERROR) << StringPrintf("%s: Failure registering PSM 0x%04x", __func__,
                                  psm);
@@ -245,8 +259,7 @@
   }
 
   if (transport == BT_TRANSPORT_LE) {
-    p_ccb->psm = L2CA_REGISTER_COC(
-        psm, &conn.reg_info, AMP_AUTOSWITCH_ALLOWED | AMP_USE_AMP_IF_POSSIBLE);
+    p_ccb->psm = L2CA_RegisterLECoc(psm, (tL2CAP_APPL_INFO*)&conn.reg_info);
     if (p_ccb->psm == 0) {
       LOG(ERROR) << StringPrintf("%s: Failure registering PSM 0x%04x", __func__,
                                  psm);
@@ -263,19 +276,6 @@
     return (GAP_INVALID_HANDLE);
   }
 
-  /* Fill in eL2CAP parameter data */
-  if (p_ccb->cfg.fcr_present) {
-    if (ertm_info == NULL) {
-      p_ccb->ertm_info.preferred_mode = p_ccb->cfg.fcr.mode;
-      p_ccb->ertm_info.user_rx_buf_size = GAP_DATA_BUF_SIZE;
-      p_ccb->ertm_info.user_tx_buf_size = GAP_DATA_BUF_SIZE;
-      p_ccb->ertm_info.fcr_rx_buf_size = L2CAP_INVALID_ERM_BUF_SIZE;
-      p_ccb->ertm_info.fcr_tx_buf_size = L2CAP_INVALID_ERM_BUF_SIZE;
-    } else {
-      p_ccb->ertm_info = *ertm_info;
-    }
-  }
-
   /* optional FCR channel modes */
   if (ertm_info != NULL) {
     p_ccb->ertm_info.allowed_modes =
@@ -302,7 +302,7 @@
 
     /* Check if L2CAP started the connection process */
     if (p_rem_bda && (transport == BT_TRANSPORT_BR_EDR)) {
-      cid = L2CA_CONNECT_REQ(p_ccb->psm, *p_rem_bda, &p_ccb->ertm_info);
+      cid = L2CA_ErtmConnectReq(p_ccb->psm, *p_rem_bda, &p_ccb->ertm_info);
       if (cid != 0) {
         p_ccb->connection_id = cid;
         return (p_ccb->gap_handle);
@@ -310,7 +310,7 @@
     }
 
     if (p_rem_bda && (transport == BT_TRANSPORT_LE)) {
-      cid = L2CA_CONNECT_COC_REQ(p_ccb->psm, *p_rem_bda, &p_ccb->local_coc_cfg);
+      cid = L2CA_ConnectLECocReq(p_ccb->psm, *p_rem_bda, &p_ccb->local_coc_cfg);
       if (cid != 0) {
         p_ccb->connection_id = cid;
         return (p_ccb->gap_handle);
@@ -342,7 +342,7 @@
   if (p_ccb) {
     /* Check if we have a connection ID */
     if (p_ccb->con_state != GAP_CCB_STATE_LISTENING)
-      L2CA_DISCONNECT_REQ(p_ccb->connection_id);
+      L2CA_DisconnectReq(p_ccb->connection_id);
 
     gap_release_ccb(p_ccb);
 
@@ -488,7 +488,7 @@
   /* Send the buffer through L2CAP */
   BT_HDR* p_buf;
   while ((p_buf = (BT_HDR*)fixed_queue_try_dequeue(p_ccb->tx_queue)) != NULL) {
-    uint8_t status = L2CA_DATA_WRITE(p_ccb->connection_id, p_buf);
+    uint8_t status = L2CA_DataWrite(p_ccb->connection_id, p_buf);
 
     if (status == L2CAP_DW_CONGESTED) {
       p_ccb->is_congested = true;
@@ -565,7 +565,7 @@
   p_ccb->cfg = *p_cfg;
 
   if (p_ccb->con_state == GAP_CCB_STATE_CONNECTED)
-    L2CA_CONFIG_REQ(p_ccb->connection_id, p_cfg);
+    L2CA_ConfigReq(p_ccb->connection_id, p_cfg);
 
   return (BT_PASS);
 }
@@ -733,7 +733,7 @@
     LOG(WARNING) << "*******";
 
     /* Disconnect because it is an unexpected connection */
-    L2CA_DISCONNECT_REQ(l2cap_cid);
+    L2CA_DisconnectReq(l2cap_cid);
     return;
   }
 
@@ -747,15 +747,15 @@
 
   /* Send response to the L2CAP layer. */
   if (p_ccb->transport == BT_TRANSPORT_BR_EDR)
-    L2CA_CONNECT_RSP(bd_addr, l2cap_id, l2cap_cid, L2CAP_CONN_OK, L2CAP_CONN_OK,
-                     &p_ccb->ertm_info);
+    L2CA_ErtmConnectRsp(bd_addr, l2cap_id, l2cap_cid, L2CAP_CONN_OK,
+                        L2CAP_CONN_OK, &p_ccb->ertm_info);
 
   if (p_ccb->transport == BT_TRANSPORT_LE) {
-    L2CA_CONNECT_COC_RSP(bd_addr, l2cap_id, l2cap_cid, L2CAP_CONN_OK,
+    L2CA_ConnectLECocRsp(bd_addr, l2cap_id, l2cap_cid, L2CAP_CONN_OK,
                          L2CAP_CONN_OK, &p_ccb->local_coc_cfg);
 
     /* get the remote coc configuration */
-    L2CA_GET_PEER_COC_CONFIG(l2cap_cid, &p_ccb->peer_coc_cfg);
+    L2CA_GetPeerLECocConfig(l2cap_cid, &p_ccb->peer_coc_cfg);
     p_ccb->rem_mtu_size = p_ccb->peer_coc_cfg.mtu;
 
     /* configuration is not required for LE COC */
@@ -769,7 +769,7 @@
 
   /* Send a Configuration Request. */
   if (p_ccb->transport == BT_TRANSPORT_BR_EDR)
-    L2CA_CONFIG_REQ(l2cap_cid, &p_ccb->cfg);
+    L2CA_ConfigReq(l2cap_cid, &p_ccb->cfg);
 }
 
 /*******************************************************************************
@@ -816,7 +816,7 @@
     gap_checks_con_flags(p_ccb);
   } else {
     /* security failed - disconnect the channel */
-    L2CA_DISCONNECT_REQ(p_ccb->connection_id);
+    L2CA_DisconnectReq(p_ccb->connection_id);
   }
 }
 
@@ -853,12 +853,12 @@
       p_ccb->con_state = GAP_CCB_STATE_CFG_SETUP;
 
       /* Send a Configuration Request. */
-      L2CA_CONFIG_REQ(l2cap_cid, &p_ccb->cfg);
+      L2CA_ConfigReq(l2cap_cid, &p_ccb->cfg);
     }
 
     if (p_ccb->transport == BT_TRANSPORT_LE) {
       /* get the remote coc configuration */
-      L2CA_GET_PEER_COC_CONFIG(l2cap_cid, &p_ccb->peer_coc_cfg);
+      L2CA_GetPeerLECocConfig(l2cap_cid, &p_ccb->peer_coc_cfg);
       p_ccb->rem_mtu_size = p_ccb->peer_coc_cfg.mtu;
 
       /* configuration is not required for LE COC */
@@ -913,7 +913,7 @@
   p_cfg->result = L2CAP_CFG_OK;
   p_cfg->fcs_present = false;
 
-  L2CA_CONFIG_RSP(l2cap_cid, p_cfg);
+  L2CA_ConfigRsp(l2cap_cid, p_cfg);
 
   p_ccb->con_flags |= GAP_CCB_FLAGS_HIS_CFG_DONE;
 
@@ -971,7 +971,7 @@
   p_ccb = gap_find_ccb_by_cid(l2cap_cid);
   if (p_ccb == NULL) return;
 
-  if (ack_needed) L2CA_DISCONNECT_RSP(l2cap_cid);
+  if (ack_needed) L2CA_DisconnectRsp(l2cap_cid);
 
   p_ccb->p_callback(p_ccb->gap_handle, GAP_EVT_CONN_CLOSED, nullptr);
   gap_release_ccb(p_ccb);
@@ -1154,8 +1154,8 @@
 
   /* Free the security record for this PSM */
   BTM_SecClrService(p_ccb->service_id);
-  if (p_ccb->transport == BT_TRANSPORT_BR_EDR) L2CA_DEREGISTER(p_ccb->psm);
-  if (p_ccb->transport == BT_TRANSPORT_LE) L2CA_DEREGISTER_COC(p_ccb->psm);
+  if (p_ccb->transport == BT_TRANSPORT_BR_EDR) L2CA_Deregister(p_ccb->psm);
+  if (p_ccb->transport == BT_TRANSPORT_LE) L2CA_DeregisterLECoc(p_ccb->psm);
 }
 
 extern void gap_attr_db_init(void);
diff --git a/stack/gatt/connection_manager.cc b/stack/gatt/connection_manager.cc
index cfbb02e..9454be0 100644
--- a/stack/gatt/connection_manager.cc
+++ b/stack/gatt/connection_manager.cc
@@ -267,12 +267,11 @@
 void dump(int fd) {
   dprintf(fd, "\nconnection_manager state:\n");
   if (bgconn_dev.empty()) {
-    dprintf(fd, "\n\tno Low Energy connection attempts\n");
+    dprintf(fd, "\tno Low Energy connection attempts\n");
     return;
   }
 
-  dprintf(fd, "\n\tdevices attempting connection: %d\n",
-          (int)bgconn_dev.size());
+  dprintf(fd, "\tdevices attempting connection: %d", (int)bgconn_dev.size());
   for (const auto& entry : bgconn_dev) {
     dprintf(fd, "\n\t * %s: ", entry.first.ToString().c_str());
 
@@ -283,13 +282,14 @@
       }
     }
 
-    if (entry.second.doing_bg_conn.empty()) {
+    if (!entry.second.doing_bg_conn.empty()) {
       dprintf(fd, "\n\t\tapps doing background connect: ");
       for (const auto& id : entry.second.doing_bg_conn) {
         dprintf(fd, "%d, ", id);
       }
     }
   }
+  dprintf(fd, "\n");
 }
 
 }  // namespace connection_manager
diff --git a/stack/gatt/gatt_api.cc b/stack/gatt/gatt_api.cc
index 37bc614..5fe9a37 100644
--- a/stack/gatt/gatt_api.cc
+++ b/stack/gatt/gatt_api.cc
@@ -708,6 +708,7 @@
   p_clcb->op_subtype = type;
   p_clcb->auth_req = p_read->by_handle.auth_req;
   p_clcb->counter = 0;
+  p_clcb->read_req_current_mtu = p_tcb->payload_size;
 
   switch (type) {
     case GATT_READ_BY_TYPE:
diff --git a/stack/gatt/gatt_attr.cc b/stack/gatt/gatt_attr.cc
index 8861a7a..1acfd81 100644
--- a/stack/gatt/gatt_attr.cc
+++ b/stack/gatt/gatt_attr.cc
@@ -285,11 +285,16 @@
   Uuid char_uuid = Uuid::From16Bit(GATT_UUID_GATT_SRV_CHGD);
 
   btgatt_db_element_t service[] = {
-      {.type = BTGATT_DB_PRIMARY_SERVICE, .uuid = service_uuid},
-      {.type = BTGATT_DB_CHARACTERISTIC,
-       .uuid = char_uuid,
-       .properties = GATT_CHAR_PROP_BIT_INDICATE,
-       .permissions = 0}};
+      {
+          .uuid = service_uuid,
+          .type = BTGATT_DB_PRIMARY_SERVICE,
+      },
+      {
+          .uuid = char_uuid,
+          .type = BTGATT_DB_CHARACTERISTIC,
+          .properties = GATT_CHAR_PROP_BIT_INDICATE,
+          .permissions = 0,
+      }};
 
   GATTS_AddService(gatt_cb.gatt_if, service,
                    sizeof(service) / sizeof(btgatt_db_element_t));
diff --git a/stack/gatt/gatt_cl.cc b/stack/gatt/gatt_cl.cc
index 3115317..842cb6c 100644
--- a/stack/gatt/gatt_cl.cc
+++ b/stack/gatt/gatt_cl.cc
@@ -925,11 +925,18 @@
 
         memcpy(p_clcb->p_attr_buf + offset, p, len);
 
-        /* send next request if needed  */
+        /* full packet for read or read blob rsp */
+        bool packet_is_full;
+        if (tcb.payload_size == p_clcb->read_req_current_mtu) {
+          packet_is_full = (len == (tcb.payload_size - 1));
+        } else {
+          packet_is_full = (len == (p_clcb->read_req_current_mtu - 1) ||
+                            len == (tcb.payload_size - 1));
+          p_clcb->read_req_current_mtu = tcb.payload_size;
+        }
 
-        if (len == (tcb.payload_size -
-                    1) && /* full packet for read or read blob rsp */
-            len + offset < GATT_MAX_ATTR_LEN) {
+        /* send next request if needed  */
+        if (packet_is_full && (len + offset < GATT_MAX_ATTR_LEN)) {
           VLOG(1) << StringPrintf(
               "full pkt issue read blob for remianing bytes old offset=%d "
               "len=%d new offset=%d",
diff --git a/stack/gatt/gatt_int.h b/stack/gatt/gatt_int.h
index 2c00fd7..e072a47 100644
--- a/stack/gatt/gatt_int.h
+++ b/stack/gatt/gatt_int.h
@@ -323,6 +323,8 @@
   bool in_use;
   alarm_t* gatt_rsp_timer_ent; /* peer response timer */
   uint8_t retry_count;
+  uint16_t read_req_current_mtu; /* This is the MTU value that the read was
+                                    initiated with */
 };
 
 typedef struct {
diff --git a/stack/gatt/gatt_main.cc b/stack/gatt/gatt_main.cc
index ed8d3fd..e0a7b6b 100644
--- a/stack/gatt/gatt_main.cc
+++ b/stack/gatt/gatt_main.cc
@@ -125,7 +125,7 @@
 
   /* Now, register with L2CAP for ATT PSM over BR/EDR */
   if (!L2CA_Register(BT_PSM_ATT, (tL2CAP_APPL_INFO*)&dyn_info,
-                     false /* enable_snoop */)) {
+                     false /* enable_snoop */, nullptr)) {
     LOG(ERROR) << "ATT Dynamic Registration failed";
   }
 
diff --git a/stack/gatt/gatt_sr.cc b/stack/gatt/gatt_sr.cc
index 94d81ef..4b4b5da 100644
--- a/stack/gatt/gatt_sr.cc
+++ b/stack/gatt/gatt_sr.cc
@@ -861,10 +861,10 @@
 /**
  * This function is called to process the write request from client.
  */
-void gatts_process_write_req(tGATT_TCB& tcb, tGATT_SRV_LIST_ELEM& el,
-                             uint16_t handle, uint8_t op_code, uint16_t len,
-                             uint8_t* p_data,
-                             bt_gatt_db_attribute_type_t gatt_type) {
+static void gatts_process_write_req(tGATT_TCB& tcb, tGATT_SRV_LIST_ELEM& el,
+                                    uint16_t handle, uint8_t op_code,
+                                    uint16_t len, uint8_t* p_data,
+                                    bt_gatt_db_attribute_type_t gatt_type) {
   tGATTS_DATA sr_data;
   uint32_t trans_id;
   tGATT_STATUS status;
@@ -875,7 +875,7 @@
 
   switch (op_code) {
     case GATT_REQ_PREPARE_WRITE:
-      if (len < 2) {
+      if (len < 2 || p == nullptr) {
         LOG(ERROR) << __func__
                    << ": Prepare write request was invalid - missing offset, "
                       "sending error response";
@@ -897,8 +897,9 @@
       if (op_code == GATT_REQ_WRITE || op_code == GATT_REQ_PREPARE_WRITE)
         sr_data.write_req.need_rsp = true;
       sr_data.write_req.handle = handle;
+      if (len > GATT_MAX_ATTR_LEN) len = GATT_MAX_ATTR_LEN;
       sr_data.write_req.len = len;
-      if (len != 0 && p != NULL) {
+      if (len != 0 && p != nullptr) {
         memcpy(sr_data.write_req.value, p, len);
       }
       break;
diff --git a/stack/hid/hidd_conn.cc b/stack/hid/hidd_conn.cc
index 55ec3b2..a239f73 100644
--- a/stack/hid/hidd_conn.cc
+++ b/stack/hid/hidd_conn.cc
@@ -763,13 +763,13 @@
   hd_cb.l2cap_intr_cfg.flush_to = HID_DEV_FLUSH_TO;
 
   if (!L2CA_Register(HID_PSM_CONTROL, (tL2CAP_APPL_INFO*)&dev_reg_info,
-                     false /* enable_snoop */)) {
+                     false /* enable_snoop */, nullptr)) {
     HIDD_TRACE_ERROR("HID Control (device) registration failed");
     return (HID_ERR_L2CAP_FAILED);
   }
 
   if (!L2CA_Register(HID_PSM_INTERRUPT, (tL2CAP_APPL_INFO*)&dev_reg_info,
-                     false /* enable_snoop */)) {
+                     false /* enable_snoop */, nullptr)) {
     L2CA_Deregister(HID_PSM_CONTROL);
     HIDD_TRACE_ERROR("HID Interrupt (device) registration failed");
     return (HID_ERR_L2CAP_FAILED);
diff --git a/stack/hid/hidh_conn.cc b/stack/hid/hidh_conn.cc
index 3c694f0..944d859 100644
--- a/stack/hid/hidh_conn.cc
+++ b/stack/hid/hidh_conn.cc
@@ -98,12 +98,12 @@
 
   /* Now, register with L2CAP */
   if (!L2CA_Register(HID_PSM_CONTROL, (tL2CAP_APPL_INFO*)&hst_reg_info,
-                     false /* enable_snoop */)) {
+                     false /* enable_snoop */, nullptr)) {
     HIDH_TRACE_ERROR("HID-Host Control Registration failed");
     return (HID_ERR_L2CAP_FAILED);
   }
   if (!L2CA_Register(HID_PSM_INTERRUPT, (tL2CAP_APPL_INFO*)&hst_reg_info,
-                     false /* enable_snoop */)) {
+                     false /* enable_snoop */, nullptr)) {
     L2CA_Deregister(HID_PSM_CONTROL);
     HIDH_TRACE_ERROR("HID-Host Interrupt Registration failed");
     return (HID_ERR_L2CAP_FAILED);
diff --git a/stack/include/a2dp_codec_api.h b/stack/include/a2dp_codec_api.h
index 45cbf9c..3297c5c 100644
--- a/stack/include/a2dp_codec_api.h
+++ b/stack/include/a2dp_codec_api.h
@@ -559,6 +559,15 @@
   // Decodes |p_buf| and calls |decode_callback| passed into init for the
   // decoded data.
   bool (*decode_packet)(BT_HDR* p_buf);
+
+  // Start the A2DP decoder.
+  void (*decoder_start)();
+
+  // Suspend the A2DP decoder.
+  void (*decoder_suspend)();
+
+  // A2DP decoder configuration.
+  void (*decoder_configure)(const uint8_t* p_codec_info);
 } tA2DP_DECODER_INTERFACE;
 
 // Gets the A2DP codec type.
diff --git a/stack/include/a2dp_vendor_ldac_decoder.h b/stack/include/a2dp_vendor_ldac_decoder.h
index 2a05baa..5efede8 100644
--- a/stack/include/a2dp_vendor_ldac_decoder.h
+++ b/stack/include/a2dp_vendor_ldac_decoder.h
@@ -40,4 +40,13 @@
 // |a2dp_vendor_ldac_decoder_init| if decoded frames are available.
 bool a2dp_vendor_ldac_decoder_decode_packet(BT_HDR* p_buf);
 
+// Start the A2DP LDAC decoder.
+void a2dp_vendor_ldac_decoder_start(void);
+
+// Suspend the A2DP LDAC decoder.
+void a2dp_vendor_ldac_decoder_suspend(void);
+
+// A2DP LDAC decoder configuration.
+void a2dp_vendor_ldac_decoder_configure(const uint8_t* p_codec_info);
+
 #endif  // A2DP_VENDOR_LDAC_DECODER_H
diff --git a/stack/include/avrc_defs.h b/stack/include/avrc_defs.h
index d1c844f..29d3e39 100644
--- a/stack/include/avrc_defs.h
+++ b/stack/include/avrc_defs.h
@@ -358,7 +358,8 @@
 #define AVRC_MEDIA_ATTR_ID_NUM_TRACKS 0x00000005
 #define AVRC_MEDIA_ATTR_ID_GENRE 0x00000006
 #define AVRC_MEDIA_ATTR_ID_PLAYING_TIME 0x00000007 /* in miliseconds */
-#define AVRC_MAX_NUM_MEDIA_ATTR_ID 7
+#define AVRC_MEDIA_ATTR_ID_COVER_ARTWORK_HANDLE 0x00000008
+#define AVRC_MAX_NUM_MEDIA_ATTR_ID 8
 
 /* Define the possible values of play state
 */
@@ -966,7 +967,7 @@
 
 #define AVRC_IS_VALID_MEDIA_ATTRIBUTE(a)            \
   (((a) >= AVRC_MEDIA_ATTR_ID_TITLE) &&             \
-           ((a) <= AVRC_MEDIA_ATTR_ID_PLAYING_TIME) \
+           ((a) <= AVRC_MAX_NUM_MEDIA_ATTR_ID) \
        ? true                                       \
        : false)
 
diff --git a/stack/include/bt_types.h b/stack/include/bt_types.h
index 7804328..e4d8fcc 100644
--- a/stack/include/bt_types.h
+++ b/stack/include/bt_types.h
@@ -80,6 +80,9 @@
 /* HCI command from upper layer     */
 #define BT_EVT_TO_BTU_HCI_CMD 0x1600
 
+/* ISO Data from HCI                */
+#define BT_EVT_TO_BTU_HCI_ISO 0x1700
+
 /* L2CAP segment(s) transmitted     */
 #define BT_EVT_TO_BTU_L2C_SEG_XMIT 0x1900
 
@@ -119,6 +122,8 @@
 #define BT_EVT_TO_LM_HCI_ACL_ACK 0x2b00
 /* LM Diagnostics commands          */
 #define BT_EVT_TO_LM_DIAG 0x2c00
+/* HCI ISO Data                     */
+#define BT_EVT_TO_LM_HCI_ISO 0x2d00
 
 #define BT_EVT_TO_BTM_CMDS 0x2f00
 #define BT_EVT_TO_BTM_PM_MDCHG_EVT (0x0001 | BT_EVT_TO_BTM_CMDS)
@@ -580,9 +585,7 @@
  * 0x4C68384139F574D836BCF34E9DFB01BF */
 constexpr Octet16 SAMPLE_LTK = {0xbf, 0x01, 0xfb, 0x9d, 0x4e, 0xf3, 0xbc, 0x36,
                                 0xd8, 0x74, 0xf5, 0x39, 0x41, 0x38, 0x68, 0x4c};
-inline bool is_sample_ltk(const Octet16& ltk) {
-  return ltk == SAMPLE_LTK;
-}
+inline bool is_sample_ltk(const Octet16& ltk) { return ltk == SAMPLE_LTK; }
 
 #endif
 
@@ -636,9 +639,9 @@
 } FLOW_SPEC;
 
 /* Values for service_type */
-#define NO_TRAFFIC 0
-#define BEST_EFFORT 1
-#define GUARANTEED 2
+#define SVC_TYPE_NO_TRAFFIC 0
+#define SVC_TYPE_BEST_EFFORT 1
+#define SVC_TYPE_GUARANTEED 2
 
 /* Service class of the CoD */
 #define SERV_CLASS_NETWORKING (1 << 1)
diff --git a/stack/include/btm_api.h b/stack/include/btm_api.h
index df4cbe0..d0abee2 100644
--- a/stack/include/btm_api.h
+++ b/stack/include/btm_api.h
@@ -309,45 +309,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_SetPeriodicInquiryMode
- *
- * Description      This function is called to set the device periodic inquiry
- *                  mode. If the duration is zero, the periodic inquiry mode is
- *                  cancelled.
- *
- * Parameters:      p_inqparms - pointer to the inquiry information
- *                      mode - GENERAL or LIMITED inquiry
- *                      duration - length in 1.28 sec intervals (If '0', the
- *                                 inquiry is CANCELLED)
- *                      max_resps - maximum amount of devices to search for
- *                                  before ending the inquiry
- *                      filter_cond_type - BTM_CLR_INQUIRY_FILTER,
- *                                         BTM_FILTER_COND_DEVICE_CLASS, or
- *                                         BTM_FILTER_COND_BD_ADDR
- *                      filter_cond - value for the filter (based on
- *                                                          filter_cond_type)
- *
- *                  max_delay - maximum amount of time between successive
- *                              inquiries
- *                  min_delay - minimum amount of time between successive
- *                              inquiries
- *                  p_results_cb - callback returning pointer to results
- *                              (tBTM_INQ_RESULTS)
- *
- * Returns          BTM_CMD_STARTED if successfully started
- *                  BTM_ILLEGAL_VALUE if a bad parameter is detected
- *                  BTM_NO_RESOURCES if could not allocate a message buffer
- *                  BTM_SUCCESS - if cancelling the periodic inquiry
- *                  BTM_BUSY - if an inquiry is already active
- *                  BTM_WRONG_MODE if the device is not up.
- *
- ******************************************************************************/
-extern tBTM_STATUS BTM_SetPeriodicInquiryMode(
-    tBTM_INQ_PARMS* p_inqparms, uint16_t max_delay, uint16_t min_delay,
-    tBTM_INQ_RESULTS_CB* p_results_cb);
-
-/*******************************************************************************
- *
  * Function         BTM_StartInquiry
  *
  * Description      This function is called to start an inquiry.
@@ -588,53 +549,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_ReadRemoteExtendedFeatures
- *
- * Description      This function is called to read a specific extended features
- *                  page of the remote device
- *
- *                  Note1: The size of device features mask page is
- *                  BTM_FEATURE_BYTES_PER_PAGE bytes.
- *                  Note2: The valid device features mask page number depends on
- *                  the remote device capabilities. It is expected to be in the
- *                  range [0 - BTM_EXT_FEATURES_PAGE_MAX].
-
- * Returns          pointer to the remote extended features mask
- *                  or NULL if page_number is not valid
- *
- ******************************************************************************/
-extern uint8_t* BTM_ReadRemoteExtendedFeatures(const RawAddress& addr,
-                                               uint8_t page_number);
-
-/*******************************************************************************
- *
- * Function         BTM_ReadNumberRemoteFeaturesPages
- *
- * Description      This function is called to retrieve the number of feature
- *                  pages read from the remote device
- *
- * Returns          number of features pages read from the remote device
- *
- ******************************************************************************/
-extern uint8_t BTM_ReadNumberRemoteFeaturesPages(const RawAddress& addr);
-
-/*******************************************************************************
- *
- * Function         BTM_ReadAllRemoteFeatures
- *
- * Description      Read all the features of the remote device
- *
- * Returns          pointer to the byte[0] of the page[0] of the remote device
- *                  feature mask.
- *
- * Note:            the function returns the pointer to the array of the size
- *                  BTM_FEATURE_BYTES_PER_PAGE * (BTM_EXT_FEATURES_PAGE_MAX + 1)
- *
- ******************************************************************************/
-extern uint8_t* BTM_ReadAllRemoteFeatures(const RawAddress& addr);
-
-/*******************************************************************************
- *
  * Function         BTM_InqDbRead
  *
  * Description      This function looks through the inquiry database for a match
@@ -690,20 +604,6 @@
  ******************************************************************************/
 extern tBTM_STATUS BTM_ClearInqDb(const RawAddress* p_bda);
 
-/*******************************************************************************
- *
- * Function         BTM_ReadInquiryRspTxPower
- *
- * Description      This command will read the inquiry Transmit Power level used
- *                  to transmit the FHS and EIR data packets.
- *                  This can be used directly in the Tx Power Level EIR data
- *                  type.
- *
- * Returns          BTM_SUCCESS if successful
- *
- ******************************************************************************/
-extern tBTM_STATUS BTM_ReadInquiryRspTxPower(tBTM_CMPL_CB* p_cb);
-
 /*****************************************************************************
  *  ACL CHANNEL MANAGEMENT FUNCTIONS
  ****************************************************************************/
@@ -889,24 +789,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_ReadLinkQuality
- *
- * Description      This function is called to read the link quality.
- *                  The value of the link quality is returned in the callback.
- *                  (tBTM_LINK_QUALITY_RESULT)
- *
- * Returns          BTM_CMD_STARTED if command issued to controller.
- *                  BTM_NO_RESOURCES if memory couldn't be allocated to issue
- *                                   the command
- *                  BTM_UNKNOWN_ADDR if no active link with bd addr specified
- *                  BTM_BUSY if command is already in progress
- *
- ******************************************************************************/
-extern tBTM_STATUS BTM_ReadLinkQuality(const RawAddress& remote_bda,
-                                       tBTM_CMPL_CB* p_cb);
-
-/*******************************************************************************
- *
  * Function         BTM_RegBusyLevelNotif
  *
  * Description      This function is called to register a callback to receive
@@ -921,18 +803,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_AclRegisterForChanges
- *
- * Description      This function is called to register a callback to receive
- *                  ACL database change events, i.e. new connection or removed.
- *
- * Returns          BTM_SUCCESS if successfully initiated, otherwise error
- *
- ******************************************************************************/
-extern tBTM_STATUS BTM_AclRegisterForChanges(tBTM_ACL_DB_CHANGE_CB* p_cb);
-
-/*******************************************************************************
- *
  * Function         BTM_GetNumAclLinks
  *
  * Description      This function is called to count the number of
@@ -943,18 +813,6 @@
  ******************************************************************************/
 extern uint16_t BTM_GetNumAclLinks(void);
 
-/*******************************************************************************
- *
- * Function         BTM_SetQoS
- *
- * Description      This function is called to setup QoS
- *
- * Returns          BTM_CMD_STARTED if successfully initiated, otherwise error
- *
- ******************************************************************************/
-extern tBTM_STATUS BTM_SetQoS(const RawAddress& bd, FLOW_SPEC* p_flow,
-                              tBTM_CMPL_CB* p_cb);
-
 /*****************************************************************************
  *  (e)SCO CHANNEL MANAGEMENT FUNCTIONS
  ****************************************************************************/
@@ -993,71 +851,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_SetScoPacketTypes
- *
- * Description      This function is called to set the packet types used for
- *                  a specific SCO connection,
- *
- * Parameters       pkt_types - One or more of the following
- *                  BTM_SCO_PKT_TYPES_MASK_HV1
- *                  BTM_SCO_PKT_TYPES_MASK_HV2
- *                  BTM_SCO_PKT_TYPES_MASK_HV3
- *                  BTM_SCO_PKT_TYPES_MASK_EV3
- *                  BTM_SCO_PKT_TYPES_MASK_EV4
- *                  BTM_SCO_PKT_TYPES_MASK_EV5
- *
- *                  BTM_SCO_LINK_ALL_MASK   - enables all supported types
- *
- * Returns          BTM_CMD_STARTED if successfully initiated, otherwise error
- *
- ******************************************************************************/
-extern tBTM_STATUS BTM_SetScoPacketTypes(uint16_t sco_inx, uint16_t pkt_types);
-
-/*******************************************************************************
- *
- * Function         BTM_ReadScoPacketTypes
- *
- * Description      This function is read the packet types used for a specific
- *                  SCO connection.
- *
- * Returns       One or more of the following (bitmask)
- *                  BTM_SCO_PKT_TYPES_MASK_HV1
- *                  BTM_SCO_PKT_TYPES_MASK_HV2
- *                  BTM_SCO_PKT_TYPES_MASK_HV3
- *                  BTM_SCO_PKT_TYPES_MASK_EV3
- *                  BTM_SCO_PKT_TYPES_MASK_EV4
- *                  BTM_SCO_PKT_TYPES_MASK_EV5
- *
- * Returns          packet types supported for the connection
- *
- ******************************************************************************/
-extern uint16_t BTM_ReadScoPacketTypes(uint16_t sco_inx);
-
-/*******************************************************************************
- *
- * Function         BTM_ReadDeviceScoPacketTypes
- *
- * Description      This function is read the SCO packet types that
- *                  the device supports.
- *
- * Returns          packet types supported by the device.
- *
- ******************************************************************************/
-extern uint16_t BTM_ReadDeviceScoPacketTypes(void);
-
-/*******************************************************************************
- *
- * Function         BTM_ReadScoHandle
- *
- * Description      Reead the HCI handle used for a specific SCO connection,
- *
- * Returns          handle for the connection, or 0xFFFF if invalid SCO index.
- *
- ******************************************************************************/
-extern uint16_t BTM_ReadScoHandle(uint16_t sco_inx);
-
-/*******************************************************************************
- *
  * Function         BTM_ReadScoBdAddr
  *
  * Description      This function is read the remote BD Address for a specific
@@ -1070,19 +863,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_ReadScoDiscReason
- *
- * Description      This function is returns the reason why an (e)SCO connection
- *                  has been removed. It contains the value until read, or until
- *                  another (e)SCO connection has disconnected.
- *
- * Returns          HCI reason or BTM_INVALID_SCO_DISC_REASON if not set.
- *
- ******************************************************************************/
-extern uint16_t BTM_ReadScoDiscReason(void);
-
-/*******************************************************************************
- *
  * Function         BTM_SetEScoMode
  *
  * Description      This function sets up the negotiated parameters for SCO or
@@ -1098,19 +878,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_SetWBSCodec
- *
- * Description      This function sends command to the controller to setup
- *                  WBS codec for the upcoming eSCO connection.
- *
- * Returns          BTM_SUCCESS.
- *
- *
- ******************************************************************************/
-extern tBTM_STATUS BTM_SetWBSCodec(tBTM_SCO_CODEC_TYPE codec_type);
-
-/*******************************************************************************
- *
  * Function         BTM_RegForEScoEvts
  *
  * Description      This function registers a SCO event callback with the
@@ -1127,30 +894,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_ReadEScoLinkParms
- *
- * Description      This function returns the current eSCO link parameters for
- *                  the specified handle.  This can be called anytime a
- *                  connection is active, but is typically called after
- *                  receiving the SCO opened callback.
- *
- *                  Note: If called over a 1.1 controller, only the packet types
- *                        field has meaning.
- *                  Note: If the upper layer doesn't know the current sco index,
- *                  BTM_FIRST_ACTIVE_SCO_INDEX can be used as the first
- *                  parameter to find the first active SCO index
- *
- * Returns          BTM_SUCCESS if returned data is valid connection.
- *                  BTM_ILLEGAL_VALUE if no connection for specified sco_inx.
- *                  BTM_MODE_UNSUPPORTED if local controller does not support
- *                      1.2 specification.
- *
- ******************************************************************************/
-extern tBTM_STATUS BTM_ReadEScoLinkParms(uint16_t sco_inx,
-                                         tBTM_ESCO_DATA* p_parms);
-
-/*******************************************************************************
- *
  * Function         BTM_ChangeEScoLinkParms
  *
  * Description      This function requests renegotiation of the parameters on
@@ -1222,19 +965,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_SecRegisterLinkKeyNotificationCallback
- *
- * Description      Profiles can register to be notified when a new Link Key
- *                  is generated per connection.
- *
- * Returns          true if registered OK, else false
- *
- ******************************************************************************/
-extern bool BTM_SecRegisterLinkKeyNotificationCallback(
-    tBTM_LINK_KEY_CALLBACK* p_callback);
-
-/*******************************************************************************
- *
  * Function         BTM_SecAddRmtNameNotifyCallback
  *
  * Description      Profiles can register to be notified when name of the
@@ -1332,24 +1062,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_SetSecureConnectionsOnly
- *
- * Description      Enable or disable default treatment for Mode 4 Level 0
- *                  services
- *
- * Parameter        secure_connections_only_mode - (true or false)
- *                  true means that the device should treat Mode 4 Level 0
- *                  services as services of other levels.
- *                  false means that the device should provide default
- *                  treatment for Mode 4 Level 0 services.
- *
- * Returns          void
- *
- ******************************************************************************/
-extern void BTM_SetSecureConnectionsOnly(bool secure_connections_only_mode);
-
-/*******************************************************************************
- *
  * Function         BTM_SetSecurityLevel
  *
  * Description      Register service security level with Security Manager.  Each
@@ -1435,21 +1147,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_SecGetDeviceLinkKey
- *
- * Description      This function is called to obtain link key for the device
- *                  it returns BTM_SUCCESS if link key is available, or
- *                  BTM_UNKNOWN_ADDR if Security Manager does not know about
- *                  the device or device record does not contain link key info
- *
- * Returns          BTM_SUCCESS if successful, otherwise error code
- *
- ******************************************************************************/
-extern tBTM_STATUS BTM_SecGetDeviceLinkKey(const RawAddress& bd_addr,
-                                           LinkKey* link_key);
-
-/*******************************************************************************
- *
  * Function         BTM_SecGetDeviceLinkKeyType
  *
  * Description      This function is called to obtain link key type for the
@@ -1492,26 +1189,10 @@
  *
  * Function         BTM_SecBond
  *
- * Description      This function is called to perform bonding with peer device.
- *
- * Parameters:      bd_addr      - Address of the device to bond
- *                  pin_len      - length in bytes of the PIN Code
- *                  p_pin        - pointer to array with the PIN Code
- *                  trusted_mask - bitwise OR of trusted services
- *                                 (array of uint32_t)
- * Returns          BTM_CMD_STARTED if successfully initiated, otherwise error
- *
- ******************************************************************************/
-extern tBTM_STATUS BTM_SecBond(const RawAddress& bd_addr, uint8_t pin_len,
-                               uint8_t* p_pin, uint32_t trusted_mask[]);
-
-/*******************************************************************************
- *
- * Function         BTM_SecBondByTransport
- *
  * Description      Perform bonding by designated transport
  *
  * Parameters:      bd_addr      - Address of the device to bond
+ *                  addr_type    - address type for LE transport
  *                  pin_len      - length in bytes of the PIN Code
  *                  p_pin        - pointer to array with the PIN Code
  *                  trusted_mask - bitwise OR of trusted services
@@ -1522,10 +1203,10 @@
  * Returns          BTM_CMD_STARTED if successfully initiated, otherwise error
  *
  ******************************************************************************/
-extern tBTM_STATUS BTM_SecBondByTransport(const RawAddress& bd_addr,
-                                          tBT_TRANSPORT transport,
-                                          uint8_t pin_len, uint8_t* p_pin,
-                                          uint32_t trusted_mask[]);
+extern tBTM_STATUS BTM_SecBond(const RawAddress& bd_addr,
+                               tBLE_ADDR_TYPE addr_type,
+                               tBT_TRANSPORT transport, uint8_t pin_len,
+                               uint8_t* p_pin, uint32_t trusted_mask[]);
 
 /*******************************************************************************
  *
@@ -1666,28 +1347,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_BuildOobData
- *
- * Description      This function is called to build the OOB data payload to
- *                  be sent over OOB (non-Bluetooth) link
- *
- * Parameters:      p_data  - the location for OOB data
- *                  max_len - p_data size.
- *                  c       - simple pairing Hash C.
- *                  r       - simple pairing Randomizer  C.
- *                  name_len- 0, local device name would not be included.
- *                            otherwise, the local device name is included for
- *                            up to this specified length
- *
- * Returns          Number of bytes in p_data.
- *
- ******************************************************************************/
-extern uint16_t BTM_BuildOobData(uint8_t* p_data, uint16_t max_len,
-                                 const Octet16& c, const Octet16& r,
-                                 uint8_t name_len);
-
-/*******************************************************************************
- *
  * Function         BTM_BothEndsSupportSecureConnections
  *
  * Description      This function is called to check if both the local device
@@ -1720,24 +1379,6 @@
 
 /*******************************************************************************
  *
- * Function         BTM_ReadOobData
- *
- * Description      This function is called to parse the OOB data payload
- *                  received over OOB (non-Bluetooth) link
- *
- * Parameters:      p_data  - the location for OOB data
- *                  eir_tag - The associated EIR tag to read the data.
- *                  *p_len(output) - the length of the data with the given tag.
- *
- * Returns          the beginning of the data with the given tag.
- *                  NULL, if the tag is not found.
- *
- ******************************************************************************/
-extern uint8_t* BTM_ReadOobData(uint8_t* p_data, uint8_t eir_tag,
-                                uint8_t* p_len);
-
-/*******************************************************************************
- *
  * Function         BTM_SecReadDevName
  *
  * Description      Looks for the device name in the security database for the
@@ -1977,85 +1618,6 @@
                                   uint8_t uuid_size, uint8_t* p_num_uuid,
                                   uint8_t* p_uuid_list, uint8_t max_num_uuid);
 
-/*****************************************************************************
- *  SCO OVER HCI
- ****************************************************************************/
-/*******************************************************************************
- *
- * Function         BTM_ConfigScoPath
- *
- * Description      This function enable/disable SCO over HCI and registers SCO
- *                  data callback if SCO over HCI is enabled.
- *
- * Parameter        path: SCO or HCI
- *                  p_sco_data_cb: callback function or SCO data if path is set
- *                                 to transport.
- *                  p_pcm_param: pointer to the PCM interface parameter. If a
- *                               NULL pointer is used, the PCM parameter
- *                               maintained in the control block will be used;
- *                               otherwise update the control block value.
- *                  err_data_rpt: Lisbon feature to enable the erronous data
- *                                report or not.
- *
- * Returns          BTM_SUCCESS if the successful.
- *                  BTM_NO_RESOURCES: no rsource to start the command.
- *                  BTM_ILLEGAL_VALUE: invalid callback function pointer.
- *                  BTM_CMD_STARTED : Command sent. Waiting for command
- *                                    complete event.
- *
- *
- ******************************************************************************/
-extern tBTM_STATUS BTM_ConfigScoPath(esco_data_path_t path,
-                                     tBTM_SCO_DATA_CB* p_sco_data_cb,
-                                     tBTM_SCO_PCM_PARAM* p_pcm_param,
-                                     bool err_data_rpt);
-
-/*******************************************************************************
- *
- * Function         BTM_WriteScoData
- *
- * Description      This function write SCO data to a specified instance. The
- *                  data to be written p_buf needs to carry an offset of
- *                  HCI_SCO_PREAMBLE_SIZE bytes, and the data length can not
- *                  exceed BTM_SCO_DATA_SIZE_MAX bytes, whose default value is
- *                  set to 60 and is configurable. Data longer than the maximum
- *                  bytes will be truncated.
- *
- * Returns          BTM_SUCCESS: data write is successful
- *                  BTM_ILLEGAL_VALUE: SCO data contains illegal offset value.
- *                  BTM_SCO_BAD_LENGTH: SCO data length exceeds the max SCO
- *                                      packet size.
- *                  BTM_NO_RESOURCES: no resources.
- *                  BTM_UNKNOWN_ADDR: unknown SCO connection handle, or SCO is
- *                                    not routed via HCI.
- *
- *
- ******************************************************************************/
-extern tBTM_STATUS BTM_WriteScoData(uint16_t sco_inx, BT_HDR* p_buf);
-
-/*******************************************************************************
- *
- * Function         BTM_SetARCMode
- *
- * Description      Send Audio Routing Control command.
- *
- * Returns          void
- *
- ******************************************************************************/
-extern void BTM_SetARCMode(uint8_t iface, uint8_t arc_mode,
-                           tBTM_VSC_CMPL_CB* p_arc_cb);
-
-/*******************************************************************************
- *
- * Function         BTM_PCM2Setup_Write
- *
- * Description      Send PCM2_Setup write command.
- *
- * Returns          void
- *
- ******************************************************************************/
-extern void BTM_PCM2Setup_Write(bool clk_master, tBTM_VSC_CMPL_CB* p_arc_cb);
-
 /*******************************************************************************
  *
  * Function         BTM_PM_ReadControllerState
diff --git a/stack/include/btm_api_types.h b/stack/include/btm_api_types.h
old mode 100644
new mode 100755
index 0d3d7d4..05f12f0
--- a/stack/include/btm_api_types.h
+++ b/stack/include/btm_api_types.h
@@ -573,9 +573,8 @@
 /***************************
  *  Device Discovery Types
  ***************************/
-/* Definitions of the parameters passed to BTM_StartInquiry and
- * BTM_SetPeriodicInquiryMode.
-*/
+/* Definitions of the parameters passed to BTM_StartInquiry.
+ */
 typedef struct /* contains the two device class condition fields */
 {
   DEV_CLASS dev_class;
@@ -611,7 +610,7 @@
 constexpr uint8_t PHY_LE_NO_PACKET = 0x00;
 constexpr uint8_t PHY_LE_1M = 0x01;
 constexpr uint8_t PHY_LE_2M = 0x02;
-constexpr uint8_t PHY_LE_CODED = 0x03;
+constexpr uint8_t PHY_LE_CODED = 0x04;
 
 constexpr uint8_t NO_ADI_PRESENT = 0xFF;
 constexpr uint8_t TX_POWER_NOT_PRESENT = 0x7F;
@@ -1046,9 +1045,6 @@
 #define BTM_SEC_MODE_SP_DEBUG 5
 #define BTM_SEC_MODE_SC 6
 
-/* Maximum Number of BTM Security Modes */
-#define BTM_SEC_MODES_MAX 7
-
 /* Security Service Levels [bit mask] (BTM_SetSecurityLevel)
  * Encryption should not be used without authentication
 */
@@ -1118,15 +1114,10 @@
 typedef uint8_t tBTM_LINK_KEY_TYPE;
 
 /* Protocol level security (BTM_SetSecurityLevel) */
-#define BTM_SEC_PROTO_L2CAP 0
-#define BTM_SEC_PROTO_SDP 1
-#define BTM_SEC_PROTO_TCS 2
 #define BTM_SEC_PROTO_RFCOMM 3
-#define BTM_SEC_PROTO_OBEX 4
 #define BTM_SEC_PROTO_BNEP 5
 #define BTM_SEC_PROTO_HID 6 /* HID      */
 #define BTM_SEC_PROTO_AVDT 7
-#define BTM_SEC_PROTO_MCA 8
 
 /* Determine the number of uint32_t's necessary for security services */
 #define BTM_SEC_ARRAY_BITS 32 /* Number of bits in each array element */
@@ -1240,62 +1231,6 @@
       ((uint32_t*)(p_dst))[trst] = 0;                         \
   }
 
-/* Following bits can be provided by host in the trusted_mask array */
-/* 0..31 bits of mask[0] (Least Significant Word) */
-#define BTM_SEC_TRUST_SDP_SERVER (1 << BTM_SEC_SERVICE_SDP_SERVER)
-#define BTM_SEC_TRUST_SERIAL_PORT (1 << BTM_SEC_SERVICE_SERIAL_PORT)
-#define BTM_SEC_TRUST_LAN_ACCESS (1 << BTM_SEC_SERVICE_LAN_ACCESS)
-#define BTM_SEC_TRUST_DUN (1 << BTM_SEC_SERVICE_DUN)
-#define BTM_SEC_TRUST_IRMC_SYNC (1 << BTM_SEC_SERVICE_IRMC_SYNC)
-#define BTM_SEC_TRUST_IRMC_SYNC_CMD (1 << BTM_SEC_SERVICE_IRMC_SYNC_CMD)
-#define BTM_SEC_TRUST_OBEX (1 << BTM_SEC_SERVICE_OBEX)
-#define BTM_SEC_TRUST_OBEX_FTP (1 << BTM_SEC_SERVICE_OBEX_FTP)
-#define BTM_SEC_TRUST_HEADSET (1 << BTM_SEC_SERVICE_HEADSET)
-#define BTM_SEC_TRUST_CORDLESS (1 << BTM_SEC_SERVICE_CORDLESS)
-#define BTM_SEC_TRUST_INTERCOM (1 << BTM_SEC_SERVICE_INTERCOM)
-#define BTM_SEC_TRUST_FAX (1 << BTM_SEC_SERVICE_FAX)
-#define BTM_SEC_TRUST_HEADSET_AG (1 << BTM_SEC_SERVICE_HEADSET_AG)
-#define BTM_SEC_TRUST_PNP_INFO (1 << BTM_SEC_SERVICE_PNP_INFO)
-#define BTM_SEC_TRUST_GEN_NET (1 << BTM_SEC_SERVICE_GEN_NET)
-#define BTM_SEC_TRUST_GEN_FILE (1 << BTM_SEC_SERVICE_GEN_FILE)
-#define BTM_SEC_TRUST_GEN_AUDIO (1 << BTM_SEC_SERVICE_GEN_AUDIO)
-#define BTM_SEC_TRUST_GEN_TEL (1 << BTM_SEC_SERVICE_GEN_TEL)
-#define BTM_SEC_TRUST_CTP_DATA (1 << BTM_SEC_SERVICE_CTP_DATA)
-#define BTM_SEC_TRUST_HCRP_CTRL (1 << BTM_SEC_SERVICE_HCRP_CTRL)
-#define BTM_SEC_TRUST_HCRP_DATA (1 << BTM_SEC_SERVICE_HCRP_DATA)
-#define BTM_SEC_TRUST_HCRP_NOTIF (1 << BTM_SEC_SERVICE_HCRP_NOTIF)
-#define BTM_SEC_TRUST_BPP_JOB (1 << BTM_SEC_SERVICE_JOB)
-#define BTM_SEC_TRUST_BPP_STATUS (1 << BTM_SEC_SERVICE_STATUS)
-#define BTM_SEC_TRUST_BPP_REF (1 << BTM_SEC_SERVICE_REF)
-#define BTM_SEC_TRUST_BNEP_PANU (1 << BTM_SEC_SERVICE_BNEP_PANU)
-#define BTM_SEC_TRUST_BNEP_GN (1 << BTM_SEC_SERVICE_BNEP_GN)
-#define BTM_SEC_TRUST_BNEP_NAP (1 << BTM_SEC_SERVICE_BNEP_NAP)
-#define BTM_SEC_TRUST_HFP_HF (1 << BTM_SEC_SERVICE_HF_HANDSFREE)
-#define BTM_SEC_TRUST_HFP_AG (1 << BTM_SEC_SERVICE_AG_HANDSFREE)
-#define BTM_SEC_TRUST_TE_PHONE_ACCESS (1 << BTM_SEC_SERVICE_TE_PHONE_ACCESS)
-#define BTM_SEC_TRUST_ME_PHONE_ACCESS (1 << BTM_SEC_SERVICE_ME_PHONE_ACCESS)
-
-/* 0..31 bits of mask[1] (Most Significant Word) */
-#define BTM_SEC_TRUST_HIDH_CTRL (1 << (BTM_SEC_SERVICE_HIDH_SEC_CTRL - 32))
-#define BTM_SEC_TRUST_HIDH_NOSEC_CTRL \
-  (1 << (BTM_SEC_SERVICE_HIDH_NOSEC_CTRL - 32))
-#define BTM_SEC_TRUST_HIDH_INTR (1 << (BTM_SEC_SERVICE_HIDH_INTR - 32))
-#define BTM_SEC_TRUST_BIP (1 << (BTM_SEC_SERVICE_BIP - 32))
-#define BTM_SEC_TRUST_BIP_REF (1 << (BTM_SEC_SERVICE_BIP_REF - 32))
-#define BTM_SEC_TRUST_AVDTP (1 << (BTM_SEC_SERVICE_AVDTP - 32))
-#define BTM_SEC_TRUST_AVDTP_NOSEC (1 << (BTM_SEC_SERVICE_AVDTP_NOSEC - 32))
-#define BTM_SEC_TRUST_AVCTP (1 << (BTM_SEC_SERVICE_AVCTP - 32))
-#define BTM_SEC_TRUST_SAP (1 << (BTM_SEC_SERVICE_SAP - 32))
-#define BTM_SEC_TRUST_PBAP (1 << (BTM_SEC_SERVICE_PBAP - 32))
-#define BTM_SEC_TRUST_RFC_MUX (1 << (BTM_SEC_SERVICE_RFC_MUX - 32))
-#define BTM_SEC_TRUST_AVCTP_BROWSE (1 << (BTM_SEC_SERVICE_AVCTP_BROWSE - 32))
-#define BTM_SEC_TRUST_MAP (1 << (BTM_SEC_SERVICE_MAP - 32))
-#define BTM_SEC_TRUST_MAP_NOTIF (1 << (BTM_SEC_SERVICE_MAP_NOTIF - 32))
-#define BTM_SEC_TRUST_MCAP_CTRL (1 << (BTM_SEC_SERVICE_MCAP_CTRL - 32))
-#define BTM_SEC_TRUST_MCAP_DATA (1 << (BTM_SEC_SERVICE_MCAP_DATA - 32))
-#define BTM_SEC_TRUST_HDP_SNK (1 << (BTM_SEC_SERVICE_HDP_SNK - 32))
-#define BTM_SEC_TRUST_HDP_SRC (1 << (BTM_SEC_SERVICE_HDP_SRC - 32))
-
 #define BTM_SEC_TRUST_ALL 0xFFFFFFFF /* for each array element */
 
 /****************************************
diff --git a/stack/include/btm_ble_api_types.h b/stack/include/btm_ble_api_types.h
index 9f0201c..6c59a75 100644
--- a/stack/include/btm_ble_api_types.h
+++ b/stack/include/btm_ble_api_types.h
@@ -36,10 +36,12 @@
 #define BTM_BLE_NON_CONNECT_EVT 0x03
 /* Connectable low duty cycle directed advertising  */
 #define BTM_BLE_CONNECT_LO_DUTY_DIR_EVT 0x04
-/* 0x00 - 0x05 can be received on adv event type */
+/* 0x00 - 0x04 can be received on adv event type */
+#define BTM_BLE_ADV_IND_EVT  0x00
+#define BTM_BLE_ADV_DIRECT_IND_EVT  0x01
+#define BTM_BLE_ADV_SCAN_IND_EVT  0x02
+#define BTM_BLE_ADV_NONCONN_IND_EVT  0x03
 #define BTM_BLE_SCAN_RSP_EVT 0x04
-#define BTM_BLE_SCAN_REQ_EVT 0x05
-#define BTM_BLE_UNKNOWN_EVT 0xff
 
 #define BTM_BLE_UNKNOWN_EVT 0xff
 
diff --git a/stack/include/btu.h b/stack/include/btu.h
index 77d5051..03fe2a1 100644
--- a/stack/include/btu.h
+++ b/stack/include/btu.h
@@ -62,6 +62,9 @@
 base::MessageLoop* get_main_message_loop();
 bt_status_t do_in_main_thread(const base::Location& from_here,
                               base::OnceClosure task);
+bt_status_t do_in_main_thread_delayed(const base::Location& from_here,
+                                      base::OnceClosure task,
+                                      const base::TimeDelta& delay);
 
 void BTU_StartUp(void);
 void BTU_ShutDown(void);
diff --git a/stack/include/hcidefs.h b/stack/include/hcidefs.h
index b11ea6c..088f2b3 100644
--- a/stack/include/hcidefs.h
+++ b/stack/include/hcidefs.h
@@ -396,35 +396,35 @@
 #define HCI_BLE_WRITE_RF_COMPENS_POWER (0x004D | HCI_GRP_BLE_CMDS)
 #define HCI_BLE_SET_PRIVACY_MODE (0x004E | HCI_GRP_BLE_CMDS)
 
-/* LE Get Vendor Capabilities Command OCF */
-#define HCI_BLE_VENDOR_CAP_OCF (0x0153 | HCI_GRP_VENDOR_SPECIFIC)
+/* LE Get Vendor Capabilities Command opcode */
+#define HCI_BLE_VENDOR_CAP (0x0153 | HCI_GRP_VENDOR_SPECIFIC)
 
-/* Multi adv OCF */
-#define HCI_BLE_MULTI_ADV_OCF (0x0154 | HCI_GRP_VENDOR_SPECIFIC)
+/* Multi adv opcode */
+#define HCI_BLE_MULTI_ADV (0x0154 | HCI_GRP_VENDOR_SPECIFIC)
 
-/* Batch scan OCF */
-#define HCI_BLE_BATCH_SCAN_OCF (0x0156 | HCI_GRP_VENDOR_SPECIFIC)
+/* Batch scan opcode */
+#define HCI_BLE_BATCH_SCAN (0x0156 | HCI_GRP_VENDOR_SPECIFIC)
 
-/* ADV filter OCF */
-#define HCI_BLE_ADV_FILTER_OCF (0x0157 | HCI_GRP_VENDOR_SPECIFIC)
+/* ADV filter opcode */
+#define HCI_BLE_ADV_FILTER (0x0157 | HCI_GRP_VENDOR_SPECIFIC)
 
-/* Tracking OCF */
-#define HCI_BLE_TRACK_ADV_OCF (0x0158 | HCI_GRP_VENDOR_SPECIFIC)
+/* Tracking opcode */
+#define HCI_BLE_TRACK_ADV (0x0158 | HCI_GRP_VENDOR_SPECIFIC)
 
-/* Energy info OCF */
-#define HCI_BLE_ENERGY_INFO_OCF (0x0159 | HCI_GRP_VENDOR_SPECIFIC)
+/* Energy info opcode */
+#define HCI_BLE_ENERGY_INFO (0x0159 | HCI_GRP_VENDOR_SPECIFIC)
 
-/* Extended BLE Scan parameters OCF */
-#define HCI_BLE_EXTENDED_SCAN_PARAMS_OCF (0x015A | HCI_GRP_VENDOR_SPECIFIC)
+/* Extended BLE Scan parameters opcode */
+#define HCI_BLE_EXTENDED_SCAN_PARAMS (0x015A | HCI_GRP_VENDOR_SPECIFIC)
 
-/* Controller debug info OCF */
-#define HCI_CONTROLLER_DEBUG_INFO_OCF (0x015B | HCI_GRP_VENDOR_SPECIFIC)
+/* Controller debug info opcode */
+#define HCI_CONTROLLER_DEBUG_INFO (0x015B | HCI_GRP_VENDOR_SPECIFIC)
 
-/* A2DP offload OCF */
-#define HCI_CONTROLLER_A2DP_OPCODE_OCF (0x015D | HCI_GRP_VENDOR_SPECIFIC)
+/* A2DP offload opcode */
+#define HCI_CONTROLLER_A2DP (0x015D | HCI_GRP_VENDOR_SPECIFIC)
 
-/* Bluetooth Quality Report OCF */
-#define HCI_CONTROLLER_BQR_OPCODE_OCF (0x015E | HCI_GRP_VENDOR_SPECIFIC)
+/* Bluetooth Quality Report opcode */
+#define HCI_CONTROLLER_BQR (0x015E | HCI_GRP_VENDOR_SPECIFIC)
 
 /* subcode for multi adv feature */
 #define BTM_BLE_MULTI_ADV_SET_PARAM 0x01
diff --git a/stack/include/l2c_api.h b/stack/include/l2c_api.h
index 8f364e7..4b2d8f4 100644
--- a/stack/include/l2c_api.h
+++ b/stack/include/l2c_api.h
@@ -85,28 +85,6 @@
 #define L2CAP_FLUSH_CHANS_ALL 0xffff
 #define L2CAP_FLUSH_CHANS_GET 0x0000
 
-/* special CID for Multi-AV for reporting congestion */
-#define L2CAP_MULTI_AV_CID 0
-
-/* length of the HCI header block */
-/* HCI header(4) + SNK count(1) + FCR bits(1) + AV data length(2) */
-#define L2CAP_MULTI_AV_HCI_HDR_LEN 8
-
-/* length of padding for 4 bytes align */
-#define L2CAP_MULTI_AV_PADDING_LEN 2
-
-/* length of the HCI header block with padding for FCR */
-/* HCI header(4) + SNK count(1) + FCR bits(1) + AV data length(2) + padding(2)
- */
-#define L2CAP_MULTI_AV_HCI_HDR_LEN_WITH_PADDING 10
-
-/* length of the L2CAP header block */
-/* HCI header(4) + L2CAP header(4) + padding(4) or control word(2) + FCS(2) */
-#define L2CAP_MULTI_AV_L2C_HDR_LEN 12
-
-/* definition used for L2CA_SetDesireRole */
-#define L2CAP_ROLE_SLAVE HCI_ROLE_SLAVE
-#define L2CAP_ROLE_MASTER HCI_ROLE_MASTER
 /* set this bit to allow switch at create conn */
 #define L2CAP_ROLE_ALLOW_SWITCH 0x80
 /* set this bit to disallow switch at create conn */
@@ -328,22 +306,6 @@
 
 } tL2CAP_ERTM_INFO;
 
-#define L2CA_REGISTER(a, b, c) L2CA_Register(a, (tL2CAP_APPL_INFO*)(b), c)
-#define L2CA_DEREGISTER(a) L2CA_Deregister(a)
-#define L2CA_CONNECT_REQ(a, b, c) L2CA_ErtmConnectReq(a, b, c)
-#define L2CA_CONNECT_RSP(a, b, c, d, e, f) L2CA_ErtmConnectRsp(a, b, c, d, e, f)
-#define L2CA_CONFIG_REQ(a, b) L2CA_ConfigReq(a, b)
-#define L2CA_CONFIG_RSP(a, b) L2CA_ConfigRsp(a, b)
-#define L2CA_DISCONNECT_REQ(a) L2CA_DisconnectReq(a)
-#define L2CA_DISCONNECT_RSP(a) L2CA_DisconnectRsp(a)
-#define L2CA_DATA_WRITE(a, b) L2CA_DataWrite(a, b)
-#define L2CA_REGISTER_COC(a, b, c) L2CA_RegisterLECoc(a, (tL2CAP_APPL_INFO*)(b))
-#define L2CA_DEREGISTER_COC(a) L2CA_DeregisterLECoc(a)
-#define L2CA_CONNECT_COC_REQ(a, b, c) L2CA_ConnectLECocReq(a, b, c)
-#define L2CA_CONNECT_COC_RSP(a, b, c, d, e, f) \
-  L2CA_ConnectLECocRsp(a, b, c, d, e, f)
-#define L2CA_GET_PEER_COC_CONFIG(a, b) L2CA_GetPeerLECocConfig(a, b)
-
 /*****************************************************************************
  *  External Function Declarations
  ****************************************************************************/
@@ -363,7 +325,7 @@
  *
  ******************************************************************************/
 extern uint16_t L2CA_Register(uint16_t psm, tL2CAP_APPL_INFO* p_cb_info,
-                              bool enable_snoop);
+                              bool enable_snoop, tL2CAP_ERTM_INFO* p_ertm_info);
 
 /*******************************************************************************
  *
@@ -527,14 +489,6 @@
 extern bool L2CA_GetPeerLECocConfig(uint16_t lcid,
                                     tL2CAP_LE_CFG_INFO* peer_cfg);
 
-// This function sets the callback routines for the L2CAP connection referred to
-// by |local_cid|. The callback routines can only be modified for outgoing
-// connections established by |L2CA_ConnectReq| or accepted incoming
-// connections. |callbacks| must not be NULL. This function returns true if the
-// callbacks could be updated, false if not (e.g. |local_cid| was not found).
-bool L2CA_SetConnectionCallbacks(uint16_t local_cid,
-                                 const tL2CAP_APPL_INFO* callbacks);
-
 /*******************************************************************************
  *
  * Function         L2CA_ErtmConnectRsp
@@ -611,30 +565,6 @@
  ******************************************************************************/
 extern uint8_t L2CA_DataWrite(uint16_t cid, BT_HDR* p_data);
 
-/*******************************************************************************
- *
- * Function         L2CA_Ping
- *
- * Description      Higher layers call this function to send an echo request.
- *
- * Returns          true if echo request sent, else false.
- *
- ******************************************************************************/
-extern bool L2CA_Ping(const RawAddress& p_bd_addr, tL2CA_ECHO_RSP_CB* p_cb);
-
-/*******************************************************************************
- *
- * Function         L2CA_Echo
- *
- * Description      Higher layers call this function to send an echo request
- *                  with application-specific data.
- *
- * Returns          true if echo request sent, else false.
- *
- ******************************************************************************/
-extern bool L2CA_Echo(const RawAddress& p_bd_addr, BT_HDR* p_data,
-                      tL2CA_ECHO_DATA_CB* p_callback);
-
 // Given a local channel identifier, |lcid|, this function returns the bound
 // remote channel identifier, |rcid|, and the ACL link handle, |handle|. If
 // |lcid| is not known or is invalid, this function returns false and does not
@@ -715,18 +645,6 @@
 
 /*******************************************************************************
  *
- * Function     L2CA_LocalLoopbackReq
- *
- * Description  This function sets up a CID for local loopback
- *
- * Returns      CID of 0 if none.
- *
- ******************************************************************************/
-extern uint16_t L2CA_LocalLoopbackReq(uint16_t psm, uint16_t handle,
-                                      const RawAddress& p_bd_addr);
-
-/*******************************************************************************
- *
  * Function     L2CA_FlushChannel
  *
  * Description  This function flushes none, some or all buffers queued up
@@ -756,31 +674,6 @@
 
 /*******************************************************************************
  *
- * Function         L2CA_FlowControl
- *
- * Description      Higher layers call this function to flow control a channel.
- *
- *                  data_enabled - true data flows, false data is stopped
- *
- * Returns          true if valid channel, else false
- *
- ******************************************************************************/
-extern bool L2CA_FlowControl(uint16_t cid, bool data_enabled);
-
-/*******************************************************************************
- *
- * Function         L2CA_SendTestSFrame
- *
- * Description      Higher layers call this function to send a test S-frame.
- *
- * Returns          true if valid Channel, else false
- *
- ******************************************************************************/
-extern bool L2CA_SendTestSFrame(uint16_t cid, uint8_t sup_type,
-                                uint8_t back_track);
-
-/*******************************************************************************
- *
  * Function         L2CA_SetTxPriority
  *
  * Description      Sets the transmission priority for a channel. (FCR Mode)
@@ -792,34 +685,6 @@
 
 /*******************************************************************************
  *
- * Function         L2CA_RegForNoCPEvt
- *
- * Description      Register callback for Number of Completed Packets event.
- *
- * Input Param      p_cb - callback for Number of completed packets event
- *                  p_bda - BT address of remote device
- *
- * Returns
- *
- ******************************************************************************/
-extern bool L2CA_RegForNoCPEvt(tL2CA_NOCP_CB* p_cb, const RawAddress& p_bda);
-
-/*******************************************************************************
- *
- * Function         L2CA_SetChnlDataRate
- *
- * Description      Sets the tx/rx data rate for a channel.
- *
- * Returns          true if a valid channel, else false
- *
- ******************************************************************************/
-extern bool L2CA_SetChnlDataRate(uint16_t cid, tL2CAP_CHNL_DATA_RATE tx,
-                                 tL2CAP_CHNL_DATA_RATE rx);
-
-typedef void(tL2CA_RESERVE_CMPL_CBACK)(void);
-
-/*******************************************************************************
- *
  * Function         L2CA_SetFlushTimeout
  *
  * Description      This function set the automatic flush time out in Baseband
@@ -847,24 +712,6 @@
 
 /*******************************************************************************
  *
- * Function         L2CA_DataWriteEx
- *
- * Description      Higher layers call this function to write data with extended
- *                  flags.
- *                  flags : L2CAP_FLUSHABLE_CH_BASED
- *                          L2CAP_FLUSHABLE_PKT
- *                          L2CAP_NON_FLUSHABLE_PKT
- *
- * Returns          L2CAP_DW_SUCCESS, if data accepted, else false
- *                  L2CAP_DW_CONGESTED, if data accepted and the channel is
- *                                      congested
- *                  L2CAP_DW_FAILED, if error
- *
- ******************************************************************************/
-extern uint8_t L2CA_DataWriteEx(uint16_t cid, BT_HDR* p_data, uint16_t flags);
-
-/*******************************************************************************
- *
  * Function         L2CA_SetChnlFlushability
  *
  * Description      Higher layers call this function to set a channels
@@ -892,161 +739,6 @@
 
 /*******************************************************************************
  *
- *  Function         L2CA_GetBDAddrbyHandle
- *
- *  Description      Get BD address for the given HCI handle
- *
- *  Parameters:      HCI handle
- *                   BD address of the peer
- *
- *  Return value:    true if found lcb for the given handle, false otherwise
- *
- ******************************************************************************/
-extern bool L2CA_GetBDAddrbyHandle(uint16_t handle, RawAddress& bd_addr);
-
-/*******************************************************************************
- *
- *  Function         L2CA_GetChnlFcrMode
- *
- *  Description      Get the channel FCR mode
- *
- *  Parameters:      Local CID
- *
- *  Return value:    Channel mode
- *
- ******************************************************************************/
-extern uint8_t L2CA_GetChnlFcrMode(uint16_t lcid);
-
-/*******************************************************************************
- *
- *                      UCD callback prototypes
- *
- ******************************************************************************/
-
-/* UCD discovery. Parameters are
- *      BD Address of remote
- *      Data Type
- *      Data
- */
-#define L2CAP_UCD_INFO_TYPE_RECEPTION 0x01
-#define L2CAP_UCD_INFO_TYPE_MTU 0x02
-
-typedef void(tL2CA_UCD_DISCOVER_CB)(const RawAddress&, uint8_t, uint32_t);
-
-/* UCD data received. Parameters are
- *      BD Address of remote
- *      Pointer to buffer with data
- */
-typedef void(tL2CA_UCD_DATA_CB)(const RawAddress&, BT_HDR*);
-
-/* Congestion status callback protype. This callback is optional. If
- * an application tries to send data when the transmit queue is full,
- * the data will anyways be dropped. The parameter is:
- *              remote BD_ADDR
- *              true if congested, false if uncongested
- */
-typedef void(tL2CA_UCD_CONGESTION_STATUS_CB)(const RawAddress&, bool);
-
-/* UCD registration info (the callback addresses and PSM)
- */
-typedef struct {
-  tL2CA_UCD_DISCOVER_CB* pL2CA_UCD_Discover_Cb;
-  tL2CA_UCD_DATA_CB* pL2CA_UCD_Data_Cb;
-  tL2CA_UCD_CONGESTION_STATUS_CB* pL2CA_UCD_Congestion_Status_Cb;
-} tL2CAP_UCD_CB_INFO;
-
-/*******************************************************************************
- *
- *  Function        L2CA_UcdRegister
- *
- *  Description     Register PSM on UCD.
- *
- *  Parameters:     tL2CAP_UCD_CB_INFO
- *
- *  Return value:   true if successs
- *
- ******************************************************************************/
-extern bool L2CA_UcdRegister(uint16_t psm, tL2CAP_UCD_CB_INFO* p_cb_info);
-
-/*******************************************************************************
- *
- *  Function        L2CA_UcdDeregister
- *
- *  Description     Deregister PSM on UCD.
- *
- *  Parameters:     PSM
- *
- *  Return value:   true if successs
- *
- ******************************************************************************/
-extern bool L2CA_UcdDeregister(uint16_t psm);
-
-/*******************************************************************************
- *
- *  Function        L2CA_UcdDiscover
- *
- *  Description     Discover UCD of remote device.
- *
- *  Parameters:     PSM
- *                  BD_ADDR of remote device
- *                  info_type : L2CAP_UCD_INFO_TYPE_RECEPTION
- *                              L2CAP_UCD_INFO_TYPE_MTU
- *
- *
- *  Return value:   true if successs
- *
- ******************************************************************************/
-extern bool L2CA_UcdDiscover(uint16_t psm, const RawAddress& rem_bda,
-                             uint8_t info_type);
-
-/*******************************************************************************
- *
- *  Function        L2CA_UcdDataWrite
- *
- *  Description     Send UCD to remote device
- *
- *  Parameters:     PSM
- *                  BD Address of remote
- *                  Pointer to buffer of type BT_HDR
- *                  flags : L2CAP_FLUSHABLE_CH_BASED
- *                          L2CAP_FLUSHABLE_PKT
- *                          L2CAP_NON_FLUSHABLE_PKT
- *
- * Return value     L2CAP_DW_SUCCESS, if data accepted
- *                  L2CAP_DW_FAILED,  if error
- *
- ******************************************************************************/
-extern uint16_t L2CA_UcdDataWrite(uint16_t psm, const RawAddress& rem_bda,
-                                  BT_HDR* p_buf, uint16_t flags);
-
-/*******************************************************************************
- *
- *  Function        L2CA_UcdSetIdleTimeout
- *
- *  Description     Set UCD Idle timeout.
- *
- *  Parameters:     BD Addr
- *                  Timeout in second
- *
- *  Return value:   true if successs
- *
- ******************************************************************************/
-extern bool L2CA_UcdSetIdleTimeout(const RawAddress& rem_bda, uint16_t timeout);
-
-/*******************************************************************************
- *
- * Function         L2CA_UCDSetTxPriority
- *
- * Description      Sets the transmission priority for a connectionless channel.
- *
- * Returns          true if a valid channel, else false
- *
- ******************************************************************************/
-extern bool L2CA_UCDSetTxPriority(const RawAddress& rem_bda,
-                                  tL2CAP_CHNL_PRIORITY priority);
-
-/*******************************************************************************
- *
  *                      Fixed Channel callback prototypes
  *
  ******************************************************************************/
@@ -1179,37 +871,6 @@
 
 /*******************************************************************************
  *
- * Function     L2CA_GetCurrentConfig
- *
- * Description  This function returns configurations of L2CAP channel
- *              pp_our_cfg : pointer of our saved configuration options
- *              p_our_cfg_bits : valid config in bitmap
- *              pp_peer_cfg: pointer of peer's saved configuration options
- *              p_peer_cfg_bits : valid config in bitmap
- *
- * Returns      true if successful
- *
- ******************************************************************************/
-extern bool L2CA_GetCurrentConfig(uint16_t lcid, tL2CAP_CFG_INFO** pp_our_cfg,
-                                  tL2CAP_CH_CFG_BITS* p_our_cfg_bits,
-                                  tL2CAP_CFG_INFO** pp_peer_cfg,
-                                  tL2CAP_CH_CFG_BITS* p_peer_cfg_bits);
-
-/*******************************************************************************
- *
- * Function     L2CA_GetConnectionConfig
- *
- * Description  This function polulates the mtu, remote cid & lm_handle for
- *              a given local L2CAP channel
- *
- * Returns      true if successful
- *
- ******************************************************************************/
-extern bool L2CA_GetConnectionConfig(uint16_t lcid, uint16_t* mtu,
-                                     uint16_t* rcid, uint16_t* handle);
-
-/*******************************************************************************
- *
  *  Function        L2CA_CancelBleConnectReq
  *
  *  Description     Cancel a pending connection attempt to a BLE device.
diff --git a/stack/include/l2cap_client.h b/stack/include/l2cap_client.h
deleted file mode 100644
index edb204e..0000000
--- a/stack/include/l2cap_client.h
+++ /dev/null
@@ -1,79 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2014 Google, Inc.
- *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at:
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- *
- ******************************************************************************/
-
-#pragma once
-
-#include <hardware/bluetooth.h>
-#include <stdbool.h>
-#include <stdint.h>
-
-typedef struct buffer_t buffer_t;
-typedef struct l2cap_client_t l2cap_client_t;
-
-typedef struct {
-  void (*connected)(l2cap_client_t* client, void* context);
-  void (*disconnected)(l2cap_client_t* client, void* context);
-  void (*read_ready)(l2cap_client_t* client, buffer_t* packet, void* context);
-  void (*write_ready)(l2cap_client_t* client, void* context);
-} l2cap_client_callbacks_t;
-
-// Returns a new buffer with enough space for |size| bytes of L2CAP payload.
-// |size| must be greater than zero. This function returns NULL if the buffer
-// could not be allocated. The returned buffer must be freed with |buffer_free|
-// when it is no longer needed.
-buffer_t* l2cap_buffer_new(size_t size);
-
-// Creates and returns a new L2CAP client object. |callbacks| must not be NULL
-// and must specify a set of functions that should be called back when events
-// occur on the L2CAP connection. |context| may be NULL and will be passed as
-// the argument to all callbacks in |l2cap_client_callbacks_t|. The returned
-// object must be freed with |l2cap_client_free|.
-l2cap_client_t* l2cap_client_new(const l2cap_client_callbacks_t* callbacks,
-                                 void* context);
-
-// Frees the L2CAP client object allocated with |l2cap_client_new|. |client| may
-// be NULL.
-void l2cap_client_free(l2cap_client_t* client);
-
-// Attempts to connect the |client| to a peer device specified by
-// |remote_bdaddr| using the |psm| protocol specifier. This function returns
-// true if the connect operation could be started and will indicate completion
-// with either a 'connected' callback (success) or a 'disconnected' callback
-// (failure).
-//
-// This function must not be called while a connect operation is in progress or
-// while |l2cap_client_is_connected|. |client| and |remote_bdaddr| must not be
-// NULL. |psm| must be greater than zero.
-bool l2cap_client_connect(l2cap_client_t* client,
-                          const RawAddress& remote_bdaddr, uint16_t psm);
-
-// Disconnects a connected |client|. This function is asynchronous and
-// idempotent. It will indicate completion with a 'disconnected' callback.
-// |client| must not be NULL.
-void l2cap_client_disconnect(l2cap_client_t* client);
-
-// Returns true if |client| is connected and is ready to accept data written
-// to it. |client| must not be NULL.
-bool l2cap_client_is_connected(const l2cap_client_t* client);
-
-// Writes data contained in |packet| to a connected |client|. This function
-// returns true if the packet was successfully queued for delivery, false if the
-// client cannot accept more data at this time. If this function returns false,
-// the caller must wait for the 'write_ready' callback to write additional data
-// to the client. Neither |client| nor |packet| may be NULL.
-bool l2cap_client_write(l2cap_client_t* client, buffer_t* packet);
diff --git a/stack/include/port_api.h b/stack/include/port_api.h
index b0a7e63..daea3d4 100644
--- a/stack/include/port_api.h
+++ b/stack/include/port_api.h
@@ -35,47 +35,27 @@
  * set settings request, or to the application in the set settings indication.
 */
 typedef struct {
-#define PORT_BAUD_RATE_2400 0x00
-#define PORT_BAUD_RATE_4800 0x01
-#define PORT_BAUD_RATE_7200 0x02
 #define PORT_BAUD_RATE_9600 0x03
-#define PORT_BAUD_RATE_19200 0x04
-#define PORT_BAUD_RATE_38400 0x05
-#define PORT_BAUD_RATE_57600 0x06
-#define PORT_BAUD_RATE_115200 0x07
-#define PORT_BAUD_RATE_230400 0x08
 
   uint8_t baud_rate;
 
-#define PORT_5_BITS 0x00
-#define PORT_6_BITS 0x01
-#define PORT_7_BITS 0x02
 #define PORT_8_BITS 0x03
 
   uint8_t byte_size;
 
 #define PORT_ONESTOPBIT 0x00
-#define PORT_ONE5STOPBITS 0x01
   uint8_t stop_bits;
 
 #define PORT_PARITY_NO 0x00
-#define PORT_PARITY_YES 0x01
   uint8_t parity;
 
 #define PORT_ODD_PARITY 0x00
-#define PORT_EVEN_PARITY 0x01
-#define PORT_MARK_PARITY 0x02
-#define PORT_SPACE_PARITY 0x03
 
   uint8_t parity_type;
 
 #define PORT_FC_OFF 0x00
-#define PORT_FC_XONXOFF_ON_INPUT 0x01
-#define PORT_FC_XONXOFF_ON_OUTPUT 0x02
 #define PORT_FC_CTS_ON_INPUT 0x04
 #define PORT_FC_CTS_ON_OUTPUT 0x08
-#define PORT_FC_DSR_ON_INPUT 0x10
-#define PORT_FC_DSR_ON_OUTPUT 0x20
 
   uint8_t fc_type;
 
@@ -133,18 +113,6 @@
 #define PORT_EV_FCS 0x00020000
 
 /*
- * To register for events application should provide bitmask with
- * corresponding bit set
-*/
-
-#define PORT_MASK_ALL                                                  \
-  (PORT_EV_RXCHAR | PORT_EV_TXEMPTY | PORT_EV_CTS | PORT_EV_DSR |      \
-   PORT_EV_RLSD | PORT_EV_BREAK | PORT_EV_ERR | PORT_EV_RING |         \
-   PORT_EV_CONNECT_ERR | PORT_EV_DSRS | PORT_EV_CTSS | PORT_EV_RLSDS | \
-   PORT_EV_RXFLAG | PORT_EV_TXCHAR | PORT_EV_OVERRUN | PORT_EV_FC |    \
-   PORT_EV_FCS | PORT_EV_CONNECTED)
-
-/*
  * Define port result codes
 */
 #define PORT_SUCCESS 0
@@ -267,21 +235,6 @@
  ******************************************************************************/
 int PORT_ClearKeepHandleFlag(uint16_t port_handle);
 
-/*******************************************************************************
- *
- * Function         PORT_SetEventCallback
- *
- * Description      Set event data callback the specified connection.
- *
- * Parameters:      handle       - Handle of the port returned in the Open
- *                  p_callback   - address of the callback function which should
- *                                 be called from the RFCOMM when a data
- *                                 packet is received.
- *
- ******************************************************************************/
-extern int PORT_SetDataCallback(uint16_t port_handle,
-                                tPORT_DATA_CALLBACK* p_cb);
-
 extern int PORT_SetDataCOCallback(uint16_t port_handle,
                                   tPORT_DATA_CO_CALLBACK* p_port_cb);
 /*******************************************************************************
@@ -341,18 +294,6 @@
 
 /*******************************************************************************
  *
- * Function         PORT_GetRxQueueCnt
- *
- * Description      This function return number of buffers on the rx queue.
- *
- * Parameters:      handle     - Handle returned in the RFCOMM_CreateConnection
- *                  p_rx_queue_count - Pointer to return queue count in.
- *
- ******************************************************************************/
-extern int PORT_GetRxQueueCnt(uint16_t handle, uint16_t* p_rx_queue_count);
-
-/*******************************************************************************
- *
  * Function         PORT_GetState
  *
  * Description      This function is called to fill tPORT_STATE structure
@@ -367,43 +308,6 @@
 
 /*******************************************************************************
  *
- * Function         PORT_Control
- *
- * Description      This function directs a specified connection to pass control
- *                  control information to the peer device.
- *
- * Parameters:      handle     - Handle returned in the RFCOMM_CreateConnection
- *                  signal     - specify the function to be passed
- *
- ******************************************************************************/
-#define PORT_SET_DTRDSR 0x01
-#define PORT_CLR_DTRDSR 0x02
-#define PORT_SET_CTSRTS 0x03
-#define PORT_CLR_CTSRTS 0x04
-#define PORT_SET_RI 0x05  /* DCE only */
-#define PORT_CLR_RI 0x06  /* DCE only */
-#define PORT_SET_DCD 0x07 /* DCE only */
-#define PORT_CLR_DCD 0x08 /* DCE only */
-#define PORT_BREAK 0x09   /* Break event */
-
-extern int PORT_Control(uint16_t handle, uint8_t signal);
-
-/*******************************************************************************
- *
- * Function         PORT_FlowControl
- *
- * Description      This function directs a specified connection to pass
- *                  flow control message to the peer device.  Enable flag passed
- *                  shows if port can accept more data.
- *
- * Parameters:      handle     - Handle returned in the RFCOMM_CreateConnection
- *                  enable     - enables data flow
- *
- ******************************************************************************/
-extern int PORT_FlowControl(uint16_t handle, bool enable);
-
-/*******************************************************************************
- *
  * Function         PORT_FlowControl_MaxCredit
  *
  * Description      This function directs a specified connection to pass
@@ -417,20 +321,6 @@
  ******************************************************************************/
 extern int PORT_FlowControl_MaxCredit(uint16_t handle, bool enable);
 
-/*******************************************************************************
- *
- * Function         PORT_GetModemStatus
- *
- * Description      This function retrieves modem control signals.  Normally
- *                  application will call this function after a callback
- *                  function is called with notification that one of signals
- *                  has been changed.
- *
- * Parameters:      handle     - Handle returned in the RFCOMM_CreateConnection
- *                               callback.
- *                  p_signal   - specify the pointer to control signals info
- *
- ******************************************************************************/
 #define PORT_DTRDSR_ON 0x01
 #define PORT_CTSRTS_ON 0x02
 #define PORT_RING_ON 0x04
@@ -447,103 +337,12 @@
   (PORT_DTRDSR_ON | PORT_CTSRTS_ON | PORT_DCD_ON)
 #define PORT_DUN_DEFAULT_SIGNAL_STATE (PORT_DTRDSR_ON | PORT_CTSRTS_ON)
 
-extern int PORT_GetModemStatus(uint16_t handle, uint8_t* p_control_signal);
-
-/*******************************************************************************
- *
- * Function         PORT_ClearError
- *
- * Description      This function retreives information about a communications
- *                  error and reports current status of a connection.  The
- *                  function should be called when an error occures to clear
- *                  the connection error flag and to enable additional read
- *                  and write operations.
- *
- * Parameters:      handle     - Handle returned in the RFCOMM_CreateConnection
- *                  p_errors   - pointer of the variable to receive error codes
- *                  p_status   - pointer to the tPORT_STATUS structur to receive
- *                               connection status
- *
- ******************************************************************************/
-
 #define PORT_ERR_BREAK 0x01   /* Break condition occured on the peer device */
 #define PORT_ERR_OVERRUN 0x02 /* Overrun is reported by peer device */
 #define PORT_ERR_FRAME 0x04   /* Framing error reported by peer device */
 #define PORT_ERR_RXOVER 0x08  /* Input queue overflow occured */
 #define PORT_ERR_TXFULL 0x10  /* Output queue overflow occured */
 
-typedef struct {
-#define PORT_FLAG_CTS_HOLD 0x01  /* Tx is waiting for CTS signal */
-#define PORT_FLAG_DSR_HOLD 0x02  /* Tx is waiting for DSR signal */
-#define PORT_FLAG_RLSD_HOLD 0x04 /* Tx is waiting for RLSD signal */
-
-  uint16_t flags;
-  uint16_t in_queue_size;  /* Number of bytes in the input queue */
-  uint16_t out_queue_size; /* Number of bytes in the output queue */
-  uint16_t mtu_size;       /* peer MTU size */
-} tPORT_STATUS;
-
-extern int PORT_ClearError(uint16_t handle, uint16_t* p_errors,
-                           tPORT_STATUS* p_status);
-
-/*******************************************************************************
- *
- * Function         PORT_SendError
- *
- * Description      This function send a communications error to the peer device
- *
- * Parameters:      handle     - Handle returned in the RFCOMM_CreateConnection
- *                  errors     - receive error codes
- *
- ******************************************************************************/
-extern int PORT_SendError(uint16_t handle, uint8_t errors);
-
-/*******************************************************************************
- *
- * Function         PORT_GetQueueStatus
- *
- * Description      This function reports current status of a connection.
- *
- * Parameters:      handle     - Handle returned in the RFCOMM_CreateConnection
- *                  p_status   - pointer to the tPORT_STATUS structur to receive
- *                               connection status
- *
- ******************************************************************************/
-extern int PORT_GetQueueStatus(uint16_t handle, tPORT_STATUS* p_status);
-
-/*******************************************************************************
- *
- * Function         PORT_Purge
- *
- * Description      This function discards all the data from the output or
- *                  input queues of the specified connection.
- *
- * Parameters:      handle     - Handle returned in the RFCOMM_CreateConnection
- *                  purge_flags - specify the action to take.
- *
- ******************************************************************************/
-#define PORT_PURGE_TXCLEAR 0x01
-#define PORT_PURGE_RXCLEAR 0x02
-
-extern int PORT_Purge(uint16_t handle, uint8_t purge_flags);
-
-/*******************************************************************************
- *
- * Function         PORT_Read
- *
- * Description      This function returns the pointer to the buffer received
- *                  from the peer device.  Normally application will call this
- *                  function after receiving PORT_EVT_RXCHAR event.
- *                  Application calling this function is responsible to free
- *                  buffer returned.
- *
- * Parameters:      handle     - Handle returned in the RFCOMM_CreateConnection
- *                                callback.
- *                  pp_buf      - pointer to address of buffer with data,
- *
- ******************************************************************************/
-extern int PORT_Read(uint16_t handle, BT_HDR** pp_buf);
-
 /*******************************************************************************
  *
  * Function         PORT_ReadData
@@ -563,19 +362,6 @@
 
 /*******************************************************************************
  *
- * Function         PORT_Write
- *
- * Description      This function to send BT buffer to the peer device.
- *                  Application should not free the buffer.
- *
- * Parameters:      handle     - Handle returned in the RFCOMM_CreateConnection
- *                  p_buf       - pointer to the buffer with data,
- *
- ******************************************************************************/
-extern int PORT_Write(uint16_t handle, BT_HDR* p_buf);
-
-/*******************************************************************************
- *
  * Function         PORT_WriteData
  *
  * Description      This function is called from the legacy application to
@@ -604,19 +390,6 @@
 
 /*******************************************************************************
  *
- * Function         PORT_Test
- *
- * Description      Application can call this function to send RFCOMM Test frame
- *
- * Parameters:      handle      - Handle returned in the RFCOMM_CreateConnection
- *                  p_data      - Data area
- *                  max_len     - Byte count requested
- *
- ******************************************************************************/
-extern int PORT_Test(uint16_t handle, uint8_t* p_data, uint16_t len);
-
-/*******************************************************************************
- *
  * Function         RFCOMM_Init
  *
  * Description      This function is called to initialize RFCOMM layer
diff --git a/stack/include/profiles_api.h b/stack/include/profiles_api.h
index f91826d..f533deb 100644
--- a/stack/include/profiles_api.h
+++ b/stack/include/profiles_api.h
@@ -19,57 +19,12 @@
 #ifndef PROFILES_API_H
 #define PROFILES_API_H
 
-#include "bt_target.h"
-#include "btm_api.h"
-
 /*****************************************************************************
  *  Constants
  ****************************************************************************/
 #define BT_PASS 0 /* Used for general successful function returns */
 
 /*** Port entity passes back 8 bit errors; will use upper byte offset ***/
-#define PORT_ERR_GRP 0x0000  /* base offset for port entity */
 #define GAP_ERR_GRP 0x0100   /* base offset for GAP profile */
-#define SPP_ERR_GRP 0x0200   /* base offset for serial port profile */
-#define HCRP_ERR_GRP 0x0300  /* base offset for HCRP */
-#define HCRPM_ERR_GRP 0x0400 /* base offset for HCRPM */
-
-/* #define HSP2_ERR_GRP 0x0F00 */
-
-/* security level definitions (tBT_SECURITY) */
-#define BT_USE_DEF_SECURITY 0
-#define BT_SEC_MODE_NONE BTM_SEC_MODE_NONE
-#define BT_SEC_MODE_SERVICE BTM_SEC_MODE_SERVICE
-#define BT_SEC_MODE_LINK BTM_SEC_MODE_LINK
-
-/* security mask definitions (tBT_SECURITY) */
-/* The following definitions are OR'd together to form the security
- * requirements */
-/* Inbound call requires authorization */
-#define BT_SEC_IN_AUTHORIZE BTM_SEC_IN_AUTHORIZE
-/* Inbound call requires authentication */
-#define BT_SEC_IN_AUTHENTICATE BTM_SEC_IN_AUTHENTICATE
-/* Inbound call requires encryption */
-#define BT_SEC_IN_ENCRYPT BTM_SEC_IN_ENCRYPT
-/* Outbound call requires authorization */
-#define BT_SEC_OUT_AUTHORIZE BTM_SEC_OUT_AUTHORIZE
-/* Outbound call requires authentication */
-#define BT_SEC_OUT_AUTHENTICATE BTM_SEC_OUT_AUTHENTICATE
-/* Outbound call requires encryption */
-#define BT_SEC_OUT_ENCRYPT BTM_SEC_OUT_ENCRYPT
-
-/*****************************************************************************
- *  Type Definitions
- ****************************************************************************/
-
-/*
- * Security Definitions
- *      This following definitions are used to indicate the security
- *      requirements for a service.
-*/
-typedef struct {
-  uint8_t level;
-  uint8_t mask;
-} tBT_SECURITY;
 
 #endif /* PROFILES_API_H */
diff --git a/stack/include/sdp_api.h b/stack/include/sdp_api.h
index 7752fd6..3bc1a30 100644
--- a/stack/include/sdp_api.h
+++ b/stack/include/sdp_api.h
@@ -83,9 +83,6 @@
   tSDP_DR_DATA data;
 } tSDP_DATA;
 
-/* Define a callback function for when discovery result is received. */
-typedef void(tSDP_DISC_RES_CB)(uint16_t event, tSDP_DATA* p_data);
-
 /* Define a structure to hold the discovered service information. */
 typedef struct {
   union {
@@ -240,21 +237,6 @@
 
 /*******************************************************************************
  *
- * Function         SDP_FindAttributeInDb
- *
- * Description      This function queries an SDP database for a specific
- *                  attribute. If the p_start_rec pointer is NULL, it looks from
- *                  the beginning of the database, else it continues from the
- *                  next record after p_start_rec.
- *
- * Returns          Pointer to matching record, or NULL
- *
- ******************************************************************************/
-tSDP_DISC_REC* SDP_FindAttributeInDb(tSDP_DISCOVERY_DB* p_db, uint16_t attr_id,
-                                     tSDP_DISC_REC* p_start_rec);
-
-/*******************************************************************************
- *
  * Function         SDP_FindAttributeInRec
  *
  * Description      This function searches an SDP discovery record for a
@@ -348,20 +330,6 @@
 
 /*******************************************************************************
  *
- * Function         SDP_FindAddProtoListsElemInRec
- *
- * Description      This function looks at a specific discovery record for a
- *                  protocol list element.
- *
- * Returns          true if found, false if not
- *                  If found, the passed protocol list element is filled in.
- *
- ******************************************************************************/
-bool SDP_FindAddProtoListsElemInRec(tSDP_DISC_REC* p_rec, uint16_t layer_uuid,
-                                    tSDP_PROTOCOL_ELEM* p_elem);
-
-/*******************************************************************************
- *
  * Function         SDP_FindProfileVersionInRec
  *
  * Description      This function looks at a specific discovery record for the
@@ -410,21 +378,6 @@
 
 /*******************************************************************************
  *
- * Function         SDP_ReadRecord
- *
- * Description      This function is called to get the raw data of the record
- *                  with the given handle from the database.
- *
- * Returns          -1, if the record is not found.
- *                  Otherwise, the offset (0 or 1) to start of data in p_data.
- *
- *                  The size of data copied into p_data is in *p_data_len.
- *
- ******************************************************************************/
-int32_t SDP_ReadRecord(uint32_t handle, uint8_t* p_data, int32_t* p_data_len);
-
-/*******************************************************************************
- *
  * Function         SDP_AddAttribute
  *
  * Description      This function is called to add an attribute to a record.
diff --git a/stack/l2cap/l2c_api.cc b/stack/l2cap/l2c_api.cc
index b5a9564..38b0ee8 100644
--- a/stack/l2cap/l2c_api.cc
+++ b/stack/l2cap/l2c_api.cc
@@ -39,6 +39,8 @@
 #include "hcimsgs.h"
 #include "l2c_int.h"
 #include "l2cdefs.h"
+#include "main/shim/l2c_api.h"
+#include "main/shim/shim.h"
 #include "osi/include/allocator.h"
 #include "osi/include/log.h"
 
@@ -59,7 +61,12 @@
  *
  ******************************************************************************/
 uint16_t L2CA_Register(uint16_t psm, tL2CAP_APPL_INFO* p_cb_info,
-                       bool enable_snoop) {
+                       bool enable_snoop, tL2CAP_ERTM_INFO* p_ertm_info) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::L2CA_Register(psm, p_cb_info, enable_snoop,
+                                          p_ertm_info);
+  }
+
   tL2C_RCB* p_rcb;
   uint16_t vpsm = psm;
 
@@ -123,6 +130,10 @@
  *
  ******************************************************************************/
 void L2CA_Deregister(uint16_t psm) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::L2CA_Deregister(psm);
+  }
+
   tL2C_RCB* p_rcb;
   tL2C_CCB* p_ccb;
   tL2C_LCB* p_lcb;
@@ -169,6 +180,10 @@
  *
  ******************************************************************************/
 uint16_t L2CA_AllocatePSM(void) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::L2CA_AllocatePSM();
+  }
+
   bool done = false;
   uint16_t psm = l2cb.dyn_psm;
 
@@ -204,6 +219,10 @@
  *
  ******************************************************************************/
 uint16_t L2CA_AllocateLePSM(void) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::L2CA_AllocateLePSM();
+  }
+
   bool done = false;
   uint16_t psm = l2cb.le_dyn_psm;
   uint16_t count = 0;
@@ -250,6 +269,10 @@
  *
  ******************************************************************************/
 void L2CA_FreeLePSM(uint16_t psm) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::L2CA_FreeLePSM(psm);
+  }
+
   L2CAP_TRACE_API("%s: to free psm=%d", __func__, psm);
 
   if ((psm < LE_DYNAMIC_PSM_START) || (psm > LE_DYNAMIC_PSM_END)) {
@@ -277,6 +300,10 @@
  *
  ******************************************************************************/
 uint16_t L2CA_ConnectReq(uint16_t psm, const RawAddress& p_bd_addr) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::L2CA_ConnectReq(psm, p_bd_addr);
+  }
+
   return L2CA_ErtmConnectReq(psm, p_bd_addr, nullptr);
 }
 
@@ -299,6 +326,10 @@
  ******************************************************************************/
 uint16_t L2CA_ErtmConnectReq(uint16_t psm, const RawAddress& p_bd_addr,
                              tL2CAP_ERTM_INFO* p_ertm_info) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::L2CA_ErtmConnectReq(psm, p_bd_addr, p_ertm_info);
+  }
+
   VLOG(1) << __func__ << "BDA " << p_bd_addr
           << StringPrintf(" PSM: 0x%04x allowed:0x%x preferred:%d", psm,
                           (p_ertm_info) ? p_ertm_info->allowed_modes : 0,
@@ -400,6 +431,10 @@
  *
  ******************************************************************************/
 uint16_t L2CA_RegisterLECoc(uint16_t psm, tL2CAP_APPL_INFO* p_cb_info) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::L2CA_RegisterLECoc(psm, p_cb_info);
+  }
+
   L2CAP_TRACE_API("%s called for LE PSM: 0x%04x", __func__, psm);
 
   /* Verify that the required callback info has been filled in
@@ -464,6 +499,10 @@
  *
  ******************************************************************************/
 void L2CA_DeregisterLECoc(uint16_t psm) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::L2CA_DeregisterLECoc(psm);
+  }
+
   L2CAP_TRACE_API("%s called for PSM: 0x%04x", __func__, psm);
 
   tL2C_RCB* p_rcb = l2cu_find_ble_rcb_by_psm(psm);
@@ -510,6 +549,10 @@
  ******************************************************************************/
 uint16_t L2CA_ConnectLECocReq(uint16_t psm, const RawAddress& p_bd_addr,
                               tL2CAP_LE_CFG_INFO* p_cfg) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::L2CA_ConnectLECocReq(psm, p_bd_addr, p_cfg);
+  }
+
   VLOG(1) << __func__ << " BDA: " << p_bd_addr
           << StringPrintf(" PSM: 0x%04x", psm);
 
@@ -598,6 +641,11 @@
 bool L2CA_ConnectLECocRsp(const RawAddress& p_bd_addr, uint8_t id,
                           uint16_t lcid, uint16_t result, uint16_t status,
                           tL2CAP_LE_CFG_INFO* p_cfg) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::L2CA_ConnectLECocRsp(p_bd_addr, id, lcid, result,
+                                                 status, p_cfg);
+  }
+
   VLOG(1) << __func__ << " BDA: " << p_bd_addr
           << StringPrintf(" CID: 0x%04x Result: %d Status: %d", lcid, result,
                           status);
@@ -656,6 +704,10 @@
  *
  ******************************************************************************/
 bool L2CA_GetPeerLECocConfig(uint16_t lcid, tL2CAP_LE_CFG_INFO* peer_cfg) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::L2CA_GetPeerLECocConfig(lcid, peer_cfg);
+  }
+
   L2CAP_TRACE_API("%s CID: 0x%04x", __func__, lcid);
 
   tL2C_CCB* p_ccb = l2cu_find_ccb_by_cid(NULL, lcid);
@@ -670,44 +722,6 @@
   return true;
 }
 
-bool L2CA_SetConnectionCallbacks(uint16_t local_cid,
-                                 const tL2CAP_APPL_INFO* callbacks) {
-  CHECK(callbacks != NULL);
-  CHECK(callbacks->pL2CA_ConnectInd_Cb == NULL);
-  CHECK(callbacks->pL2CA_ConnectCfm_Cb != NULL);
-  CHECK(callbacks->pL2CA_ConfigInd_Cb != NULL);
-  CHECK(callbacks->pL2CA_ConfigCfm_Cb != NULL);
-  CHECK(callbacks->pL2CA_DisconnectInd_Cb != NULL);
-  CHECK(callbacks->pL2CA_DisconnectCfm_Cb != NULL);
-  CHECK(callbacks->pL2CA_CongestionStatus_Cb != NULL);
-  CHECK(callbacks->pL2CA_DataInd_Cb != NULL);
-  CHECK(callbacks->pL2CA_TxComplete_Cb != NULL);
-
-  tL2C_CCB* channel_control_block = l2cu_find_ccb_by_cid(NULL, local_cid);
-  if (!channel_control_block) {
-    LOG_ERROR(LOG_TAG,
-              "%s no channel control block found for L2CAP LCID=0x%04x.",
-              __func__, local_cid);
-    return false;
-  }
-
-  // We're making a connection-specific registration control block so we check
-  // if we already have a private one allocated to us on the heap. If not, we
-  // make a new allocation, mark it as heap-allocated, and inherit the fields
-  // from the old control block.
-  tL2C_RCB* registration_control_block = channel_control_block->p_rcb;
-  if (!channel_control_block->should_free_rcb) {
-    registration_control_block = (tL2C_RCB*)osi_calloc(sizeof(tL2C_RCB));
-
-    *registration_control_block = *channel_control_block->p_rcb;
-    channel_control_block->p_rcb = registration_control_block;
-    channel_control_block->should_free_rcb = true;
-  }
-
-  registration_control_block->api = *callbacks;
-  return true;
-}
-
 /*******************************************************************************
  *
  * Function         L2CA_ConnectRsp
@@ -721,6 +735,11 @@
  ******************************************************************************/
 bool L2CA_ConnectRsp(const RawAddress& p_bd_addr, uint8_t id, uint16_t lcid,
                      uint16_t result, uint16_t status) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::L2CA_ConnectRsp(p_bd_addr, id, lcid, result,
+                                            status);
+  }
+
   return L2CA_ErtmConnectRsp(p_bd_addr, id, lcid, result, status, NULL);
 }
 
@@ -738,6 +757,11 @@
 bool L2CA_ErtmConnectRsp(const RawAddress& p_bd_addr, uint8_t id, uint16_t lcid,
                          uint16_t result, uint16_t status,
                          tL2CAP_ERTM_INFO* p_ertm_info) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::L2CA_ErtmConnectRsp(p_bd_addr, id, lcid, result,
+                                                status, p_ertm_info);
+  }
+
   tL2C_LCB* p_lcb;
   tL2C_CCB* p_ccb;
 
@@ -817,6 +841,10 @@
  *
  ******************************************************************************/
 bool L2CA_ConfigReq(uint16_t cid, tL2CAP_CFG_INFO* p_cfg) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::L2CA_ConfigReq(cid, p_cfg);
+  }
+
   tL2C_CCB* p_ccb;
 
   L2CAP_TRACE_API(
@@ -866,6 +894,10 @@
  *
  ******************************************************************************/
 bool L2CA_ConfigRsp(uint16_t cid, tL2CAP_CFG_INFO* p_cfg) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::L2CA_ConfigRsp(cid, p_cfg);
+  }
+
   tL2C_CCB* p_ccb;
 
   L2CAP_TRACE_API(
@@ -909,6 +941,10 @@
  *
  ******************************************************************************/
 bool L2CA_DisconnectReq(uint16_t cid) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::L2CA_DisconnectReq(cid);
+  }
+
   tL2C_CCB* p_ccb;
 
   L2CAP_TRACE_API("L2CA_DisconnectReq()  CID: 0x%04x", cid);
@@ -936,6 +972,10 @@
  *
  ******************************************************************************/
 bool L2CA_DisconnectRsp(uint16_t cid) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::L2CA_DisconnectRsp(cid);
+  }
+
   tL2C_CCB* p_ccb;
 
   L2CAP_TRACE_API("L2CA_DisconnectRsp()  CID: 0x%04x", cid);
@@ -952,119 +992,11 @@
   return (true);
 }
 
-/*******************************************************************************
- *
- * Function         L2CA_Ping
- *
- * Description      Higher layers call this function to send an echo request.
- *
- * Returns          true if echo request sent, else false.
- *
- ******************************************************************************/
-bool L2CA_Ping(const RawAddress& p_bd_addr, tL2CA_ECHO_RSP_CB* p_callback) {
-  tL2C_LCB* p_lcb;
-
-  VLOG(1) << __func__ << " BDA: " << p_bd_addr;
-
-  /* Fail if we have not established communications with the controller */
-  if (!BTM_IsDeviceUp()) return (false);
-
-  /* First, see if we already have a link to the remote */
-  p_lcb = l2cu_find_lcb_by_bd_addr(p_bd_addr, BT_TRANSPORT_BR_EDR);
-  if (p_lcb == NULL) {
-    /* No link. Get an LCB and start link establishment */
-    p_lcb = l2cu_allocate_lcb(p_bd_addr, false, BT_TRANSPORT_BR_EDR);
-    if (p_lcb == NULL) {
-      L2CAP_TRACE_WARNING("L2CAP - no LCB for L2CA_ping");
-      return (false);
-    }
-    if (!l2cu_create_conn_br_edr(p_lcb)) {
-      return (false);
-    }
-
-    p_lcb->p_echo_rsp_cb = p_callback;
-
-    return (true);
-  }
-
-  /* We only allow 1 ping outstanding at a time */
-  if (p_lcb->p_echo_rsp_cb != NULL) {
-    L2CAP_TRACE_WARNING("L2CAP - rejected second L2CA_ping");
-    return (false);
-  }
-
-  /* Have a link control block. If link is disconnecting, tell user to retry
-   * later */
-  if (p_lcb->link_state == LST_DISCONNECTING) {
-    L2CAP_TRACE_WARNING("L2CAP - L2CA_ping rejected - link disconnecting");
-    return (false);
-  }
-
-  /* Save address of callback */
-  p_lcb->p_echo_rsp_cb = p_callback;
-
-  if (p_lcb->link_state == LST_CONNECTED) {
-    l2cu_adj_id(p_lcb, L2CAP_ADJ_BRCM_ID); /* Make sure not using Broadcom ID */
-    l2cu_send_peer_echo_req(p_lcb, NULL, 0);
-    alarm_set_on_mloop(p_lcb->l2c_lcb_timer, L2CAP_ECHO_RSP_TIMEOUT_MS,
-                       l2c_lcb_timer_timeout, p_lcb);
-  }
-
-  return (true);
-}
-
-/*******************************************************************************
- *
- * Function         L2CA_Echo
- *
- * Description      Higher layers call this function to send an echo request
- *                  with application-specific data.
- *
- * Returns          true if echo request sent, else false.
- *
- ******************************************************************************/
-bool L2CA_Echo(const RawAddress& p_bd_addr, BT_HDR* p_data,
-               tL2CA_ECHO_DATA_CB* p_callback) {
-  tL2C_LCB* p_lcb;
-  uint8_t* pp;
-
-  VLOG(1) << __func__ << " BDA: " << p_bd_addr;
-  ;
-
-  /* Fail if we have not established communications with the controller */
-  if (!BTM_IsDeviceUp()) return (false);
-
-  if (RawAddress::kAny == p_bd_addr && (p_data == NULL)) {
-    /* Only register callback without sending message. */
-    l2cb.p_echo_data_cb = p_callback;
-    return true;
-  }
-
-  /* We assume the upper layer will call this function only when the link is
-   * established. */
-  p_lcb = l2cu_find_lcb_by_bd_addr(p_bd_addr, BT_TRANSPORT_BR_EDR);
-  if (p_lcb == NULL) {
-    L2CAP_TRACE_ERROR("L2CA_Echo ERROR : link not established");
-    return false;
-  }
-
-  if (p_lcb->link_state != LST_CONNECTED) {
-    L2CAP_TRACE_ERROR("L2CA_Echo ERROR : link is not connected");
-    return false;
-  }
-
-  /* Save address of callback */
-  l2cb.p_echo_data_cb = p_callback;
-
-  /* Set the pointer to the beginning of the data */
-  pp = (uint8_t*)(p_data + 1) + p_data->offset;
-  l2cu_adj_id(p_lcb, L2CAP_ADJ_BRCM_ID); /* Make sure not using Broadcom ID */
-  l2cu_send_peer_echo_req(p_lcb, pp, p_data->len);
-
-  return (true);
-}
-
 bool L2CA_GetIdentifiers(uint16_t lcid, uint16_t* rcid, uint16_t* handle) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::L2CA_GetIdentifiers(lcid, rcid, handle);
+  }
+
   tL2C_CCB* control_block = l2cu_find_ccb_by_cid(NULL, lcid);
   if (!control_block) return false;
 
@@ -1094,6 +1026,10 @@
  *                  set up.
  ******************************************************************************/
 bool L2CA_SetIdleTimeout(uint16_t cid, uint16_t timeout, bool is_global) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::L2CA_SetIdleTimeout(cid, timeout, is_global);
+  }
+
   tL2C_CCB* p_ccb;
   tL2C_LCB* p_lcb;
 
@@ -1140,6 +1076,11 @@
  ******************************************************************************/
 bool L2CA_SetIdleTimeoutByBdAddr(const RawAddress& bd_addr, uint16_t timeout,
                                  tBT_TRANSPORT transport) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::L2CA_SetIdleTimeoutByBdAddr(bd_addr, timeout,
+                                                        transport);
+  }
+
   tL2C_LCB* p_lcb;
 
   if (RawAddress::kAny != bd_addr) {
@@ -1200,6 +1141,10 @@
  *
  ******************************************************************************/
 uint8_t L2CA_SetDesireRole(uint8_t new_role) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::L2CA_SetDesireRole(new_role);
+  }
+
   L2CAP_TRACE_API("L2CA_SetDesireRole() new:x%x, disallow_switch:%d", new_role,
                   l2cb.disallow_switch);
 
@@ -1221,63 +1166,6 @@
 
 /*******************************************************************************
  *
- * Function     L2CA_LocalLoopbackReq
- *
- * Description  This function sets up a CID for local loopback
- *
- * Returns      CID of 0 if none.
- *
- ******************************************************************************/
-uint16_t L2CA_LocalLoopbackReq(uint16_t psm, uint16_t handle,
-                               const RawAddress& p_bd_addr) {
-  tL2C_LCB* p_lcb;
-  tL2C_CCB* p_ccb;
-  tL2C_RCB* p_rcb;
-
-  L2CAP_TRACE_API("L2CA_LocalLoopbackReq()  PSM: %d  Handle: 0x%04x", psm,
-                  handle);
-
-  /* Fail if we have not established communications with the controller */
-  if (!BTM_IsDeviceUp()) {
-    L2CAP_TRACE_WARNING("L2CAP loop req - BTU not ready");
-    return (0);
-  }
-
-  /* Fail if the PSM is not registered */
-  p_rcb = l2cu_find_rcb_by_psm(psm);
-  if (p_rcb == NULL) {
-    L2CAP_TRACE_WARNING("L2CAP - no RCB for L2CA_conn_req, PSM: %d", psm);
-    return (0);
-  }
-
-  p_lcb = l2cu_allocate_lcb(p_bd_addr, false, BT_TRANSPORT_BR_EDR);
-  if (p_lcb == NULL) {
-    L2CAP_TRACE_WARNING("L2CAP - no LCB for L2CA_conn_req");
-    return (0);
-  }
-
-  p_lcb->link_state = LST_CONNECTED;
-  p_lcb->handle = handle;
-
-  /* Allocate a channel control block */
-  p_ccb = l2cu_allocate_ccb(p_lcb, 0);
-  if (p_ccb == NULL) {
-    L2CAP_TRACE_WARNING("L2CAP - no CCB for L2CA_conn_req");
-    return (0);
-  }
-
-  /* Save registration info */
-  p_ccb->p_rcb = p_rcb;
-  p_ccb->chnl_state = CST_OPEN;
-  p_ccb->remote_cid = p_ccb->local_cid;
-  p_ccb->config_done = CFG_DONE_MASK;
-
-  /* Return the local CID as our handle */
-  return (p_ccb->local_cid);
-}
-
-/*******************************************************************************
- *
  * Function         L2CA_SetAclPriority
  *
  * Description      Sets the transmission priority for a channel.
@@ -1288,6 +1176,10 @@
  *
  ******************************************************************************/
 bool L2CA_SetAclPriority(const RawAddress& bd_addr, uint8_t priority) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::L2CA_SetAclPriority(bd_addr, priority);
+  }
+
   VLOG(1) << __func__ << " BDA: " << bd_addr
           << ", priority: " << std::to_string(priority);
   return (l2cu_set_acl_priority(bd_addr, priority, false));
@@ -1295,87 +1187,6 @@
 
 /*******************************************************************************
  *
- * Function         L2CA_FlowControl
- *
- * Description      Higher layers call this function to flow control a channel.
- *
- *                  data_enabled - true data flows, false data is stopped
- *
- * Returns          true if valid channel, else false
- *
- ******************************************************************************/
-bool L2CA_FlowControl(uint16_t cid, bool data_enabled) {
-  tL2C_CCB* p_ccb;
-  bool on_off = !data_enabled;
-
-  L2CAP_TRACE_API("L2CA_FlowControl(%d)  CID: 0x%04x", on_off, cid);
-
-  /* Find the channel control block. We don't know the link it is on. */
-  p_ccb = l2cu_find_ccb_by_cid(NULL, cid);
-  if (p_ccb == NULL) {
-    L2CAP_TRACE_WARNING(
-        "L2CAP - no CCB for L2CA_FlowControl, CID: 0x%04x  data_enabled: %d",
-        cid, data_enabled);
-    return (false);
-  }
-
-  if (p_ccb->peer_cfg.fcr.mode != L2CAP_FCR_ERTM_MODE) {
-    L2CAP_TRACE_EVENT("L2CA_FlowControl()  invalid mode:%d",
-                      p_ccb->peer_cfg.fcr.mode);
-    return (false);
-  }
-  if (p_ccb->fcrb.local_busy != on_off) {
-    p_ccb->fcrb.local_busy = on_off;
-
-    if ((p_ccb->chnl_state == CST_OPEN) && (!p_ccb->fcrb.wait_ack)) {
-      if (on_off)
-        l2c_fcr_send_S_frame(p_ccb, L2CAP_FCR_SUP_RNR, 0);
-      else
-        l2c_fcr_send_S_frame(p_ccb, L2CAP_FCR_SUP_RR, L2CAP_FCR_P_BIT);
-    }
-  }
-
-  return (true);
-}
-
-/*******************************************************************************
- *
- * Function         L2CA_SendTestSFrame
- *
- * Description      Higher layers call this function to send a test S-frame.
- *
- * Returns          true if valid Channel, else false
- *
- ******************************************************************************/
-bool L2CA_SendTestSFrame(uint16_t cid, uint8_t sup_type, uint8_t back_track) {
-  tL2C_CCB* p_ccb;
-
-  L2CAP_TRACE_API(
-      "L2CA_SendTestSFrame()  CID: 0x%04x  Type: 0x%02x  back_track: %u", cid,
-      sup_type, back_track);
-
-  /* Find the channel control block. We don't know the link it is on. */
-  p_ccb = l2cu_find_ccb_by_cid(NULL, cid);
-  if (p_ccb == NULL) {
-    L2CAP_TRACE_WARNING("L2CAP - no CCB for L2CA_SendTestSFrame, CID: %d", cid);
-    return (false);
-  }
-
-  if ((p_ccb->chnl_state != CST_OPEN) ||
-      (p_ccb->peer_cfg.fcr.mode != L2CAP_FCR_ERTM_MODE))
-    return (false);
-
-  p_ccb->fcrb.next_seq_expected -= back_track;
-
-  l2c_fcr_send_S_frame(
-      p_ccb, (uint16_t)(sup_type & 3),
-      (uint16_t)(sup_type & (L2CAP_FCR_P_BIT | L2CAP_FCR_F_BIT)));
-
-  return (true);
-}
-
-/*******************************************************************************
- *
  * Function         L2CA_SetTxPriority
  *
  * Description      Sets the transmission priority for a channel.
@@ -1384,6 +1195,10 @@
  *
  ******************************************************************************/
 bool L2CA_SetTxPriority(uint16_t cid, tL2CAP_CHNL_PRIORITY priority) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::L2CA_SetTxPriority(cid, priority);
+  }
+
   tL2C_CCB* p_ccb;
 
   L2CAP_TRACE_API("L2CA_SetTxPriority()  CID: 0x%04x, priority:%d", cid,
@@ -1405,39 +1220,6 @@
 
 /*******************************************************************************
  *
- * Function         L2CA_SetChnlDataRate
- *
- * Description      Sets the tx/rx data rate for a channel.
- *
- * Returns          true if a valid channel, else false
- *
- ******************************************************************************/
-bool L2CA_SetChnlDataRate(uint16_t cid, tL2CAP_CHNL_DATA_RATE tx,
-                          tL2CAP_CHNL_DATA_RATE rx) {
-  tL2C_CCB* p_ccb;
-
-  L2CAP_TRACE_API("L2CA_SetChnlDataRate()  CID: 0x%04x, tx:%d, rx:%d", cid, tx,
-                  rx);
-
-  /* Find the channel control block. We don't know the link it is on. */
-  p_ccb = l2cu_find_ccb_by_cid(NULL, cid);
-  if (p_ccb == NULL) {
-    L2CAP_TRACE_WARNING("L2CAP - no CCB for L2CA_SetChnlDataRate, CID: %d",
-                        cid);
-    return (false);
-  }
-
-  p_ccb->tx_data_rate = tx;
-  p_ccb->rx_data_rate = rx;
-
-  /* Adjust channel buffer allocation */
-  l2c_link_adjust_chnl_allocation();
-
-  return (true);
-}
-
-/*******************************************************************************
- *
  * Function         L2CA_SetFlushTimeout
  *
  * Description      This function set the automatic flush time out in Baseband
@@ -1461,6 +1243,10 @@
  *                  the ACL link.
  ******************************************************************************/
 bool L2CA_SetFlushTimeout(const RawAddress& bd_addr, uint16_t flush_tout) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::L2CA_SetFlushTimeout(bd_addr, flush_tout);
+  }
+
   tL2C_LCB* p_lcb;
   uint16_t hci_flush_to;
   uint32_t temp;
@@ -1545,6 +1331,11 @@
  ******************************************************************************/
 bool L2CA_GetPeerFeatures(const RawAddress& bd_addr, uint32_t* p_ext_feat,
                           uint8_t* p_chnl_mask) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::L2CA_GetPeerFeatures(bd_addr, p_ext_feat,
+                                                 p_chnl_mask);
+  }
+
   tL2C_LCB* p_lcb;
 
   /* We must already have a link to the remote */
@@ -1565,55 +1356,6 @@
   return true;
 }
 
-/*******************************************************************************
- *
- *  Function         L2CA_GetBDAddrbyHandle
- *
- *  Description      Get BD address for the given HCI handle
- *
- *  Parameters:      HCI handle
- *                   BD address of the peer
- *
- *  Return value:    true if found lcb for the given handle, false otherwise
- *
- ******************************************************************************/
-bool L2CA_GetBDAddrbyHandle(uint16_t handle, RawAddress& bd_addr) {
-  tL2C_LCB* p_lcb = NULL;
-  bool found_dev = false;
-
-  p_lcb = l2cu_find_lcb_by_handle(handle);
-  if (p_lcb) {
-    found_dev = true;
-    bd_addr = p_lcb->remote_bd_addr;
-  }
-
-  return found_dev;
-}
-
-/*******************************************************************************
- *
- *  Function         L2CA_GetChnlFcrMode
- *
- *  Description      Get the channel FCR mode
- *
- *  Parameters:      Local CID
- *
- *  Return value:    Channel mode
- *
- ******************************************************************************/
-uint8_t L2CA_GetChnlFcrMode(uint16_t lcid) {
-  tL2C_CCB* p_ccb = l2cu_find_ccb_by_cid(NULL, lcid);
-
-  if (p_ccb) {
-    L2CAP_TRACE_API("L2CA_GetChnlFcrMode() returns mode %d",
-                    p_ccb->peer_cfg.fcr.mode);
-    return (p_ccb->peer_cfg.fcr.mode);
-  }
-
-  L2CAP_TRACE_API("L2CA_GetChnlFcrMode() returns mode L2CAP_FCR_BASIC_MODE");
-  return (L2CAP_FCR_BASIC_MODE);
-}
-
 #if (L2CAP_NUM_FIXED_CHNLS > 0)
 /*******************************************************************************
  *
@@ -1629,6 +1371,10 @@
  ******************************************************************************/
 bool L2CA_RegisterFixedChannel(uint16_t fixed_cid,
                                tL2CAP_FIXED_CHNL_REG* p_freg) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::L2CA_RegisterFixedChannel(fixed_cid, p_freg);
+  }
+
   if ((fixed_cid < L2CAP_FIRST_FIXED_CHNL) ||
       (fixed_cid > L2CAP_LAST_FIXED_CHNL)) {
     L2CAP_TRACE_ERROR("L2CA_RegisterFixedChannel()  Invalid CID: 0x%04x",
@@ -1654,12 +1400,21 @@
  *
  ******************************************************************************/
 bool L2CA_ConnectFixedChnl(uint16_t fixed_cid, const RawAddress& rem_bda) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::L2CA_ConnectFixedChnl(fixed_cid, rem_bda);
+  }
+
   uint8_t phy = controller_get_interface()->get_le_all_initiating_phys();
   return L2CA_ConnectFixedChnl(fixed_cid, rem_bda, phy);
 }
 
 bool L2CA_ConnectFixedChnl(uint16_t fixed_cid, const RawAddress& rem_bda,
                            uint8_t initiating_phys) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::L2CA_ConnectFixedChnl(fixed_cid, rem_bda,
+                                                  initiating_phys);
+  }
+
   tL2C_LCB* p_lcb;
   tBT_TRANSPORT transport = BT_TRANSPORT_BR_EDR;
 
@@ -1772,6 +1527,10 @@
  ******************************************************************************/
 uint16_t L2CA_SendFixedChnlData(uint16_t fixed_cid, const RawAddress& rem_bda,
                                 BT_HDR* p_buf) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::L2CA_SendFixedChnlData(fixed_cid, rem_bda, p_buf);
+  }
+
   tL2C_LCB* p_lcb;
   tBT_TRANSPORT transport = BT_TRANSPORT_BR_EDR;
 
@@ -1885,6 +1644,10 @@
  *
  ******************************************************************************/
 bool L2CA_RemoveFixedChnl(uint16_t fixed_cid, const RawAddress& rem_bda) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::L2CA_RemoveFixedChnl(fixed_cid, rem_bda);
+  }
+
   tL2C_LCB* p_lcb;
   tL2C_CCB* p_ccb;
   tBT_TRANSPORT transport = BT_TRANSPORT_BR_EDR;
@@ -1952,6 +1715,11 @@
  ******************************************************************************/
 bool L2CA_SetFixedChannelTout(const RawAddress& rem_bda, uint16_t fixed_cid,
                               uint16_t idle_tout) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::L2CA_SetFixedChannelTout(rem_bda, fixed_cid,
+                                                     idle_tout);
+  }
+
   tL2C_LCB* p_lcb;
   tBT_TRANSPORT transport = BT_TRANSPORT_BR_EDR;
 
@@ -1984,109 +1752,6 @@
 
 /*******************************************************************************
  *
- * Function     L2CA_GetCurrentConfig
- *
- * Description  This function returns configurations of L2CAP channel
- *              pp_our_cfg : pointer of our saved configuration options
- *              p_our_cfg_bits : valid config in bitmap
- *              pp_peer_cfg: pointer of peer's saved configuration options
- *              p_peer_cfg_bits : valid config in bitmap
- *
- * Returns      true if successful
- *
- ******************************************************************************/
-bool L2CA_GetCurrentConfig(uint16_t lcid, tL2CAP_CFG_INFO** pp_our_cfg,
-                           tL2CAP_CH_CFG_BITS* p_our_cfg_bits,
-                           tL2CAP_CFG_INFO** pp_peer_cfg,
-                           tL2CAP_CH_CFG_BITS* p_peer_cfg_bits) {
-  tL2C_CCB* p_ccb;
-
-  L2CAP_TRACE_API("L2CA_GetCurrentConfig()  CID: 0x%04x", lcid);
-
-  p_ccb = l2cu_find_ccb_by_cid(NULL, lcid);
-
-  if (p_ccb) {
-    *pp_our_cfg = &(p_ccb->our_cfg);
-
-    /* convert valid config items into bitmap */
-    *p_our_cfg_bits = 0;
-    if (p_ccb->our_cfg.mtu_present) *p_our_cfg_bits |= L2CAP_CH_CFG_MASK_MTU;
-    if (p_ccb->our_cfg.qos_present) *p_our_cfg_bits |= L2CAP_CH_CFG_MASK_QOS;
-    if (p_ccb->our_cfg.flush_to_present)
-      *p_our_cfg_bits |= L2CAP_CH_CFG_MASK_FLUSH_TO;
-    if (p_ccb->our_cfg.fcr_present) *p_our_cfg_bits |= L2CAP_CH_CFG_MASK_FCR;
-    if (p_ccb->our_cfg.fcs_present) *p_our_cfg_bits |= L2CAP_CH_CFG_MASK_FCS;
-    if (p_ccb->our_cfg.ext_flow_spec_present)
-      *p_our_cfg_bits |= L2CAP_CH_CFG_MASK_EXT_FLOW_SPEC;
-
-    *pp_peer_cfg = &(p_ccb->peer_cfg);
-    *p_peer_cfg_bits = p_ccb->peer_cfg_bits;
-
-    return true;
-  } else {
-    L2CAP_TRACE_ERROR("No CCB for CID:0x%04x", lcid);
-    return false;
-  }
-}
-
-/*******************************************************************************
- *
- * Function      L2CA_GetConnectionConfig
- *
- * Description  This function returns configurations of L2CAP channel
- *              pp_l2c_ccb : pointer to this channels L2CAP ccb data.
- *
- * Returns      true if successful
- *
- ******************************************************************************/
-bool L2CA_GetConnectionConfig(uint16_t lcid, uint16_t* mtu, uint16_t* rcid,
-                              uint16_t* handle) {
-  tL2C_CCB* p_ccb = l2cu_find_ccb_by_cid(NULL, lcid);
-  ;
-
-  L2CAP_TRACE_API("%s CID: 0x%04x", __func__, lcid);
-
-  if (p_ccb) {
-    *mtu = L2CAP_MTU_SIZE;
-    if (p_ccb->our_cfg.mtu_present) *mtu = p_ccb->our_cfg.mtu;
-
-    *rcid = p_ccb->remote_cid;
-    *handle = p_ccb->p_lcb->handle;
-    return true;
-  }
-
-  L2CAP_TRACE_ERROR("%s No CCB for CID:0x%04x", __func__, lcid);
-  return false;
-}
-
-/*******************************************************************************
- *
- * Function         L2CA_RegForNoCPEvt
- *
- * Description      Register callback for Number of Completed Packets event.
- *
- * Input Param      p_cb - callback for Number of completed packets event
- *                  p_bda - BT address of remote device
- *
- * Returns          true if registered OK, else false
- *
- ******************************************************************************/
-bool L2CA_RegForNoCPEvt(tL2CA_NOCP_CB* p_cb, const RawAddress& p_bda) {
-  tL2C_LCB* p_lcb;
-
-  /* Find the link that is associated with this remote bdaddr */
-  p_lcb = l2cu_find_lcb_by_bd_addr(p_bda, BT_TRANSPORT_BR_EDR);
-
-  /* If no link for this handle, nothing to do. */
-  if (!p_lcb) return false;
-
-  p_lcb->p_nocp_cb = p_cb;
-
-  return true;
-}
-
-/*******************************************************************************
- *
  * Function         L2CA_DataWrite
  *
  * Description      Higher layers call this function to write data.
@@ -2098,6 +1763,10 @@
  *
  ******************************************************************************/
 uint8_t L2CA_DataWrite(uint16_t cid, BT_HDR* p_data) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::L2CA_DataWrite(cid, p_data);
+  }
+
   L2CAP_TRACE_API("L2CA_DataWrite()  CID: 0x%04x  Len: %d", cid, p_data->len);
   return l2c_data_write(cid, p_data, L2CAP_FLUSHABLE_CH_BASED);
 }
@@ -2113,8 +1782,11 @@
  *
  ******************************************************************************/
 bool L2CA_SetChnlFlushability(uint16_t cid, bool is_flushable) {
-#if (L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE)
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::L2CA_SetChnlFlushability(cid, is_flushable);
+  }
 
+#if (L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE)
   tL2C_CCB* p_ccb;
 
   /* Find the channel control block. We don't know the link it is on. */
@@ -2137,28 +1809,6 @@
 
 /*******************************************************************************
  *
- * Function         L2CA_DataWriteEx
- *
- * Description      Higher layers call this function to write data with extended
- *                  flags.
- *                  flags : L2CAP_FLUSHABLE_CH_BASED
- *                          L2CAP_FLUSHABLE_PKT
- *                          L2CAP_NON_FLUSHABLE_PKT
- *
- * Returns          L2CAP_DW_SUCCESS, if data accepted, else false
- *                  L2CAP_DW_CONGESTED, if data accepted and the channel is
- *                                      congested
- *                  L2CAP_DW_FAILED, if error
- *
- ******************************************************************************/
-uint8_t L2CA_DataWriteEx(uint16_t cid, BT_HDR* p_data, uint16_t flags) {
-  L2CAP_TRACE_API("L2CA_DataWriteEx()  CID: 0x%04x  Len: %d Flags:0x%04X", cid,
-                  p_data->len, flags);
-  return l2c_data_write(cid, p_data, flags);
-}
-
-/*******************************************************************************
- *
  * Function     L2CA_FlushChannel
  *
  * Description  This function flushes none, some or all buffers queued up
@@ -2172,6 +1822,10 @@
  *
  ******************************************************************************/
 uint16_t L2CA_FlushChannel(uint16_t lcid, uint16_t num_to_flush) {
+  if (bluetooth::shim::is_gd_shim_enabled()) {
+    return bluetooth::shim::L2CA_FlushChannel(lcid, num_to_flush);
+  }
+
   tL2C_CCB* p_ccb;
   tL2C_LCB* p_lcb;
   uint16_t num_left = 0, num_flushed1 = 0, num_flushed2 = 0;
diff --git a/stack/l2cap/l2c_fcr.cc b/stack/l2cap/l2c_fcr.cc
index 857a0bf..3cb7c20 100644
--- a/stack/l2cap/l2c_fcr.cc
+++ b/stack/l2cap/l2c_fcr.cc
@@ -31,8 +31,6 @@
 
 #include "bt_common.h"
 #include "bt_types.h"
-#include "btm_api.h"
-#include "btm_int.h"
 #include "btu.h"
 #include "common/time_util.h"
 #include "hcimsgs.h"
@@ -2163,23 +2161,29 @@
           /* Peer wants ERTM and we support it */
           if ((peer_mode == L2CAP_FCR_ERTM_MODE) &&
               (p_ccb->ertm_info.allowed_modes & L2CAP_FCR_CHAN_OPT_ERTM)) {
-            L2CAP_TRACE_DEBUG("l2c_fcr_renegotiate_chan(Trying ERTM)");
+            L2CAP_TRACE_DEBUG("%s(Trying ERTM)", __func__);
             p_ccb->our_cfg.fcr.mode = L2CAP_FCR_ERTM_MODE;
             can_renegotiate = true;
-          } else /* Falls through */
-
-          case L2CAP_FCR_ERTM_MODE: {
+          } else if (p_ccb->ertm_info.allowed_modes &
+                     L2CAP_FCR_CHAN_OPT_BASIC) {
             /* We can try basic for any other peer mode if we support it */
-            if (p_ccb->ertm_info.allowed_modes & L2CAP_FCR_CHAN_OPT_BASIC) {
-              L2CAP_TRACE_DEBUG("l2c_fcr_renegotiate_chan(Trying Basic)");
-              can_renegotiate = true;
-              p_ccb->our_cfg.fcr.mode = L2CAP_FCR_BASIC_MODE;
-            }
-          } break;
+            L2CAP_TRACE_DEBUG("%s(Trying Basic)", __func__);
+            can_renegotiate = true;
+            p_ccb->our_cfg.fcr.mode = L2CAP_FCR_BASIC_MODE;
+          }
+          break;
+        case L2CAP_FCR_ERTM_MODE:
+          /* We can try basic for any other peer mode if we support it */
+          if (p_ccb->ertm_info.allowed_modes & L2CAP_FCR_CHAN_OPT_BASIC) {
+            L2CAP_TRACE_DEBUG("%s(Trying Basic)", __func__);
+            can_renegotiate = true;
+            p_ccb->our_cfg.fcr.mode = L2CAP_FCR_BASIC_MODE;
+          }
+          break;
 
-          default:
-            /* All other scenarios cannot be renegotiated */
-            break;
+        default:
+          /* All other scenarios cannot be renegotiated */
+          break;
       }
 
       if (can_renegotiate) {
diff --git a/stack/l2cap/l2c_link.cc b/stack/l2cap/l2c_link.cc
index 958425d..f15e123 100644
--- a/stack/l2cap/l2c_link.cc
+++ b/stack/l2cap/l2c_link.cc
@@ -171,7 +171,12 @@
     p_lcb->link_state = LST_CONNECTING;
   }
 
-  if (p_lcb->link_state != LST_CONNECTING) {
+  if ((p_lcb->link_state == LST_CONNECTED) &&
+      (status == HCI_ERR_CONNECTION_EXISTS)) {
+    L2CAP_TRACE_WARNING("%s: An ACL connection already exists. Handle:%d",
+                        __func__, handle);
+    return (true);
+  } else if (p_lcb->link_state != LST_CONNECTING) {
     L2CAP_TRACE_ERROR("L2CAP got conn_comp in bad state: %d  status: 0x%d",
                       p_lcb->link_state, status);
 
diff --git a/stack/l2cap/l2c_main.cc b/stack/l2cap/l2c_main.cc
index 52d77c5..2e533bc 100644
--- a/stack/l2cap/l2c_main.cc
+++ b/stack/l2cap/l2c_main.cc
@@ -30,7 +30,6 @@
 
 #include "bt_common.h"
 #include "bt_target.h"
-#include "btm_int.h"
 #include "btu.h"
 #include "device/include/controller.h"
 #include "hci/include/btsnoop.h"
diff --git a/stack/l2cap/l2c_utils.cc b/stack/l2cap/l2c_utils.cc
index 593f12f..da30f2b 100644
--- a/stack/l2cap/l2c_utils.cc
+++ b/stack/l2cap/l2c_utils.cc
@@ -1902,13 +1902,13 @@
     /* Make sure service type is not a reserved value; otherwise let upper
        layer decide if acceptable
     */
-    if (p_cfg->qos.service_type <= GUARANTEED) {
+    if (p_cfg->qos.service_type <= SVC_TYPE_GUARANTEED) {
       p_ccb->peer_cfg.qos = p_cfg->qos;
       p_ccb->peer_cfg.qos_present = true;
       p_ccb->peer_cfg_bits |= L2CAP_CH_CFG_MASK_QOS;
     } else /* Illegal service type value */
     {
-      p_cfg->qos.service_type = BEST_EFFORT;
+      p_cfg->qos.service_type = SVC_TYPE_BEST_EFFORT;
       qos_type_ok = false;
     }
   }
@@ -2244,7 +2244,8 @@
 
   /* Check with the BT manager if details about remote device are known */
   p_inq_info = BTM_InqDbRead(p_lcb->remote_bd_addr);
-  if (p_inq_info != NULL) {
+  if ((p_inq_info != NULL) &&
+      (p_inq_info->results.inq_result_type & BTM_INQ_RESULT_BR)) {
     page_scan_rep_mode = p_inq_info->results.page_scan_rep_mode;
     page_scan_mode = p_inq_info->results.page_scan_mode;
     clock_offset = (uint16_t)(p_inq_info->results.clock_offset);
diff --git a/stack/l2cap/l2cap_client.cc b/stack/l2cap/l2cap_client.cc
deleted file mode 100644
index 76e4138..0000000
--- a/stack/l2cap/l2cap_client.cc
+++ /dev/null
@@ -1,460 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2014 Google, Inc.
- *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at:
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- *
- ******************************************************************************/
-
-#define LOG_TAG "bt_l2cap_client"
-
-#include "stack/include/l2cap_client.h"
-
-#include <base/logging.h>
-#include <string.h>
-
-#include "osi/include/allocator.h"
-#include "osi/include/buffer.h"
-#include "osi/include/list.h"
-#include "osi/include/log.h"
-#include "osi/include/osi.h"
-#include "stack/include/l2c_api.h"
-
-struct l2cap_client_t {
-  l2cap_client_callbacks_t callbacks;
-  void* context;
-
-  uint16_t local_channel_id;
-  uint16_t remote_mtu;
-  bool configured_self;
-  bool configured_peer;
-  bool is_congested;
-  list_t* outbound_fragments;
-};
-
-static void connect_completed_cb(uint16_t local_channel_id,
-                                 uint16_t error_code);
-static void config_request_cb(uint16_t local_channel_id,
-                              tL2CAP_CFG_INFO* requested_parameters);
-static void config_completed_cb(uint16_t local_channel_id,
-                                tL2CAP_CFG_INFO* negotiated_parameters);
-static void disconnect_request_cb(uint16_t local_channel_id, bool ack_required);
-static void disconnect_completed_cb(uint16_t local_channel_id,
-                                    uint16_t error_code);
-static void congestion_cb(uint16_t local_channel_id, bool is_congested);
-static void read_ready_cb(uint16_t local_channel_id, BT_HDR* packet);
-static void write_completed_cb(uint16_t local_channel_id,
-                               uint16_t packets_completed);
-
-static void fragment_packet(l2cap_client_t* client, buffer_t* packet);
-static void dispatch_fragments(l2cap_client_t* client);
-static l2cap_client_t* find(uint16_t local_channel_id);
-
-// From the Bluetooth Core specification.
-static const uint16_t L2CAP_MTU_DEFAULT = 672;
-static const uint16_t L2CAP_MTU_MINIMUM = 48;
-
-static const tL2CAP_APPL_INFO l2cap_callbacks = {
-    .pL2CA_ConnectCfm_Cb = connect_completed_cb,
-    .pL2CA_ConfigInd_Cb = config_request_cb,
-    .pL2CA_ConfigCfm_Cb = config_completed_cb,
-    .pL2CA_DisconnectInd_Cb = disconnect_request_cb,
-    .pL2CA_DisconnectCfm_Cb = disconnect_completed_cb,
-    .pL2CA_CongestionStatus_Cb = congestion_cb,
-    .pL2CA_DataInd_Cb = read_ready_cb,
-    .pL2CA_TxComplete_Cb = write_completed_cb,
-};
-
-static list_t*
-    l2cap_clients;  // A list of l2cap_client_t. Container does not own objects.
-
-buffer_t* l2cap_buffer_new(size_t size) {
-  buffer_t* buf = buffer_new(size + L2CAP_MIN_OFFSET);
-  buffer_t* slice = NULL;
-  if (buf) slice = buffer_new_slice(buf, size);
-  buffer_free(buf);
-  return slice;
-}
-
-l2cap_client_t* l2cap_client_new(const l2cap_client_callbacks_t* callbacks,
-                                 void* context) {
-  CHECK(callbacks != NULL);
-  CHECK(callbacks->connected != NULL);
-  CHECK(callbacks->disconnected != NULL);
-  CHECK(callbacks->read_ready != NULL);
-  CHECK(callbacks->write_ready != NULL);
-
-  if (!l2cap_clients) {
-    l2cap_clients = list_new(NULL);
-    if (!l2cap_clients) {
-      LOG_ERROR(LOG_TAG, "%s unable to allocate space for L2CAP client list.",
-                __func__);
-      return NULL;
-    }
-  }
-
-  l2cap_client_t* ret = (l2cap_client_t*)osi_calloc(sizeof(l2cap_client_t));
-
-  ret->callbacks = *callbacks;
-  ret->context = context;
-
-  ret->remote_mtu = L2CAP_MTU_DEFAULT;
-  ret->outbound_fragments = list_new(NULL);
-
-  list_append(l2cap_clients, ret);
-
-  return ret;
-}
-
-void l2cap_client_free(l2cap_client_t* client) {
-  if (!client) return;
-
-  list_remove(l2cap_clients, client);
-  l2cap_client_disconnect(client);
-  list_free(client->outbound_fragments);
-  osi_free(client);
-}
-
-bool l2cap_client_connect(l2cap_client_t* client,
-                          const RawAddress& remote_bdaddr, uint16_t psm) {
-  CHECK(client != NULL);
-  CHECK(psm != 0);
-  CHECK(!remote_bdaddr.IsEmpty());
-  CHECK(client->local_channel_id == 0);
-  CHECK(!client->configured_self);
-  CHECK(!client->configured_peer);
-  CHECK(!L2C_INVALID_PSM(psm));
-
-  client->local_channel_id = L2CA_ConnectReq(psm, remote_bdaddr);
-  if (!client->local_channel_id) {
-    LOG_ERROR(LOG_TAG, "%s unable to create L2CAP connection.", __func__);
-    return false;
-  }
-
-  L2CA_SetConnectionCallbacks(client->local_channel_id, &l2cap_callbacks);
-  return true;
-}
-
-void l2cap_client_disconnect(l2cap_client_t* client) {
-  CHECK(client != NULL);
-
-  if (client->local_channel_id && !L2CA_DisconnectReq(client->local_channel_id))
-    LOG_ERROR(LOG_TAG, "%s unable to send disconnect message for LCID 0x%04x.",
-              __func__, client->local_channel_id);
-
-  client->local_channel_id = 0;
-  client->remote_mtu = L2CAP_MTU_DEFAULT;
-  client->configured_self = false;
-  client->configured_peer = false;
-  client->is_congested = false;
-
-  for (const list_node_t* node = list_begin(client->outbound_fragments);
-       node != list_end(client->outbound_fragments); node = list_next(node))
-    osi_free(list_node(node));
-
-  list_clear(client->outbound_fragments);
-}
-
-bool l2cap_client_is_connected(const l2cap_client_t* client) {
-  CHECK(client != NULL);
-
-  return client->local_channel_id != 0 && client->configured_self &&
-         client->configured_peer;
-}
-
-bool l2cap_client_write(l2cap_client_t* client, buffer_t* packet) {
-  CHECK(client != NULL);
-  CHECK(packet != NULL);
-  CHECK(l2cap_client_is_connected(client));
-
-  if (client->is_congested) return false;
-
-  fragment_packet(client, packet);
-  dispatch_fragments(client);
-  return true;
-}
-
-static void connect_completed_cb(uint16_t local_channel_id,
-                                 uint16_t error_code) {
-  CHECK(local_channel_id != 0);
-
-  l2cap_client_t* client = find(local_channel_id);
-  if (!client) {
-    LOG_ERROR(LOG_TAG, "%s unable to find L2CAP client for LCID 0x%04x.",
-              __func__, local_channel_id);
-    return;
-  }
-
-  if (error_code != L2CAP_CONN_OK) {
-    LOG_ERROR(LOG_TAG, "%s error connecting L2CAP channel: %d.", __func__,
-              error_code);
-    client->callbacks.disconnected(client, client->context);
-    return;
-  }
-
-  // Use default L2CAP parameters.
-  tL2CAP_CFG_INFO desired_parameters;
-  memset(&desired_parameters, 0, sizeof(desired_parameters));
-  if (!L2CA_ConfigReq(local_channel_id, &desired_parameters)) {
-    LOG_ERROR(LOG_TAG, "%s error sending L2CAP config parameters.", __func__);
-    client->callbacks.disconnected(client, client->context);
-  }
-}
-
-static void config_request_cb(uint16_t local_channel_id,
-                              tL2CAP_CFG_INFO* requested_parameters) {
-  tL2CAP_CFG_INFO response;
-  l2cap_client_t* client = find(local_channel_id);
-
-  if (!client) {
-    LOG_ERROR(LOG_TAG, "%s unable to find L2CAP client matching LCID 0x%04x.",
-              __func__, local_channel_id);
-    return;
-  }
-
-  memset(&response, 0, sizeof(response));
-  response.result = L2CAP_CFG_OK;
-
-  if (requested_parameters->mtu_present) {
-    // Make sure the peer chose an MTU at least as large as the minimum L2CAP
-    // MTU defined by the Bluetooth Core spec.
-    if (requested_parameters->mtu < L2CAP_MTU_MINIMUM) {
-      response.mtu = L2CAP_MTU_MINIMUM;
-      response.mtu_present = true;
-      response.result = L2CAP_CFG_UNACCEPTABLE_PARAMS;
-    } else {
-      client->remote_mtu = requested_parameters->mtu;
-    }
-  }
-
-  if (requested_parameters->fcr_present) {
-    if (requested_parameters->fcr.mode != L2CAP_FCR_BASIC_MODE) {
-      response.fcr_present = true;
-      response.fcr = requested_parameters->fcr;
-      response.fcr.mode = L2CAP_FCR_BASIC_MODE;
-      response.result = L2CAP_CFG_UNACCEPTABLE_PARAMS;
-    }
-  }
-
-  if (!L2CA_ConfigRsp(local_channel_id, &response)) {
-    LOG_ERROR(LOG_TAG, "%s unable to send config response for LCID 0x%04x.",
-              __func__, local_channel_id);
-    l2cap_client_disconnect(client);
-    return;
-  }
-
-  // If we've configured both endpoints, let the listener know we've connected.
-  client->configured_peer = true;
-  if (l2cap_client_is_connected(client))
-    client->callbacks.connected(client, client->context);
-}
-
-static void config_completed_cb(uint16_t local_channel_id,
-                                tL2CAP_CFG_INFO* negotiated_parameters) {
-  l2cap_client_t* client = find(local_channel_id);
-
-  if (!client) {
-    LOG_ERROR(LOG_TAG, "%s unable to find L2CAP client matching LCID 0x%04x.",
-              __func__, local_channel_id);
-    return;
-  }
-
-  switch (negotiated_parameters->result) {
-    // We'll get another configuration response later.
-    case L2CAP_CFG_PENDING:
-      break;
-
-    case L2CAP_CFG_UNACCEPTABLE_PARAMS:
-      // TODO: see if we can renegotiate parameters instead of dropping the
-      // connection.
-      LOG_WARN(
-          LOG_TAG,
-          "%s dropping L2CAP connection due to unacceptable config parameters.",
-          __func__);
-      l2cap_client_disconnect(client);
-      break;
-
-    case L2CAP_CFG_OK:
-      // If we've configured both endpoints, let the listener know we've
-      // connected.
-      client->configured_self = true;
-      if (l2cap_client_is_connected(client))
-        client->callbacks.connected(client, client->context);
-      break;
-
-    // Failure, no further parameter negotiation possible.
-    default:
-      LOG_WARN(LOG_TAG,
-               "%s L2CAP parameter negotiation failed with error code %d.",
-               __func__, negotiated_parameters->result);
-      l2cap_client_disconnect(client);
-      break;
-  }
-}
-
-static void disconnect_request_cb(uint16_t local_channel_id,
-                                  bool ack_required) {
-  l2cap_client_t* client = find(local_channel_id);
-  if (!client) {
-    LOG_ERROR(LOG_TAG, "%s unable to find L2CAP client with LCID 0x%04x.",
-              __func__, local_channel_id);
-    return;
-  }
-
-  if (ack_required) L2CA_DisconnectRsp(local_channel_id);
-
-  // We already sent a disconnect response so this LCID is now invalid.
-  client->local_channel_id = 0;
-  l2cap_client_disconnect(client);
-
-  client->callbacks.disconnected(client, client->context);
-}
-
-static void disconnect_completed_cb(uint16_t local_channel_id,
-                                    UNUSED_ATTR uint16_t error_code) {
-  CHECK(local_channel_id != 0);
-
-  l2cap_client_t* client = find(local_channel_id);
-  if (!client) {
-    LOG_ERROR(LOG_TAG, "%s unable to find L2CAP client with LCID 0x%04x.",
-              __func__, local_channel_id);
-    return;
-  }
-
-  client->local_channel_id = 0;
-  l2cap_client_disconnect(client);
-
-  client->callbacks.disconnected(client, client->context);
-}
-
-static void congestion_cb(uint16_t local_channel_id, bool is_congested) {
-  CHECK(local_channel_id != 0);
-
-  l2cap_client_t* client = find(local_channel_id);
-  if (!client) {
-    LOG_ERROR(LOG_TAG, "%s unable to find L2CAP client matching LCID 0x%04x.",
-              __func__, local_channel_id);
-    return;
-  }
-
-  client->is_congested = is_congested;
-
-  if (!is_congested) {
-    // If we just decongested, dispatch whatever we have left over in our queue.
-    // Once that's done, if we're still decongested, notify the listener so it
-    // can start writing again.
-    dispatch_fragments(client);
-    if (!client->is_congested)
-      client->callbacks.write_ready(client, client->context);
-  }
-}
-
-static void read_ready_cb(uint16_t local_channel_id, BT_HDR* packet) {
-  CHECK(local_channel_id != 0);
-
-  l2cap_client_t* client = find(local_channel_id);
-  if (!client) {
-    LOG_ERROR(LOG_TAG, "%s unable to find L2CAP client matching LCID 0x%04x.",
-              __func__, local_channel_id);
-    return;
-  }
-
-  // TODO(sharvil): eliminate copy from BT_HDR.
-  buffer_t* buffer = buffer_new(packet->len);
-  memcpy(buffer_ptr(buffer), packet->data + packet->offset, packet->len);
-  osi_free(packet);
-
-  client->callbacks.read_ready(client, buffer, client->context);
-  buffer_free(buffer);
-}
-
-static void write_completed_cb(UNUSED_ATTR uint16_t local_channel_id,
-                               UNUSED_ATTR uint16_t packets_completed) {
-  // Do nothing. We update congestion state based on the congestion callback
-  // and we've already removed items from outbound_fragments list so we don't
-  // really care how many packets were successfully dispatched.
-}
-
-static void fragment_packet(l2cap_client_t* client, buffer_t* packet) {
-  CHECK(client != NULL);
-  CHECK(packet != NULL);
-
-  // TODO(sharvil): eliminate copy into BT_HDR.
-  BT_HDR* bt_packet = static_cast<BT_HDR*>(
-      osi_malloc(buffer_length(packet) + L2CAP_MIN_OFFSET + sizeof(BT_HDR)));
-  bt_packet->offset = L2CAP_MIN_OFFSET;
-  bt_packet->len = buffer_length(packet);
-  memcpy(bt_packet->data + bt_packet->offset, buffer_ptr(packet),
-         buffer_length(packet));
-
-  for (;;) {
-    if (bt_packet->len <= client->remote_mtu) {
-      if (bt_packet->len > 0)
-        list_append(client->outbound_fragments, bt_packet);
-      else
-        osi_free(bt_packet);
-      break;
-    }
-
-    BT_HDR* fragment = static_cast<BT_HDR*>(
-        osi_malloc(client->remote_mtu + L2CAP_MIN_OFFSET + sizeof(BT_HDR)));
-    fragment->offset = L2CAP_MIN_OFFSET;
-    fragment->len = client->remote_mtu;
-    memcpy(fragment->data + fragment->offset,
-           bt_packet->data + bt_packet->offset, client->remote_mtu);
-
-    list_append(client->outbound_fragments, fragment);
-
-    bt_packet->offset += client->remote_mtu;
-    bt_packet->len -= client->remote_mtu;
-  }
-}
-
-static void dispatch_fragments(l2cap_client_t* client) {
-  CHECK(client != NULL);
-  CHECK(!client->is_congested);
-
-  while (!list_is_empty(client->outbound_fragments)) {
-    BT_HDR* packet = (BT_HDR*)list_front(client->outbound_fragments);
-    list_remove(client->outbound_fragments, packet);
-
-    switch (L2CA_DataWrite(client->local_channel_id, packet)) {
-      case L2CAP_DW_CONGESTED:
-        client->is_congested = true;
-        return;
-
-      case L2CAP_DW_FAILED:
-        LOG_ERROR(LOG_TAG,
-                  "%s error writing data to L2CAP connection LCID 0x%04x; "
-                  "disconnecting.",
-                  __func__, client->local_channel_id);
-        l2cap_client_disconnect(client);
-        return;
-
-      case L2CAP_DW_SUCCESS:
-        break;
-    }
-  }
-}
-
-static l2cap_client_t* find(uint16_t local_channel_id) {
-  CHECK(local_channel_id != 0);
-
-  for (const list_node_t* node = list_begin(l2cap_clients);
-       node != list_end(l2cap_clients); node = list_next(node)) {
-    l2cap_client_t* client = (l2cap_client_t*)list_node(node);
-    if (client->local_channel_id == local_channel_id) return client;
-  }
-
-  return NULL;
-}
diff --git a/stack/mcap/mca_cact.cc b/stack/mcap/mca_cact.cc
deleted file mode 100644
index e6d73d6..0000000
--- a/stack/mcap/mca_cact.cc
+++ /dev/null
@@ -1,563 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2009-2012 Broadcom Corporation
- *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at:
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- *
- ******************************************************************************/
-
-/******************************************************************************
- *
- *  This is the implementation file for the MCAP Control Channel Action
- *  Functions.
- *
- ******************************************************************************/
-#include <log/log.h>
-#include <string.h>
-#include "bt_common.h"
-#include "bt_target.h"
-#include "bt_utils.h"
-#include "btm_api.h"
-#include "mca_api.h"
-#include "mca_defs.h"
-#include "mca_int.h"
-#include "osi/include/osi.h"
-
-#include "btu.h"
-
-/*****************************************************************************
- * constants
- ****************************************************************************/
-/*******************************************************************************
- *
- * Function         mca_ccb_rsp_tout
- *
- * Description      This function processes the response timeout.
- *
- * Returns          void.
- *
- ******************************************************************************/
-void mca_ccb_rsp_tout(tMCA_CCB* p_ccb, UNUSED_ATTR tMCA_CCB_EVT* p_data) {
-  tMCA_CTRL evt_data;
-
-  mca_ccb_report_event(p_ccb, MCA_RSP_TOUT_IND_EVT, &evt_data);
-}
-
-/*******************************************************************************
- *
- * Function         mca_ccb_report_event
- *
- * Description      This function reports the given event.
- *
- * Returns          void.
- *
- ******************************************************************************/
-void mca_ccb_report_event(tMCA_CCB* p_ccb, uint8_t event, tMCA_CTRL* p_data) {
-  if (p_ccb && p_ccb->p_rcb && p_ccb->p_rcb->p_cback)
-    (*p_ccb->p_rcb->p_cback)(mca_rcb_to_handle(p_ccb->p_rcb),
-                             mca_ccb_to_hdl(p_ccb), event, p_data);
-}
-
-/*******************************************************************************
- *
- * Function         mca_ccb_free_msg
- *
- * Description      This function frees the received message.
- *
- * Returns          void.
- *
- ******************************************************************************/
-void mca_ccb_free_msg(UNUSED_ATTR tMCA_CCB* p_ccb, tMCA_CCB_EVT* p_data) {
-  osi_free(p_data);
-}
-
-/*******************************************************************************
- *
- * Function         mca_ccb_snd_req
- *
- * Description      This function builds a request and sends it to the peer.
- *
- * Returns          void.
- *
- ******************************************************************************/
-void mca_ccb_snd_req(tMCA_CCB* p_ccb, tMCA_CCB_EVT* p_data) {
-  tMCA_CCB_MSG* p_msg = (tMCA_CCB_MSG*)p_data;
-  uint8_t *p, *p_start;
-  bool is_abort = false;
-  tMCA_DCB* p_dcb;
-
-  MCA_TRACE_DEBUG("mca_ccb_snd_req cong=%d req=%d", p_ccb->cong,
-                  p_msg->op_code);
-  /* check for abort request */
-  if ((p_ccb->status == MCA_CCB_STAT_PENDING) &&
-      (p_msg->op_code == MCA_OP_MDL_ABORT_REQ)) {
-    p_dcb = mca_dcb_by_hdl(p_ccb->p_tx_req->dcb_idx);
-    /* the Abort API does not have the associated mdl_id.
-     * Get the mdl_id in dcb to compose the request */
-    p_msg->mdl_id = p_dcb->mdl_id;
-    mca_dcb_event(p_dcb, MCA_DCB_API_CLOSE_EVT, NULL);
-    osi_free_and_reset((void**)&p_ccb->p_tx_req);
-    p_ccb->status = MCA_CCB_STAT_NORM;
-    is_abort = true;
-  }
-
-  /* no pending outgoing messages or it's an abort request for a pending data
-   * channel */
-  if ((!p_ccb->p_tx_req) || is_abort) {
-    p_ccb->p_tx_req = p_msg;
-    if (!p_ccb->cong) {
-      BT_HDR* p_pkt = (BT_HDR*)osi_malloc(MCA_CTRL_MTU + sizeof(BT_HDR));
-
-      p_pkt->offset = L2CAP_MIN_OFFSET;
-      p = p_start = (uint8_t*)(p_pkt + 1) + L2CAP_MIN_OFFSET;
-      *p++ = p_msg->op_code;
-      UINT16_TO_BE_STREAM(p, p_msg->mdl_id);
-      if (p_msg->op_code == MCA_OP_MDL_CREATE_REQ) {
-        *p++ = p_msg->mdep_id;
-        *p++ = p_msg->param;
-      }
-      p_msg->hdr.layer_specific = true; /* mark this message as sent */
-      p_pkt->len = p - p_start;
-      L2CA_DataWrite(p_ccb->lcid, p_pkt);
-      uint64_t interval_ms = p_ccb->p_rcb->reg.rsp_tout * 1000;
-      alarm_set_on_mloop(p_ccb->mca_ccb_timer, interval_ms,
-                         mca_ccb_timer_timeout, p_ccb);
-    }
-    /* else the L2CAP channel is congested. keep the message to be sent later */
-  } else {
-    MCA_TRACE_WARNING("dropping api req");
-    osi_free(p_data);
-  }
-}
-
-/*******************************************************************************
- *
- * Function         mca_ccb_snd_rsp
- *
- * Description      This function builds a response and sends it to
- *                  the peer.
- *
- * Returns          void.
- *
- ******************************************************************************/
-void mca_ccb_snd_rsp(tMCA_CCB* p_ccb, tMCA_CCB_EVT* p_data) {
-  tMCA_CCB_MSG* p_msg = (tMCA_CCB_MSG*)p_data;
-  uint8_t *p, *p_start;
-  BT_HDR* p_pkt = (BT_HDR*)osi_malloc(MCA_CTRL_MTU + sizeof(BT_HDR));
-
-  MCA_TRACE_DEBUG("%s cong=%d req=%d", __func__, p_ccb->cong, p_msg->op_code);
-  /* assume that API functions verified the parameters */
-
-  p_pkt->offset = L2CAP_MIN_OFFSET;
-  p = p_start = (uint8_t*)(p_pkt + 1) + L2CAP_MIN_OFFSET;
-  *p++ = p_msg->op_code;
-  *p++ = p_msg->rsp_code;
-  UINT16_TO_BE_STREAM(p, p_msg->mdl_id);
-  // Only add extra parameters for MCA_RSP_SUCCESS message
-  if (p_msg->rsp_code == MCA_RSP_SUCCESS) {
-    // Append MDL configuration parameters
-    if (p_msg->op_code == MCA_OP_MDL_CREATE_RSP) {
-      *p++ = p_msg->param;
-    }
-    // Check MDL
-    if (p_msg->op_code == MCA_OP_MDL_CREATE_RSP ||
-        p_msg->op_code == MCA_OP_MDL_RECONNECT_RSP) {
-      mca_dcb_by_hdl(p_msg->dcb_idx);
-      BTM_SetSecurityLevel(false, "", BTM_SEC_SERVICE_MCAP_DATA,
-                           p_ccb->sec_mask, p_ccb->p_rcb->reg.data_psm,
-                           BTM_SEC_PROTO_MCA, p_msg->dcb_idx);
-      p_ccb->status = MCA_CCB_STAT_PENDING;
-      /* set p_tx_req to block API_REQ/API_RSP before DL is up */
-      osi_free_and_reset((void**)&p_ccb->p_tx_req);
-      p_ccb->p_tx_req = p_ccb->p_rx_msg;
-      p_ccb->p_rx_msg = NULL;
-      p_ccb->p_tx_req->dcb_idx = p_msg->dcb_idx;
-    }
-  }
-
-  osi_free_and_reset((void**)&p_ccb->p_rx_msg);
-  p_pkt->len = p - p_start;
-  L2CA_DataWrite(p_ccb->lcid, p_pkt);
-}
-
-/*******************************************************************************
- *
- * Function         mca_ccb_do_disconn
- *
- * Description      This function closes a control channel.
- *
- * Returns          void.
- *
- ******************************************************************************/
-void mca_ccb_do_disconn(tMCA_CCB* p_ccb, UNUSED_ATTR tMCA_CCB_EVT* p_data) {
-  mca_dcb_close_by_mdl_id(p_ccb, MCA_ALL_MDL_ID);
-  L2CA_DisconnectReq(p_ccb->lcid);
-}
-
-/*******************************************************************************
- *
- * Function         mca_ccb_cong
- *
- * Description      This function sets the congestion state for the CCB.
- *
- * Returns          void.
- *
- ******************************************************************************/
-void mca_ccb_cong(tMCA_CCB* p_ccb, tMCA_CCB_EVT* p_data) {
-  MCA_TRACE_DEBUG("mca_ccb_cong cong=%d/%d", p_ccb->cong, p_data->llcong);
-  p_ccb->cong = p_data->llcong;
-  if (!p_ccb->cong) {
-    /* if there's a held packet, send it now */
-    if (p_ccb->p_tx_req && !p_ccb->p_tx_req->hdr.layer_specific) {
-      p_data = (tMCA_CCB_EVT*)p_ccb->p_tx_req;
-      p_ccb->p_tx_req = NULL;
-      mca_ccb_snd_req(p_ccb, p_data);
-    }
-  }
-}
-
-/*******************************************************************************
- *
- * Function         mca_ccb_hdl_req
- *
- * Description      This function is called when a MCAP request is received from
- *                  the peer. It calls the application callback function to
- *                  report the event.
- *
- * Returns          void.
- *
- ******************************************************************************/
-void mca_ccb_hdl_req(tMCA_CCB* p_ccb, tMCA_CCB_EVT* p_data) {
-  BT_HDR* p_pkt = &p_data->hdr;
-  uint8_t *p, *p_start;
-  tMCA_DCB* p_dcb;
-  tMCA_CTRL evt_data;
-  tMCA_CCB_MSG* p_rx_msg = NULL;
-  uint8_t reject_code = MCA_RSP_NO_RESOURCE;
-  bool send_rsp = false;
-  bool check_req = false;
-  uint8_t reject_opcode;
-
-  MCA_TRACE_DEBUG("mca_ccb_hdl_req status:%d", p_ccb->status);
-  p_rx_msg = (tMCA_CCB_MSG*)p_pkt;
-  p = (uint8_t*)(p_pkt + 1) + p_pkt->offset;
-  evt_data.hdr.op_code = *p++;
-  reject_opcode = evt_data.hdr.op_code + 1;
-
-  if (p_pkt->len >= 3) {
-    BE_STREAM_TO_UINT16(evt_data.hdr.mdl_id, p);
-  } else {
-    android_errorWriteLog(0x534e4554, "110791536");
-    evt_data.hdr.mdl_id = 0;
-  }
-
-  MCA_TRACE_DEBUG("received mdl id: %d ", evt_data.hdr.mdl_id);
-  if (p_ccb->status == MCA_CCB_STAT_PENDING) {
-    MCA_TRACE_DEBUG("received req inpending state");
-    /* allow abort in pending state */
-    if ((p_ccb->status == MCA_CCB_STAT_PENDING) &&
-        (evt_data.hdr.op_code == MCA_OP_MDL_ABORT_REQ)) {
-      reject_code = MCA_RSP_SUCCESS;
-      send_rsp = true;
-      /* clear the pending status */
-      p_ccb->status = MCA_CCB_STAT_NORM;
-      if (p_ccb->p_tx_req &&
-          ((p_dcb = mca_dcb_by_hdl(p_ccb->p_tx_req->dcb_idx)) != NULL)) {
-        mca_dcb_dealloc(p_dcb, NULL);
-        osi_free_and_reset((void**)&p_ccb->p_tx_req);
-      }
-    } else
-      reject_code = MCA_RSP_BAD_OP;
-  } else if (p_ccb->p_rx_msg) {
-    MCA_TRACE_DEBUG("still handling prev req");
-    /* still holding previous message, reject this new one ?? */
-
-  } else if (p_ccb->p_tx_req) {
-    MCA_TRACE_DEBUG("still waiting for a response ctrl_vpsm:0x%x",
-                    p_ccb->ctrl_vpsm);
-    /* sent a request; waiting for response */
-    if (p_ccb->ctrl_vpsm == 0) {
-      MCA_TRACE_DEBUG("local is ACP. accept the cmd from INT");
-      /* local is acceptor, need to handle the request */
-      check_req = true;
-      reject_code = MCA_RSP_SUCCESS;
-      /* drop the previous request */
-      if ((p_ccb->p_tx_req->op_code == MCA_OP_MDL_CREATE_REQ) &&
-          ((p_dcb = mca_dcb_by_hdl(p_ccb->p_tx_req->dcb_idx)) != NULL)) {
-        mca_dcb_dealloc(p_dcb, NULL);
-      }
-      osi_free_and_reset((void**)&p_ccb->p_tx_req);
-      mca_stop_timer(p_ccb);
-    } else {
-      /*  local is initiator, ignore the req */
-      osi_free(p_pkt);
-      return;
-    }
-  } else if (p_pkt->layer_specific != MCA_RSP_SUCCESS) {
-    reject_code = (uint8_t)p_pkt->layer_specific;
-    if (((evt_data.hdr.op_code >= MCA_NUM_STANDARD_OPCODE) &&
-         (evt_data.hdr.op_code < MCA_FIRST_SYNC_OP)) ||
-        (evt_data.hdr.op_code > MCA_LAST_SYNC_OP)) {
-      /* invalid op code */
-      reject_opcode = MCA_OP_ERROR_RSP;
-      evt_data.hdr.mdl_id = 0;
-    }
-  } else {
-    check_req = true;
-    reject_code = MCA_RSP_SUCCESS;
-  }
-
-  if (check_req) {
-    if (reject_code == MCA_RSP_SUCCESS) {
-      reject_code = MCA_RSP_BAD_MDL;
-      if (MCA_IS_VALID_MDL_ID(evt_data.hdr.mdl_id) ||
-          ((evt_data.hdr.mdl_id == MCA_ALL_MDL_ID) &&
-           (evt_data.hdr.op_code == MCA_OP_MDL_DELETE_REQ))) {
-        reject_code = MCA_RSP_SUCCESS;
-        /* mdl_id is valid according to the spec */
-        switch (evt_data.hdr.op_code) {
-          case MCA_OP_MDL_CREATE_REQ:
-            evt_data.create_ind.dep_id = *p++;
-            evt_data.create_ind.cfg = *p++;
-            p_rx_msg->mdep_id = evt_data.create_ind.dep_id;
-            if (!mca_is_valid_dep_id(p_ccb->p_rcb, p_rx_msg->mdep_id)) {
-              MCA_TRACE_ERROR("%s: Invalid local MDEP ID %d", __func__,
-                              p_rx_msg->mdep_id);
-              reject_code = MCA_RSP_BAD_MDEP;
-            } else if (mca_ccb_uses_mdl_id(p_ccb, evt_data.hdr.mdl_id)) {
-              MCA_TRACE_DEBUG("the mdl_id is currently used in the CL(create)");
-              mca_dcb_close_by_mdl_id(p_ccb, evt_data.hdr.mdl_id);
-            } else {
-              /* check if this dep still have MDL available */
-              if (mca_dep_free_mdl(p_ccb, evt_data.create_ind.dep_id) == 0) {
-                MCA_TRACE_ERROR("%s: MAX_MDL is used by MDEP %d", __func__,
-                                evt_data.create_ind.dep_id);
-                reject_code = MCA_RSP_MDEP_BUSY;
-              }
-            }
-            break;
-
-          case MCA_OP_MDL_RECONNECT_REQ:
-            if (mca_ccb_uses_mdl_id(p_ccb, evt_data.hdr.mdl_id)) {
-              MCA_TRACE_ERROR("%s: MDL_ID %d busy, in CL(reconn)", __func__,
-                              evt_data.hdr.mdl_id);
-              reject_code = MCA_RSP_MDL_BUSY;
-            }
-            break;
-
-          case MCA_OP_MDL_ABORT_REQ:
-            reject_code = MCA_RSP_BAD_OP;
-            break;
-
-          case MCA_OP_MDL_DELETE_REQ:
-            /* delete the associated mdl */
-            mca_dcb_close_by_mdl_id(p_ccb, evt_data.hdr.mdl_id);
-            send_rsp = true;
-            break;
-        }
-      }
-    }
-  }
-
-  if (((reject_code != MCA_RSP_SUCCESS) &&
-       (evt_data.hdr.op_code != MCA_OP_SYNC_INFO_IND)) ||
-      send_rsp) {
-    BT_HDR* p_buf = (BT_HDR*)osi_malloc(MCA_CTRL_MTU + sizeof(BT_HDR));
-    p_buf->offset = L2CAP_MIN_OFFSET;
-    p = p_start = (uint8_t*)(p_buf + 1) + L2CAP_MIN_OFFSET;
-    *p++ = reject_opcode;
-    *p++ = reject_code;
-    bool valid_response = true;
-    switch (reject_opcode) {
-      // Fill in the rest of standard opcode response packet with mdl_id
-      case MCA_OP_ERROR_RSP:
-      case MCA_OP_MDL_CREATE_RSP:
-      case MCA_OP_MDL_RECONNECT_RSP:
-      case MCA_OP_MDL_ABORT_RSP:
-      case MCA_OP_MDL_DELETE_RSP:
-        UINT16_TO_BE_STREAM(p, evt_data.hdr.mdl_id);
-        break;
-      // Fill in the rest of clock sync opcode response packet with 0
-      case MCA_OP_SYNC_CAP_RSP:
-        // Page 37/58 MCAP V1.0 Spec: Total length (9) - 2 = 7
-        memset(p, 0, 7);
-        p += 7;
-        break;
-      case MCA_OP_SYNC_SET_RSP:
-        // Page 39/58 MCAP V1.0 Spec: Total length (16) - 2 = 14
-        memset(p, 0, 14);
-        p += 14;
-        break;
-      default:
-        MCA_TRACE_ERROR("%s: reject_opcode 0x%02x not recognized", __func__,
-                        reject_opcode);
-        valid_response = false;
-        break;
-    }
-    if (valid_response) {
-      p_buf->len = p - p_start;
-      MCA_TRACE_ERROR("%s: reject_opcode=0x%02x, reject_code=0x%02x, length=%d",
-                      __func__, reject_opcode, reject_code, p_buf->len);
-      L2CA_DataWrite(p_ccb->lcid, p_buf);
-    } else {
-      osi_free(p_buf);
-    }
-  }
-
-  if (reject_code == MCA_RSP_SUCCESS) {
-    /* use the received GKI buffer to store information to double check response
-     * API */
-    p_rx_msg->op_code = evt_data.hdr.op_code;
-    p_rx_msg->mdl_id = evt_data.hdr.mdl_id;
-    p_ccb->p_rx_msg = p_rx_msg;
-    if (send_rsp) {
-      osi_free(p_pkt);
-      p_ccb->p_rx_msg = NULL;
-    }
-    mca_ccb_report_event(p_ccb, evt_data.hdr.op_code, &evt_data);
-  } else
-    osi_free(p_pkt);
-}
-
-/*******************************************************************************
- *
- * Function         mca_ccb_hdl_rsp
- *
- * Description      This function is called when a MCAP response is received
- *                  from the peer.  It calls the application callback function
- *                  with the results.
- *
- * Returns          void.
- *
- ******************************************************************************/
-void mca_ccb_hdl_rsp(tMCA_CCB* p_ccb, tMCA_CCB_EVT* p_data) {
-  BT_HDR* p_pkt = &p_data->hdr;
-  uint8_t* p;
-  tMCA_CTRL evt_data;
-  bool chk_mdl = false;
-  tMCA_DCB* p_dcb;
-  tMCA_RESULT result = MCA_BAD_HANDLE;
-  tMCA_TC_TBL* p_tbl;
-
-  if (p_pkt->len < sizeof(evt_data.hdr.op_code) +
-                       sizeof(evt_data.rsp.rsp_code) +
-                       sizeof(evt_data.hdr.mdl_id)) {
-    android_errorWriteLog(0x534e4554, "116319076");
-    MCA_TRACE_ERROR("%s: Response packet is too short", __func__);
-  } else if (p_ccb->p_tx_req) {
-    /* verify that the received response matches the sent request */
-    p = (uint8_t*)(p_pkt + 1) + p_pkt->offset;
-    evt_data.hdr.op_code = *p++;
-    if ((evt_data.hdr.op_code == MCA_OP_MDL_CREATE_RSP) &&
-        (p_pkt->len <
-         sizeof(evt_data.hdr.op_code) + sizeof(evt_data.rsp.rsp_code) +
-             sizeof(evt_data.hdr.mdl_id) + sizeof(evt_data.create_cfm.cfg))) {
-      android_errorWriteLog(0x534e4554, "116319076");
-      MCA_TRACE_ERROR("%s: MDL Create Response packet is too short", __func__);
-    } else if ((evt_data.hdr.op_code == 0) ||
-               ((p_ccb->p_tx_req->op_code + 1) == evt_data.hdr.op_code)) {
-      evt_data.rsp.rsp_code = *p++;
-      mca_stop_timer(p_ccb);
-      BE_STREAM_TO_UINT16(evt_data.hdr.mdl_id, p);
-      if (evt_data.hdr.op_code == MCA_OP_MDL_CREATE_RSP) {
-        evt_data.create_cfm.cfg = *p++;
-        chk_mdl = true;
-      } else if (evt_data.hdr.op_code == MCA_OP_MDL_RECONNECT_RSP)
-        chk_mdl = true;
-
-      if (chk_mdl) {
-        p_dcb = mca_dcb_by_hdl(p_ccb->p_tx_req->dcb_idx);
-        if (evt_data.rsp.rsp_code == MCA_RSP_SUCCESS) {
-          if (evt_data.hdr.mdl_id != p_dcb->mdl_id) {
-            MCA_TRACE_ERROR("peer's mdl_id=%d != our mdl_id=%d",
-                            evt_data.hdr.mdl_id, p_dcb->mdl_id);
-            /* change the response code to be an error */
-            if (evt_data.rsp.rsp_code == MCA_RSP_SUCCESS) {
-              evt_data.rsp.rsp_code = MCA_RSP_BAD_MDL;
-              /* send Abort */
-              p_ccb->status = MCA_CCB_STAT_PENDING;
-              MCA_Abort(mca_ccb_to_hdl(p_ccb));
-            }
-          } else if (p_dcb->p_chnl_cfg) {
-            /* the data channel configuration is known. Proceed with data
-             * channel initiation */
-            BTM_SetSecurityLevel(true, "", BTM_SEC_SERVICE_MCAP_DATA,
-                                 p_ccb->sec_mask, p_ccb->data_vpsm,
-                                 BTM_SEC_PROTO_MCA, p_ccb->p_tx_req->dcb_idx);
-            p_dcb->lcid = mca_l2c_open_req(p_ccb->peer_addr, p_ccb->data_vpsm,
-                                           p_dcb->p_chnl_cfg);
-            if (p_dcb->lcid) {
-              p_tbl = mca_tc_tbl_dalloc(p_dcb);
-              if (p_tbl) {
-                p_tbl->state = MCA_TC_ST_CONN;
-                p_ccb->status = MCA_CCB_STAT_PENDING;
-                result = MCA_SUCCESS;
-              }
-            }
-          } else {
-            /* mark this MCL as pending and wait for MCA_DataChnlCfg */
-            p_ccb->status = MCA_CCB_STAT_PENDING;
-            result = MCA_SUCCESS;
-          }
-        }
-
-        if (result != MCA_SUCCESS && p_dcb) {
-          mca_dcb_dealloc(p_dcb, NULL);
-        }
-      } /* end of chk_mdl */
-
-      if (p_ccb->status != MCA_CCB_STAT_PENDING)
-        osi_free_and_reset((void**)&p_ccb->p_tx_req);
-      mca_ccb_report_event(p_ccb, evt_data.hdr.op_code, &evt_data);
-    }
-    /* else a bad response is received */
-  } else {
-    /* not expecting any response. drop it */
-    MCA_TRACE_WARNING("dropping received rsp (not expecting a response)");
-  }
-  osi_free(p_data);
-}
-
-/*******************************************************************************
- *
- * Function         mca_ccb_ll_open
- *
- * Description      This function is called to report MCA_CONNECT_IND_EVT event.
- *                  It also clears the congestion flag (ccb.cong).
- *
- * Returns          void.
- *
- ******************************************************************************/
-void mca_ccb_ll_open(tMCA_CCB* p_ccb, tMCA_CCB_EVT* p_data) {
-  tMCA_CTRL evt_data;
-  p_ccb->cong = false;
-  evt_data.connect_ind.mtu = p_data->open.peer_mtu;
-  evt_data.connect_ind.bd_addr = p_ccb->peer_addr;
-  mca_ccb_report_event(p_ccb, MCA_CONNECT_IND_EVT, &evt_data);
-}
-
-/*******************************************************************************
- *
- * Function         mca_ccb_dl_open
- *
- * Description      This function is called when data channel is open. It clears
- *                  p_tx_req to allow other message exchage on this CL.
- *
- * Returns          void.
- *
- ******************************************************************************/
-void mca_ccb_dl_open(tMCA_CCB* p_ccb, UNUSED_ATTR tMCA_CCB_EVT* p_data) {
-  osi_free_and_reset((void**)&p_ccb->p_tx_req);
-  osi_free_and_reset((void**)&p_ccb->p_rx_msg);
-  p_ccb->status = MCA_CCB_STAT_NORM;
-}
diff --git a/stack/rfcomm/port_api.cc b/stack/rfcomm/port_api.cc
index 0bb817d..f2b9583 100644
--- a/stack/rfcomm/port_api.cc
+++ b/stack/rfcomm/port_api.cc
@@ -31,8 +31,6 @@
 #include "osi/include/mutex.h"
 
 #include "bt_common.h"
-#include "btm_api.h"
-#include "btm_int.h"
 #include "l2c_api.h"
 #include "port_api.h"
 #include "port_int.h"
@@ -40,17 +38,8 @@
 #include "rfcdefs.h"
 #include "sdp_api.h"
 
-/* duration of break in 200ms units */
-#define PORT_BREAK_DURATION 1
-
-#define info(fmt, ...) LOG_INFO(LOG_TAG, "%s: " fmt, __func__, ##__VA_ARGS__)
-#define debug(fmt, ...) LOG_DEBUG(LOG_TAG, "%s: " fmt, __func__, ##__VA_ARGS__)
 #define error(fmt, ...) \
   LOG_ERROR(LOG_TAG, "## ERROR : %s: " fmt "##", __func__, ##__VA_ARGS__)
-#define asrt(s)                                                            \
-  if (!(s))                                                                \
-  LOG_ERROR(LOG_TAG, "## %s assert %s failed at line:%d ##", __func__, #s, \
-            __LINE__)
 
 /* Mapping from PORT_* result codes to human readable strings. */
 static const char* result_code_strings[] = {"Success",
@@ -368,40 +357,6 @@
 
 /*******************************************************************************
  *
- * Function         PORT_SetDataCallback
- *
- * Description      This function is when a data packet is received
- *
- * Parameters:      handle     - Handle returned in the RFCOMM_CreateConnection
- *                  p_callback - address of the callback function which should
- *                               be called from the RFCOMM when data packet
- *                               is received.
- *
- *
- ******************************************************************************/
-int PORT_SetDataCallback(uint16_t port_handle, tPORT_DATA_CALLBACK* p_port_cb) {
-  tPORT* p_port;
-
-  RFCOMM_TRACE_API("PORT_SetDataCallback() handle:%d cb 0x%x", port_handle,
-                   p_port_cb);
-
-  /* Check if handle is valid to avoid crashing */
-  if ((port_handle == 0) || (port_handle > MAX_RFC_PORTS)) {
-    return (PORT_BAD_HANDLE);
-  }
-
-  p_port = &rfc_cb.port.port[port_handle - 1];
-
-  if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) {
-    return (PORT_NOT_OPENED);
-  }
-
-  p_port->p_data_callback = p_port_cb;
-
-  return (PORT_SUCCESS);
-}
-/*******************************************************************************
- *
  * Function         PORT_SetCODataCallback
  *
  * Description      This function is when a data packet is received
@@ -602,45 +557,6 @@
 
 /*******************************************************************************
  *
- * Function         PORT_GetRxQueueCnt
- *
- * Description      This function return number of buffers on the rx queue.
- *
- * Parameters:      handle     - Handle returned in the RFCOMM_CreateConnection
- *                  p_rx_queue_count - Pointer to return queue count in.
- *
- ******************************************************************************/
-int PORT_GetRxQueueCnt(uint16_t handle, uint16_t* p_rx_queue_count) {
-  tPORT* p_port;
-
-  RFCOMM_TRACE_API("PORT_GetRxQueueCnt() handle:%d", handle);
-
-  /* Check if handle is valid to avoid crashing */
-  if ((handle == 0) || (handle > MAX_RFC_PORTS)) {
-    return (PORT_BAD_HANDLE);
-  }
-
-  p_port = &rfc_cb.port.port[handle - 1];
-
-  if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) {
-    return (PORT_NOT_OPENED);
-  }
-
-  if (p_port->line_status) {
-    return (PORT_LINE_ERR);
-  }
-
-  *p_rx_queue_count = p_port->rx.queue_size;
-
-  RFCOMM_TRACE_API(
-      "PORT_GetRxQueueCnt() p_rx_queue_count:%d, p_port->rx.queue.count = %d",
-      *p_rx_queue_count, p_port->rx.queue_size);
-
-  return (PORT_SUCCESS);
-}
-
-/*******************************************************************************
- *
  * Function         PORT_GetState
  *
  * Description      This function is called to fill tPORT_STATE structure
@@ -677,153 +593,6 @@
 
 /*******************************************************************************
  *
- * Function         PORT_Control
- *
- * Description      This function directs a specified connection to pass control
- *                  control information to the peer device.
- *
- * Parameters:      handle     - Handle returned in the RFCOMM_CreateConnection
- *                  signal     = specify the function to be passed
- *
- ******************************************************************************/
-int PORT_Control(uint16_t handle, uint8_t signal) {
-  tPORT* p_port;
-  uint8_t old_modem_signal;
-
-  RFCOMM_TRACE_API("PORT_Control() handle:%d signal:0x%x", handle, signal);
-
-  /* Check if handle is valid to avoid crashing */
-  if ((handle == 0) || (handle > MAX_RFC_PORTS)) {
-    return (PORT_BAD_HANDLE);
-  }
-
-  p_port = &rfc_cb.port.port[handle - 1];
-
-  if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) {
-    return (PORT_NOT_OPENED);
-  }
-
-  old_modem_signal = p_port->local_ctrl.modem_signal;
-  p_port->local_ctrl.break_signal = 0;
-
-  switch (signal) {
-    case PORT_SET_CTSRTS:
-      p_port->local_ctrl.modem_signal |= PORT_CTSRTS_ON;
-      break;
-
-    case PORT_CLR_CTSRTS:
-      p_port->local_ctrl.modem_signal &= ~PORT_CTSRTS_ON;
-      break;
-
-    case PORT_SET_DTRDSR:
-      p_port->local_ctrl.modem_signal |= PORT_DTRDSR_ON;
-      break;
-
-    case PORT_CLR_DTRDSR:
-      p_port->local_ctrl.modem_signal &= ~PORT_DTRDSR_ON;
-      break;
-
-    case PORT_SET_RI:
-      p_port->local_ctrl.modem_signal |= PORT_RING_ON;
-      break;
-
-    case PORT_CLR_RI:
-      p_port->local_ctrl.modem_signal &= ~PORT_RING_ON;
-      break;
-
-    case PORT_SET_DCD:
-      p_port->local_ctrl.modem_signal |= PORT_DCD_ON;
-      break;
-
-    case PORT_CLR_DCD:
-      p_port->local_ctrl.modem_signal &= ~PORT_DCD_ON;
-      break;
-  }
-
-  if (signal == PORT_BREAK)
-    p_port->local_ctrl.break_signal = PORT_BREAK_DURATION;
-  else if (p_port->local_ctrl.modem_signal == old_modem_signal)
-    return (PORT_SUCCESS);
-
-  port_start_control(p_port);
-
-  RFCOMM_TRACE_EVENT(
-      "PORT_Control DTR_DSR : %d, RTS_CTS : %d, RI : %d, DCD : %d",
-      ((p_port->local_ctrl.modem_signal & MODEM_SIGNAL_DTRDSR) ? 1 : 0),
-      ((p_port->local_ctrl.modem_signal & MODEM_SIGNAL_RTSCTS) ? 1 : 0),
-      ((p_port->local_ctrl.modem_signal & MODEM_SIGNAL_RI) ? 1 : 0),
-      ((p_port->local_ctrl.modem_signal & MODEM_SIGNAL_DCD) ? 1 : 0));
-
-  return (PORT_SUCCESS);
-}
-
-/*******************************************************************************
- *
- * Function         PORT_FlowControl
- *
- * Description      This function directs a specified connection to pass
- *                  flow control message to the peer device.  Enable flag passed
- *                  shows if port can accept more data.
- *
- * Parameters:      handle     - Handle returned in the RFCOMM_CreateConnection
- *                  enable     - enables data flow
- *
- ******************************************************************************/
-int PORT_FlowControl(uint16_t handle, bool enable) {
-  tPORT* p_port;
-  bool old_fc;
-  uint32_t events;
-
-  RFCOMM_TRACE_API("PORT_FlowControl() handle:%d enable: %d", handle, enable);
-
-  /* Check if handle is valid to avoid crashing */
-  if ((handle == 0) || (handle > MAX_RFC_PORTS)) {
-    return (PORT_BAD_HANDLE);
-  }
-
-  p_port = &rfc_cb.port.port[handle - 1];
-
-  if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) {
-    return (PORT_NOT_OPENED);
-  }
-
-  if (!p_port->rfc.p_mcb) {
-    return (PORT_NOT_OPENED);
-  }
-
-  p_port->rx.user_fc = !enable;
-
-  if (p_port->rfc.p_mcb->flow == PORT_FC_CREDIT) {
-    if (!p_port->rx.user_fc) {
-      port_flow_control_peer(p_port, true, 0);
-    }
-  } else {
-    old_fc = p_port->local_ctrl.fc;
-
-    /* FC is set if user is set or peer is set */
-    p_port->local_ctrl.fc = (p_port->rx.user_fc | p_port->rx.peer_fc);
-
-    if (p_port->local_ctrl.fc != old_fc) port_start_control(p_port);
-  }
-
-  /* Need to take care of the case when we could not deliver events */
-  /* to the application because we were flow controlled */
-  if (enable && (p_port->rx.queue_size != 0)) {
-    events = PORT_EV_RXCHAR;
-    if (p_port->rx_flag_ev_pending) {
-      p_port->rx_flag_ev_pending = false;
-      events |= PORT_EV_RXFLAG;
-    }
-
-    events &= p_port->ev_mask;
-    if (p_port->p_callback && events) {
-      p_port->p_callback(events, p_port->handle);
-    }
-  }
-  return (PORT_SUCCESS);
-}
-/*******************************************************************************
- *
  * Function         PORT_FlowControl_MaxCredit
  *
  * Description      This function directs a specified connection to pass
@@ -892,230 +661,6 @@
 
 /*******************************************************************************
  *
- * Function         PORT_GetModemStatus
- *
- * Description      This function retrieves modem control signals.  Normally
- *                  application will call this function after a callback
- *                  function is called with notification that one of signals
- *                  has been changed.
- *
- * Parameters:      handle     - Handle returned in the RFCOMM_CreateConnection
- *                  p_signal   - specify the pointer to control signals info
- *
- ******************************************************************************/
-int PORT_GetModemStatus(uint16_t handle, uint8_t* p_signal) {
-  tPORT* p_port;
-
-  if ((handle == 0) || (handle > MAX_RFC_PORTS)) {
-    return (PORT_BAD_HANDLE);
-  }
-
-  p_port = &rfc_cb.port.port[handle - 1];
-
-  if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) {
-    return (PORT_NOT_OPENED);
-  }
-
-  *p_signal = p_port->peer_ctrl.modem_signal;
-
-  RFCOMM_TRACE_API("PORT_GetModemStatus() handle:%d signal:%x", handle,
-                   *p_signal);
-
-  return (PORT_SUCCESS);
-}
-
-/*******************************************************************************
- *
- * Function         PORT_ClearError
- *
- * Description      This function retreives information about a communications
- *                  error and reports current status of a connection.  The
- *                  function should be called when an error occures to clear
- *                  the connection error flag and to enable additional read
- *                  and write operations.
- *
- * Parameters:      handle     - Handle returned in the RFCOMM_CreateConnection
- *                  p_errors   - pointer of the variable to receive error codes
- *                  p_status   - pointer to the tPORT_STATUS structur to receive
- *                               connection status
- *
- ******************************************************************************/
-int PORT_ClearError(uint16_t handle, uint16_t* p_errors,
-                    tPORT_STATUS* p_status) {
-  tPORT* p_port;
-
-  RFCOMM_TRACE_API("PORT_ClearError() handle:%d", handle);
-
-  if ((handle == 0) || (handle > MAX_RFC_PORTS)) {
-    return (PORT_BAD_HANDLE);
-  }
-
-  p_port = &rfc_cb.port.port[handle - 1];
-
-  if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) {
-    return (PORT_NOT_OPENED);
-  }
-
-  *p_errors = p_port->line_status;
-
-  /* This is the only call to clear error status.  We can not clear */
-  /* connection failed status.  To clean it port should be closed and reopened
-   */
-  p_port->line_status = (p_port->line_status & LINE_STATUS_FAILED);
-
-  PORT_GetQueueStatus(handle, p_status);
-  return (PORT_SUCCESS);
-}
-
-/*******************************************************************************
- *
- * Function         PORT_SendError
- *
- * Description      This function send a communications error to the peer device
- *
- * Parameters:      handle     - Handle returned in the RFCOMM_CreateConnection
- *                  errors     - receive error codes
- *
- ******************************************************************************/
-int PORT_SendError(uint16_t handle, uint8_t errors) {
-  tPORT* p_port;
-
-  RFCOMM_TRACE_API("PORT_SendError() handle:%d errors:0x%x", handle, errors);
-
-  if ((handle == 0) || (handle > MAX_RFC_PORTS)) {
-    return (PORT_BAD_HANDLE);
-  }
-
-  p_port = &rfc_cb.port.port[handle - 1];
-
-  if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) {
-    return (PORT_NOT_OPENED);
-  }
-
-  if (!p_port->rfc.p_mcb) {
-    return (PORT_NOT_OPENED);
-  }
-
-  RFCOMM_LineStatusReq(p_port->rfc.p_mcb, p_port->dlci, errors);
-  return (PORT_SUCCESS);
-}
-
-/*******************************************************************************
- *
- * Function         PORT_GetQueueStatus
- *
- * Description      This function reports current status of a connection.
- *
- * Parameters:      handle     - Handle returned in the RFCOMM_CreateConnection
- *                  p_status   - pointer to the tPORT_STATUS structur to receive
- *                               connection status
- *
- ******************************************************************************/
-int PORT_GetQueueStatus(uint16_t handle, tPORT_STATUS* p_status) {
-  tPORT* p_port;
-
-  /* RFCOMM_TRACE_API ("PORT_GetQueueStatus() handle:%d", handle); */
-
-  if ((handle == 0) || (handle > MAX_RFC_PORTS)) {
-    return (PORT_BAD_HANDLE);
-  }
-
-  p_port = &rfc_cb.port.port[handle - 1];
-
-  if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) {
-    return (PORT_NOT_OPENED);
-  }
-
-  p_status->in_queue_size = (uint16_t)p_port->rx.queue_size;
-  p_status->out_queue_size = (uint16_t)p_port->tx.queue_size;
-
-  p_status->mtu_size = (uint16_t)p_port->peer_mtu;
-
-  p_status->flags = 0;
-
-  if (!(p_port->peer_ctrl.modem_signal & PORT_CTSRTS_ON))
-    p_status->flags |= PORT_FLAG_CTS_HOLD;
-
-  if (!(p_port->peer_ctrl.modem_signal & PORT_DTRDSR_ON))
-    p_status->flags |= PORT_FLAG_DSR_HOLD;
-
-  if (!(p_port->peer_ctrl.modem_signal & PORT_DCD_ON))
-    p_status->flags |= PORT_FLAG_RLSD_HOLD;
-
-  return (PORT_SUCCESS);
-}
-
-/*******************************************************************************
- *
- * Function         PORT_Purge
- *
- * Description      This function discards all the data from the output or
- *                  input queues of the specified connection.
- *
- * Parameters:      handle     - Handle returned in the RFCOMM_CreateConnection
- *                  purge_flags - specify the action to take.
- *
- ******************************************************************************/
-int PORT_Purge(uint16_t handle, uint8_t purge_flags) {
-  tPORT* p_port;
-  BT_HDR* p_buf;
-  uint16_t count;
-  uint32_t events;
-
-  RFCOMM_TRACE_API("PORT_Purge() handle:%d flags:0x%x", handle, purge_flags);
-
-  /* Check if handle is valid to avoid crashing */
-  if ((handle == 0) || (handle > MAX_RFC_PORTS)) {
-    return (PORT_BAD_HANDLE);
-  }
-
-  p_port = &rfc_cb.port.port[handle - 1];
-
-  if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) {
-    return (PORT_NOT_OPENED);
-  }
-
-  if (purge_flags & PORT_PURGE_RXCLEAR) {
-    mutex_global_lock(); /* to prevent missing credit */
-
-    count = fixed_queue_length(p_port->rx.queue);
-
-    while ((p_buf = (BT_HDR*)fixed_queue_try_dequeue(p_port->rx.queue)) != NULL)
-      osi_free(p_buf);
-
-    p_port->rx.queue_size = 0;
-
-    mutex_global_unlock();
-
-    /* If we flowed controlled peer based on rx_queue size enable data again */
-    if (count) port_flow_control_peer(p_port, true, count);
-  }
-
-  if (purge_flags & PORT_PURGE_TXCLEAR) {
-    mutex_global_lock(); /* to prevent tx.queue_size from being negative */
-
-    while ((p_buf = (BT_HDR*)fixed_queue_try_dequeue(p_port->tx.queue)) != NULL)
-      osi_free(p_buf);
-
-    p_port->tx.queue_size = 0;
-
-    mutex_global_unlock();
-
-    events = PORT_EV_TXEMPTY;
-
-    events |= port_flow_control_user(p_port);
-
-    events &= p_port->ev_mask;
-
-    if ((p_port->p_callback != NULL) && events)
-      (p_port->p_callback)(events, p_port->handle);
-  }
-
-  return (PORT_SUCCESS);
-}
-
-/*******************************************************************************
- *
  * Function         PORT_ReadData
  *
  * Description      Normally not GKI aware application will call this function
@@ -1214,56 +759,6 @@
 
 /*******************************************************************************
  *
- * Function         PORT_Read
- *
- * Description      Normally application will call this function after receiving
- *                  PORT_EV_RXCHAR event.
- *
- * Parameters:      handle     - Handle returned in the RFCOMM_CreateConnection
- *                  pp_buf      - pointer to address of buffer with data,
- *
- ******************************************************************************/
-int PORT_Read(uint16_t handle, BT_HDR** pp_buf) {
-  tPORT* p_port;
-  BT_HDR* p_buf;
-
-  RFCOMM_TRACE_API("PORT_Read() handle:%d", handle);
-
-  /* Check if handle is valid to avoid crashing */
-  if ((handle == 0) || (handle > MAX_RFC_PORTS)) {
-    return (PORT_BAD_HANDLE);
-  }
-  p_port = &rfc_cb.port.port[handle - 1];
-
-  if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) {
-    return (PORT_NOT_OPENED);
-  }
-
-  if (p_port->line_status) {
-    return (PORT_LINE_ERR);
-  }
-
-  mutex_global_lock();
-
-  p_buf = (BT_HDR*)fixed_queue_try_dequeue(p_port->rx.queue);
-  if (p_buf) {
-    p_port->rx.queue_size -= p_buf->len;
-
-    mutex_global_unlock();
-
-    /* If rfcomm suspended traffic from the peer based on the rx_queue_size */
-    /* check if it can be resumed now */
-    port_flow_control_peer(p_port, true, 1);
-  } else {
-    mutex_global_unlock();
-  }
-
-  *pp_buf = p_buf;
-  return (PORT_SUCCESS);
-}
-
-/*******************************************************************************
- *
  * Function         port_write
  *
  * Description      This function when a data packet is received from the apper
@@ -1322,64 +817,6 @@
 
 /*******************************************************************************
  *
- * Function         PORT_Write
- *
- * Description      This function when a data packet is received from the apper
- *                  layer task.
- *
- * Parameters:      handle     - Handle returned in the RFCOMM_CreateConnection
- *                  pp_buf      - pointer to address of buffer with data,
- *
- ******************************************************************************/
-int PORT_Write(uint16_t handle, BT_HDR* p_buf) {
-  tPORT* p_port;
-  uint32_t event = 0;
-  int rc;
-
-  RFCOMM_TRACE_API("PORT_Write() handle:%d", handle);
-
-  /* Check if handle is valid to avoid crashing */
-  if ((handle == 0) || (handle > MAX_RFC_PORTS)) {
-    osi_free(p_buf);
-    return (PORT_BAD_HANDLE);
-  }
-
-  p_port = &rfc_cb.port.port[handle - 1];
-
-  if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) {
-    osi_free(p_buf);
-    return (PORT_NOT_OPENED);
-  }
-
-  if (p_port->line_status) {
-    RFCOMM_TRACE_WARNING("PORT_Write: Data dropped line_status:0x%x",
-                         p_port->line_status);
-    osi_free(p_buf);
-    return (PORT_LINE_ERR);
-  }
-
-  rc = port_write(p_port, p_buf);
-  event |= port_flow_control_user(p_port);
-
-  switch (rc) {
-    case PORT_TX_FULL:
-      event |= PORT_EV_ERR;
-      break;
-
-    case PORT_SUCCESS:
-      event |= (PORT_EV_TXCHAR | PORT_EV_TXEMPTY);
-      break;
-  }
-  /* Mask out all events that are not of interest to user */
-  event &= p_port->ev_mask;
-
-  /* Send event to the application */
-  if (p_port->p_callback && event) (p_port->p_callback)(event, p_port->handle);
-
-  return (PORT_SUCCESS);
-}
-/*******************************************************************************
- *
  * Function         PORT_WriteDataCO
  *
  * Description      Normally not GKI aware application will call this function
@@ -1647,46 +1084,6 @@
 
 /*******************************************************************************
  *
- * Function         PORT_Test
- *
- * Description      Application can call this function to send RFCOMM Test frame
- *
- * Parameters:      handle      - Handle returned in the RFCOMM_CreateConnection
- *                  p_data      - Data area
- *                  max_len     - Byte count requested
- *
- ******************************************************************************/
-int PORT_Test(uint16_t handle, uint8_t* p_data, uint16_t len) {
-  tPORT* p_port;
-
-  RFCOMM_TRACE_API("PORT_Test() len:%d", len);
-
-  if ((handle == 0) || (handle > MAX_RFC_PORTS)) {
-    return (PORT_BAD_HANDLE);
-  }
-  p_port = &rfc_cb.port.port[handle - 1];
-
-  if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) {
-    return (PORT_NOT_OPENED);
-  }
-
-  if (len > ((p_port->mtu == 0) ? RFCOMM_DEFAULT_MTU : p_port->mtu)) {
-    return (PORT_UNKNOWN_ERROR);
-  }
-
-  BT_HDR* p_buf = (BT_HDR*)osi_malloc(RFCOMM_CMD_BUF_SIZE);
-  p_buf->offset = L2CAP_MIN_OFFSET + RFCOMM_MIN_OFFSET + 2;
-  p_buf->len = len;
-
-  memcpy((uint8_t*)(p_buf + 1) + p_buf->offset, p_data, p_buf->len);
-
-  rfc_send_test(p_port->rfc.p_mcb, true, p_buf);
-
-  return (PORT_SUCCESS);
-}
-
-/*******************************************************************************
- *
  * Function         RFCOMM_Init
  *
  * Description      This function is called to initialize RFCOMM layer
diff --git a/stack/rfcomm/port_int.h b/stack/rfcomm/port_int.h
index ecd8fbc..01eb95f 100644
--- a/stack/rfcomm/port_int.h
+++ b/stack/rfcomm/port_int.h
@@ -32,17 +32,6 @@
 #include "port_api.h"
 #include "rfcdefs.h"
 
-/* Local events passed when application event is sent from the api to PORT */
-/* ???*/
-#define PORT_EVENT_OPEN (1 | BT_EVT_TO_BTU_SP_EVT)
-#define PORT_EVENT_CONTROL (2 | BT_EVT_TO_BTU_SP_EVT)
-#define PORT_EVENT_SET_STATE (3 | BT_EVT_TO_BTU_SP_EVT)
-#define PORT_EVENT_SET_CALLBACK (5 | BT_EVT_TO_BTU_SP_EVT)
-#define PORT_EVENT_WRITE (6 | BT_EVT_TO_BTU_SP_EVT)
-#define PORT_EVENT_PURGE (7 | BT_EVT_TO_BTU_SP_EVT)
-#define PORT_EVENT_SEND_ERROR (8 | BT_EVT_TO_BTU_SP_EVT)
-#define PORT_EVENT_FLOW_CONTROL (9 | BT_EVT_TO_BTU_SP_EVT)
-
 /*
  * Flow control configuration values for the mux
 */
@@ -114,12 +103,6 @@
  * RFCOMM Port Connection Control Block
 */
 typedef struct {
-#define RFC_PORT_STATE_IDLE 0
-#define RFC_PORT_STATE_WAIT_START 1
-#define RFC_PORT_STATE_OPENING 2
-#define RFC_PORT_STATE_OPENED 3
-#define RFC_PORT_STATE_CLOSING 4
-
   uint8_t state; /* Current state of the connection */
 
 #define RFC_RSP_PN 0x01
diff --git a/stack/rfcomm/rfc_l2cap_if.cc b/stack/rfcomm/rfc_l2cap_if.cc
index 3dc4ec2..34e0ceb 100644
--- a/stack/rfcomm/rfc_l2cap_if.cc
+++ b/stack/rfcomm/rfc_l2cap_if.cc
@@ -74,7 +74,7 @@
   p_l2c->pL2CA_CongestionStatus_Cb = RFCOMM_CongestionStatusInd;
   p_l2c->pL2CA_TxComplete_Cb = NULL;
 
-  L2CA_Register(BT_PSM_RFCOMM, p_l2c, true /* enable_snoop */);
+  L2CA_Register(BT_PSM_RFCOMM, p_l2c, true /* enable_snoop */, nullptr);
 }
 
 /*******************************************************************************
diff --git a/stack/sdp/sdp_api.cc b/stack/sdp/sdp_api.cc
index 6e5f6f1..34ac349 100644
--- a/stack/sdp/sdp_api.cc
+++ b/stack/sdp/sdp_api.cc
@@ -22,18 +22,10 @@
  *
  ******************************************************************************/
 
-#include <stdio.h>
-#include <stdlib.h>
 #include <string.h>
 
-#include "bt_common.h"
 #include "bt_target.h"
-#include "bt_utils.h"
-#include "hcidefs.h"
-#include "hcimsgs.h"
-#include "l2cdefs.h"
 
-#include "btu.h"
 #include "sdp_api.h"
 #include "sdpint.h"
 
@@ -217,45 +209,6 @@
 
 /*******************************************************************************
  *
- * Function         SDP_FindAttributeInDb
- *
- * Description      This function queries an SDP database for a specific
- *                  attribute. If the p_start_rec pointer is NULL, it looks from
- *                  the beginning of the database, else it continues from the
- *                  next record after p_start_rec.
- *
- * Returns          Pointer to matching record, or NULL
- *
- ******************************************************************************/
-tSDP_DISC_REC* SDP_FindAttributeInDb(tSDP_DISCOVERY_DB* p_db, uint16_t attr_id,
-                                     tSDP_DISC_REC* p_start_rec) {
-  tSDP_DISC_REC* p_rec;
-  tSDP_DISC_ATTR* p_attr;
-
-  /* Must have a valid database */
-  if (p_db == NULL) return (NULL);
-
-  if (!p_start_rec)
-    p_rec = p_db->p_first_rec;
-  else
-    p_rec = p_start_rec->p_next_rec;
-
-  while (p_rec) {
-    p_attr = p_rec->p_first_attr;
-    while (p_attr) {
-      if (p_attr->attr_id == attr_id) return (p_rec);
-
-      p_attr = p_attr->p_next_attr;
-    }
-
-    p_rec = p_rec->p_next_rec;
-  }
-  /* If here, no matching attribute found */
-  return (NULL);
-}
-
-/*******************************************************************************
- *
  * Function         SDP_FindAttributeInRec
  *
  * Description      This function searches an SDP discovery record for a
@@ -695,44 +648,6 @@
 
 /*******************************************************************************
  *
- * Function         SDP_FindAddProtoListsElemInRec
- *
- * Description      This function looks at a specific discovery record for a
- *                  protocol list element.
- *
- * Returns          true if found, false if not
- *                  If found, the passed protocol list element is filled in.
- *
- ******************************************************************************/
-bool SDP_FindAddProtoListsElemInRec(tSDP_DISC_REC* p_rec, uint16_t layer_uuid,
-                                    tSDP_PROTOCOL_ELEM* p_elem) {
-  tSDP_DISC_ATTR *p_attr, *p_sattr;
-  bool ret = false;
-
-  p_attr = p_rec->p_first_attr;
-  while (p_attr) {
-    /* Find the additional protocol descriptor list attribute */
-    if ((p_attr->attr_id == ATTR_ID_ADDITION_PROTO_DESC_LISTS) &&
-        (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == DATA_ELE_SEQ_DESC_TYPE)) {
-      for (p_sattr = p_attr->attr_value.v.p_sub_attr; p_sattr;
-           p_sattr = p_sattr->p_next_attr) {
-        /* Safety check - each entry should itself be a sequence */
-        if (SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) ==
-            DATA_ELE_SEQ_DESC_TYPE) {
-          ret = sdp_fill_proto_elem(p_sattr, layer_uuid, p_elem);
-          if (ret) break;
-        }
-      }
-      return ret;
-    }
-    p_attr = p_attr->p_next_attr;
-  }
-  /* If here, no match found */
-  return (false);
-}
-
-/*******************************************************************************
- *
  * Function         SDP_FindProfileVersionInRec
  *
  * Description      This function looks at a specific discovery record for the
diff --git a/stack/sdp/sdp_db.cc b/stack/sdp/sdp_db.cc
index ea5b84d..2cc04b0 100644
--- a/stack/sdp/sdp_db.cc
+++ b/stack/sdp/sdp_db.cc
@@ -23,17 +23,12 @@
  ******************************************************************************/
 
 #include <stdio.h>
-#include <stdlib.h>
 #include <string.h>
 
 #include "bt_target.h"
 
 #include "bt_common.h"
 
-#include "hcidefs.h"
-#include "hcimsgs.h"
-#include "l2cdefs.h"
-
 #include "sdp_api.h"
 #include "sdpint.h"
 
@@ -844,67 +839,3 @@
   /* If here, not found */
   return (false);
 }
-
-/*******************************************************************************
- *
- * Function         SDP_ReadRecord
- *
- * Description      This function is called to get the raw data of the record
- *                  with the given handle from the database.
- *
- * Returns          -1, if the record is not found.
- *                  Otherwise, the offset (0 or 1) to start of data in p_data.
- *
- *                  The size of data copied into p_data is in *p_data_len.
- *
- ******************************************************************************/
-#if (SDP_RAW_DATA_INCLUDED == TRUE)
-int32_t SDP_ReadRecord(uint32_t handle, uint8_t* p_data, int32_t* p_data_len) {
-  int32_t len = 0;     /* Number of bytes in the entry */
-  int32_t offset = -1; /* default to not found */
-#if (SDP_SERVER_ENABLED == TRUE)
-  tSDP_RECORD* p_rec;
-  uint16_t start = 0;
-  uint16_t end = 0xffff;
-  tSDP_ATTRIBUTE* p_attr;
-  uint16_t rem_len;
-  uint8_t* p_rsp;
-
-  /* Find the record in the database */
-  p_rec = sdp_db_find_record(handle);
-  if (p_rec && p_data && p_data_len) {
-    p_rsp = &p_data[3];
-    while ((p_attr = sdp_db_find_attr_in_rec(p_rec, start, end)) != NULL) {
-      /* Check if attribute fits. Assume 3-byte value type/length */
-      rem_len = *p_data_len - (uint16_t)(p_rsp - p_data);
-
-      if (p_attr->len > (uint32_t)(rem_len - 6)) break;
-
-      p_rsp = sdpu_build_attrib_entry(p_rsp, p_attr);
-
-      /* next attr id */
-      start = p_attr->id + 1;
-    }
-    len = (int32_t)(p_rsp - p_data);
-
-    /* Put in the sequence header (2 or 3 bytes) */
-    if (len > 255) {
-      offset = 0;
-      p_data[0] = (uint8_t)((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_WORD);
-      p_data[1] = (uint8_t)((len - 3) >> 8);
-      p_data[2] = (uint8_t)(len - 3);
-    } else {
-      offset = 1;
-
-      p_data[1] = (uint8_t)((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE);
-      p_data[2] = (uint8_t)(len - 3);
-
-      len--;
-    }
-    *p_data_len = len;
-  }
-#endif
-  /* If here, not found */
-  return (offset);
-}
-#endif
diff --git a/stack/sdp/sdp_main.cc b/stack/sdp/sdp_main.cc
index 2211c39..93a4f06 100644
--- a/stack/sdp/sdp_main.cc
+++ b/stack/sdp/sdp_main.cc
@@ -22,22 +22,17 @@
  *
  ******************************************************************************/
 
-#include <stdio.h>
-#include <stdlib.h>
 #include <string.h>
 
 #include "bt_common.h"
 #include "bt_target.h"
-#include "bt_utils.h"
 #include "hcidefs.h"
-#include "hcimsgs.h"
 
 #include "l2c_api.h"
 #include "l2cdefs.h"
 #include "osi/include/osi.h"
 
 #include "btm_api.h"
-#include "btu.h"
 
 #include "sdp_api.h"
 #include "sdpint.h"
@@ -89,7 +84,7 @@
 #if (SDP_SERVER_ENABLED == TRUE)
   /* Register with Security Manager for the specific security level */
   if (!BTM_SetSecurityLevel(false, SDP_SERVICE_NAME, BTM_SEC_SERVICE_SDP_SERVER,
-                            SDP_SECURITY_LEVEL, SDP_PSM, 0, 0)) {
+                            BTM_SEC_NONE, SDP_PSM, 0, 0)) {
     SDP_TRACE_ERROR("Security Registration Server failed");
     return;
   }
@@ -97,7 +92,7 @@
 
   /* Register with Security Manager for the specific security level */
   if (!BTM_SetSecurityLevel(true, SDP_SERVICE_NAME, BTM_SEC_SERVICE_SDP_SERVER,
-                            SDP_SECURITY_LEVEL, SDP_PSM, 0, 0)) {
+                            BTM_SEC_NONE, SDP_PSM, 0, 0)) {
     SDP_TRACE_ERROR("Security Registration for Client failed");
     return;
   }
@@ -121,7 +116,8 @@
   sdp_cb.reg_info.pL2CA_TxComplete_Cb = NULL;
 
   /* Now, register with L2CAP */
-  if (!L2CA_Register(SDP_PSM, &sdp_cb.reg_info, true /* enable_snoop */)) {
+  if (!L2CA_Register(SDP_PSM, &sdp_cb.reg_info, true /* enable_snoop */,
+                     nullptr)) {
     SDP_TRACE_ERROR("SDP Registration failed");
   }
 }
@@ -133,26 +129,6 @@
   }
 }
 
-#if (SDP_DEBUG == TRUE)
-/*******************************************************************************
- *
- * Function         sdp_set_max_attr_list_size
- *
- * Description      This function sets the max attribute list size to use
- *
- * Returns          void
- *
- ******************************************************************************/
-uint16_t sdp_set_max_attr_list_size(uint16_t max_size) {
-  if (max_size > (sdp_cb.l2cap_my_cfg.mtu - 16))
-    max_size = sdp_cb.l2cap_my_cfg.mtu - 16;
-
-  sdp_cb.max_attr_list_size = max_size;
-
-  return sdp_cb.max_attr_list_size;
-}
-#endif
-
 /*******************************************************************************
  *
  * Function         sdp_connect_ind
diff --git a/stack/sdp/sdp_server.cc b/stack/sdp/sdp_server.cc
index a457bb8..50bcfeb 100644
--- a/stack/sdp/sdp_server.cc
+++ b/stack/sdp/sdp_server.cc
@@ -24,18 +24,10 @@
  ******************************************************************************/
 
 #include <log/log.h>
-#include <stdio.h>
-#include <stdlib.h>
 #include <string.h>
 
 #include "bt_common.h"
 #include "bt_types.h"
-#include "bt_utils.h"
-#include "btu.h"
-
-#include "hcidefs.h"
-#include "hcimsgs.h"
-#include "l2cdefs.h"
 
 #include "osi/include/osi.h"
 #include "sdp_api.h"
diff --git a/stack/sdp/sdp_utils.cc b/stack/sdp/sdp_utils.cc
index 33f30b0..f66a8fd 100644
--- a/stack/sdp/sdp_utils.cc
+++ b/stack/sdp/sdp_utils.cc
@@ -22,24 +22,18 @@
  *
  ******************************************************************************/
 
-#include <netinet/in.h>
 #include <stdio.h>
-#include <stdlib.h>
 #include <string.h>
 #include <utility>
 #include <vector>
 
 #include "bt_common.h"
 #include "bt_types.h"
-
-#include "hcidefs.h"
-#include "hcimsgs.h"
-#include "l2cdefs.h"
+#include "btif_config.h"
 
 #include "sdp_api.h"
 #include "sdpint.h"
 
-#include "btu.h"
 #include "common/metrics.h"
 
 using bluetooth::Uuid;
@@ -281,6 +275,17 @@
           bda, android::bluetooth::DeviceInfoSrcEnum::DEVICE_INFO_INTERNAL,
           ss.str(), loghex(di_record.rec.vendor), loghex(di_record.rec.product),
           loghex(di_record.rec.version), "");
+
+      std::string bda_string = bda.ToString();
+      // write manufacturer, model, HW version to config
+      btif_config_set_int(bda_string, BT_CONFIG_KEY_SDP_DI_MANUFACTURER,
+                          di_record.rec.vendor);
+      btif_config_set_int(bda_string, BT_CONFIG_KEY_SDP_DI_MODEL,
+                          di_record.rec.product);
+      btif_config_set_int(bda_string, BT_CONFIG_KEY_SDP_DI_HW_VERSION,
+                          di_record.rec.version);
+      btif_config_set_int(bda_string, BT_CONFIG_KEY_SDP_DI_VENDOR_ID_SRC,
+                          di_record.rec.vendor_id_source);
     }
   }
 }
diff --git a/stack/sdp/sdpint.h b/stack/sdp/sdpint.h
index 20c6fbd..af9ccb8 100644
--- a/stack/sdp/sdpint.h
+++ b/stack/sdp/sdpint.h
@@ -38,33 +38,6 @@
 /* Timeout definitions. */
 #define SDP_INACT_TIMEOUT_MS (30 * 1000) /* Inactivity timeout (in ms) */
 
-/* Define the Out-Flow default values. */
-#define SDP_OFLOW_QOS_FLAG 0
-#define SDP_OFLOW_SERV_TYPE 0
-#define SDP_OFLOW_TOKEN_RATE 0
-#define SDP_OFLOW_TOKEN_BUCKET_SIZE 0
-#define SDP_OFLOW_PEAK_BANDWIDTH 0
-#define SDP_OFLOW_LATENCY 0
-#define SDP_OFLOW_DELAY_VARIATION 0
-
-/* Define the In-Flow default values. */
-#define SDP_IFLOW_QOS_FLAG 0
-#define SDP_IFLOW_SERV_TYPE 0
-#define SDP_IFLOW_TOKEN_RATE 0
-#define SDP_IFLOW_TOKEN_BUCKET_SIZE 0
-#define SDP_IFLOW_PEAK_BANDWIDTH 0
-#define SDP_IFLOW_LATENCY 0
-#define SDP_IFLOW_DELAY_VARIATION 0
-
-#define SDP_LINK_TO 0
-
-/* Define the type of device notification. */
-/* (Inquiry Scan and Page Scan)            */
-#define SDP_DEVICE_NOTI_LEN \
-  (sizeof(BT_HDR) + HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_WRITE_PARAM1)
-
-#define SDP_DEVICE_NOTI_FLAG 0x03
-
 /* Define the Protocol Data Unit (PDU) types.
  */
 #define SDP_PDU_ERROR_RESPONSE 0x01
@@ -133,11 +106,6 @@
   tSDP_RECORD record[SDP_MAX_RECORDS];
 } tSDP_DB;
 
-enum {
-  SDP_IS_SEARCH,
-  SDP_IS_ATTR_SEARCH,
-};
-
 #if (SDP_SERVER_ENABLED == TRUE)
 /* Continuation information for the SDP server response */
 typedef struct {
@@ -226,21 +194,6 @@
 extern void sdp_free(void);
 extern void sdp_disconnect(tCONN_CB* p_ccb, uint16_t reason);
 
-#if (SDP_DEBUG == TRUE)
-extern uint16_t sdp_set_max_attr_list_size(uint16_t max_size);
-#endif
-
-/* Functions provided by sdp_conn.cc
- */
-extern void sdp_conn_rcv_l2e_conn_ind(BT_HDR* p_msg);
-extern void sdp_conn_rcv_l2e_conn_cfm(BT_HDR* p_msg);
-extern void sdp_conn_rcv_l2e_disc(BT_HDR* p_msg);
-extern void sdp_conn_rcv_l2e_config_ind(BT_HDR* p_msg);
-extern void sdp_conn_rcv_l2e_config_cfm(BT_HDR* p_msg);
-extern void sdp_conn_rcv_l2e_conn_failed(BT_HDR* p_msg);
-extern void sdp_conn_rcv_l2e_connected(BT_HDR* p_msg);
-extern void sdp_conn_rcv_l2e_conn_failed(BT_HDR* p_msg);
-extern void sdp_conn_rcv_l2e_data(BT_HDR* p_msg);
 extern void sdp_conn_timer_timeout(void* data);
 
 extern tCONN_CB* sdp_conn_originate(const RawAddress& p_bd_addr);
diff --git a/stack/smp/p_256_curvepara.cc b/stack/smp/p_256_curvepara.cc
index 5fb71a3..ebdf266 100644
--- a/stack/smp/p_256_curvepara.cc
+++ b/stack/smp/p_256_curvepara.cc
@@ -25,53 +25,49 @@
 #include <string.h>
 #include "p_256_ecc_pp.h"
 
-void p_256_init_curve(uint32_t keyLength) {
-  elliptic_curve_t* ec;
+void p_256_init_curve() {
+  elliptic_curve_t* ec = &curve_p256;
 
-  if (keyLength == KEY_LENGTH_DWORDS_P256) {
-    ec = &curve_p256;
+  ec->p[7] = 0xFFFFFFFF;
+  ec->p[6] = 0x00000001;
+  ec->p[5] = 0x0;
+  ec->p[4] = 0x0;
+  ec->p[3] = 0x0;
+  ec->p[2] = 0xFFFFFFFF;
+  ec->p[1] = 0xFFFFFFFF;
+  ec->p[0] = 0xFFFFFFFF;
 
-    ec->p[7] = 0xFFFFFFFF;
-    ec->p[6] = 0x00000001;
-    ec->p[5] = 0x0;
-    ec->p[4] = 0x0;
-    ec->p[3] = 0x0;
-    ec->p[2] = 0xFFFFFFFF;
-    ec->p[1] = 0xFFFFFFFF;
-    ec->p[0] = 0xFFFFFFFF;
+  memset(ec->omega, 0, KEY_LENGTH_DWORDS_P256);
+  memset(ec->a, 0, KEY_LENGTH_DWORDS_P256);
 
-    memset(ec->omega, 0, KEY_LENGTH_DWORDS_P256);
-    memset(ec->a, 0, KEY_LENGTH_DWORDS_P256);
+  ec->a_minus3 = true;
 
-    ec->a_minus3 = true;
+  // b
+  ec->b[7] = 0x5ac635d8;
+  ec->b[6] = 0xaa3a93e7;
+  ec->b[5] = 0xb3ebbd55;
+  ec->b[4] = 0x769886bc;
+  ec->b[3] = 0x651d06b0;
+  ec->b[2] = 0xcc53b0f6;
+  ec->b[1] = 0x3bce3c3e;
+  ec->b[0] = 0x27d2604b;
 
-    // b
-    ec->b[7] = 0x5ac635d8;
-    ec->b[6] = 0xaa3a93e7;
-    ec->b[5] = 0xb3ebbd55;
-    ec->b[4] = 0x769886bc;
-    ec->b[3] = 0x651d06b0;
-    ec->b[2] = 0xcc53b0f6;
-    ec->b[1] = 0x3bce3c3e;
-    ec->b[0] = 0x27d2604b;
+  // base point
+  ec->G.x[7] = 0x6b17d1f2;
+  ec->G.x[6] = 0xe12c4247;
+  ec->G.x[5] = 0xf8bce6e5;
+  ec->G.x[4] = 0x63a440f2;
+  ec->G.x[3] = 0x77037d81;
+  ec->G.x[2] = 0x2deb33a0;
+  ec->G.x[1] = 0xf4a13945;
+  ec->G.x[0] = 0xd898c296;
 
-    // base point
-    ec->G.x[7] = 0x6b17d1f2;
-    ec->G.x[6] = 0xe12c4247;
-    ec->G.x[5] = 0xf8bce6e5;
-    ec->G.x[4] = 0x63a440f2;
-    ec->G.x[3] = 0x77037d81;
-    ec->G.x[2] = 0x2deb33a0;
-    ec->G.x[1] = 0xf4a13945;
-    ec->G.x[0] = 0xd898c296;
-
-    ec->G.y[7] = 0x4fe342e2;
-    ec->G.y[6] = 0xfe1a7f9b;
-    ec->G.y[5] = 0x8ee7eb4a;
-    ec->G.y[4] = 0x7c0f9e16;
-    ec->G.y[3] = 0x2bce3357;
-    ec->G.y[2] = 0x6b315ece;
-    ec->G.y[1] = 0xcbb64068;
-    ec->G.y[0] = 0x37bf51f5;
-  }
+  ec->G.y[7] = 0x4fe342e2;
+  ec->G.y[6] = 0xfe1a7f9b;
+  ec->G.y[5] = 0x8ee7eb4a;
+  ec->G.y[4] = 0x7c0f9e16;
+  ec->G.y[3] = 0x2bce3357;
+  ec->G.y[2] = 0x6b315ece;
+  ec->G.y[1] = 0xcbb64068;
+  ec->G.y[0] = 0x37bf51f5;
 }
diff --git a/stack/smp/p_256_ecc_pp.cc b/stack/smp/p_256_ecc_pp.cc
index ff5dbde..cef368a 100644
--- a/stack/smp/p_256_ecc_pp.cc
+++ b/stack/smp/p_256_ecc_pp.cc
@@ -38,7 +38,7 @@
 }
 
 // q=2q
-static void ECC_Double(Point* q, Point* p, uint32_t keyLength) {
+static void ECC_Double(Point* q, Point* p) {
   uint32_t t1[KEY_LENGTH_DWORDS_P256];
   uint32_t t2[KEY_LENGTH_DWORDS_P256];
   uint32_t t3[KEY_LENGTH_DWORDS_P256];
@@ -49,8 +49,8 @@
   uint32_t* z1;
   uint32_t* z3;
 
-  if (multiprecision_iszero(p->z, keyLength)) {
-    multiprecision_init(q->z, keyLength);
+  if (multiprecision_iszero(p->z)) {
+    multiprecision_init(q->z);
     return;  // return infinity
   }
 
@@ -61,33 +61,33 @@
   y3 = q->y;
   z3 = q->z;
 
-  multiprecision_mersenns_squa_mod(t1, z1, keyLength);      // t1=z1^2
-  multiprecision_sub_mod(t2, x1, t1, keyLength);            // t2=x1-t1
-  multiprecision_add_mod(t1, x1, t1, keyLength);            // t1=x1+t1
-  multiprecision_mersenns_mult_mod(t2, t1, t2, keyLength);  // t2=t2*t1
-  multiprecision_lshift_mod(t3, t2, keyLength);
-  multiprecision_add_mod(t2, t3, t2, keyLength);  // t2=3t2
+  multiprecision_mersenns_squa_mod(t1, z1);      // t1=z1^2
+  multiprecision_sub_mod(t2, x1, t1);            // t2=x1-t1
+  multiprecision_add_mod(t1, x1, t1);            // t1=x1+t1
+  multiprecision_mersenns_mult_mod(t2, t1, t2);  // t2=t2*t1
+  multiprecision_lshift_mod(t3, t2);
+  multiprecision_add_mod(t2, t3, t2);  // t2=3t2
 
-  multiprecision_mersenns_mult_mod(z3, y1, z1, keyLength);  // z3=y1*z1
-  multiprecision_lshift_mod(z3, z3, keyLength);
+  multiprecision_mersenns_mult_mod(z3, y1, z1);  // z3=y1*z1
+  multiprecision_lshift_mod(z3, z3);
 
-  multiprecision_mersenns_squa_mod(y3, y1, keyLength);  // y3=y1^2
-  multiprecision_lshift_mod(y3, y3, keyLength);
-  multiprecision_mersenns_mult_mod(t3, y3, x1, keyLength);  // t3=y3*x1=x1*y1^2
-  multiprecision_lshift_mod(t3, t3, keyLength);
-  multiprecision_mersenns_squa_mod(y3, y3, keyLength);  // y3=y3^2=y1^4
-  multiprecision_lshift_mod(y3, y3, keyLength);
+  multiprecision_mersenns_squa_mod(y3, y1);  // y3=y1^2
+  multiprecision_lshift_mod(y3, y3);
+  multiprecision_mersenns_mult_mod(t3, y3, x1);  // t3=y3*x1=x1*y1^2
+  multiprecision_lshift_mod(t3, t3);
+  multiprecision_mersenns_squa_mod(y3, y3);  // y3=y3^2=y1^4
+  multiprecision_lshift_mod(y3, y3);
 
-  multiprecision_mersenns_squa_mod(x3, t2, keyLength);      // x3=t2^2
-  multiprecision_lshift_mod(t1, t3, keyLength);             // t1=2t3
-  multiprecision_sub_mod(x3, x3, t1, keyLength);            // x3=x3-t1
-  multiprecision_sub_mod(t1, t3, x3, keyLength);            // t1=t3-x3
-  multiprecision_mersenns_mult_mod(t1, t1, t2, keyLength);  // t1=t1*t2
-  multiprecision_sub_mod(y3, t1, y3, keyLength);            // y3=t1-y3
+  multiprecision_mersenns_squa_mod(x3, t2);      // x3=t2^2
+  multiprecision_lshift_mod(t1, t3);             // t1=2t3
+  multiprecision_sub_mod(x3, x3, t1);            // x3=x3-t1
+  multiprecision_sub_mod(t1, t3, x3);            // t1=t3-x3
+  multiprecision_mersenns_mult_mod(t1, t1, t2);  // t1=t1*t2
+  multiprecision_sub_mod(y3, t1, y3);            // y3=t1-y3
 }
 
 // q=q+p,     zp must be 1
-static void ECC_Add(Point* r, Point* p, Point* q, uint32_t keyLength) {
+static void ECC_Add(Point* r, Point* p, Point* q) {
   uint32_t t1[KEY_LENGTH_DWORDS_P256];
   uint32_t t2[KEY_LENGTH_DWORDS_P256];
   uint32_t* x1;
@@ -111,58 +111,57 @@
   z3 = r->z;
 
   // if Q=infinity, return p
-  if (multiprecision_iszero(z2, keyLength)) {
+  if (multiprecision_iszero(z2)) {
     p_256_copy_point(r, p);
     return;
   }
 
   // if P=infinity, return q
-  if (multiprecision_iszero(z1, keyLength)) {
+  if (multiprecision_iszero(z1)) {
     p_256_copy_point(r, q);
     return;
   }
 
-  multiprecision_mersenns_squa_mod(t1, z1, keyLength);      // t1=z1^2
-  multiprecision_mersenns_mult_mod(t2, z1, t1, keyLength);  // t2=t1*z1
-  multiprecision_mersenns_mult_mod(t1, x2, t1, keyLength);  // t1=t1*x2
-  multiprecision_mersenns_mult_mod(t2, y2, t2, keyLength);  // t2=t2*y2
+  multiprecision_mersenns_squa_mod(t1, z1);      // t1=z1^2
+  multiprecision_mersenns_mult_mod(t2, z1, t1);  // t2=t1*z1
+  multiprecision_mersenns_mult_mod(t1, x2, t1);  // t1=t1*x2
+  multiprecision_mersenns_mult_mod(t2, y2, t2);  // t2=t2*y2
 
-  multiprecision_sub_mod(t1, t1, x1, keyLength);  // t1=t1-x1
-  multiprecision_sub_mod(t2, t2, y1, keyLength);  // t2=t2-y1
+  multiprecision_sub_mod(t1, t1, x1);  // t1=t1-x1
+  multiprecision_sub_mod(t2, t2, y1);  // t2=t2-y1
 
-  if (multiprecision_iszero(t1, keyLength)) {
-    if (multiprecision_iszero(t2, keyLength)) {
-      ECC_Double(r, q, keyLength);
+  if (multiprecision_iszero(t1)) {
+    if (multiprecision_iszero(t2)) {
+      ECC_Double(r, q);
       return;
     } else {
-      multiprecision_init(z3, keyLength);
+      multiprecision_init(z3);
       return;  // return infinity
     }
   }
 
-  multiprecision_mersenns_mult_mod(z3, z1, t1, keyLength);  // z3=z1*t1
-  multiprecision_mersenns_squa_mod(y3, t1, keyLength);      // t3=t1^2
-  multiprecision_mersenns_mult_mod(z1, y3, t1, keyLength);  // t4=t3*t1
-  multiprecision_mersenns_mult_mod(y3, y3, x1, keyLength);  // t3=t3*x1
-  multiprecision_lshift_mod(t1, y3, keyLength);             // t1=2*t3
-  multiprecision_mersenns_squa_mod(x3, t2, keyLength);      // x3=t2^2
-  multiprecision_sub_mod(x3, x3, t1, keyLength);            // x3=x3-t1
-  multiprecision_sub_mod(x3, x3, z1, keyLength);            // x3=x3-t4
-  multiprecision_sub_mod(y3, y3, x3, keyLength);            // t3=t3-x3
-  multiprecision_mersenns_mult_mod(y3, y3, t2, keyLength);  // t3=t3*t2
-  multiprecision_mersenns_mult_mod(z1, z1, y1, keyLength);  // t4=t4*t1
-  multiprecision_sub_mod(y3, y3, z1, keyLength);
+  multiprecision_mersenns_mult_mod(z3, z1, t1);  // z3=z1*t1
+  multiprecision_mersenns_squa_mod(y3, t1);      // t3=t1^2
+  multiprecision_mersenns_mult_mod(z1, y3, t1);  // t4=t3*t1
+  multiprecision_mersenns_mult_mod(y3, y3, x1);  // t3=t3*x1
+  multiprecision_lshift_mod(t1, y3);             // t1=2*t3
+  multiprecision_mersenns_squa_mod(x3, t2);      // x3=t2^2
+  multiprecision_sub_mod(x3, x3, t1);            // x3=x3-t1
+  multiprecision_sub_mod(x3, x3, z1);            // x3=x3-t4
+  multiprecision_sub_mod(y3, y3, x3);            // t3=t3-x3
+  multiprecision_mersenns_mult_mod(y3, y3, t2);  // t3=t3*t2
+  multiprecision_mersenns_mult_mod(z1, z1, y1);  // t4=t4*t1
+  multiprecision_sub_mod(y3, y3, z1);
 }
 
 // Computing the Non-Adjacent Form of a positive integer
-static void ECC_NAF(uint8_t* naf, uint32_t* NumNAF, uint32_t* k,
-                    uint32_t keyLength) {
+static void ECC_NAF(uint8_t* naf, uint32_t* NumNAF, uint32_t* k) {
   uint32_t sign;
   int i = 0;
   int j;
   uint32_t var;
 
-  while ((var = multiprecision_most_signbits(k, keyLength)) >= 1) {
+  while ((var = multiprecision_most_signbits(k)) >= 1) {
     if (k[0] & 0x01)  // k is odd
     {
       sign = (k[0] & 0x03);  // 1 or 3
@@ -183,7 +182,7 @@
     } else
       sign = 0;
 
-    multiprecision_rshift(k, k, keyLength);
+    multiprecision_rshift(k, k);
     naf[i / 4] |= (sign) << ((i % 4) * 2);
     i++;
   }
@@ -192,8 +191,7 @@
 }
 
 // Binary Non-Adjacent Form for point multiplication
-void ECC_PointMult_Bin_NAF(Point* q, Point* p, uint32_t* n,
-                           uint32_t keyLength) {
+void ECC_PointMult_Bin_NAF(Point* q, Point* p, uint32_t* n) {
   uint32_t sign;
   uint8_t naf[256 / 4 + 1];
   uint32_t NumNaf;
@@ -201,69 +199,64 @@
   Point r;
   uint32_t* modp;
 
-  if (keyLength == KEY_LENGTH_DWORDS_P256) {
-    modp = curve_p256.p;
-  } else {
-    modp = curve.p;
-  }
+  modp = curve_p256.p;
 
   p_256_init_point(&r);
-  multiprecision_init(p->z, keyLength);
+  multiprecision_init(p->z);
   p->z[0] = 1;
 
   // initialization
   p_256_init_point(q);
 
   // -p
-  multiprecision_copy(minus_p.x, p->x, keyLength);
-  multiprecision_sub(minus_p.y, modp, p->y, keyLength);
+  multiprecision_copy(minus_p.x, p->x);
+  multiprecision_sub(minus_p.y, modp, p->y);
 
-  multiprecision_init(minus_p.z, keyLength);
+  multiprecision_init(minus_p.z);
   minus_p.z[0] = 1;
 
   // NAF
   memset(naf, 0, sizeof(naf));
-  ECC_NAF(naf, &NumNaf, n, keyLength);
+  ECC_NAF(naf, &NumNaf, n);
 
   for (int i = NumNaf - 1; i >= 0; i--) {
     p_256_copy_point(&r, q);
-    ECC_Double(q, &r, keyLength);
+    ECC_Double(q, &r);
     sign = (naf[i / 4] >> ((i % 4) * 2)) & 0x03;
 
     if (sign == 1) {
       p_256_copy_point(&r, q);
-      ECC_Add(q, &r, p, keyLength);
+      ECC_Add(q, &r, p);
     } else if (sign == 3) {
       p_256_copy_point(&r, q);
-      ECC_Add(q, &r, &minus_p, keyLength);
+      ECC_Add(q, &r, &minus_p);
     }
   }
 
-  multiprecision_inv_mod(minus_p.x, q->z, keyLength);
-  multiprecision_mersenns_squa_mod(q->z, minus_p.x, keyLength);
-  multiprecision_mersenns_mult_mod(q->x, q->x, q->z, keyLength);
-  multiprecision_mersenns_mult_mod(q->z, q->z, minus_p.x, keyLength);
-  multiprecision_mersenns_mult_mod(q->y, q->y, q->z, keyLength);
+  multiprecision_inv_mod(minus_p.x, q->z);
+  multiprecision_mersenns_squa_mod(q->z, minus_p.x);
+  multiprecision_mersenns_mult_mod(q->x, q->x, q->z);
+  multiprecision_mersenns_mult_mod(q->z, q->z, minus_p.x);
+  multiprecision_mersenns_mult_mod(q->y, q->y, q->z);
 }
 
 bool ECC_ValidatePoint(const Point& pt) {
-  const size_t kl = KEY_LENGTH_DWORDS_P256;
-  p_256_init_curve(kl);
+  p_256_init_curve();
 
   // Ensure y^2 = x^3 + a*x + b (mod p); a = -3
 
   // y^2 mod p
-  uint32_t y2_mod[kl] = {0};
-  multiprecision_mersenns_squa_mod(y2_mod, (uint32_t*)pt.y, kl);
+  uint32_t y2_mod[KEY_LENGTH_DWORDS_P256] = {0};
+  multiprecision_mersenns_squa_mod(y2_mod, (uint32_t*)pt.y);
 
   // Right hand side calculation
-  uint32_t rhs[kl] = {0};
-  multiprecision_mersenns_squa_mod(rhs, (uint32_t*)pt.x, kl);
-  uint32_t three[kl] = {0};
+  uint32_t rhs[KEY_LENGTH_DWORDS_P256] = {0};
+  multiprecision_mersenns_squa_mod(rhs, (uint32_t*)pt.x);
+  uint32_t three[KEY_LENGTH_DWORDS_P256] = {0};
   three[0] = 3;
-  multiprecision_sub_mod(rhs, rhs, three, kl);
-  multiprecision_mersenns_mult_mod(rhs, rhs, (uint32_t*)pt.x, kl);
-  multiprecision_add_mod(rhs, rhs, curve_p256.b, kl);
+  multiprecision_sub_mod(rhs, rhs, three);
+  multiprecision_mersenns_mult_mod(rhs, rhs, (uint32_t*)pt.x);
+  multiprecision_add_mod(rhs, rhs, curve_p256.b);
 
-  return multiprecision_compare(rhs, y2_mod, kl) == 0;
+  return multiprecision_compare(rhs, y2_mod) == 0;
 }
diff --git a/stack/smp/p_256_ecc_pp.h b/stack/smp/p_256_ecc_pp.h
index b49b72a..4dff0a8 100644
--- a/stack/smp/p_256_ecc_pp.h
+++ b/stack/smp/p_256_ecc_pp.h
@@ -58,9 +58,8 @@
 
 bool ECC_ValidatePoint(const Point& p);
 
-void ECC_PointMult_Bin_NAF(Point* q, Point* p, uint32_t* n, uint32_t keyLength);
+void ECC_PointMult_Bin_NAF(Point* q, Point* p, uint32_t* n);
 
-#define ECC_PointMult(q, p, n, keyLength) \
-  ECC_PointMult_Bin_NAF(q, p, n, keyLength)
+#define ECC_PointMult(q, p, n) ECC_PointMult_Bin_NAF(q, p, n)
 
-void p_256_init_curve(uint32_t keyLength);
+void p_256_init_curve();
diff --git a/stack/smp/p_256_multprecision.cc b/stack/smp/p_256_multprecision.cc
index 286aca1..0310829 100644
--- a/stack/smp/p_256_multprecision.cc
+++ b/stack/smp/p_256_multprecision.cc
@@ -27,24 +27,24 @@
 #include "bt_target.h"
 #include "p_256_ecc_pp.h"
 
-void multiprecision_init(uint32_t* c, uint32_t keyLength) {
-  for (uint32_t i = 0; i < keyLength; i++) c[i] = 0;
+void multiprecision_init(uint32_t* c) {
+  for (uint32_t i = 0; i < KEY_LENGTH_DWORDS_P256; i++) c[i] = 0;
 }
 
-void multiprecision_copy(uint32_t* c, uint32_t* a, uint32_t keyLength) {
-  for (uint32_t i = 0; i < keyLength; i++) c[i] = a[i];
+void multiprecision_copy(uint32_t* c, uint32_t* a) {
+  for (uint32_t i = 0; i < KEY_LENGTH_DWORDS_P256; i++) c[i] = a[i];
 }
 
-int multiprecision_compare(uint32_t* a, uint32_t* b, uint32_t keyLength) {
-  for (int i = keyLength - 1; i >= 0; i--) {
+int multiprecision_compare(uint32_t* a, uint32_t* b) {
+  for (int i = KEY_LENGTH_DWORDS_P256 - 1; i >= 0; i--) {
     if (a[i] > b[i]) return 1;
     if (a[i] < b[i]) return -1;
   }
   return 0;
 }
 
-int multiprecision_iszero(uint32_t* a, uint32_t keyLength) {
-  for (uint32_t i = 0; i < keyLength; i++)
+int multiprecision_iszero(uint32_t* a) {
+  for (uint32_t i = 0; i < KEY_LENGTH_DWORDS_P256; i++)
     if (a[i]) return 0;
 
   return 1;
@@ -58,30 +58,29 @@
   return i;
 }
 
-uint32_t multiprecision_most_signdwords(uint32_t* a, uint32_t keyLength) {
+uint32_t multiprecision_most_signdwords(uint32_t* a) {
   int i;
-  for (i = keyLength - 1; i >= 0; i--)
+  for (i = KEY_LENGTH_DWORDS_P256 - 1; i >= 0; i--)
     if (a[i]) break;
   return (i + 1);
 }
 
-uint32_t multiprecision_most_signbits(uint32_t* a, uint32_t keyLength) {
+uint32_t multiprecision_most_signbits(uint32_t* a) {
   int aMostSignDWORDs;
 
-  aMostSignDWORDs = multiprecision_most_signdwords(a, keyLength);
+  aMostSignDWORDs = multiprecision_most_signdwords(a);
   if (aMostSignDWORDs == 0) return 0;
 
   return (((aMostSignDWORDs - 1) << DWORD_BITS_SHIFT) +
           multiprecision_dword_bits(a[aMostSignDWORDs - 1]));
 }
 
-uint32_t multiprecision_add(uint32_t* c, uint32_t* a, uint32_t* b,
-                            uint32_t keyLength) {
+uint32_t multiprecision_add(uint32_t* c, uint32_t* a, uint32_t* b) {
   uint32_t carrier;
   uint32_t temp;
 
   carrier = 0;
-  for (uint32_t i = 0; i < keyLength; i++) {
+  for (uint32_t i = 0; i < KEY_LENGTH_DWORDS_P256; i++) {
     temp = a[i] + carrier;
     carrier = (temp < carrier);
     temp += b[i];
@@ -93,13 +92,12 @@
 }
 
 // c=a-b
-uint32_t multiprecision_sub(uint32_t* c, uint32_t* a, uint32_t* b,
-                            uint32_t keyLength) {
+uint32_t multiprecision_sub(uint32_t* c, uint32_t* a, uint32_t* b) {
   uint32_t borrow;
   uint32_t temp;
 
   borrow = 0;
-  for (uint32_t i = 0; i < keyLength; i++) {
+  for (uint32_t i = 0; i < KEY_LENGTH_DWORDS_P256; i++) {
     temp = a[i] - borrow;
     borrow = (temp > a[i]);
     c[i] = temp - b[i];
@@ -110,27 +108,20 @@
 }
 
 // c = a << 1
-void multiprecision_lshift_mod(uint32_t* c, uint32_t* a, uint32_t keyLength) {
+void multiprecision_lshift_mod(uint32_t* c, uint32_t* a) {
   uint32_t carrier;
-  uint32_t* modp;
+  uint32_t* modp = curve_p256.p;
 
-  if (keyLength == KEY_LENGTH_DWORDS_P192) {
-    modp = curve.p;
-  } else if (keyLength == KEY_LENGTH_DWORDS_P256) {
-    modp = curve_p256.p;
-  } else
-    return;
-
-  carrier = multiprecision_lshift(c, a, keyLength);
+  carrier = multiprecision_lshift(c, a);
   if (carrier) {
-    multiprecision_sub(c, c, modp, keyLength);
-  } else if (multiprecision_compare(c, modp, keyLength) >= 0) {
-    multiprecision_sub(c, c, modp, keyLength);
+    multiprecision_sub(c, c, modp);
+  } else if (multiprecision_compare(c, modp) >= 0) {
+    multiprecision_sub(c, c, modp);
   }
 }
 
 // c=a>>1
-void multiprecision_rshift(uint32_t* c, uint32_t* a, uint32_t keyLength) {
+void multiprecision_rshift(uint32_t* c, uint32_t* a) {
   int j;
   uint32_t b = 1;
 
@@ -138,7 +129,7 @@
 
   uint32_t carrier = 0;
   uint32_t temp;
-  for (int i = keyLength - 1; i >= 0; i--) {
+  for (int i = KEY_LENGTH_DWORDS_P256 - 1; i >= 0; i--) {
     temp = a[i];  // in case of c==a
     c[i] = (temp >> b) | carrier;
     carrier = temp << j;
@@ -147,64 +138,42 @@
 
 // Curve specific optimization when p is a pseudo-Mersenns prime,
 // p=2^(KEY_LENGTH_BITS)-omega
-void multiprecision_mersenns_mult_mod(uint32_t* c, uint32_t* a, uint32_t* b,
-                                      uint32_t keyLength) {
+void multiprecision_mersenns_mult_mod(uint32_t* c, uint32_t* a, uint32_t* b) {
   uint32_t cc[2 * KEY_LENGTH_DWORDS_P256];
 
-  multiprecision_mult(cc, a, b, keyLength);
-  if (keyLength == 6) {
-    multiprecision_fast_mod(c, cc);
-  } else if (keyLength == 8) {
-    multiprecision_fast_mod_P256(c, cc);
-  }
+  multiprecision_mult(cc, a, b);
+  multiprecision_fast_mod_P256(c, cc);
 }
 
 // Curve specific optimization when p is a pseudo-Mersenns prime
-void multiprecision_mersenns_squa_mod(uint32_t* c, uint32_t* a,
-                                      uint32_t keyLength) {
-  multiprecision_mersenns_mult_mod(c, a, a, keyLength);
+void multiprecision_mersenns_squa_mod(uint32_t* c, uint32_t* a) {
+  multiprecision_mersenns_mult_mod(c, a, a);
 }
 
 // c=(a+b) mod p, b<p, a<p
-void multiprecision_add_mod(uint32_t* c, uint32_t* a, uint32_t* b,
-                            uint32_t keyLength) {
+void multiprecision_add_mod(uint32_t* c, uint32_t* a, uint32_t* b) {
   uint32_t carrier;
-  uint32_t* modp;
+  uint32_t* modp = curve_p256.p;
 
-  if (keyLength == KEY_LENGTH_DWORDS_P192) {
-    modp = curve.p;
-  } else if (keyLength == KEY_LENGTH_DWORDS_P256) {
-    modp = curve_p256.p;
-  } else
-    return;
-
-  carrier = multiprecision_add(c, a, b, keyLength);
+  carrier = multiprecision_add(c, a, b);
   if (carrier) {
-    multiprecision_sub(c, c, modp, keyLength);
-  } else if (multiprecision_compare(c, modp, keyLength) >= 0) {
-    multiprecision_sub(c, c, modp, keyLength);
+    multiprecision_sub(c, c, modp);
+  } else if (multiprecision_compare(c, modp) >= 0) {
+    multiprecision_sub(c, c, modp);
   }
 }
 
 // c=(a-b) mod p, a<p, b<p
-void multiprecision_sub_mod(uint32_t* c, uint32_t* a, uint32_t* b,
-                            uint32_t keyLength) {
+void multiprecision_sub_mod(uint32_t* c, uint32_t* a, uint32_t* b) {
   uint32_t borrow;
-  uint32_t* modp;
+  uint32_t* modp = curve_p256.p;
 
-  if (keyLength == KEY_LENGTH_DWORDS_P192) {
-    modp = curve.p;
-  } else if (keyLength == KEY_LENGTH_DWORDS_P256) {
-    modp = curve_p256.p;
-  } else
-    return;
-
-  borrow = multiprecision_sub(c, a, b, keyLength);
-  if (borrow) multiprecision_add(c, c, modp, keyLength);
+  borrow = multiprecision_sub(c, a, b);
+  if (borrow) multiprecision_add(c, c, modp);
 }
 
 // c=a<<b, b<DWORD_BITS, c has a buffer size of Numuint32_ts+1
-uint32_t multiprecision_lshift(uint32_t* c, uint32_t* a, uint32_t keyLength) {
+uint32_t multiprecision_lshift(uint32_t* c, uint32_t* a) {
   int j;
   uint32_t b = 1;
   j = DWORD_BITS - b;
@@ -212,7 +181,7 @@
   uint32_t carrier = 0;
   uint32_t temp;
 
-  for (uint32_t i = 0; i < keyLength; i++) {
+  for (uint32_t i = 0; i < KEY_LENGTH_DWORDS_P256; i++) {
     temp = a[i];  // in case c==a
     c[i] = (temp << b) | carrier;
     carrier = temp >> j;
@@ -222,19 +191,18 @@
 }
 
 // c=a*b; c must have a buffer of 2*Key_LENGTH_uint32_tS, c != a != b
-void multiprecision_mult(uint32_t* c, uint32_t* a, uint32_t* b,
-                         uint32_t keyLength) {
+void multiprecision_mult(uint32_t* c, uint32_t* a, uint32_t* b) {
   uint32_t W;
   uint32_t U;
   uint32_t V;
 
   U = V = W = 0;
-  multiprecision_init(c, keyLength);
+  multiprecision_init(c);
 
   // assume little endian right now
-  for (uint32_t i = 0; i < keyLength; i++) {
+  for (uint32_t i = 0; i < KEY_LENGTH_DWORDS_P256; i++) {
     U = 0;
-    for (uint32_t j = 0; j < keyLength; j++) {
+    for (uint32_t j = 0; j < KEY_LENGTH_DWORDS_P256; j++) {
       uint64_t result;
       result = ((uint64_t)a[i]) * ((uint64_t)b[j]);
       W = result >> 32;
@@ -246,78 +214,7 @@
       U += (V < c[i + j]);
       c[i + j] = V;
     }
-    c[i + keyLength] = U;
-  }
-}
-
-void multiprecision_fast_mod(uint32_t* c, uint32_t* a) {
-  uint32_t U;
-  uint32_t V;
-  uint32_t* modp = curve.p;
-
-  c[0] = a[0] + a[6];
-  U = c[0] < a[0];
-  c[0] += a[10];
-  U += c[0] < a[10];
-
-  c[1] = a[1] + U;
-  U = c[1] < a[1];
-  c[1] += a[7];
-  U += c[1] < a[7];
-  c[1] += a[11];
-  U += c[1] < a[11];
-
-  c[2] = a[2] + U;
-  U = c[2] < a[2];
-  c[2] += a[6];
-  U += c[2] < a[6];
-  c[2] += a[8];
-  U += c[2] < a[8];
-  c[2] += a[10];
-  U += c[2] < a[10];
-
-  c[3] = a[3] + U;
-  U = c[3] < a[3];
-  c[3] += a[7];
-  U += c[3] < a[7];
-  c[3] += a[9];
-  U += c[3] < a[9];
-  c[3] += a[11];
-  U += c[3] < a[11];
-
-  c[4] = a[4] + U;
-  U = c[4] < a[4];
-  c[4] += a[8];
-  U += c[4] < a[8];
-  c[4] += a[10];
-  U += c[4] < a[10];
-
-  c[5] = a[5] + U;
-  U = c[5] < a[5];
-  c[5] += a[9];
-  U += c[5] < a[9];
-  c[5] += a[11];
-  U += c[5] < a[11];
-
-  c[0] += U;
-  V = c[0] < U;
-  c[1] += V;
-  V = c[1] < V;
-  c[2] += V;
-  V = c[2] < V;
-  c[2] += U;
-  V = c[2] < U;
-  c[3] += V;
-  V = c[3] < V;
-  c[4] += V;
-  V = c[4] < V;
-  c[5] += V;
-  V = c[5] < V;
-
-  if (V) {
-    multiprecision_sub(c, c, modp, KEY_LENGTH_DWORDS_P192);
-  } else if (multiprecision_compare(c, modp, KEY_LENGTH_DWORDS_P192) >= 0) {
-    multiprecision_sub(c, c, modp, KEY_LENGTH_DWORDS_P192);
+    c[i + KEY_LENGTH_DWORDS_P256] = U;
   }
 }
 
@@ -541,74 +438,67 @@
 
   if (U & 0x80000000) {
     while (U) {
-      multiprecision_add(c, c, modp, KEY_LENGTH_DWORDS_P256);
+      multiprecision_add(c, c, modp);
       U++;
     }
   } else if (U) {
     while (U) {
-      multiprecision_sub(c, c, modp, KEY_LENGTH_DWORDS_P256);
+      multiprecision_sub(c, c, modp);
       U--;
     }
   }
 
-  if (multiprecision_compare(c, modp, KEY_LENGTH_DWORDS_P256) >= 0)
-    multiprecision_sub(c, c, modp, KEY_LENGTH_DWORDS_P256);
+  if (multiprecision_compare(c, modp) >= 0) multiprecision_sub(c, c, modp);
 }
 
-void multiprecision_inv_mod(uint32_t* aminus, uint32_t* u, uint32_t keyLength) {
+void multiprecision_inv_mod(uint32_t* aminus, uint32_t* u) {
   uint32_t v[KEY_LENGTH_DWORDS_P256];
   uint32_t A[KEY_LENGTH_DWORDS_P256 + 1];
   uint32_t C[KEY_LENGTH_DWORDS_P256 + 1];
-  uint32_t* modp;
+  uint32_t* modp = curve_p256.p;
 
-  if (keyLength == KEY_LENGTH_DWORDS_P256) {
-    modp = curve_p256.p;
-  } else {
-    modp = curve.p;
-  }
-
-  multiprecision_copy(v, modp, keyLength);
-  multiprecision_init(A, keyLength);
-  multiprecision_init(C, keyLength);
+  multiprecision_copy(v, modp);
+  multiprecision_init(A);
+  multiprecision_init(C);
   A[0] = 1;
 
-  while (!multiprecision_iszero(u, keyLength)) {
+  while (!multiprecision_iszero(u)) {
     while (!(u[0] & 0x01))  // u is even
     {
-      multiprecision_rshift(u, u, keyLength);
+      multiprecision_rshift(u, u);
       if (!(A[0] & 0x01))  // A is even
-        multiprecision_rshift(A, A, keyLength);
+        multiprecision_rshift(A, A);
       else {
-        A[keyLength] = multiprecision_add(A, A, modp, keyLength);  // A =A+p
-        multiprecision_rshift(A, A, keyLength);
-        A[keyLength - 1] |= (A[keyLength] << 31);
+        A[KEY_LENGTH_DWORDS_P256] = multiprecision_add(A, A, modp);  // A =A+p
+        multiprecision_rshift(A, A);
+        A[KEY_LENGTH_DWORDS_P256 - 1] |= (A[KEY_LENGTH_DWORDS_P256] << 31);
       }
     }
 
     while (!(v[0] & 0x01))  // v is even
     {
-      multiprecision_rshift(v, v, keyLength);
+      multiprecision_rshift(v, v);
       if (!(C[0] & 0x01))  // C is even
       {
-        multiprecision_rshift(C, C, keyLength);
+        multiprecision_rshift(C, C);
       } else {
-        C[keyLength] = multiprecision_add(C, C, modp, keyLength);  // C =C+p
-        multiprecision_rshift(C, C, keyLength);
-        C[keyLength - 1] |= (C[keyLength] << 31);
+        C[KEY_LENGTH_DWORDS_P256] = multiprecision_add(C, C, modp);  // C =C+p
+        multiprecision_rshift(C, C);
+        C[KEY_LENGTH_DWORDS_P256 - 1] |= (C[KEY_LENGTH_DWORDS_P256] << 31);
       }
     }
 
-    if (multiprecision_compare(u, v, keyLength) >= 0) {
-      multiprecision_sub(u, u, v, keyLength);
-      multiprecision_sub_mod(A, A, C, keyLength);
+    if (multiprecision_compare(u, v) >= 0) {
+      multiprecision_sub(u, u, v);
+      multiprecision_sub_mod(A, A, C);
     } else {
-      multiprecision_sub(v, v, u, keyLength);
-      multiprecision_sub_mod(C, C, A, keyLength);
+      multiprecision_sub(v, v, u);
+      multiprecision_sub_mod(C, C, A);
     }
   }
 
-  if (multiprecision_compare(C, modp, keyLength) >= 0)
-    multiprecision_sub(aminus, C, modp, keyLength);
+  if (multiprecision_compare(C, modp) >= 0)
+    multiprecision_sub(aminus, C, modp);
   else
-    multiprecision_copy(aminus, C, keyLength);
+    multiprecision_copy(aminus, C);
 }
diff --git a/stack/smp/p_256_multprecision.h b/stack/smp/p_256_multprecision.h
index fbbd88d..a98fe4d 100644
--- a/stack/smp/p_256_multprecision.h
+++ b/stack/smp/p_256_multprecision.h
@@ -12,8 +12,7 @@
  *  distributed under the License is distributed on an "AS IS" BASIS,
  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  *  See the License for the specific language governing permissions and
- *  limitations under the License.
- *
+ *  limitations under the License. *
  ******************************************************************************/
 
 /*******************************************************************************
@@ -29,40 +28,30 @@
 #define DWORD_BYTES 4
 #define DWORD_BITS_SHIFT 5
 
-#define KEY_LENGTH_DWORDS_P192 6
 #define KEY_LENGTH_DWORDS_P256 8
 /* Arithmetic Operations */
 
-int multiprecision_compare(uint32_t* a, uint32_t* b, uint32_t keyLength);
-int multiprecision_iszero(uint32_t* a, uint32_t keyLength);
-void multiprecision_init(uint32_t* c, uint32_t keyLength);
-void multiprecision_copy(uint32_t* c, uint32_t* a, uint32_t keyLength);
+int multiprecision_compare(uint32_t* a, uint32_t* b);
+int multiprecision_iszero(uint32_t* a);
+void multiprecision_init(uint32_t* c);
+void multiprecision_copy(uint32_t* c, uint32_t* a);
 uint32_t multiprecision_dword_bits(uint32_t a);
-uint32_t multiprecision_most_signdwords(uint32_t* a, uint32_t keyLength);
-uint32_t multiprecision_most_signbits(uint32_t* a, uint32_t keyLength);
-void multiprecision_inv_mod(uint32_t* aminus, uint32_t* a, uint32_t keyLength);
-uint32_t multiprecision_add(uint32_t* c, uint32_t* a, uint32_t* b,
-                            uint32_t keyLength);  // c=a+b
-void multiprecision_add_mod(uint32_t* c, uint32_t* a, uint32_t* b,
-                            uint32_t keyLength);
-uint32_t multiprecision_sub(uint32_t* c, uint32_t* a, uint32_t* b,
-                            uint32_t keyLength);  // c=a-b
-void multiprecision_sub_mod(uint32_t* c, uint32_t* a, uint32_t* b,
-                            uint32_t keyLength);
-void multiprecision_rshift(uint32_t* c, uint32_t* a,
-                           uint32_t keyLength);  // c=a>>1, return carrier
-void multiprecision_lshift_mod(uint32_t* c, uint32_t* a,
-                               uint32_t keyLength);  // c=a<<b, return carrier
-uint32_t multiprecision_lshift(uint32_t* c, uint32_t* a,
-                               uint32_t keyLength);  // c=a<<b, return carrier
-void multiprecision_mult(uint32_t* c, uint32_t* a, uint32_t* b,
-                         uint32_t keyLength);  // c=a*b
-void multiprecision_mersenns_mult_mod(uint32_t* c, uint32_t* a, uint32_t* b,
-                                      uint32_t keyLength);
-void multiprecision_mersenns_squa_mod(uint32_t* c, uint32_t* a,
-                                      uint32_t keyLength);
-uint32_t multiprecision_lshift(uint32_t* c, uint32_t* a, uint32_t keyLength);
-void multiprecision_mult(uint32_t* c, uint32_t* a, uint32_t* b,
-                         uint32_t keyLength);
+uint32_t multiprecision_most_signdwords(uint32_t* a);
+uint32_t multiprecision_most_signbits(uint32_t* a);
+void multiprecision_inv_mod(uint32_t* aminus, uint32_t* a);
+uint32_t multiprecision_add(uint32_t* c, uint32_t* a, uint32_t* b);  // c=a+b
+void multiprecision_add_mod(uint32_t* c, uint32_t* a, uint32_t* b);
+uint32_t multiprecision_sub(uint32_t* c, uint32_t* a, uint32_t* b);  // c=a-b
+void multiprecision_sub_mod(uint32_t* c, uint32_t* a, uint32_t* b);
+void multiprecision_rshift(uint32_t* c, uint32_t* a);  // c=a>>1, return carrier
+void multiprecision_lshift_mod(uint32_t* c,
+                               uint32_t* a);  // c=a<<b, return carrier
+uint32_t multiprecision_lshift(uint32_t* c,
+                               uint32_t* a);  // c=a<<b, return carrier
+void multiprecision_mult(uint32_t* c, uint32_t* a, uint32_t* b);  // c=a*b
+void multiprecision_mersenns_mult_mod(uint32_t* c, uint32_t* a, uint32_t* b);
+void multiprecision_mersenns_squa_mod(uint32_t* c, uint32_t* a);
+uint32_t multiprecision_lshift(uint32_t* c, uint32_t* a);
+void multiprecision_mult(uint32_t* c, uint32_t* a, uint32_t* b);
 void multiprecision_fast_mod(uint32_t* c, uint32_t* a);
 void multiprecision_fast_mod_P256(uint32_t* c, uint32_t* a);
diff --git a/stack/smp/smp_api.cc b/stack/smp/smp_api.cc
index f8e8a68..f2c7ed8 100644
--- a/stack/smp/smp_api.cc
+++ b/stack/smp/smp_api.cc
@@ -62,7 +62,7 @@
 
   smp_l2cap_if_init();
   /* initialization of P-256 parameters */
-  p_256_init_curve(KEY_LENGTH_DWORDS_P256);
+  p_256_init_curve();
 
   /* Initialize failure case for certification */
   smp_cb.cert_failure =
diff --git a/stack/smp/smp_keys.cc b/stack/smp/smp_keys.cc
index da8d0f4..a372ba4 100644
--- a/stack/smp/smp_keys.cc
+++ b/stack/smp/smp_keys.cc
@@ -177,7 +177,7 @@
   SMP_TRACE_DEBUG("%s", __func__);
 
   if (p_cb->le_secure_connections_mode_is_used) {
-    SMP_TRACE_WARNING("FOR LE SC LTK IS USED INSTEAD OF STK");
+    SMP_TRACE_DEBUG("FOR LE SC LTK IS USED INSTEAD OF STK");
     output = p_cb->ltk;
   } else {
     output = smp_calculate_legacy_short_term_key(p_cb);
@@ -691,8 +691,7 @@
   SMP_TRACE_DEBUG("%s", __func__);
 
   memcpy(private_key, p_cb->private_key, BT_OCTET32_LEN);
-  ECC_PointMult(&public_key, &(curve_p256.G), (uint32_t*)private_key,
-                KEY_LENGTH_DWORDS_P256);
+  ECC_PointMult(&public_key, &(curve_p256.G), (uint32_t*)private_key);
   memcpy(p_cb->loc_publ_key.x, public_key.x, BT_OCTET32_LEN);
   memcpy(p_cb->loc_publ_key.y, public_key.y, BT_OCTET32_LEN);
 
@@ -728,8 +727,7 @@
   memcpy(peer_publ_key.x, p_cb->peer_publ_key.x, BT_OCTET32_LEN);
   memcpy(peer_publ_key.y, p_cb->peer_publ_key.y, BT_OCTET32_LEN);
 
-  ECC_PointMult(&new_publ_key, &peer_publ_key, (uint32_t*)private_key,
-                KEY_LENGTH_DWORDS_P256);
+  ECC_PointMult(&new_publ_key, &peer_publ_key, (uint32_t*)private_key);
 
   memcpy(p_cb->dhkey, new_publ_key.x, BT_OCTET32_LEN);
 
@@ -989,7 +987,8 @@
 
   link_key_type += BTM_LTK_DERIVED_LKEY_OFFSET;
 
-  Octet16 notif_link_key = link_key;
+  Octet16 notif_link_key;
+  std::reverse_copy(link_key.begin(), link_key.end(), notif_link_key.begin());
   btm_sec_link_key_notification(bda_for_lk, notif_link_key, link_key_type);
 
   SMP_TRACE_EVENT("%s is completed", __func__);
diff --git a/stack/test/ble_advertiser_test.cc b/stack/test/ble_advertiser_test.cc
index 9c7d5a5..98142dd 100644
--- a/stack/test/ble_advertiser_test.cc
+++ b/stack/test/ble_advertiser_test.cc
@@ -68,6 +68,8 @@
 void alarm_free(alarm_t* alarm) {}
 const controller_t* controller_get_interface() { return nullptr; }
 
+uint64_t btm_get_next_private_addrress_interval_ms() { return 15 * 60 * 1000; }
+
 namespace {
 void DoNothing(uint8_t) {}
 
@@ -131,7 +133,7 @@
                    cmd_complete);
   };
 
-  bool QuirkAdvertiserZeroHandle() { return false; }
+  bool QuirkAdvertiserZeroHandle() override { return false; }
 
  private:
   DISALLOW_COPY_AND_ASSIGN(AdvertiserHciMock);
@@ -153,7 +155,7 @@
 
   std::unique_ptr<AdvertiserHciMock> hci_mock;
 
-  virtual void SetUp() {
+  void SetUp() override {
     hci_mock.reset(new AdvertiserHciMock());
 
     base::Callback<void(uint8_t)> inst_cnt_Cb;
@@ -168,7 +170,7 @@
     inst_cnt_Cb.Run(num_adv_instances);
   }
 
-  virtual void TearDown() {
+  void TearDown() override {
     BleAdvertisingManager::CleanUp();
     hci_mock.reset();
   }
diff --git a/stack/test/common/mock_l2cap_layer.cc b/stack/test/common/mock_l2cap_layer.cc
index 4756f17..605eb49 100644
--- a/stack/test/common/mock_l2cap_layer.cc
+++ b/stack/test/common/mock_l2cap_layer.cc
@@ -25,10 +25,10 @@
 }
 
 uint16_t L2CA_Register(uint16_t psm, tL2CAP_APPL_INFO* p_cb_info,
-                       bool enable_snoop) {
+                       bool enable_snoop, tL2CAP_ERTM_INFO* p_ertm_info) {
   VLOG(1) << __func__ << ": psm=" << psm << ", p_cb_info=" << p_cb_info
           << ", enable_snoop=" << enable_snoop;
-  return l2cap_interface->Register(psm, p_cb_info, enable_snoop);
+  return l2cap_interface->Register(psm, p_cb_info, enable_snoop, p_ertm_info);
 }
 
 uint16_t L2CA_ConnectReq(uint16_t psm, const RawAddress& bd_addr) {
diff --git a/stack/test/common/mock_l2cap_layer.h b/stack/test/common/mock_l2cap_layer.h
index ade7585..732d415 100644
--- a/stack/test/common/mock_l2cap_layer.h
+++ b/stack/test/common/mock_l2cap_layer.h
@@ -27,7 +27,8 @@
 class L2capInterface {
  public:
   virtual uint16_t Register(uint16_t psm, tL2CAP_APPL_INFO* p_cb_info,
-                            bool enable_snoop) = 0;
+                            bool enable_snoop,
+                            tL2CAP_ERTM_INFO* p_ertm_info) = 0;
   virtual uint16_t ConnectRequest(uint16_t psm, const RawAddress& bd_addr) = 0;
   virtual bool ConnectResponse(const RawAddress& bd_addr, uint8_t id,
                                uint16_t lcid, uint16_t result,
@@ -42,8 +43,9 @@
 
 class MockL2capInterface : public L2capInterface {
  public:
-  MOCK_METHOD3(Register, uint16_t(uint16_t psm, tL2CAP_APPL_INFO* p_cb_info,
-                                  bool enable_snoop));
+  MOCK_METHOD4(Register,
+               uint16_t(uint16_t psm, tL2CAP_APPL_INFO* p_cb_info,
+                        bool enable_snoop, tL2CAP_ERTM_INFO* p_ertm_info));
   MOCK_METHOD2(ConnectRequest,
                uint16_t(uint16_t psm, const RawAddress& bd_addr));
   MOCK_METHOD5(ConnectResponse,
diff --git a/stack/test/gatt/gatt_sr_test.cc b/stack/test/gatt/gatt_sr_test.cc
new file mode 100644
index 0000000..a5fe4cd
--- /dev/null
+++ b/stack/test/gatt/gatt_sr_test.cc
@@ -0,0 +1,320 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <base/logging.h>
+#include <gtest/gtest.h>
+#include <stdio.h>
+#include <cstdint>
+
+#include "osi/test/AllocationTestHarness.h"
+#include "stack/gatt/gatt_int.h"
+#undef LOG_TAG
+#include "stack/gatt/gatt_sr.cc"
+#include "types/raw_address.h"
+
+#define MAX_UINT16 ((uint16_t)0xffff)
+
+tGATT_CB gatt_cb;
+
+namespace {
+
+struct TestMutables {
+  struct {
+    uint8_t op_code_;
+  } attp_build_sr_msg;
+  struct {
+    uint16_t conn_id_{0};
+    uint32_t trans_id_{0};
+    tGATTS_REQ_TYPE type_{0xff};
+    tGATTS_DATA data_;
+  } application_request_callback;
+  struct {
+    int access_count_{0};
+    tGATT_STATUS return_status_{GATT_SUCCESS};
+  } gatts_write_attr_perm_check;
+};
+
+TestMutables test_state_;
+}  // namespace
+
+namespace connection_manager {
+bool background_connect_remove(uint8_t app_id, const RawAddress& address) {
+  return false;
+}
+bool direct_connect_remove(uint8_t app_id, const RawAddress& address) {
+  return false;
+}
+}  // namespace connection_manager
+
+BT_HDR* attp_build_sr_msg(tGATT_TCB& tcb, uint8_t op_code,
+                          tGATT_SR_MSG* p_msg) {
+  test_state_.attp_build_sr_msg.op_code_ = op_code;
+  return nullptr;
+}
+tGATT_STATUS attp_send_cl_msg(tGATT_TCB& tcb, tGATT_CLCB* p_clcb,
+                              uint8_t op_code, tGATT_CL_MSG* p_msg) {
+  return 0;
+}
+tGATT_STATUS attp_send_sr_msg(tGATT_TCB& tcb, BT_HDR* p_msg) { return 0; }
+uint8_t btm_ble_read_sec_key_size(const RawAddress& bd_addr) { return 0; }
+bool BTM_GetSecurityFlagsByTransport(const RawAddress& bd_addr,
+                                     uint8_t* p_sec_flags,
+                                     tBT_TRANSPORT transport) {
+  return false;
+}
+void gatt_act_discovery(tGATT_CLCB* p_clcb) {}
+bool gatt_disconnect(tGATT_TCB* p_tcb) { return false; }
+tGATT_CH_STATE gatt_get_ch_state(tGATT_TCB* p_tcb) { return 0; }
+tGATT_STATUS gatts_db_read_attr_value_by_type(
+    tGATT_TCB& tcb, tGATT_SVC_DB* p_db, uint8_t op_code, BT_HDR* p_rsp,
+    uint16_t s_handle, uint16_t e_handle, const Uuid& type, uint16_t* p_len,
+    tGATT_SEC_FLAG sec_flag, uint8_t key_size, uint32_t trans_id,
+    uint16_t* p_cur_handle) {
+  return 0;
+}
+void gatt_set_ch_state(tGATT_TCB* p_tcb, tGATT_CH_STATE ch_state) {}
+Uuid* gatts_get_service_uuid(tGATT_SVC_DB* p_db) { return nullptr; }
+tGATT_STATUS GATTS_HandleValueIndication(uint16_t conn_id, uint16_t attr_handle,
+                                         uint16_t val_len, uint8_t* p_val) {
+  return GATT_SUCCESS;
+}
+tGATT_STATUS gatts_read_attr_perm_check(tGATT_SVC_DB* p_db, bool is_long,
+                                        uint16_t handle,
+                                        tGATT_SEC_FLAG sec_flag,
+                                        uint8_t key_size) {
+  return GATT_SUCCESS;
+}
+tGATT_STATUS gatts_read_attr_value_by_handle(
+    tGATT_TCB& tcb, tGATT_SVC_DB* p_db, uint8_t op_code, uint16_t handle,
+    uint16_t offset, uint8_t* p_value, uint16_t* p_len, uint16_t mtu,
+    tGATT_SEC_FLAG sec_flag, uint8_t key_size, uint32_t trans_id) {
+  return GATT_SUCCESS;
+}
+tGATT_STATUS gatts_write_attr_perm_check(tGATT_SVC_DB* p_db, uint8_t op_code,
+                                         uint16_t handle, uint16_t offset,
+                                         uint8_t* p_data, uint16_t len,
+                                         tGATT_SEC_FLAG sec_flag,
+                                         uint8_t key_size) {
+  test_state_.gatts_write_attr_perm_check.access_count_++;
+  return test_state_.gatts_write_attr_perm_check.return_status_;
+}
+void gatt_update_app_use_link_flag(tGATT_IF gatt_if, tGATT_TCB* p_tcb,
+                                   bool is_add, bool check_acl_link) {}
+base::MessageLoop* get_main_message_loop() { return nullptr; }
+void l2cble_set_fixed_channel_tx_data_length(const RawAddress& remote_bda,
+                                             uint16_t fix_cid,
+                                             uint16_t tx_mtu) {}
+bool SDP_AddAttribute(uint32_t handle, uint16_t attr_id, uint8_t attr_type,
+                      uint32_t attr_len, uint8_t* p_val) {
+  return false;
+}
+bool SDP_AddProtocolList(uint32_t handle, uint16_t num_elem,
+                         tSDP_PROTOCOL_ELEM* p_elem_list) {
+  return false;
+}
+bool SDP_AddServiceClassIdList(uint32_t handle, uint16_t num_services,
+                               uint16_t* p_service_uuids) {
+  return false;
+}
+bool SDP_AddUuidSequence(uint32_t handle, uint16_t attr_id, uint16_t num_uuids,
+                         uint16_t* p_uuids) {
+  return false;
+}
+uint32_t SDP_CreateRecord(void) { return 0; }
+
+void ApplicationRequestCallback(uint16_t conn_id, uint32_t trans_id,
+                                tGATTS_REQ_TYPE type, tGATTS_DATA* p_data) {
+  test_state_.application_request_callback.conn_id_ = conn_id;
+  test_state_.application_request_callback.trans_id_ = trans_id;
+  test_state_.application_request_callback.type_ = type;
+  test_state_.application_request_callback.data_ = *p_data;
+}
+
+/**
+ * Test class to test selected functionality in stack/gatt/gatt_sr.cc
+ */
+extern void allocation_tracker_uninit(void);
+namespace {
+uint16_t kHandle = 1;
+bt_gatt_db_attribute_type_t kGattCharacteristicType = BTGATT_DB_CHARACTERISTIC;
+}  // namespace
+class GattSrTest : public AllocationTestHarness {
+ protected:
+  void SetUp() override {
+    AllocationTestHarness::SetUp();
+    // Disable our allocation tracker to allow ASAN full range
+    allocation_tracker_uninit();
+    memset(&tcb_, 0, sizeof(tcb_));
+    memset(&el_, 0, sizeof(el_));
+
+    tcb_.trans_id = 0x12345677;
+    el_.gatt_if = 1;
+    gatt_cb.cl_rcb[el_.gatt_if - 1].in_use = true;
+    gatt_cb.cl_rcb[el_.gatt_if - 1].app_cb.p_req_cb =
+        ApplicationRequestCallback;
+
+    test_state_ = TestMutables();
+  }
+
+  void TearDown() override { AllocationTestHarness::TearDown(); }
+
+  tGATT_TCB tcb_;
+  tGATT_SRV_LIST_ELEM el_;
+};
+
+TEST_F(GattSrTest, gatts_process_write_req_request_prepare_write_no_data) {
+  gatts_process_write_req(tcb_, el_, kHandle, GATT_REQ_PREPARE_WRITE, 0,
+                          nullptr, kGattCharacteristicType);
+}
+
+TEST_F(GattSrTest,
+       gatts_process_write_req_request_prepare_write_max_len_no_data) {
+  gatts_process_write_req(tcb_, el_, kHandle, GATT_REQ_PREPARE_WRITE,
+                          MAX_UINT16, nullptr, kGattCharacteristicType);
+}
+
+TEST_F(GattSrTest,
+       gatts_process_write_req_request_prepare_write_zero_len_max_data) {
+  uint8_t max_mem[MAX_UINT16];
+  gatts_process_write_req(tcb_, el_, kHandle, GATT_REQ_PREPARE_WRITE, 0,
+                          max_mem, kGattCharacteristicType);
+}
+
+TEST_F(GattSrTest, gatts_process_write_req_request_prepare_write_typical) {
+  uint8_t p_data[2] = {0x34, 0x12};
+  uint16_t length = static_cast<uint16_t>(sizeof(p_data) / sizeof(p_data[0]));
+  gatts_process_write_req(tcb_, el_, kHandle, GATT_REQ_PREPARE_WRITE, length,
+                          p_data, kGattCharacteristicType);
+
+  CHECK(test_state_.gatts_write_attr_perm_check.access_count_ == 1);
+  CHECK(test_state_.application_request_callback.conn_id_ == el_.gatt_if);
+  CHECK(test_state_.application_request_callback.trans_id_ == 0x12345678);
+  CHECK(test_state_.application_request_callback.type_ ==
+        GATTS_REQ_TYPE_WRITE_CHARACTERISTIC);
+  CHECK(test_state_.application_request_callback.data_.write_req.offset ==
+        0x1234);
+  CHECK(test_state_.application_request_callback.data_.write_req.is_prep ==
+        true);
+  CHECK(test_state_.application_request_callback.data_.write_req.len == 0);
+}
+
+TEST_F(GattSrTest, gatts_process_write_req_signed_command_write_no_data) {
+  gatts_process_write_req(tcb_, el_, kHandle, GATT_SIGN_CMD_WRITE, 0, nullptr,
+                          kGattCharacteristicType);
+}
+
+TEST_F(GattSrTest,
+       gatts_process_write_req_signed_command_write_max_len_no_data) {
+  gatts_process_write_req(tcb_, el_, kHandle, GATT_SIGN_CMD_WRITE, MAX_UINT16,
+                          nullptr, kGattCharacteristicType);
+}
+
+TEST_F(GattSrTest,
+       gatts_process_write_req_signed_command_write_zero_len_max_data) {
+  uint8_t max_mem[MAX_UINT16];
+  gatts_process_write_req(tcb_, el_, kHandle, GATT_SIGN_CMD_WRITE, 0, max_mem,
+                          kGattCharacteristicType);
+}
+
+TEST_F(GattSrTest, gatts_process_write_req_signed_command_write_typical) {
+  static constexpr size_t kDataLength = 4;
+  uint8_t p_data[GATT_AUTH_SIGN_LEN + kDataLength] = {
+      0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
+      0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x01};
+  uint16_t length = static_cast<uint16_t>(sizeof(p_data) / sizeof(p_data[0]));
+  gatts_process_write_req(tcb_, el_, kHandle, GATT_SIGN_CMD_WRITE, length,
+                          p_data, kGattCharacteristicType);
+
+  CHECK(test_state_.gatts_write_attr_perm_check.access_count_ == 1);
+  CHECK(test_state_.application_request_callback.conn_id_ == el_.gatt_if);
+  CHECK(test_state_.application_request_callback.trans_id_ == 0x12345678);
+  CHECK(test_state_.application_request_callback.type_ ==
+        GATTS_REQ_TYPE_WRITE_CHARACTERISTIC);
+  CHECK(test_state_.application_request_callback.data_.write_req.offset == 0x0);
+  CHECK(test_state_.application_request_callback.data_.write_req.is_prep ==
+        false);
+  CHECK(test_state_.application_request_callback.data_.write_req.len ==
+        kDataLength);
+}
+
+TEST_F(GattSrTest, gatts_process_write_req_command_write_no_data) {
+  gatts_process_write_req(tcb_, el_, kHandle, GATT_CMD_WRITE, 0, nullptr,
+                          kGattCharacteristicType);
+}
+
+TEST_F(GattSrTest, gatts_process_write_req_command_write_max_len_no_data) {
+  gatts_process_write_req(tcb_, el_, kHandle, GATT_CMD_WRITE, MAX_UINT16,
+                          nullptr, kGattCharacteristicType);
+}
+
+TEST_F(GattSrTest, gatts_process_write_req_command_write_zero_len_max_data) {
+  uint8_t max_mem[MAX_UINT16];
+  gatts_process_write_req(tcb_, el_, kHandle, GATT_CMD_WRITE, 0, max_mem,
+                          kGattCharacteristicType);
+}
+
+TEST_F(GattSrTest, gatts_process_write_req_command_write_typical) {
+  uint8_t p_data[16] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
+                        0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x01};
+  uint16_t length = static_cast<uint16_t>(sizeof(p_data) / sizeof(p_data[0]));
+  gatts_process_write_req(tcb_, el_, kHandle, GATT_CMD_WRITE, length, p_data,
+                          kGattCharacteristicType);
+
+  CHECK(test_state_.gatts_write_attr_perm_check.access_count_ == 1);
+  CHECK(test_state_.application_request_callback.conn_id_ == el_.gatt_if);
+  CHECK(test_state_.application_request_callback.trans_id_ == 0x12345678);
+  CHECK(test_state_.application_request_callback.type_ ==
+        GATTS_REQ_TYPE_WRITE_CHARACTERISTIC);
+  CHECK(test_state_.application_request_callback.data_.write_req.offset == 0x0);
+  CHECK(test_state_.application_request_callback.data_.write_req.is_prep ==
+        false);
+  CHECK(test_state_.application_request_callback.data_.write_req.len == length);
+}
+
+TEST_F(GattSrTest, gatts_process_write_req_request_write_no_data) {
+  gatts_process_write_req(tcb_, el_, kHandle, GATT_REQ_WRITE, 0, nullptr,
+                          kGattCharacteristicType);
+}
+
+TEST_F(GattSrTest, gatts_process_write_req_request_write_max_len_no_data) {
+  gatts_process_write_req(tcb_, el_, kHandle, GATT_REQ_WRITE, MAX_UINT16,
+                          nullptr, kGattCharacteristicType);
+}
+
+TEST_F(GattSrTest, gatts_process_write_req_request_write_zero_len_max_data) {
+  uint8_t max_mem[MAX_UINT16];
+  gatts_process_write_req(tcb_, el_, kHandle, GATT_REQ_WRITE, 0, max_mem,
+                          kGattCharacteristicType);
+}
+
+TEST_F(GattSrTest, gatts_process_write_req_request_write_typical) {
+  uint8_t p_data[16] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
+                        0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x01};
+  uint16_t length = static_cast<uint16_t>(sizeof(p_data) / sizeof(p_data[0]));
+
+  gatts_process_write_req(tcb_, el_, kHandle, GATT_REQ_WRITE, length, p_data,
+                          kGattCharacteristicType);
+
+  CHECK(test_state_.gatts_write_attr_perm_check.access_count_ == 1);
+  CHECK(test_state_.application_request_callback.conn_id_ == el_.gatt_if);
+  CHECK(test_state_.application_request_callback.trans_id_ == 0x12345678);
+  CHECK(test_state_.application_request_callback.type_ ==
+        GATTS_REQ_TYPE_WRITE_CHARACTERISTIC);
+  CHECK(test_state_.application_request_callback.data_.write_req.offset == 0x0);
+  CHECK(test_state_.application_request_callback.data_.write_req.is_prep ==
+        false);
+  CHECK(test_state_.application_request_callback.data_.write_req.len == length);
+}
diff --git a/stack/test/gatt_connection_manager_test.cc b/stack/test/gatt_connection_manager_test.cc
index eed3776..0997aa8 100644
--- a/stack/test/gatt_connection_manager_test.cc
+++ b/stack/test/gatt_connection_manager_test.cc
@@ -61,11 +61,11 @@
 
 namespace connection_manager {
 class BleConnectionManager : public testing::Test {
-  virtual void SetUp() {
+  void SetUp() override {
     localWhiteListMock = std::make_unique<WhiteListMock>();
   }
 
-  virtual void TearDown() {
+  void TearDown() override {
     connection_manager::reset(true);
     AlarmMock::Reset();
     localWhiteListMock.reset();
diff --git a/stack/test/rfcomm/stack_rfcomm_test.cc b/stack/test/rfcomm/stack_rfcomm_test.cc
index 52e1170..d0d1c6e 100644
--- a/stack/test/rfcomm/stack_rfcomm_test.cc
+++ b/stack/test/rfcomm/stack_rfcomm_test.cc
@@ -453,7 +453,7 @@
         &btm_security_internal_interface_);
     bluetooth::l2cap::SetMockInterface(&l2cap_interface_);
     rfcomm_callback = &rfcomm_callback_;
-    EXPECT_CALL(l2cap_interface_, Register(BT_PSM_RFCOMM, _, _))
+    EXPECT_CALL(l2cap_interface_, Register(BT_PSM_RFCOMM, _, _, _))
         .WillOnce(
             DoAll(SaveArgPointee<1>(&l2cap_appl_info_), Return(BT_PSM_RFCOMM)));
     RFCOMM_Init();
diff --git a/stack/test/stack_btu_test.cc b/stack/test/stack_btu_test.cc
index 5976bb2..2502646 100644
--- a/stack/test/stack_btu_test.cc
+++ b/stack/test/stack_btu_test.cc
@@ -89,7 +89,7 @@
   MOCK_METHOD0(TestCallback, void(void));
   base::MessageLoop* message_loop;
 
-  virtual void SetUp() {
+  void SetUp() override {
     // Initialize alarms to prevent btu_task_shut_down from crashing
     alarm_new("test alarm");
     bt_startup_thread.StartUp();
@@ -100,7 +100,7 @@
                               "BTU startup timed out"));
   }
 
-  virtual void TearDown() {
+  void TearDown() override {
     btu_task_shut_down(nullptr);
     alarm_cleanup();
     bt_startup_thread.ShutDown();
diff --git a/stack/test/stack_smp_test.cc b/stack/test/stack_smp_test.cc
index 0b1f0a9..d83430c 100644
--- a/stack/test/stack_smp_test.cc
+++ b/stack/test/stack_smp_test.cc
@@ -131,7 +131,7 @@
   Octet16 rand_{0x57, 0x83, 0xD5, 0x21, 0x56, 0xAD, 0x6F, 0x0E,
                 0x63, 0x88, 0x27, 0x4E, 0xC6, 0x70, 0x2E, 0xE0};
 
-  void SetUp() {
+  void SetUp() override {
     p_cb_.tk = {0};
     // Set pairing request packet to 0x070710000001(01)
     p_cb_.local_io_capability = 0x01;
@@ -151,7 +151,7 @@
     p_cb_.role = HCI_ROLE_MASTER;
     std::reverse(rand_.begin(), rand_.end());
   }
-  void TearDown() {}
+  void TearDown() override {}
 
  public:
 };
@@ -261,8 +261,8 @@
 
 TEST(SmpEccValidationTest, test_invalid_points) {
   Point p;
-  multiprecision_init(p.x, 8);
-  multiprecision_init(p.y, 8);
+  multiprecision_init(p.x);
+  multiprecision_init(p.y);
 
   EXPECT_FALSE(ECC_ValidatePoint(p));
 
diff --git a/test/gen_coverage.py b/test/gen_coverage.py
index 3697c37..c7b6c33 100755
--- a/test/gen_coverage.py
+++ b/test/gen_coverage.py
@@ -25,7 +25,6 @@
 import webbrowser
 
 from run_host_unit_tests import *
-
 """
 This script is used to generate code coverage results host supported libraries.
 The script by default will generate an html report that summarizes the coverage
@@ -63,23 +62,28 @@
         "covered_files": [
             "system/bt/profile/avrcp",
         ],
-    }, {
+    },
+    {
         "test_name": "bluetooth_test_sdp",
         "covered_files": [
             "system/bt/profile/sdp",
         ],
-    }, {
-        "test_name": "test-vendor_test_host",
+    },
+    {
+        "test_name":
+        "test-vendor_test_host",
         "covered_files": [
             "system/bt/vendor_libs/test_vendor_lib/include",
             "system/bt/vendor_libs/test_vendor_lib/src",
         ],
-    }, {
+    },
+    {
         "test_name": "rootcanal-packets_test_host",
         "covered_files": [
             "system/bt/vendor_libs/test_vendor_lib/packets",
         ],
-    }, {
+    },
+    {
         "test_name": "bluetooth_test_common",
         "covered_files": [
             "system/bt/common",
@@ -89,324 +93,346 @@
 
 WORKING_DIR = '/tmp/coverage'
 SOONG_UI_BASH = 'build/soong/soong_ui.bash'
-LLVM_DIR = 'prebuilts/clang/host/linux-x86/clang-r328903/bin'
+LLVM_DIR = 'prebuilts/clang/host/linux-x86/clang-r353983b/bin'
 LLVM_MERGE = LLVM_DIR + '/llvm-profdata'
 LLVM_COV = LLVM_DIR + '/llvm-cov'
 
+
 def write_root_html_head(f):
-  # Write the header part of the root html file. This was pulled from the
-  # page source of one of the generated html files.
-  f.write("<!doctype html><html><head>" \
-    "<meta name='viewport' content='width=device-width,initial-scale=1'><met" \
-    "a charset='UTF-8'><link rel='stylesheet' type='text/css' href='style.cs" \
-    "s'></head><body><h2>Coverage Report</h2><h4>Created: " +
-    str(datetime.datetime.now().strftime('%Y-%m-%d %H:%M')) +
-    "</h4><p>Click <a href='http://clang.llvm.org/docs/SourceBasedCodeCovera" \
-    "ge.html#interpreting-reports'>here</a> for information about interpreti" \
-    "ng this report.</p><div class='centered'><table><tr><td class='column-e" \
-    "ntry-bold'>Filename</td><td class='column-entry-bold'>Function Coverage" \
-    "</td><td class='column-entry-bold'>Instantiation Coverage</td><td class" \
-    "='column-entry-bold'>Line Coverage</td><td class='column-entry-bold'>Re" \
-    "gion Coverage</td></tr>"
-  )
+    # Write the header part of the root html file. This was pulled from the
+    # page source of one of the generated html files.
+    f.write("<!doctype html><html><head>" \
+      "<meta name='viewport' content='width=device-width,initial-scale=1'><met" \
+      "a charset='UTF-8'><link rel='stylesheet' type='text/css' href='style.cs" \
+      "s'></head><body><h2>Coverage Report</h2><h4>Created: " +
+      str(datetime.datetime.now().strftime('%Y-%m-%d %H:%M')) +
+      "</h4><p>Click <a href='http://clang.llvm.org/docs/SourceBasedCodeCovera" \
+      "ge.html#interpreting-reports'>here</a> for information about interpreti" \
+      "ng this report.</p><div class='centered'><table><tr><td class='column-e" \
+      "ntry-bold'>Filename</td><td class='column-entry-bold'>Function Coverage" \
+      "</td><td class='column-entry-bold'>Instantiation Coverage</td><td class" \
+      "='column-entry-bold'>Line Coverage</td><td class='column-entry-bold'>Re" \
+      "gion Coverage</td></tr>"
+    )
 
 
 def write_root_html_column(f, covered, count):
-  percent = covered * 100.0 / count
-  value = "%.2f%% (%d/%d) " % (percent, covered, count)
-  color = 'column-entry-yellow'
-  if percent == 100:
-    color = 'column-entry-green'
-  if percent < 80.0:
-    color = 'column-entry-red'
-  f.write("<td class=\'" + color + "\'><pre>" + value + "</pre></td>")
+    percent = covered * 100.0 / count
+    value = "%.2f%% (%d/%d) " % (percent, covered, count)
+    color = 'column-entry-yellow'
+    if percent == 100:
+        color = 'column-entry-green'
+    if percent < 80.0:
+        color = 'column-entry-red'
+    f.write("<td class=\'" + color + "\'><pre>" + value + "</pre></td>")
 
 
 def write_root_html_rows(f, tests):
-  totals = {
-      "functions":{
-          "covered": 0,
-          "count": 0
-      },
-      "instantiations":{
-          "covered": 0,
-          "count": 0
-      },
-      "lines":{
-          "covered": 0,
-          "count": 0
-      },
-      "regions":{
-          "covered": 0,
-          "count": 0
-      }
-  }
+    totals = {
+        "functions": {
+            "covered": 0,
+            "count": 0
+        },
+        "instantiations": {
+            "covered": 0,
+            "count": 0
+        },
+        "lines": {
+            "covered": 0,
+            "count": 0
+        },
+        "regions": {
+            "covered": 0,
+            "count": 0
+        }
+    }
 
-  # Write the tests with their coverage summaries.
-  for test in tests:
-    test_name = test['test_name']
-    covered_files = test['covered_files']
-    json_results = generate_coverage_json(test)
-    test_totals = json_results['data'][0]['totals']
+    # Write the tests with their coverage summaries.
+    for test in tests:
+        test_name = test['test_name']
+        covered_files = test['covered_files']
+        json_results = generate_coverage_json(test)
+        test_totals = json_results['data'][0]['totals']
 
-    f.write("<tr class='light-row'><td><pre><a href=\'" +
-        os.path.join(test_name, "index.html") + "\'>" + test_name +
-        "</a></pre></td>")
+        f.write("<tr class='light-row'><td><pre><a href=\'" +
+                os.path.join(test_name, "index.html") + "\'>" + test_name +
+                "</a></pre></td>")
+        for field_name in ['functions', 'instantiations', 'lines', 'regions']:
+            field = test_totals[field_name]
+            totals[field_name]['covered'] += field['covered']
+            totals[field_name]['count'] += field['count']
+            write_root_html_column(f, field['covered'], field['count'])
+        f.write("</tr>")
+
+    #Write the totals row.
+    f.write("<tr class='light-row-bold'><td><pre>Totals</a></pre></td>")
     for field_name in ['functions', 'instantiations', 'lines', 'regions']:
-      field = test_totals[field_name]
-      totals[field_name]['covered'] += field['covered']
-      totals[field_name]['count'] += field['count']
-      write_root_html_column(f, field['covered'], field['count'])
-    f.write("</tr>");
-
-  #Write the totals row.
-  f.write("<tr class='light-row-bold'><td><pre>Totals</a></pre></td>")
-  for field_name in ['functions', 'instantiations', 'lines', 'regions']:
-    field = totals[field_name]
-    write_root_html_column(f, field['covered'], field['count'])
-  f.write("</tr>");
+        field = totals[field_name]
+        write_root_html_column(f, field['covered'], field['count'])
+    f.write("</tr>")
 
 
 def write_root_html_tail(f):
-  # Pulled from the generated html coverage report.
-  f.write("</table></div><h5>Generated by llvm-cov -- llvm version 7.0.2svn<" \
-    "/h5></body></html>")
+    # Pulled from the generated html coverage report.
+    f.write("</table></div><h5>Generated by llvm-cov -- llvm version 7.0.2svn<" \
+      "/h5></body></html>")
 
 
 def generate_root_html(tests):
-  # Copy the css file from one of the coverage reports.
-  source_file = os.path.join(os.path.join(WORKING_DIR, tests[0]['test_name']), "style.css")
-  dest_file = os.path.join(WORKING_DIR, "style.css")
-  shutil.copy2(source_file, dest_file)
+    # Copy the css file from one of the coverage reports.
+    source_file = os.path.join(
+        os.path.join(WORKING_DIR, tests[0]['test_name']), "style.css")
+    dest_file = os.path.join(WORKING_DIR, "style.css")
+    shutil.copy2(source_file, dest_file)
 
-  # Write the root index.html file that sumarizes all the tests.
-  f = open(os.path.join(WORKING_DIR, "index.html"), "w")
-  write_root_html_head(f)
-  write_root_html_rows(f, tests)
-  write_root_html_tail(f)
+    # Write the root index.html file that sumarizes all the tests.
+    f = open(os.path.join(WORKING_DIR, "index.html"), "w")
+    write_root_html_head(f)
+    write_root_html_rows(f, tests)
+    write_root_html_tail(f)
 
 
 def get_profraw_for_test(test_name):
-  test_root = get_native_test_root_or_die()
-  test_cmd = os.path.join(os.path.join(test_root, test_name), test_name)
-  if not os.path.isfile(test_cmd):
-    logging.error('The test ' + test_name + ' does not exist, please compile first')
-    sys.exit(1)
+    test_root = get_native_test_root_or_die()
+    test_cmd = os.path.join(os.path.join(test_root, test_name), test_name)
+    if not os.path.isfile(test_cmd):
+        logging.error('The test ' + test_name +
+                      ' does not exist, please compile first')
+        sys.exit(1)
 
-  profraw_file_name = test_name + ".profraw"
-  profraw_path = os.path.join(WORKING_DIR, os.path.join(test_name, profraw_file_name))
-  llvm_env_var = "LLVM_PROFILE_FILE=\"" + profraw_path + "\""
+    profraw_file_name = test_name + ".profraw"
+    profraw_path = os.path.join(WORKING_DIR,
+                                os.path.join(test_name, profraw_file_name))
+    llvm_env_var = "LLVM_PROFILE_FILE=\"" + profraw_path + "\""
 
-  test_cmd = llvm_env_var + " " + test_cmd
-  logging.info('Generating profraw data for ' + test_name)
-  logging.debug('cmd: ' + test_cmd)
-  if subprocess.call(test_cmd, shell=True) != 0:
-    logging.error('Test ' + test_name + ' failed. Please fix the test before generating coverage.')
-    sys.exit(1)
+    test_cmd = llvm_env_var + " " + test_cmd
+    logging.info('Generating profraw data for ' + test_name)
+    logging.debug('cmd: ' + test_cmd)
+    if subprocess.call(test_cmd, shell=True) != 0:
+        logging.error(
+            'Test ' + test_name +
+            ' failed. Please fix the test before generating coverage.')
+        sys.exit(1)
 
-  if not os.path.isfile(profraw_path):
-    logging.error('Generating the profraw file failed. Did you remember to add the proper compiler flags to your build?')
-    sys.exit(1)
+    if not os.path.isfile(profraw_path):
+        logging.error(
+            'Generating the profraw file failed. Did you remember to add the proper compiler flags to your build?'
+        )
+        sys.exit(1)
 
-  return profraw_file_name
+    return profraw_file_name
 
 
 def merge_profraw_data(test_name):
-  cmd = []
-  cmd.append(os.path.join(get_android_root_or_die(), LLVM_MERGE + " merge "))
+    cmd = []
+    cmd.append(os.path.join(get_android_root_or_die(), LLVM_MERGE + " merge "))
 
-  test_working_dir = os.path.join(WORKING_DIR, test_name);
-  cmd.append(os.path.join(test_working_dir, test_name + ".profraw"))
-  profdata_file = os.path.join(test_working_dir, test_name + ".profdata")
+    test_working_dir = os.path.join(WORKING_DIR, test_name)
+    cmd.append(os.path.join(test_working_dir, test_name + ".profraw"))
+    profdata_file = os.path.join(test_working_dir, test_name + ".profdata")
 
-  cmd.append('-o ' + profdata_file)
-  logging.info('Combining profraw files into profdata for ' + test_name)
-  logging.debug('cmd: ' + " ".join(cmd))
-  if subprocess.call(" ".join(cmd), shell=True) != 0:
-    logging.error('Failed to merge profraw files for ' + test_name)
-    sys.exit(1)
+    cmd.append('-o ' + profdata_file)
+    logging.info('Combining profraw files into profdata for ' + test_name)
+    logging.debug('cmd: ' + " ".join(cmd))
+    if subprocess.call(" ".join(cmd), shell=True) != 0:
+        logging.error('Failed to merge profraw files for ' + test_name)
+        sys.exit(1)
 
 
 def generate_coverage_html(test):
-  COVERAGE_ROOT = '/proc/self/cwd'
+    COVERAGE_ROOT = '/proc/self/cwd'
 
-  test_name = test['test_name']
-  file_list = test['covered_files']
+    test_name = test['test_name']
+    file_list = test['covered_files']
 
-  test_working_dir = os.path.join(WORKING_DIR, test_name)
-  test_profdata_file = os.path.join(test_working_dir, test_name + ".profdata")
+    test_working_dir = os.path.join(WORKING_DIR, test_name)
+    test_profdata_file = os.path.join(test_working_dir, test_name + ".profdata")
 
-  cmd = [
-    os.path.join(get_android_root_or_die(), LLVM_COV),
-    "show",
-    "-format=html",
-    "-summary-only",
-    "-show-line-counts-or-regions",
-    "-show-instantiation-summary",
-    "-instr-profile=" + test_profdata_file,
-    "-path-equivalence=\"" + COVERAGE_ROOT + "\",\"" +
-        get_android_root_or_die() + "\"",
-    "-output-dir=" + test_working_dir
-  ]
+    cmd = [
+        os.path.join(get_android_root_or_die(), LLVM_COV), "show",
+        "-format=html", "-summary-only", "-show-line-counts-or-regions",
+        "-show-instantiation-summary", "-instr-profile=" + test_profdata_file,
+        "-path-equivalence=\"" + COVERAGE_ROOT + "\",\"" +
+        get_android_root_or_die() + "\"", "-output-dir=" + test_working_dir
+    ]
 
-  # We have to have one object file not as an argument otherwise we can't specify source files.
-  test_cmd = os.path.join(os.path.join(get_native_test_root_or_die(), test_name), test_name)
-  cmd.append(test_cmd)
+    # We have to have one object file not as an argument otherwise we can't specify source files.
+    test_cmd = os.path.join(
+        os.path.join(get_native_test_root_or_die(), test_name), test_name)
+    cmd.append(test_cmd)
 
-  # Filter out the specific files we want coverage for
-  for filename in file_list:
-    cmd.append(os.path.join(get_android_root_or_die(), filename))
+    # Filter out the specific files we want coverage for
+    for filename in file_list:
+        cmd.append(os.path.join(get_android_root_or_die(), filename))
 
-  logging.info('Generating coverage report for ' + test['test_name'])
-  logging.debug('cmd: ' + " ".join(cmd))
-  if subprocess.call(" ".join(cmd), shell=True) != 0:
-    logging.error('Failed to generate coverage for ' + test['test_name'])
-    sys.exit(1)
+    logging.info('Generating coverage report for ' + test['test_name'])
+    logging.debug('cmd: ' + " ".join(cmd))
+    if subprocess.call(" ".join(cmd), shell=True) != 0:
+        logging.error('Failed to generate coverage for ' + test['test_name'])
+        sys.exit(1)
 
 
 def generate_coverage_json(test):
-  COVERAGE_ROOT = '/proc/self/cwd'
-  test_name = test['test_name']
-  file_list = test['covered_files']
+    COVERAGE_ROOT = '/proc/self/cwd'
+    test_name = test['test_name']
+    file_list = test['covered_files']
 
-  test_working_dir = os.path.join(WORKING_DIR, test_name)
-  test_profdata_file = os.path.join(test_working_dir, test_name + ".profdata")
+    test_working_dir = os.path.join(WORKING_DIR, test_name)
+    test_profdata_file = os.path.join(test_working_dir, test_name + ".profdata")
 
-  cmd = [
-    os.path.join(get_android_root_or_die(), LLVM_COV),
-    "export",
-    "-summary-only",
-    "-show-region-summary",
-    "-instr-profile=" + test_profdata_file,
-    "-path-equivalence=\"" + COVERAGE_ROOT + "\",\"" + get_android_root_or_die() + "\"",
-  ]
+    cmd = [
+        os.path.join(get_android_root_or_die(), LLVM_COV),
+        "export",
+        "-summary-only",
+        "-show-region-summary",
+        "-instr-profile=" + test_profdata_file,
+        "-path-equivalence=\"" + COVERAGE_ROOT + "\",\"" +
+        get_android_root_or_die() + "\"",
+    ]
 
-  test_cmd = os.path.join(os.path.join(get_native_test_root_or_die(), test_name), test_name)
-  cmd.append(test_cmd)
+    test_cmd = os.path.join(
+        os.path.join(get_native_test_root_or_die(), test_name), test_name)
+    cmd.append(test_cmd)
 
-  # Filter out the specific files we want coverage for
-  for filename in file_list:
-    cmd.append(os.path.join(get_android_root_or_die(), filename))
+    # Filter out the specific files we want coverage for
+    for filename in file_list:
+        cmd.append(os.path.join(get_android_root_or_die(), filename))
 
-  logging.info('Generating coverage json for ' + test['test_name'])
-  logging.debug('cmd: ' + " ".join(cmd))
+    logging.info('Generating coverage json for ' + test['test_name'])
+    logging.debug('cmd: ' + " ".join(cmd))
 
-  json_str = subprocess.check_output(" ".join(cmd), shell=True)
-  return json.loads(json_str)
+    json_str = subprocess.check_output(" ".join(cmd), shell=True)
+    return json.loads(json_str)
 
 
 def write_json_summary(test):
-  test_name = test['test_name']
-  test_working_dir = os.path.join(WORKING_DIR, test_name)
-  test_json_summary_file = os.path.join(test_working_dir, test_name + '.json')
-  logging.debug('Writing json summary file: ' + test_json_summary_file)
-  json_file = open(test_json_summary_file, 'w')
-  json.dump(generate_coverage_json(test), json_file)
-  json_file.close()
+    test_name = test['test_name']
+    test_working_dir = os.path.join(WORKING_DIR, test_name)
+    test_json_summary_file = os.path.join(test_working_dir, test_name + '.json')
+    logging.debug('Writing json summary file: ' + test_json_summary_file)
+    json_file = open(test_json_summary_file, 'w')
+    json.dump(generate_coverage_json(test), json_file)
+    json_file.close()
 
 
 def list_tests():
-  for test in COVERAGE_TESTS:
-    print "Test Name: " + test['test_name']
-    print "Covered Files: "
-    for covered_file in test['covered_files']:
-        print "  " + covered_file
-    print
+    for test in COVERAGE_TESTS:
+        print "Test Name: " + test['test_name']
+        print "Covered Files: "
+        for covered_file in test['covered_files']:
+            print "  " + covered_file
+        print
 
 
 def main():
-  parser = argparse.ArgumentParser(description='Generate code coverage for enabled tests.')
-  parser.add_argument(
-    '-l', '--list-tests',
-    action='store_true',
-    dest='list_tests',
-    help='List all the available tests to be run as well as covered files.')
-  parser.add_argument(
-    '-a', '--all',
-    action='store_true',
-    help='Runs all available tests and prints their outputs. If no tests ' \
-         'are specified via the -t option all tests will be run.')
-  parser.add_argument(
-    '-t', '--test',
-    dest='tests',
-    action='append',
-    type=str,
-    metavar='TESTNAME',
-    default=[],
-    help='Specifies a test to be run. Multiple tests can be specified by ' \
-         'using this option multiple times. ' \
-         'Example: \"gen_coverage.py -t test1 -t test2\"')
-  parser.add_argument(
-    '-o', '--output',
-    type=str,
-    metavar='DIRECTORY',
-    default='/tmp/coverage',
-    help='Specifies the directory to store all files. The directory will be ' \
-         'created if it does not exist. Default is \"/tmp/coverage\"')
-  parser.add_argument(
-    '-s', '--skip-html',
-    dest='skip_html',
-    action='store_true',
-    help='Skip opening up the results of the coverage report in a browser.')
-  parser.add_argument(
-    '-j', '--json-file',
-    dest='json_file',
-    action='store_true',
-    help='Write out summary results to json file in test directory.')
+    parser = argparse.ArgumentParser(
+        description='Generate code coverage for enabled tests.')
+    parser.add_argument(
+        '-l',
+        '--list-tests',
+        action='store_true',
+        dest='list_tests',
+        help='List all the available tests to be run as well as covered files.')
+    parser.add_argument(
+      '-a', '--all',
+      action='store_true',
+      help='Runs all available tests and prints their outputs. If no tests ' \
+           'are specified via the -t option all tests will be run.')
+    parser.add_argument(
+      '-t', '--test',
+      dest='tests',
+      action='append',
+      type=str,
+      metavar='TESTNAME',
+      default=[],
+      help='Specifies a test to be run. Multiple tests can be specified by ' \
+           'using this option multiple times. ' \
+           'Example: \"gen_coverage.py -t test1 -t test2\"')
+    parser.add_argument(
+      '-o', '--output',
+      type=str,
+      metavar='DIRECTORY',
+      default='/tmp/coverage',
+      help='Specifies the directory to store all files. The directory will be ' \
+           'created if it does not exist. Default is \"/tmp/coverage\"')
+    parser.add_argument(
+        '-s',
+        '--skip-html',
+        dest='skip_html',
+        action='store_true',
+        help='Skip opening up the results of the coverage report in a browser.')
+    parser.add_argument(
+        '-j',
+        '--json-file',
+        dest='json_file',
+        action='store_true',
+        help='Write out summary results to json file in test directory.')
 
-  logging.basicConfig(stream=sys.stderr, level=logging.DEBUG, format='%(levelname)s %(message)s')
-  logging.addLevelName(logging.DEBUG, "[\033[1;34m%s\033[0m]" % logging.getLevelName(logging.DEBUG))
-  logging.addLevelName(logging.INFO, "[\033[1;34m%s\033[0m]" % logging.getLevelName(logging.INFO))
-  logging.addLevelName(logging.WARNING, "[\033[1;31m%s\033[0m]" % logging.getLevelName(logging.WARNING))
-  logging.addLevelName(logging.ERROR, "[\033[1;31m%s\033[0m]" % logging.getLevelName(logging.ERROR))
+    logging.basicConfig(
+        stream=sys.stderr,
+        level=logging.DEBUG,
+        format='%(levelname)s %(message)s')
+    logging.addLevelName(
+        logging.DEBUG,
+        "[\033[1;34m%s\033[0m]" % logging.getLevelName(logging.DEBUG))
+    logging.addLevelName(
+        logging.INFO,
+        "[\033[1;34m%s\033[0m]" % logging.getLevelName(logging.INFO))
+    logging.addLevelName(
+        logging.WARNING,
+        "[\033[1;31m%s\033[0m]" % logging.getLevelName(logging.WARNING))
+    logging.addLevelName(
+        logging.ERROR,
+        "[\033[1;31m%s\033[0m]" % logging.getLevelName(logging.ERROR))
 
-  args = parser.parse_args()
-  logging.debug("Args: " + str(args))
+    args = parser.parse_args()
+    logging.debug("Args: " + str(args))
 
-  # Set the working directory
-  global WORKING_DIR
-  WORKING_DIR = os.path.abspath(args.output)
-  logging.debug("Working Dir: " + WORKING_DIR)
+    # Set the working directory
+    global WORKING_DIR
+    WORKING_DIR = os.path.abspath(args.output)
+    logging.debug("Working Dir: " + WORKING_DIR)
 
-  # Print out the list of tests then exit
-  if args.list_tests:
-    list_tests()
-    sys.exit(0)
+    # Print out the list of tests then exit
+    if args.list_tests:
+        list_tests()
+        sys.exit(0)
 
-  # Check to see if a test was specified and if so only generate coverage for
-  # that test.
-  if len(args.tests) == 0:
-    args.all = True
+    # Check to see if a test was specified and if so only generate coverage for
+    # that test.
+    if len(args.tests) == 0:
+        args.all = True
 
-  tests_to_run = []
-  for test in COVERAGE_TESTS:
-    if args.all or test['test_name'] in args.tests:
-      tests_to_run.append(test)
-    if test['test_name'] in args.tests:
-      args.tests.remove(test['test_name'])
+    tests_to_run = []
+    for test in COVERAGE_TESTS:
+        if args.all or test['test_name'] in args.tests:
+            tests_to_run.append(test)
+        if test['test_name'] in args.tests:
+            args.tests.remove(test['test_name'])
 
-  # Error if a test was specified but doesn't exist.
-  if len(args.tests) != 0:
-    for test_name in args.tests:
-        logging.error('\"' + test_name + '\" was not found in the list of available tests.')
-    sys.exit(1)
+    # Error if a test was specified but doesn't exist.
+    if len(args.tests) != 0:
+        for test_name in args.tests:
+            logging.error('\"' + test_name +
+                          '\" was not found in the list of available tests.')
+        sys.exit(1)
 
-  # Generate the info for the tests
-  for test in tests_to_run:
-    logging.info('Getting coverage for ' + test['test_name'])
-    get_profraw_for_test(test['test_name'])
-    merge_profraw_data(test['test_name'])
-    if args.json_file:
-      write_json_summary(test)
-    generate_coverage_html(test)
+    # Generate the info for the tests
+    for test in tests_to_run:
+        logging.info('Getting coverage for ' + test['test_name'])
+        get_profraw_for_test(test['test_name'])
+        merge_profraw_data(test['test_name'])
+        if args.json_file:
+            write_json_summary(test)
+        generate_coverage_html(test)
 
-  # Generate the root index.html page that sumarizes all of the coverage reports.
-  generate_root_html(tests_to_run)
+    # Generate the root index.html page that sumarizes all of the coverage reports.
+    generate_root_html(tests_to_run)
 
-  # Open the results in a browser.
-  if not args.skip_html:
-    webbrowser.open('file://' + os.path.join(WORKING_DIR, 'index.html'))
+    # Open the results in a browser.
+    if not args.skip_html:
+        webbrowser.open('file://' + os.path.join(WORKING_DIR, 'index.html'))
 
 
 if __name__ == '__main__':
-  main()
+    main()
diff --git a/test/headless/Android.bp b/test/headless/Android.bp
new file mode 100644
index 0000000..fbfb2f4
--- /dev/null
+++ b/test/headless/Android.bp
@@ -0,0 +1,61 @@
+cc_test {
+    name: "bt_headless",
+    test_suites: ["device-tests"],
+    defaults: ["fluoride_defaults"],
+    srcs: [
+        "get_options.cc",
+        "headless.cc",
+        "main.cc",
+        "pairing/pairing.cc",
+        "sdp/sdp.cc",
+        "sdp/sdp_db.cc",
+        "nop/nop.cc",
+        "read/read.cc",
+        "read/name.cc",
+    ],
+    include_dirs: [
+        "system/bt",
+        "system/bt/stack/include",
+    ],
+    whole_static_libs: [
+        "libbtcore",
+    ],
+    static_libs: [
+        "libFraunhoferAAC",
+        "libbluetooth_gd",
+        "libbt-bta",
+        "libbt-common",
+        "libbt-hci",
+        "libbt-protos-lite",
+        "libbt-sbc-decoder",
+        "libbt-sbc-encoder",
+        "libbt-stack",
+        "libbt-utils",
+        "libbtdevice",
+        "libbte",
+        "libbtif",
+        "libg722codec",
+        "libosi",
+        "libprotobuf-cpp-lite",
+        "libudrv-uipc",
+        "libz",
+    ],
+    shared_libs: [
+        "android.hardware.bluetooth.a2dp@1.0",
+        "android.hardware.bluetooth.audio@2.0",
+        "android.hardware.bluetooth@1.0",
+        "android.hardware.bluetooth@1.1",
+        "libaaudio",
+        "libbase",
+        "libcrypto",
+        "libcutils",  // property_get_bool
+        "libfmq",
+        "libhidlbase",
+        "libjsoncpp",
+        "liblog",  // __android_log_print
+        "libprocessgroup",
+        "libtinyxml2",
+        "libutils",
+    ],
+    ldflags: ["-rdynamic"],
+}
diff --git a/test/headless/get_options.cc b/test/headless/get_options.cc
new file mode 100644
index 0000000..f8d8576
--- /dev/null
+++ b/test/headless/get_options.cc
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "test/headless/get_options.h"
+
+#include <base/logging.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <list>
+#include <string>
+
+namespace {
+constexpr struct option long_options[] = {
+    {"device", required_argument, 0, 0}, {"loop", required_argument, 0, 0},
+    {"uuid", required_argument, 0, 0},   {"msleep", required_argument, 0, 0},
+    {"stderr", no_argument, 0, 0},       {0, 0, 0, 0}};
+
+enum OptionType {
+  kOptionDevice = 0,
+  kOptionLoop = 1,
+  kOptionUuid = 2,
+  kOptionMsleep = 3,
+  kOptionStdErr = 4,
+};
+
+}  // namespace
+
+void bluetooth::test::headless::GetOpt::Usage() const {
+  fprintf(stdout, "%s: Usage:\n", name_);
+  fprintf(stdout,
+          "%s  --device=<device,>  Comma separated list of remote devices\n",
+          name_);
+  fprintf(stdout, "%s  --uuid=<uuid,>      Comma separated list of uuids\n",
+          name_);
+  fprintf(stdout, "%s  --loop=<loop>       Number of loops\n", name_);
+  fprintf(stdout, "%s  --msleep=<msecs>    Sleep msec between loops\n", name_);
+  fprintf(stdout, "%s  --stderr            Dump stderr to stdout\n", name_);
+  fflush(nullptr);
+}
+
+void bluetooth::test::headless::GetOpt::ParseValue(
+    char* optarg, std::list<std::string>& string_list) {
+  CHECK(optarg != nullptr);
+  char* p = optarg;
+  char* pp = optarg;
+  while (*p != '\0') {
+    if (*p == ',') {
+      *p = 0;
+      string_list.push_back(std::string(pp));
+      pp = p + 1;
+    }
+    p++;
+  }
+  if (pp != p) string_list.push_back(std::string(pp));
+}
+
+void bluetooth::test::headless::GetOpt::ProcessOption(int option_index,
+                                                      char* optarg) {
+  std::list<std::string> string_list;
+  OptionType option_type = static_cast<OptionType>(option_index);
+
+  switch (option_type) {
+    case kOptionDevice:
+      if (!optarg) return;
+      ParseValue(optarg, string_list);
+      for (auto& entry : string_list) {
+        if (RawAddress::IsValidAddress(entry)) {
+          RawAddress address;
+          RawAddress::FromString(entry, address);
+          device_.push_back(address);
+        }
+      }
+      break;
+    case kOptionLoop:
+      loop_ = std::stoul(optarg, nullptr, 0);
+      break;
+    case kOptionUuid:
+      if (!optarg) return;
+      ParseValue(optarg, string_list);
+      for (auto& entry : string_list) {
+        uuid_.push_back(
+            bluetooth::Uuid::From16Bit(std::stoul(entry.c_str(), nullptr, 0)));
+      }
+      break;
+    case kOptionMsleep:
+      if (!optarg) return;
+      msec_ = std::stoul(optarg, nullptr, 0);
+      break;
+    case kOptionStdErr:
+      close_stderr_ = false;
+      break;
+    default:
+      fflush(nullptr);
+      valid_ = false;
+      return;
+      break;
+  }
+}
+
+bluetooth::test::headless::GetOpt::GetOpt(int argc, char** argv)
+    : name_(argv[0]) {
+  while (1) {
+    int option_index = 0;
+    int c = getopt_long_only(argc, argv, "d:l:u:", long_options, &option_index);
+    if (c == -1) break;
+
+    switch (c) {
+      case 0:
+        ProcessOption(static_cast<OptionType>(option_index), optarg);
+        break;
+      case '?':
+        Usage();
+        valid_ = false;
+        return;
+      default:
+        printf("?? getopt returned character code 0%o ??\n", c);
+    }
+  }
+
+  while (optind < argc) {
+    non_options_.push_back(argv[optind++]);
+  }
+  fflush(nullptr);
+}
+
diff --git a/test/headless/get_options.h b/test/headless/get_options.h
new file mode 100644
index 0000000..04a5025
--- /dev/null
+++ b/test/headless/get_options.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstddef>
+#include <list>
+#include "types/bluetooth/uuid.h"
+#include "types/raw_address.h"
+
+namespace bluetooth {
+namespace test {
+namespace headless {
+
+class GetOpt {
+ public:
+  GetOpt(int argc, char** arv);
+  virtual ~GetOpt() = default;
+
+  virtual void Usage() const;
+  virtual bool IsValid() const { return valid_; };
+
+  std::string GetNextSubTest() const {
+    std::string test = non_options_.front();
+    non_options_.pop_front();
+    return test;
+  }
+
+  std::list<RawAddress> device_;
+  std::list<bluetooth::Uuid> uuid_;
+  unsigned long loop_{1};
+  unsigned long msec_{0};
+
+  bool close_stderr_{true};
+
+  mutable std::list<std::string> non_options_;
+
+ private:
+  void ParseValue(char* optarg, std::list<std::string>& my_list);
+  void ProcessOption(int option_index, char* optarg);
+  const char* name_{nullptr};
+  bool valid_{true};
+};
+
+}  // namespace headless
+}  // namespace test
+}  // namespace bluetooth
diff --git a/test/headless/headless.cc b/test/headless/headless.cc
new file mode 100644
index 0000000..9083e9c
--- /dev/null
+++ b/test/headless/headless.cc
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "bt_headless"
+
+#include <dlfcn.h>  //  dlopen
+
+#include "base/logging.h"  // LOG() stdout and android log
+#include "include/hardware/bluetooth.h"
+#include "osi/include/log.h"  // android log only
+#include "test/headless/get_options.h"
+#include "test/headless/headless.h"
+
+extern bt_interface_t bluetoothInterface;
+
+using namespace bluetooth::test::headless;
+
+namespace {
+std::mutex adapter_state_mutex_;
+std::condition_variable adapter_state_cv_;
+bt_state_t bt_state_{BT_STATE_OFF};
+
+void adapter_state_changed(bt_state_t state) {
+  std::unique_lock<std::mutex> lck(adapter_state_mutex_);
+  bt_state_ = state;
+  adapter_state_cv_.notify_all();
+}
+void adapter_properties(bt_status_t status, int num_properties,
+                        bt_property_t* properties) {
+  LOG_INFO(LOG_TAG, "%s", __func__);
+}
+
+void remote_device_properties(bt_status_t status, RawAddress* bd_addr,
+                              int num_properties, bt_property_t* properties) {
+  LOG_INFO(LOG_TAG, "%s", __func__);
+}
+
+void device_found(int num_properties, bt_property_t* properties) {
+  LOG_INFO(LOG_TAG, "%s", __func__);
+}
+
+void discovery_state_changed(bt_discovery_state_t state) {
+  LOG_INFO(LOG_TAG, "%s", __func__);
+}
+
+/** Bluetooth Legacy PinKey Request callback */
+void pin_request(RawAddress* remote_bd_addr, bt_bdname_t* bd_name, uint32_t cod,
+                 bool min_16_digit) {
+  LOG_INFO(LOG_TAG, "%s", __func__);
+}
+
+void ssp_request(RawAddress* remote_bd_addr, bt_bdname_t* bd_name, uint32_t cod,
+                 bt_ssp_variant_t pairing_variant, uint32_t pass_key) {
+  LOG_INFO(LOG_TAG, "%s", __func__);
+}
+
+/** Bluetooth Bond state changed callback */
+/* Invoked in response to create_bond, cancel_bond or remove_bond */
+void bond_state_changed(bt_status_t status, RawAddress* remote_bd_addr,
+                        bt_bond_state_t state) {
+  LOG_INFO(LOG_TAG, "%s", __func__);
+}
+
+/** Bluetooth ACL connection state changed callback */
+void acl_state_changed(bt_status_t status, RawAddress* remote_bd_addr,
+                       bt_acl_state_t state) {
+  LOG_INFO(LOG_TAG, "%s", __func__);
+}
+
+void thread_event(bt_cb_thread_evt evt) { LOG_INFO(LOG_TAG, "%s", __func__); }
+
+void dut_mode_recv(uint16_t opcode, uint8_t* buf, uint8_t len) {
+  LOG_INFO(LOG_TAG, "%s", __func__);
+}
+
+void le_test_mode(bt_status_t status, uint16_t num_packets) {
+  LOG_INFO(LOG_TAG, "%s", __func__);
+}
+
+void energy_info(bt_activity_energy_info* energy_info,
+                 bt_uid_traffic_t* uid_data) {
+  LOG_INFO(LOG_TAG, "%s", __func__);
+}
+
+bt_callbacks_t bt_callbacks{
+    /** set to sizeof(bt_callbacks_t) */
+    .size = sizeof(bt_callbacks_t),
+    .adapter_state_changed_cb = adapter_state_changed,
+    .adapter_properties_cb = adapter_properties,
+    .remote_device_properties_cb = remote_device_properties,
+    .device_found_cb = device_found,
+    .discovery_state_changed_cb = discovery_state_changed,
+    .pin_request_cb = pin_request,
+    .ssp_request_cb = ssp_request,
+    .bond_state_changed_cb = bond_state_changed,
+    .acl_state_changed_cb = acl_state_changed,
+    .thread_evt_cb = thread_event,
+    .dut_mode_recv_cb = dut_mode_recv,
+    .le_test_mode_cb = le_test_mode,
+    .energy_info_cb = energy_info,
+};
+// HAL HARDWARE CALLBACKS
+
+// OS CALLOUTS
+bool set_wake_alarm_co(uint64_t delay_millis, bool should_wake, alarm_cb cb,
+                       void* data) {
+  LOG_INFO(LOG_TAG, "%s", __func__);
+  return true;
+}
+int acquire_wake_lock_co(const char* lock_name) {
+  LOG_INFO(LOG_TAG, "%s", __func__);
+  return 1;
+}
+
+int release_wake_lock_co(const char* lock_name) {
+  LOG_INFO(LOG_TAG, "%s", __func__);
+  return 0;
+}
+
+bt_os_callouts_t bt_os_callouts{
+    .size = sizeof(bt_os_callouts_t),
+    .set_wake_alarm = set_wake_alarm_co,
+    .acquire_wake_lock = acquire_wake_lock_co,
+    .release_wake_lock = release_wake_lock_co,
+};
+}  // namespace
+
+void HeadlessStack::SetUp() {
+  LOG(INFO) << __func__ << " Entry";
+
+  int status = bluetoothInterface.init(&bt_callbacks, false, false, 0, false);
+  (status == BT_STATUS_SUCCESS)
+      ? LOG(INFO) << __func__ << " Initialized bluetooth callbacks"
+      : LOG(FATAL) << "Failed to initialize Bluetooth stack";
+
+  status = bluetoothInterface.set_os_callouts(&bt_os_callouts);
+  (status == BT_STATUS_SUCCESS)
+      ? LOG(INFO) << __func__ << " Initialized os callouts"
+      : LOG(ERROR) << "Failed to set up Bluetooth OS callouts";
+
+  bluetoothInterface.enable();
+  LOG_INFO(LOG_TAG, "%s HeadlessStack stack has enabled", __func__);
+
+  std::unique_lock<std::mutex> lck(adapter_state_mutex_);
+  while (bt_state_ != BT_STATE_ON) adapter_state_cv_.wait(lck);
+  LOG_INFO(LOG_TAG, "%s HeadlessStack stack is operational", __func__);
+}
+
+void HeadlessStack::TearDown() {
+  LOG_INFO(LOG_TAG, "Stack has disabled");
+  int status = bluetoothInterface.disable();
+
+  LOG(INFO) << __func__ << " Interface has been disabled status:" << status;
+
+  bluetoothInterface.cleanup();
+  LOG(INFO) << __func__ << " Cleaned up hal bluetooth library";
+
+  std::unique_lock<std::mutex> lck(adapter_state_mutex_);
+  while (bt_state_ != BT_STATE_OFF) adapter_state_cv_.wait(lck);
+  LOG_INFO(LOG_TAG, "%s HeadlessStack stack has exited", __func__);
+}
diff --git a/test/headless/headless.h b/test/headless/headless.h
new file mode 100644
index 0000000..551f60f
--- /dev/null
+++ b/test/headless/headless.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <unordered_map>
+
+#include <unistd.h>
+
+#include "base/logging.h"  // LOG() stdout and android log
+#include "test/headless/get_options.h"
+
+namespace bluetooth {
+namespace test {
+namespace headless {
+
+namespace {
+
+template <typename T>
+using ExecutionUnit = std::function<T()>;
+
+constexpr char kHeadlessStartSentinel[] =
+    " START HEADLESS HEADLESS HEADLESS HEADLESS HEADLESS HEADLESS HEADLESS "
+    "HEADLESS";
+constexpr char kHeadlessStopSentinel[] =
+    " STOP HEADLESS HEADLESS HEADLESS HEADLESS HEADLESS HEADLESS HEADLESS "
+    "HEADLESS";
+
+}  // namespace
+
+class HeadlessStack {
+ protected:
+  HeadlessStack() = default;
+  virtual ~HeadlessStack() = default;
+
+  void SetUp();
+  void TearDown();
+};
+
+class HeadlessRun : public HeadlessStack {
+ protected:
+  const bluetooth::test::headless::GetOpt& options_;
+  unsigned long loop_{0};
+
+  HeadlessRun(const bluetooth::test::headless::GetOpt& options)
+      : options_(options) {}
+
+  template <typename T>
+  T RunOnHeadlessStack(ExecutionUnit<T> func) {
+    SetUp();
+    LOG(INFO) << kHeadlessStartSentinel;
+
+    T rc;
+    for (loop_ = 0; loop_ < options_.loop_; loop_++) {
+      rc = func();
+      if (options_.msec_ != 0) {
+        usleep(options_.msec_ * 1000);
+      }
+      if (rc) {
+        break;
+      }
+    }
+    if (rc) {
+      LOG(ERROR) << "FAIL:" << rc << " loop/loops:" << loop_ << "/"
+                 << options_.loop_;
+    } else {
+      LOG(INFO) << "PASS:" << rc << " loop/loops:" << loop_ << "/"
+                << options_.loop_;
+    }
+
+    LOG(INFO) << kHeadlessStopSentinel;
+    TearDown();
+    return rc;
+  }
+  virtual ~HeadlessRun() = default;
+};
+
+template <typename T>
+class HeadlessTest : public HeadlessRun {
+ public:
+  virtual T Run() {
+    if (options_.non_options_.size() == 0) {
+      fprintf(stdout, "Must supply at least one subtest name\n");
+      return -1;
+    }
+
+    std::string subtest = options_.GetNextSubTest();
+    if (test_nodes_.find(subtest) == test_nodes_.end()) {
+      fprintf(stdout, "Unknown subtest module:%s\n", subtest.c_str());
+      return -1;
+    }
+    return test_nodes_.at(subtest)->Run();
+  }
+
+  virtual ~HeadlessTest() = default;
+
+ protected:
+  HeadlessTest(const bluetooth::test::headless::GetOpt& options)
+      : HeadlessRun(options) {}
+
+  std::unordered_map<std::string, std::unique_ptr<HeadlessTest<T>>> test_nodes_;
+};
+
+}  // namespace headless
+}  // namespace test
+}  // namespace bluetooth
diff --git a/test/headless/main.cc b/test/headless/main.cc
new file mode 100644
index 0000000..487e96c
--- /dev/null
+++ b/test/headless/main.cc
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "bt_headless"
+
+#include <unordered_map>
+
+#include "base/logging.h"     // LOG() stdout and android log
+#include "osi/include/log.h"  // android log only
+#include "test/headless/get_options.h"
+#include "test/headless/headless.h"
+#include "test/headless/nop/nop.h"
+#include "test/headless/pairing/pairing.h"
+#include "test/headless/read/read.h"
+#include "test/headless/sdp/sdp.h"
+
+using namespace bluetooth::test::headless;
+
+namespace {
+
+class Main : public HeadlessTest<int> {
+ public:
+  Main(const bluetooth::test::headless::GetOpt& options)
+      : HeadlessTest<int>(options) {
+    test_nodes_.emplace(
+        "nop", std::make_unique<bluetooth::test::headless::Nop>(options));
+    test_nodes_.emplace(
+        "pairing",
+        std::make_unique<bluetooth::test::headless::Pairing>(options));
+    test_nodes_.emplace(
+        "read", std::make_unique<bluetooth::test::headless::Read>(options));
+    test_nodes_.emplace(
+        "sdp", std::make_unique<bluetooth::test::headless::Sdp>(options));
+  }
+
+  int Run() override {
+    if (options_.close_stderr_) {
+      fclose(stderr);
+    }
+    return HeadlessTest<int>::Run();
+  }
+};
+
+}  // namespace
+
+int main(int argc, char** argv) {
+  fflush(nullptr);
+  setvbuf(stdout, nullptr, _IOLBF, 0);
+
+  bluetooth::test::headless::GetOpt options(argc, argv);
+  if (!options.IsValid()) {
+    return -1;
+  }
+
+  Main main(options);
+  return main.Run();
+}
diff --git a/test/headless/nop/nop.cc b/test/headless/nop/nop.cc
new file mode 100644
index 0000000..f6ac42c
--- /dev/null
+++ b/test/headless/nop/nop.cc
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "bt_headless_sdp"
+
+#include <future>
+
+#include "base/logging.h"     // LOG() stdout and android log
+#include "osi/include/log.h"  // android log only
+#include "stack/include/sdp_api.h"
+#include "test/headless/get_options.h"
+#include "test/headless/headless.h"
+#include "test/headless/nop/nop.h"
+#include "types/raw_address.h"
+
+int bluetooth::test::headless::Nop::Run() {
+  return RunOnHeadlessStack<int>([this]() {
+    fprintf(stdout, "Nop loop:%lu\n", loop_);
+    return 0;
+  });
+}
diff --git a/test/headless/nop/nop.h b/test/headless/nop/nop.h
new file mode 100644
index 0000000..e65efa3
--- /dev/null
+++ b/test/headless/nop/nop.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "test/headless/get_options.h"
+#include "test/headless/headless.h"
+
+namespace bluetooth {
+namespace test {
+namespace headless {
+
+class Nop : public HeadlessTest<int> {
+ public:
+  Nop(const bluetooth::test::headless::GetOpt& options)
+      : HeadlessTest<int>(options) {}
+  int Run() override;
+};
+
+}  // namespace headless
+}  // namespace test
+}  // namespace bluetooth
diff --git a/test/headless/pairing/pairing.cc b/test/headless/pairing/pairing.cc
new file mode 100644
index 0000000..7785ea4
--- /dev/null
+++ b/test/headless/pairing/pairing.cc
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "bt_headless_sdp"
+
+#include <future>
+
+#include "base/logging.h"  // LOG() stdout and android log
+#include "btif/include/btif_api.h"
+#include "osi/include/log.h"  // android log only
+#include "test/headless/get_options.h"
+#include "test/headless/headless.h"
+#include "test/headless/pairing/pairing.h"
+#include "types/raw_address.h"
+
+int bluetooth::test::headless::Pairing::Run() {
+  if (options_.loop_ < 1) {
+    fprintf(stdout, "This test requires at least a single loop");
+    options_.Usage();
+    return -1;
+  }
+  if (options_.device_.size() != 1) {
+    fprintf(stdout, "This test requires a single device specified");
+    options_.Usage();
+    return -1;
+  }
+
+  RawAddress raw_address = options_.device_.front();
+
+  return RunOnHeadlessStack<int>([raw_address]() {
+    bt_status_t status = btif_dm_create_bond(&raw_address, BT_TRANSPORT_BR_EDR);
+    switch (status) {
+      case BT_STATUS_SUCCESS:
+        break;
+      default:
+        fprintf(stdout, "Failed to create bond status:%d", status);
+        break;
+    }
+    return status;
+  });
+}
diff --git a/test/headless/pairing/pairing.h b/test/headless/pairing/pairing.h
new file mode 100644
index 0000000..4125d48
--- /dev/null
+++ b/test/headless/pairing/pairing.h
@@ -0,0 +1,20 @@
+
+#pragma once
+
+#include "test/headless/get_options.h"
+#include "test/headless/headless.h"
+
+namespace bluetooth {
+namespace test {
+namespace headless {
+
+class Pairing : public HeadlessTest<int> {
+ public:
+  Pairing(const bluetooth::test::headless::GetOpt& options)
+      : HeadlessTest<int>(options) {}
+  int Run() override;
+};
+
+}  // namespace headless
+}  // namespace test
+}  // namespace bluetooth
diff --git a/test/headless/read/name.cc b/test/headless/read/name.cc
new file mode 100644
index 0000000..6a8ea9f
--- /dev/null
+++ b/test/headless/read/name.cc
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "bt_headless_sdp"
+
+#include <future>
+
+#include "base/logging.h"     // LOG() stdout and android log
+#include "osi/include/log.h"  // android log only
+#include "stack/include/btm_api.h"
+#include "stack/include/btm_api_types.h"
+#include "test/headless/get_options.h"
+#include "test/headless/headless.h"
+#include "test/headless/read/name.h"
+#include "types/raw_address.h"
+
+std::promise<tBTM_REMOTE_DEV_NAME> promise_;
+
+void RemoteNameCallback(void* data) {
+  promise_.set_value(*static_cast<tBTM_REMOTE_DEV_NAME*>(data));
+}
+
+int bluetooth::test::headless::Name::Run() {
+  if (options_.loop_ < 1) {
+    fprintf(stdout, "This test requires at least a single loop");
+    options_.Usage();
+    return -1;
+  }
+  if (options_.device_.size() != 1) {
+    fprintf(stdout, "This test requires a single device specified");
+    options_.Usage();
+    return -1;
+  }
+
+  const RawAddress& raw_address = options_.device_.front();
+
+  return RunOnHeadlessStack<int>([&raw_address]() {
+    promise_ = std::promise<tBTM_REMOTE_DEV_NAME>();
+
+    auto future = promise_.get_future();
+
+    tBTM_STATUS status = BTM_ReadRemoteDeviceName(
+        raw_address, &RemoteNameCallback, BT_TRANSPORT_BR_EDR);
+    if (status != BTM_CMD_STARTED) {
+      fprintf(stdout, "Failure to start read remote device\n");
+      return -1;
+    }
+
+    tBTM_REMOTE_DEV_NAME name_packet = future.get();
+    switch (name_packet.status) {
+      case BTM_SUCCESS: {
+        char buf[BD_NAME_LEN];
+        memcpy(buf, name_packet.remote_bd_name, BD_NAME_LEN);
+        std::string name(buf);
+        fprintf(stdout, "Name result mac:%s name:%s\n",
+                raw_address.ToString().c_str(), name.c_str());
+      } break;
+      case BTM_BAD_VALUE_RET:
+        fprintf(stdout, "Name Timeout or other failure");
+        return -2;
+      default:
+        fprintf(stdout, "Unexpected remote name request failure status:%hd",
+                name_packet.status);
+        return -2;
+    }
+    return 0;
+  });
+}
diff --git a/test/headless/read/name.h b/test/headless/read/name.h
new file mode 100644
index 0000000..587cee2
--- /dev/null
+++ b/test/headless/read/name.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "test/headless/get_options.h"
+#include "test/headless/headless.h"
+
+namespace bluetooth {
+namespace test {
+namespace headless {
+
+class Name : public HeadlessTest<int> {
+ public:
+  Name(const bluetooth::test::headless::GetOpt& options)
+      : HeadlessTest<int>(options) {}
+  int Run() override;
+};
+
+}  // namespace headless
+}  // namespace test
+}  // namespace bluetooth
diff --git a/test/headless/read/read.cc b/test/headless/read/read.cc
new file mode 100644
index 0000000..d1a3b8d
--- /dev/null
+++ b/test/headless/read/read.cc
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "bt_headless"
+
+#include "test/headless/read/read.h"
+#include "base/logging.h"     // LOG() stdout and android log
+#include "osi/include/log.h"  // android log only
+#include "test/headless/get_options.h"
+#include "test/headless/headless.h"
+#include "test/headless/read/name.h"
+
+using namespace bluetooth::test::headless;
+
+Read::Read(const bluetooth::test::headless::GetOpt& options)
+    : HeadlessTest<int>(options) {
+  test_nodes_.emplace(
+      "name", std::make_unique<bluetooth::test::headless::Name>(options));
+}
diff --git a/test/headless/read/read.h b/test/headless/read/read.h
new file mode 100644
index 0000000..04ab0f2
--- /dev/null
+++ b/test/headless/read/read.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "test/headless/get_options.h"
+#include "test/headless/headless.h"
+
+namespace bluetooth {
+namespace test {
+namespace headless {
+
+class Read : public HeadlessTest<int> {
+ public:
+  Read(const bluetooth::test::headless::GetOpt& options);
+};
+
+}  // namespace headless
+}  // namespace test
+}  // namespace bluetooth
diff --git a/test/headless/sdp/sdp.cc b/test/headless/sdp/sdp.cc
new file mode 100644
index 0000000..9e20340
--- /dev/null
+++ b/test/headless/sdp/sdp.cc
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "bt_headless_sdp"
+
+#include <future>
+
+#include "base/logging.h"     // LOG() stdout and android log
+#include "osi/include/log.h"  // android log only
+#include "stack/include/sdp_api.h"
+#include "test/headless/get_options.h"
+#include "test/headless/headless.h"
+#include "test/headless/sdp/sdp.h"
+#include "test/headless/sdp/sdp_db.h"
+#include "types/raw_address.h"
+
+using namespace bluetooth::test::headless;
+
+static void bta_jv_start_discovery_callback(uint16_t result, void* user_data) {
+  auto promise = static_cast<std::promise<uint16_t>*>(user_data);
+  promise->set_value(result);
+}
+
+namespace {
+
+struct sdp_error_code_s {
+  const char* name;
+  uint16_t error_code;
+} sdp_error_code[] = {
+    {"KsdpSuccess", 0},
+    {"KsdpInvalidVersion", 0x0001},
+    {"KsdpInvalidServRecHdl", 0x0002},
+    {"KsdpInvalidReqSyntax", 0x0003},
+    {"KsdpInvalidPduSize", 0x0004},
+    {"KsdpInvalidContState", 0x0005},
+    {"KsdpNoResources", 0x0006},
+    {"KsdpDiRegFailed", 0x0007},
+    {"KsdpDiDiscFailed", 0x0008},
+    {"KsdpNoDiRecordFound", 0x0009},
+    {"KsdpErrAttrNotPresent", 0x000a},
+    {"KsdpIllegalParameter", 0x000b},
+    {"KsdpNoRecsMatch", 0xFFF0},
+    {"KsdpConnFailed", 0xFFF1},
+    {"KsdpCfgFailed", 0xFFF2},
+    {"KsdpGenericError", 0xFFF3},
+    {"KsdpDbFull", 0xFFF4},
+    {"KsdpInvalidPdu", 0xFFF5},
+    {"KsdpSecurityErr", 0xFFF6},
+    {"KsdpConnRejected", 0xFFF7},
+    {"KsdpCancel", 0xFFF8},
+};
+
+const char* kUnknownText = "Unknown";
+
+const char* SdpErrorCodeToString(uint16_t code) {
+  for (size_t i = 0; i < sizeof(sdp_error_code) / sizeof(sdp_error_code_s);
+       ++i) {
+    if (sdp_error_code[i].error_code == code) {
+      return sdp_error_code[i].name;
+    }
+  }
+  return kUnknownText;
+}
+
+constexpr size_t kMaxDiscoveryRecords = 64;
+
+int sdp_query_uuid(unsigned int num_loops, const RawAddress& raw_address,
+                   const bluetooth::Uuid& uuid) {
+  SdpDb sdp_discovery_db(kMaxDiscoveryRecords);
+
+  if (!SDP_InitDiscoveryDb(sdp_discovery_db.RawPointer(),
+                           sdp_discovery_db.Length(),
+                           1,  // num_uuid,
+                           &uuid, 0, nullptr)) {
+    fprintf(stdout, "%s Unable to initialize sdp discovery\n", __func__);
+    return -1;
+  }
+
+  std::promise<uint16_t> promise;
+  auto future = promise.get_future();
+
+  sdp_discovery_db.Print(stdout);
+
+  if (!SDP_ServiceSearchAttributeRequest2(
+          raw_address, sdp_discovery_db.RawPointer(),
+          bta_jv_start_discovery_callback, (void*)&promise)) {
+    fprintf(stdout, "%s Failed to start search attribute request\n", __func__);
+    return -2;
+  }
+
+  uint16_t result = future.get();
+  if (result != 0) {
+    fprintf(stdout, "Failed search discovery result:%s\n",
+            SdpErrorCodeToString(result));
+    return result;
+  }
+
+  tSDP_DISC_REC* rec = SDP_FindServiceInDb(sdp_discovery_db.RawPointer(),
+                                           uuid.As16Bit(), nullptr);
+  if (rec == nullptr) {
+    fprintf(stdout, "discovery record is null from:%s uuid:%s\n",
+            raw_address.ToString().c_str(), uuid.ToString().c_str());
+  } else {
+    fprintf(stdout, "result:%d attr_id:%x from:%s uuid:%s\n", result,
+            rec->p_first_attr->attr_id, rec->remote_bd_addr.ToString().c_str(),
+            uuid.ToString().c_str());
+  }
+  return 0;
+}
+
+}  // namespace
+
+int bluetooth::test::headless::Sdp::Run() {
+  if (options_.loop_ < 1) {
+    printf("This test requires at least a single loop\n");
+    options_.Usage();
+    return -1;
+  }
+  if (options_.device_.size() != 1) {
+    printf("This test requires a single device specified\n");
+    options_.Usage();
+    return -1;
+  }
+  if (options_.uuid_.size() != 1) {
+    printf("This test requires a single uuid specified\n");
+    options_.Usage();
+    return -1;
+  }
+
+  return RunOnHeadlessStack<int>([this]() {
+    return sdp_query_uuid(options_.loop_, options_.device_.front(),
+                          options_.uuid_.front());
+  });
+}
diff --git a/test/headless/sdp/sdp.h b/test/headless/sdp/sdp.h
new file mode 100644
index 0000000..6542a1d
--- /dev/null
+++ b/test/headless/sdp/sdp.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "test/headless/get_options.h"
+#include "test/headless/headless.h"
+
+namespace bluetooth {
+namespace test {
+namespace headless {
+
+class Sdp : public HeadlessTest<int> {
+ public:
+  Sdp(const bluetooth::test::headless::GetOpt& options)
+      : HeadlessTest<int>(options) {}
+  int Run() override;
+};
+
+}  // namespace headless
+}  // namespace test
+}  // namespace bluetooth
diff --git a/test/headless/sdp/sdp_db.cc b/test/headless/sdp/sdp_db.cc
new file mode 100644
index 0000000..023861b
--- /dev/null
+++ b/test/headless/sdp/sdp_db.cc
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "bt_headless"
+
+#include "test/headless/sdp/sdp_db.h"
+#include "base/logging.h"     // LOG() stdout and android log
+#include "osi/include/log.h"  // android log only
+#include "stack/include/sdp_api.h"
+#include "types/bluetooth/uuid.h"
+#include "types/raw_address.h"
+
+using namespace bluetooth::test::headless;
+
+SdpDb::SdpDb(unsigned int max_records) : max_records_(max_records) {
+  db_ = (tSDP_DISCOVERY_DB*)malloc(max_records_ * sizeof(tSDP_DISC_REC) +
+                                   sizeof(tSDP_DISCOVERY_DB));
+}
+
+SdpDb::~SdpDb() { free(db_); }
+
+tSDP_DISCOVERY_DB* SdpDb::RawPointer() { return db_; }
+
+uint32_t SdpDb::Length() const {
+  return max_records_ * sizeof(tSDP_DISC_REC) + sizeof(tSDP_DISCOVERY_DB);
+}
+
+void SdpDb::Print(FILE* filep) const {
+  fprintf(filep, "memory size:0x%x free:0x%x\n", db_->mem_size, db_->mem_free);
+  fprintf(filep, "number of filters:%hd\n", db_->num_uuid_filters);
+  for (int i = 0; i < db_->num_uuid_filters; i++) {
+    fprintf(filep, "  uuid:%s\n", db_->uuid_filters[i].ToString().c_str());
+  }
+  fprintf(filep, "raw data size:0x%x used:0x%x\n", db_->raw_size,
+          db_->raw_used);
+}
diff --git a/test/headless/sdp/sdp_db.h b/test/headless/sdp/sdp_db.h
new file mode 100644
index 0000000..63de0cc
--- /dev/null
+++ b/test/headless/sdp/sdp_db.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <cstdio>
+
+#include "stack/include/sdp_api.h"
+
+namespace bluetooth {
+namespace test {
+namespace headless {
+
+class SdpDb {
+ public:
+  SdpDb(unsigned int max_records);
+  ~SdpDb();
+
+  tSDP_DISCOVERY_DB* RawPointer();
+
+  uint32_t Length() const;
+
+  void Print(FILE* filep) const;
+
+ private:
+  unsigned int max_records_;
+  tSDP_DISCOVERY_DB* db_;
+};
+
+}  // namespace headless
+}  // namespace test
+}  // namespace bluetooth
diff --git a/test/rootcanal/Android.bp b/test/rootcanal/Android.bp
index e312c0e..916b53c 100644
--- a/test/rootcanal/Android.bp
+++ b/test/rootcanal/Android.bp
@@ -14,7 +14,7 @@
 // limitations under the License.
 
 cc_binary {
-    name: "android.hardware.bluetooth@1.0-service.sim",
+    name: "android.hardware.bluetooth@1.1-service.sim",
     proprietary: true,
     relative_install_path: "hw",
     srcs: [
@@ -25,14 +25,14 @@
     header_libs: ["libbluetooth_headers"],
     shared_libs: [
         "android.hardware.bluetooth@1.0",
+        "android.hardware.bluetooth@1.1",
         "libbase",
         "libchrome",
         "libcutils",
-        "libhwbinder",
         "libhidlbase",
-        "libhidltransport",
         "liblog",
         "libutils",
+        "libprotobuf-cpp-lite",
     ],
     cflags: [
         "-fvisibility=hidden",
@@ -41,23 +41,29 @@
         "-Werror",
         "-DHAS_NO_BDROID_BUILDCFG",
     ],
+    generated_headers: [
+        "RootCanalGeneratedPackets_h",
+        "BluetoothGeneratedPackets_h",
+    ],
     static_libs: [
         "android.hardware.bluetooth-async",
         "android.hardware.bluetooth-hci",
         "libbt-rootcanal",
         "libbt-rootcanal-types",
+	"libscriptedbeaconpayload-protos-lite",
     ],
     include_dirs: [
         "system/bt",
+        "system/bt/gd",
         "system/bt/hci/include",
         "system/bt/internal_include",
         "system/bt/stack/include",
     ],
-    init_rc: ["android.hardware.bluetooth@1.0-service.sim.rc"],
+    init_rc: ["android.hardware.bluetooth@1.1-service.sim.rc"],
 }
 
 cc_library_shared {
-    name: "android.hardware.bluetooth@1.0-impl-sim",
+    name: "android.hardware.bluetooth@1.1-impl-sim",
     proprietary: true,
     relative_install_path: "hw",
     srcs: [
@@ -67,13 +73,14 @@
     header_libs: ["libbluetooth_headers"],
     shared_libs: [
         "android.hardware.bluetooth@1.0",
+        "android.hardware.bluetooth@1.1",
         "libbase",
         "libchrome",
         "libcutils",
         "libhidlbase",
-        "libhidltransport",
         "liblog",
         "libutils",
+        "libprotobuf-cpp-lite",
     ],
     cflags: [
         "-Wall",
@@ -81,14 +88,20 @@
         "-Werror",
         "-DHAS_NO_BDROID_BUILDCFG",
     ],
+    generated_headers: [
+        "RootCanalGeneratedPackets_h",
+        "BluetoothGeneratedPackets_h",
+    ],
     static_libs: [
         "android.hardware.bluetooth-async",
         "android.hardware.bluetooth-hci",
         "libbt-rootcanal",
         "libbt-rootcanal-types",
+        "libscriptedbeaconpayload-protos-lite",
     ],
     include_dirs: [
         "system/bt",
+        "system/bt/gd",
         "system/bt/hci/include",
         "system/bt/internal_include",
         "system/bt/stack/include",
diff --git a/test/rootcanal/android.hardware.bluetooth@1.0-service.sim.rc b/test/rootcanal/android.hardware.bluetooth@1.0-service.sim.rc
deleted file mode 100644
index 9d99c8a..0000000
--- a/test/rootcanal/android.hardware.bluetooth@1.0-service.sim.rc
+++ /dev/null
@@ -1,4 +0,0 @@
-service vendor.bluetooth-1-0 /vendor/bin/hw/android.hardware.bluetooth@1.0-service.sim
-    class hal
-    user bluetooth
-    group bluetooth
diff --git a/test/rootcanal/android.hardware.bluetooth@1.1-service.sim.rc b/test/rootcanal/android.hardware.bluetooth@1.1-service.sim.rc
new file mode 100644
index 0000000..2626841
--- /dev/null
+++ b/test/rootcanal/android.hardware.bluetooth@1.1-service.sim.rc
@@ -0,0 +1,4 @@
+service vendor.bluetooth-1-1 /vendor/bin/hw/android.hardware.bluetooth@1.1-service.sim
+    class hal
+    user bluetooth
+    group bluetooth
diff --git a/test/rootcanal/bluetooth_hci.cc b/test/rootcanal/bluetooth_hci.cc
index e40a0c1..963f1a7 100644
--- a/test/rootcanal/bluetooth_hci.cc
+++ b/test/rootcanal/bluetooth_hci.cc
@@ -14,23 +14,22 @@
 // limitations under the License.
 //
 
-#define LOG_TAG "android.hardware.bluetooth@1.0.sim"
+#define LOG_TAG "android.hardware.bluetooth@1.1.sim"
 
 #include "bluetooth_hci.h"
 
-#include <base/logging.h>
+#include "log/log.h"
 #include <cutils/properties.h>
 #include <netdb.h>
 #include <netinet/in.h>
 #include <string.h>
-#include <utils/Log.h>
 
 #include "hci_internals.h"
 
 namespace android {
 namespace hardware {
 namespace bluetooth {
-namespace V1_0 {
+namespace V1_1 {
 namespace sim {
 
 using android::hardware::hidl_vec;
@@ -51,8 +50,10 @@
  public:
   BluetoothDeathRecipient(const sp<IBluetoothHci> hci) : mHci(hci) {}
 
-  virtual void serviceDied(uint64_t /* cookie */, const wp<::android::hidl::base::V1_0::IBase>& /* who */) {
-    ALOGE("BluetoothDeathRecipient::serviceDied - Bluetooth service died");
+  void serviceDied(
+      uint64_t /* cookie */,
+      const wp<::android::hidl::base::V1_0::IBase>& /* who */) override {
+    LOG_ERROR("BluetoothDeathRecipient::serviceDied - Bluetooth service died");
     has_died_ = true;
     mHci->close();
   }
@@ -70,59 +71,126 @@
 
 BluetoothHci::BluetoothHci() : death_recipient_(new BluetoothDeathRecipient(this)) {}
 
-Return<void> BluetoothHci::initialize(const sp<IBluetoothHciCallbacks>& cb) {
-  ALOGI("%s", __func__);
+Return<void> BluetoothHci::initialize(
+    const sp<V1_0::IBluetoothHciCallbacks>& cb) {
+  return initialize_impl(cb, nullptr);
+}
 
+Return<void> BluetoothHci::initialize_1_1(
+    const sp<V1_1::IBluetoothHciCallbacks>& cb) {
+  return initialize_impl(cb, cb);
+}
+
+Return<void> BluetoothHci::initialize_impl(
+    const sp<V1_0::IBluetoothHciCallbacks>& cb,
+    const sp<V1_1::IBluetoothHciCallbacks>& cb_1_1) {
+  LOG_INFO("%s", __func__);
   if (cb == nullptr) {
-    ALOGE("cb == nullptr! -> Unable to call initializationComplete(ERR)");
+    LOG_ERROR("cb == nullptr! -> Unable to call initializationComplete(ERR)");
     return Void();
   }
 
   death_recipient_->setHasDied(false);
-  cb->linkToDeath(death_recipient_, 0);
+  auto link_ret = cb->linkToDeath(death_recipient_, 0);
+  CHECK(link_ret.isOk()) << "Error calling linkToDeath.";
 
-  test_channel_transport_.RegisterCommandHandler([this](const std::string& name, const std::vector<std::string>& args) {
-    async_manager_.ExecAsync(std::chrono::milliseconds(0),
-                             [this, name, args]() { test_channel_.HandleCommand(name, args); });
-  });
+  test_channel_transport_.RegisterCommandHandler(
+      [this](const std::string& name, const std::vector<std::string>& args) {
+        async_manager_.ExecAsync(
+            std::chrono::milliseconds(0),
+            [this, name, args]() { test_channel_.HandleCommand(name, args); });
+      });
 
   controller_ = std::make_shared<DualModeController>();
 
-  controller_->Initialize({"dmc", "3C:5A:B4:01:02:03"});
+  char mac_property[PROPERTY_VALUE_MAX] = "";
+  property_get("bt.rootcanal_mac_address", mac_property, "3C:5A:B4:01:02:03");
+  controller_->Initialize({"dmc", std::string(mac_property)});
 
-  controller_->RegisterEventChannel([cb](std::shared_ptr<std::vector<uint8_t>> packet) {
-    hidl_vec<uint8_t> hci_event(packet->begin(), packet->end());
-    cb->hciEventReceived(hci_event);
-  });
+  controller_->RegisterEventChannel(
+      [this, cb](std::shared_ptr<std::vector<uint8_t>> packet) {
+        hidl_vec<uint8_t> hci_event(packet->begin(), packet->end());
+        auto ret = cb->hciEventReceived(hci_event);
+        if (!ret.isOk()) {
+          LOG_ERROR("Error sending event callback");
+          if (!death_recipient_->getHasDied()) {
+            LOG_ERROR("Closing");
+            close();
+          }
+        }
+      });
 
-  controller_->RegisterAclChannel([cb](std::shared_ptr<std::vector<uint8_t>> packet) {
-    hidl_vec<uint8_t> acl_packet(packet->begin(), packet->end());
-    cb->aclDataReceived(acl_packet);
-  });
+  controller_->RegisterAclChannel(
+      [this, cb](std::shared_ptr<std::vector<uint8_t>> packet) {
+        hidl_vec<uint8_t> acl_packet(packet->begin(), packet->end());
+        auto ret = cb->aclDataReceived(acl_packet);
+        if (!ret.isOk()) {
+          LOG_ERROR("Error sending acl callback");
+          if (!death_recipient_->getHasDied()) {
+            LOG_ERROR("Closing");
+            close();
+          }
+        }
+      });
 
-  controller_->RegisterScoChannel([cb](std::shared_ptr<std::vector<uint8_t>> packet) {
-    hidl_vec<uint8_t> sco_packet(packet->begin(), packet->end());
-    cb->aclDataReceived(sco_packet);
-  });
+  controller_->RegisterScoChannel(
+      [this, cb](std::shared_ptr<std::vector<uint8_t>> packet) {
+        hidl_vec<uint8_t> sco_packet(packet->begin(), packet->end());
+        auto ret = cb->aclDataReceived(sco_packet);
+        if (!ret.isOk()) {
+          LOG_ERROR("Error sending sco callback");
+          if (!death_recipient_->getHasDied()) {
+            LOG_ERROR("Closing");
+            close();
+          }
+        }
+      });
 
-  controller_->RegisterTaskScheduler([this](std::chrono::milliseconds delay, const TaskCallback& task) {
-    return async_manager_.ExecAsync(delay, task);
-  });
+  if (cb_1_1 != nullptr) {
+    controller_->RegisterIsoChannel(
+        [this, cb_1_1](std::shared_ptr<std::vector<uint8_t>> packet) {
+          hidl_vec<uint8_t> iso_packet(packet->begin(), packet->end());
+          auto ret = cb_1_1->isoDataReceived(iso_packet);
+          if (!ret.isOk()) {
+            LOG_ERROR("Error sending iso callback");
+            if (!death_recipient_->getHasDied()) {
+              LOG_ERROR("Closing");
+              close();
+            }
+          }
+        });
+  }
+
+  controller_->RegisterTaskScheduler(
+      [this](std::chrono::milliseconds delay, const TaskCallback& task) {
+        return async_manager_.ExecAsync(delay, task);
+      });
 
   controller_->RegisterPeriodicTaskScheduler(
-      [this](std::chrono::milliseconds delay, std::chrono::milliseconds period, const TaskCallback& task) {
+      [this](std::chrono::milliseconds delay, std::chrono::milliseconds period,
+             const TaskCallback& task) {
         return async_manager_.ExecAsyncPeriodically(delay, period, task);
       });
 
-  controller_->RegisterTaskCancel([this](AsyncTaskId task) { async_manager_.CancelAsyncTask(task); });
+  controller_->RegisterTaskCancel(
+      [this](AsyncTaskId task) { async_manager_.CancelAsyncTask(task); });
 
   test_model_.Reset();
+
   // Add the controller as a device in the model.
-  test_model_.Add(controller_);
+  size_t controller_index = test_model_.Add(controller_);
+  size_t low_energy_phy_index =
+      test_model_.AddPhy(test_vendor_lib::Phy::Type::LOW_ENERGY);
+  size_t classic_phy_index =
+      test_model_.AddPhy(test_vendor_lib::Phy::Type::BR_EDR);
+  test_model_.AddDeviceToPhy(controller_index, low_energy_phy_index);
+  test_model_.AddDeviceToPhy(controller_index, classic_phy_index);
+  test_model_.SetTimerPeriod(std::chrono::milliseconds(10));
+  test_model_.StartTimer();
 
   // Send responses to logcat if the test channel is not configured.
   test_channel_.RegisterSendResponse([](const std::string& response) {
-    ALOGI("No test channel yet: %s", response.c_str());
+    LOG_INFO("No test channel yet: %s", response.c_str());
   });
 
   if (BtTestConsoleEnabled()) {
@@ -131,32 +199,43 @@
                    [this](int fd) { test_model_.IncomingHciConnection(fd); });
     SetUpLinkLayerServer(
         6311, [this](int fd) { test_model_.IncomingLinkLayerConnection(fd); });
+  } else {
+    // This should be configurable in the future.
+    LOG_INFO("Adding Beacons so the scan list is not empty.");
+    test_channel_.Add({"beacon", "be:ac:10:00:00:01", "1000"});
+    test_model_.AddDeviceToPhy(controller_index + 1, low_energy_phy_index);
+    test_channel_.Add({"beacon", "be:ac:10:00:00:02", "1000"});
+    test_model_.AddDeviceToPhy(controller_index + 2, low_energy_phy_index);
+    test_channel_.Add(
+        {"scripted_beacon", "5b:ea:c1:00:00:03",
+         "/data/vendor/bluetooth/bluetooth_sim_ble_playback_file",
+         "/data/vendor/bluetooth/bluetooth_sim_ble_playback_events"});
+    test_model_.AddDeviceToPhy(controller_index + 3, low_energy_phy_index);
+    test_channel_.List({});
   }
 
-  // Add some default devices for easier debugging
-  test_channel_.AddDefaults();
-
-  // This should be configurable in the future.
-  ALOGI("Adding Beacons so the scan list is not empty.");
-  test_channel_.Add({"beacon", "be:ac:10:00:00:01", "1000"});
-  test_channel_.AddDeviceToPhy({"1", "0"});
-  test_channel_.Add({"beacon", "be:ac:10:00:00:02", "1000"});
-  test_channel_.AddDeviceToPhy({"2", "0"});
-  test_channel_.SetTimerPeriod({"1000"});
-
-  unlink_cb_ = [cb](sp<BluetoothDeathRecipient>& death_recipient) {
+  unlink_cb_ = [this, cb](sp<BluetoothDeathRecipient>& death_recipient) {
     if (death_recipient->getHasDied())
-      ALOGI("Skipping unlink call, service died.");
-    else
-      cb->unlinkToDeath(death_recipient);
+      LOG_INFO("Skipping unlink call, service died.");
+    else {
+      auto ret = cb->unlinkToDeath(death_recipient);
+      if (!ret.isOk()) {
+        CHECK(death_recipient_->getHasDied())
+            << "Error calling unlink, but no death notification.";
+      }
+    }
   };
 
-  cb->initializationComplete(Status::SUCCESS);
+  auto init_ret = cb->initializationComplete(V1_0::Status::SUCCESS);
+  if (!init_ret.isOk()) {
+    CHECK(death_recipient_->getHasDied())
+        << "Error sending init callback, but no death notification.";
+  }
   return Void();
 }
 
 Return<void> BluetoothHci::close() {
-  ALOGI("%s", __func__);
+  LOG_INFO("%s", __func__);
   return Void();
 }
 
@@ -187,21 +266,31 @@
   return Void();
 }
 
+Return<void> BluetoothHci::sendIsoData(const hidl_vec<uint8_t>& packet) {
+  async_manager_.ExecAsync(std::chrono::milliseconds(0), [this, packet]() {
+    std::shared_ptr<std::vector<uint8_t>> packet_copy =
+        std::shared_ptr<std::vector<uint8_t>>(new std::vector<uint8_t>(packet));
+    controller_->HandleIso(packet_copy);
+  });
+  return Void();
+}
+
 void BluetoothHci::SetUpHciServer(int port, const std::function<void(int)>& connection_callback) {
   int socket_fd = remote_hci_transport_.SetUp(port);
 
-  test_channel_.RegisterSendResponse(
-      [](const std::string& response) { ALOGI("No HCI Response channel: %s", response.c_str()); });
+  test_channel_.RegisterSendResponse([](const std::string& response) {
+    LOG_INFO("No HCI Response channel: %s", response.c_str());
+  });
 
   if (socket_fd == -1) {
-    ALOGE("Remote HCI channel SetUp(%d) failed.", port);
+    LOG_ERROR("Remote HCI channel SetUp(%d) failed.", port);
     return;
   }
 
   async_manager_.WatchFdForNonBlockingReads(socket_fd, [this, connection_callback](int socket_fd) {
     int conn_fd = remote_hci_transport_.Accept(socket_fd);
     if (conn_fd < 0) {
-      ALOGE("Error watching remote HCI channel fd.");
+      LOG_ERROR("Error watching remote HCI channel fd.");
       return;
     }
     int flags = fcntl(conn_fd, F_GETFL, NULL);
@@ -216,18 +305,19 @@
 void BluetoothHci::SetUpLinkLayerServer(int port, const std::function<void(int)>& connection_callback) {
   int socket_fd = remote_link_layer_transport_.SetUp(port);
 
-  test_channel_.RegisterSendResponse(
-      [](const std::string& response) { ALOGI("No LinkLayer Response channel: %s", response.c_str()); });
+  test_channel_.RegisterSendResponse([](const std::string& response) {
+    LOG_INFO("No LinkLayer Response channel: %s", response.c_str());
+  });
 
   if (socket_fd == -1) {
-    ALOGE("Remote LinkLayer channel SetUp(%d) failed.", port);
+    LOG_ERROR("Remote LinkLayer channel SetUp(%d) failed.", port);
     return;
   }
 
   async_manager_.WatchFdForNonBlockingReads(socket_fd, [this, connection_callback](int socket_fd) {
     int conn_fd = remote_link_layer_transport_.Accept(socket_fd);
     if (conn_fd < 0) {
-      ALOGE("Error watching remote LinkLayer channel fd.");
+      LOG_ERROR("Error watching remote LinkLayer channel fd.");
       return;
     }
     int flags = fcntl(conn_fd, F_GETFL, NULL);
@@ -241,14 +331,15 @@
 int BluetoothHci::ConnectToRemoteServer(const std::string& server, int port) {
   int socket_fd = socket(AF_INET, SOCK_STREAM, 0);
   if (socket_fd < 1) {
-    ALOGI("socket() call failed: %s", strerror(errno));
+    LOG_INFO("socket() call failed: %s", strerror(errno));
     return -1;
   }
 
   struct hostent* host;
   host = gethostbyname(server.c_str());
   if (host == NULL) {
-    ALOGI("gethostbyname() failed for %s: %s", server.c_str(), strerror(errno));
+    LOG_INFO("gethostbyname() failed for %s: %s", server.c_str(),
+             strerror(errno));
     return -1;
   }
 
@@ -260,7 +351,8 @@
 
   int result = connect(socket_fd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
   if (result < 0) {
-    ALOGI("connect() failed for %s@%d: %s", server.c_str(), port, strerror(errno));
+    LOG_INFO("connect() failed for %s@%d: %s", server.c_str(), port,
+             strerror(errno));
     return -1;
   }
 
@@ -274,22 +366,23 @@
 void BluetoothHci::SetUpTestChannel(int port) {
   int socket_fd = test_channel_transport_.SetUp(port);
 
-  test_channel_.RegisterSendResponse(
-      [](const std::string& response) { ALOGI("No test channel: %s", response.c_str()); });
+  test_channel_.RegisterSendResponse([](const std::string& response) {
+    LOG_INFO("No test channel: %s", response.c_str());
+  });
 
   if (socket_fd == -1) {
-    ALOGE("Test channel SetUp(%d) failed.", port);
+    LOG_ERROR("Test channel SetUp(%d) failed.", port);
     return;
   }
 
-  ALOGI("Test channel SetUp() successful");
+  LOG_INFO("Test channel SetUp() successful");
   async_manager_.WatchFdForNonBlockingReads(socket_fd, [this](int socket_fd) {
     int conn_fd = test_channel_transport_.Accept(socket_fd);
     if (conn_fd < 0) {
-      ALOGE("Error watching test channel fd.");
+      LOG_ERROR("Error watching test channel fd.");
       return;
     }
-    ALOGI("Test channel connection accepted.");
+    LOG_INFO("Test channel connection accepted.");
     test_channel_.RegisterSendResponse(
         [this, conn_fd](const std::string& response) { test_channel_transport_.SendResponse(conn_fd, response); });
 
@@ -306,7 +399,7 @@
 }
 
 }  // namespace sim
-}  // namespace V1_0
+}  // namespace V1_1
 }  // namespace bluetooth
 }  // namespace hardware
 }  // namespace android
diff --git a/test/rootcanal/bluetooth_hci.h b/test/rootcanal/bluetooth_hci.h
index 96d0ed8..99eda9f 100644
--- a/test/rootcanal/bluetooth_hci.h
+++ b/test/rootcanal/bluetooth_hci.h
@@ -16,7 +16,9 @@
 
 #pragma once
 
-#include <android/hardware/bluetooth/1.0/IBluetoothHci.h>
+#include "os/log.h"
+
+#include <android/hardware/bluetooth/1.1/IBluetoothHci.h>
 
 #include <hidl/MQDescriptor.h>
 
@@ -31,7 +33,7 @@
 namespace android {
 namespace hardware {
 namespace bluetooth {
-namespace V1_0 {
+namespace V1_1 {
 namespace sim {
 
 class BluetoothDeathRecipient;
@@ -40,7 +42,10 @@
  public:
   BluetoothHci();
 
-  ::android::hardware::Return<void> initialize(const sp<IBluetoothHciCallbacks>& cb) override;
+  ::android::hardware::Return<void> initialize(
+      const sp<V1_0::IBluetoothHciCallbacks>& cb) override;
+  ::android::hardware::Return<void> initialize_1_1(
+      const sp<V1_1::IBluetoothHciCallbacks>& cb) override;
 
   ::android::hardware::Return<void> sendHciCommand(const ::android::hardware::hidl_vec<uint8_t>& packet) override;
 
@@ -48,6 +53,9 @@
 
   ::android::hardware::Return<void> sendScoData(const ::android::hardware::hidl_vec<uint8_t>& packet) override;
 
+  ::android::hardware::Return<void> sendIsoData(
+      const ::android::hardware::hidl_vec<uint8_t>& packet) override;
+
   ::android::hardware::Return<void> close() override;
 
   static void OnPacketReady();
@@ -55,6 +63,10 @@
   static BluetoothHci* get();
 
  private:
+  ::android::hardware::Return<void> initialize_impl(
+      const sp<V1_0::IBluetoothHciCallbacks>& cb,
+      const sp<V1_1::IBluetoothHciCallbacks>& cb_1_1);
+
   sp<BluetoothDeathRecipient> death_recipient_;
 
   std::function<void(sp<BluetoothDeathRecipient>&)> unlink_cb_;
@@ -94,7 +106,7 @@
 extern "C" IBluetoothHci* HIDL_FETCH_IBluetoothHci(const char* name);
 
 }  // namespace sim
-}  // namespace V1_0
+}  // namespace V1_1
 }  // namespace bluetooth
 }  // namespace hardware
 }  // namespace android
diff --git a/test/rootcanal/service.cc b/test/rootcanal/service.cc
index 64fa9c7..7856dcf 100644
--- a/test/rootcanal/service.cc
+++ b/test/rootcanal/service.cc
@@ -14,20 +14,21 @@
 // limitations under the License.
 //
 
-#define LOG_TAG "android.hardware.bluetooth@1.0-service.sim"
+#define LOG_TAG "android.hardware.bluetooth@1.1-service.sim"
 
-#include <android/hardware/bluetooth/1.0/IBluetoothHci.h>
+#include "os/log.h"
+
+#include <android/hardware/bluetooth/1.1/IBluetoothHci.h>
 #include <hidl/HidlSupport.h>
 #include <hidl/HidlTransportSupport.h>
-#include <utils/Log.h>
 
 #include "bluetooth_hci.h"
 
 using ::android::sp;
 using ::android::hardware::configureRpcThreadpool;
 using ::android::hardware::joinRpcThreadpool;
-using ::android::hardware::bluetooth::V1_0::IBluetoothHci;
-using ::android::hardware::bluetooth::V1_0::sim::BluetoothHci;
+using ::android::hardware::bluetooth::V1_1::IBluetoothHci;
+using ::android::hardware::bluetooth::V1_1::sim::BluetoothHci;
 
 int main(int /* argc */, char** /* argv */) {
   sp<IBluetoothHci> bluetooth = new BluetoothHci;
@@ -36,5 +37,5 @@
   if (status == android::OK)
     joinRpcThreadpool();
   else
-    ALOGE("Could not register as a service!");
+    LOG_ERROR("Could not register as a service!");
 }
diff --git a/test/run_host_unit_tests.py b/test/run_host_unit_tests.py
index 3d3f1c7..5ef34f7 100755
--- a/test/run_host_unit_tests.py
+++ b/test/run_host_unit_tests.py
@@ -21,201 +21,201 @@
 # Registered host based unit tests
 # Must have 'host_supported: true'
 HOST_TESTS = [
-  'bluetooth_test_common',
-  'bluetoothtbd_test',
-  'net_test_avrcp',
-  'net_test_btcore',
-  'net_test_types',
-  'net_test_btpackets',
+    'bluetooth_test_common',
+    'bluetoothtbd_test',
+    'net_test_avrcp',
+    'net_test_btcore',
+    'net_test_types',
+    'net_test_btpackets',
 ]
 
 SOONG_UI_BASH = 'build/soong/soong_ui.bash'
 
+
 def str2bool(argument, default=False):
-  """ Convert a string to a booleen value. """
-  argument = str(argument)
-  if argument.lower() in ['0', 'f', 'false', 'off', 'no', 'n']:
-    return False
-  elif argument.lower() in ['1', 't', 'true', 'on', 'yes', 'y']:
-    return True
-  return default
+    """ Convert a string to a booleen value. """
+    argument = str(argument)
+    if argument.lower() in ['0', 'f', 'false', 'off', 'no', 'n']:
+        return False
+    elif argument.lower() in ['1', 't', 'true', 'on', 'yes', 'y']:
+        return True
+    return default
 
 
 def check_dir_exists(dir, dirname):
-  if not os.path.isdir(dir):
-    print "Couldn't find %s (%s)!" % (dirname, dir)
-    sys.exit(0)
+    if not os.path.isdir(dir):
+        print "Couldn't find %s (%s)!" % (dirname, dir)
+        sys.exit(0)
 
 
 def get_output_from_command(cmd):
-  try:
-    return subprocess.check_output(cmd).strip()
-  except subprocess.CalledProcessError as e:
-    print 'Failed to call {cmd}, return code {code}'.format(cmd=cmd,
-                                                            code=e.returncode)
-    print e
-    return None
+    try:
+        return subprocess.check_output(cmd).strip()
+    except subprocess.CalledProcessError as e:
+        print 'Failed to call {cmd}, return code {code}'.format(
+            cmd=cmd, code=e.returncode)
+        print e
+        return None
 
 
 def get_android_root_or_die():
-  value = os.environ.get('ANDROID_BUILD_TOP')
-  if not value:
-    # Try to find build/soong/soong_ui.bash upwards until root directory
-    current_path = os.path.abspath(os.getcwd())
-    while current_path and os.path.isdir(current_path):
-      soong_ui_bash_path = os.path.join(current_path, SOONG_UI_BASH)
-      if os.path.isfile(soong_ui_bash_path):
-        # Use value returned from Soong UI instead in case definition to TOP
-        # changes in the future
-        value = get_output_from_command((soong_ui_bash_path,
-                                         '--dumpvar-mode',
-                                         '--abs',
-                                         'TOP'))
-        break
-      parent_path = os.path.abspath(os.path.join(current_path, os.pardir))
-      if parent_path == current_path:
-        current_path = None
-      else:
-        current_path = parent_path
+    value = os.environ.get('ANDROID_BUILD_TOP')
     if not value:
-      print 'Cannot determine ANDROID_BUILD_TOP'
-      sys.exit(1)
-  check_dir_exists(value, '$ANDROID_BUILD_TOP')
-  return value
+        # Try to find build/soong/soong_ui.bash upwards until root directory
+        current_path = os.path.abspath(os.getcwd())
+        while current_path and os.path.isdir(current_path):
+            soong_ui_bash_path = os.path.join(current_path, SOONG_UI_BASH)
+            if os.path.isfile(soong_ui_bash_path):
+                # Use value returned from Soong UI instead in case definition to TOP
+                # changes in the future
+                value = get_output_from_command(
+                    (soong_ui_bash_path, '--dumpvar-mode', '--abs', 'TOP'))
+                break
+            parent_path = os.path.abspath(os.path.join(current_path, os.pardir))
+            if parent_path == current_path:
+                current_path = None
+            else:
+                current_path = parent_path
+        if not value:
+            print 'Cannot determine ANDROID_BUILD_TOP'
+            sys.exit(1)
+    check_dir_exists(value, '$ANDROID_BUILD_TOP')
+    return value
 
 
 def get_android_host_out_or_die():
-  value = os.environ.get('ANDROID_HOST_OUT')
-  if not value:
-    ANDROID_BUILD_TOP = get_android_root_or_die()
-    value = get_output_from_command((os.path.join(ANDROID_BUILD_TOP,
-                                                  SOONG_UI_BASH),
-                                     '--dumpvar-mode',
-                                     '--abs',
-                                     'HOST_OUT'))
+    value = os.environ.get('ANDROID_HOST_OUT')
     if not value:
-      print 'Cannot determine ANDROID_HOST_OUT'
-      sys.exit(1)
-  check_dir_exists(value, '$ANDROID_HOST_OUT')
-  return value
+        ANDROID_BUILD_TOP = get_android_root_or_die()
+        value = get_output_from_command(
+            (os.path.join(ANDROID_BUILD_TOP, SOONG_UI_BASH), '--dumpvar-mode',
+             '--abs', 'HOST_OUT'))
+        if not value:
+            print 'Cannot determine ANDROID_HOST_OUT'
+            sys.exit(1)
+    check_dir_exists(value, '$ANDROID_HOST_OUT')
+    return value
 
 
 def get_android_dist_dir_or_die():
-  # Check if $DIST_DIR is predefined as environment variable
-  value = os.environ.get('DIST_DIR')
-  if not value:
-    # If not use the default path
-    ANDROID_BUILD_TOP = get_android_root_or_die()
-    value = os.path.join(os.path.join(ANDROID_BUILD_TOP, 'out'), 'dist')
-  if not os.path.isdir(value):
-    if os.path.exists(value):
-      print '%s is not a directory!' % (value)
-      sys.exit(1)
-    os.makedirs(value)
-  return value
+    # Check if $DIST_DIR is predefined as environment variable
+    value = os.environ.get('DIST_DIR')
+    if not value:
+        # If not use the default path
+        ANDROID_BUILD_TOP = get_android_root_or_die()
+        value = os.path.join(os.path.join(ANDROID_BUILD_TOP, 'out'), 'dist')
+    if not os.path.isdir(value):
+        if os.path.exists(value):
+            print '%s is not a directory!' % (value)
+            sys.exit(1)
+        os.makedirs(value)
+    return value
 
 
 def get_native_test_root_or_die():
-  android_host_out = get_android_host_out_or_die()
-  test_root = os.path.join(android_host_out, 'nativetest64')
-  if not os.path.isdir(test_root):
-    test_root = os.path.join(android_host_out, 'nativetest')
+    android_host_out = get_android_host_out_or_die()
+    test_root = os.path.join(android_host_out, 'nativetest64')
     if not os.path.isdir(test_root):
-      print 'Neither nativetest64 nor nativetest directory exist,' \
-            ' please compile first'
-      sys.exit(1)
-  return test_root
+        test_root = os.path.join(android_host_out, 'nativetest')
+        if not os.path.isdir(test_root):
+            print 'Neither nativetest64 nor nativetest directory exist,' \
+                  ' please compile first'
+            sys.exit(1)
+    return test_root
 
 
 def get_test_cmd_or_die(test_root, test_name, enable_xml, test_filter):
-  test_path = os.path.join(os.path.join(test_root, test_name), test_name)
-  if not os.path.isfile(test_path):
-    print 'Cannot find: ' + test_path
-    sys.exit(1)
-  cmd = [test_path]
-  if enable_xml:
-    dist_dir = get_android_dist_dir_or_die()
-    log_output_path = os.path.join(dist_dir, 'gtest/{0}_test_details.xml'
-                                   .format(test_name))
-    cmd.append('--gtest_output=xml:{0}'.format(log_output_path))
-  if test_filter:
-    cmd.append('--gtest_filter=%s' % test_filter)
-  return cmd
+    test_path = os.path.join(os.path.join(test_root, test_name), test_name)
+    if not os.path.isfile(test_path):
+        print 'Cannot find: ' + test_path
+        sys.exit(1)
+    cmd = [test_path]
+    if enable_xml:
+        dist_dir = get_android_dist_dir_or_die()
+        log_output_path = os.path.join(
+            dist_dir, 'gtest/{0}_test_details.xml'.format(test_name))
+        cmd.append('--gtest_output=xml:{0}'.format(log_output_path))
+    if test_filter:
+        cmd.append('--gtest_filter=%s' % test_filter)
+    return cmd
 
 
 # path is relative to Android build top
 def build_target(target, num_tasks):
-  ANDROID_BUILD_TOP = get_android_root_or_die()
-  build_cmd = [SOONG_UI_BASH, '--make-mode']
-  if num_tasks > 1:
-    build_cmd.append('-j' + str(num_tasks))
-  build_cmd.append(target)
-  p = subprocess.Popen(build_cmd, cwd=ANDROID_BUILD_TOP, env=os.environ.copy())
-  return_code = p.wait()
-  if return_code != 0:
-    print 'BUILD FAILED, return code: {0}'.format(str(return_code))
-    sys.exit(1)
-  return
+    ANDROID_BUILD_TOP = get_android_root_or_die()
+    build_cmd = [SOONG_UI_BASH, '--make-mode']
+    if num_tasks > 1:
+        build_cmd.append('-j' + str(num_tasks))
+    build_cmd.append(target)
+    p = subprocess.Popen(
+        build_cmd, cwd=ANDROID_BUILD_TOP, env=os.environ.copy())
+    return_code = p.wait()
+    if return_code != 0:
+        print 'BUILD FAILED, return code: {0}'.format(str(return_code))
+        sys.exit(1)
+    return
 
 
 def main():
-  """ run_host_unit_tests.py - Run registered host based unit tests
+    """ run_host_unit_tests.py - Run registered host based unit tests
   """
-  parser = argparse.ArgumentParser(description='Run host based unit tests.')
-  parser.add_argument(
-      '--enable_xml',
-      type=str2bool,
-      dest='enable_xml',
-      nargs='?',
-      const=True,
-      default=False,
-      help=
-      'Whether to output structured XML log output in out/dist/gtest directory')
-  parser.add_argument(
-      '-j',
-      type=int,
-      nargs='?',
-      dest='num_tasks',
-      const=-1,
-      default=-1,
-      help='Number of tasks to run at the same time')
-  parser.add_argument(
-      'rest',
-      nargs=argparse.REMAINDER,
-      help='-- args, other gtest arguments for each individual test')
-  args = parser.parse_args()
+    parser = argparse.ArgumentParser(description='Run host based unit tests.')
+    parser.add_argument(
+        '--enable_xml',
+        type=str2bool,
+        dest='enable_xml',
+        nargs='?',
+        const=True,
+        default=False,
+        help=
+        'Whether to output structured XML log output in out/dist/gtest directory'
+    )
+    parser.add_argument(
+        '-j',
+        type=int,
+        nargs='?',
+        dest='num_tasks',
+        const=-1,
+        default=-1,
+        help='Number of tasks to run at the same time')
+    parser.add_argument(
+        'rest',
+        nargs=argparse.REMAINDER,
+        help='-- args, other gtest arguments for each individual test')
+    args = parser.parse_args()
 
-  build_target('MODULES-IN-system-bt', args.num_tasks)
-  TEST_ROOT = get_native_test_root_or_die()
-  test_results = []
-  for test in HOST_TESTS:
-    test_cmd = get_test_cmd_or_die(TEST_ROOT, test, args.enable_xml, args.rest)
-    if subprocess.call(test_cmd) != 0:
-      test_results.append(False)
-    else:
-      test_results.append(True)
-  if not all(test_results):
-    failures = [i for i, x in enumerate(test_results) if not x]
-    for index in failures:
-      print 'TEST FAILLED: ' + HOST_TESTS[index]
+    build_target('MODULES-IN-system-bt', args.num_tasks)
+    TEST_ROOT = get_native_test_root_or_die()
+    test_results = []
+    for test in HOST_TESTS:
+        test_cmd = get_test_cmd_or_die(TEST_ROOT, test, args.enable_xml,
+                                       args.rest)
+        if subprocess.call(test_cmd) != 0:
+            test_results.append(False)
+        else:
+            test_results.append(True)
+    if not all(test_results):
+        failures = [i for i, x in enumerate(test_results) if not x]
+        for index in failures:
+            print 'TEST FAILLED: ' + HOST_TESTS[index]
+        sys.exit(0)
+    print 'TEST PASSED ' + str(len(test_results)) + ' tests were run'
+
+    dist_dir = get_android_dist_dir_or_die()
+    log_output_path = os.path.join(dist_dir, 'gtest/coverage')
+    cmd_path = os.path.join(get_android_root_or_die(), 'system/bt/test')
+    print cmd_path
+    cmd = [
+        os.path.join(cmd_path, 'gen_coverage.py'),
+        '--skip-html',
+        '--json-file',
+        '-o',
+        log_output_path,
+    ]
+    subprocess.call(cmd)
+
     sys.exit(0)
-  print 'TEST PASSED ' + str(len(test_results)) + ' tests were run'
-
-  dist_dir = get_android_dist_dir_or_die()
-  log_output_path = os.path.join(dist_dir, 'gtest/coverage')
-  cmd_path = os.path.join(get_android_root_or_die(), 'system/bt/test')
-  print cmd_path
-  cmd = [
-    os.path.join(cmd_path, 'gen_coverage.py'),
-    '--skip-html',
-    '--json-file',
-    '-o',
-    log_output_path,
-  ]
-  subprocess.call(cmd)
-
-  sys.exit(0)
 
 
 if __name__ == '__main__':
-  main()
+    main()
diff --git a/test/run_unit_tests.sh b/test/run_unit_tests.sh
index 8041afc..754d9c5 100755
--- a/test/run_unit_tests.sh
+++ b/test/run_unit_tests.sh
@@ -12,6 +12,7 @@
   net_test_bta
   net_test_btif
   net_test_btif_profile_queue
+  net_test_btif_config_cache
   net_test_device
   net_test_hci
   net_test_stack
diff --git a/test/suite/Android.bp b/test/suite/Android.bp
index 3967ecc..1d919ed 100644
--- a/test/suite/Android.bp
+++ b/test/suite/Android.bp
@@ -38,7 +38,7 @@
         "rfcomm/rfcomm_test.cc",
         "rfcomm/rfcomm_unittest.cc",
     ],
-    header_libs: [ "libhardware_headers" ],
+    header_libs: ["libhardware_headers"],
     shared_libs: [
         "liblog",
         "libcutils",
diff --git a/test/suite/adapter/adapter_unittest.cc b/test/suite/adapter/adapter_unittest.cc
index 5079835..85549b0 100644
--- a/test/suite/adapter/adapter_unittest.cc
+++ b/test/suite/adapter/adapter_unittest.cc
@@ -179,7 +179,7 @@
   ASSERT_TRUE(bt_callbacks != nullptr);
 
   for (int i = 0; i < kTestRepeatCount; ++i) {
-    bt_interface()->init(bt_callbacks, false, false, false);
+    bt_interface()->init(bt_callbacks, false, false, 0, false);
     EXPECT_EQ(bt_interface()->enable(), BT_STATUS_SUCCESS);
     semaphore_wait(adapter_state_changed_callback_sem_);
     EXPECT_EQ(GetState(), BT_STATE_ON) << "Adapter did not turn on.";
diff --git a/test/suite/gatt/gatt_unittest.cc b/test/suite/gatt/gatt_unittest.cc
index 26fd850..b02c96a 100644
--- a/test/suite/gatt/gatt_unittest.cc
+++ b/test/suite/gatt/gatt_unittest.cc
@@ -69,12 +69,21 @@
   int server_if = server_interface_id();
 
   std::vector<btgatt_db_element_t> service = {
-      {.type = BTGATT_DB_PRIMARY_SERVICE, .uuid = srvc_uuid},
-      {.type = BTGATT_DB_CHARACTERISTIC,
-       .uuid = char_uuid,
-       .properties = 0x10 /* notification */,
-       .permissions = 0x01 /* read only */},
-      {.type = BTGATT_DB_DESCRIPTOR, .uuid = desc_uuid, .permissions = 0x01}};
+      {
+          .uuid = srvc_uuid,
+          .type = BTGATT_DB_PRIMARY_SERVICE,
+      },
+      {
+          .uuid = char_uuid,
+          .type = BTGATT_DB_CHARACTERISTIC,
+          .properties = 0x10,  /* notification */
+          .permissions = 0x01, /* read only */
+      },
+      {
+          .uuid = desc_uuid,
+          .type = BTGATT_DB_DESCRIPTOR,
+          .permissions = 0x01,
+      }};
 
   gatt_server_interface()->add_service(server_if, service);
   semaphore_wait(service_added_callback_sem_);
diff --git a/tools/hci/main.c b/tools/hci/main.c
index c167886..093af6b 100644
--- a/tools/hci/main.c
+++ b/tools/hci/main.c
@@ -15,6 +15,7 @@
   HCI_PACKET_ACL_DATA = 2,
   HCI_PACKET_SCO_DATA = 3,
   HCI_PACKET_EVENT = 4,
+  HCI_PACKET_ISO = 5,
 } hci_packet_t;
 
 typedef struct {
diff --git a/tools/scripts/btsnoop_live.py b/tools/scripts/btsnoop_live.py
new file mode 100644
index 0000000..00b45fd
--- /dev/null
+++ b/tools/scripts/btsnoop_live.py
@@ -0,0 +1,299 @@
+#!/usr/bin/env python3
+###############################################################################################################
+#
+#  Copyright (C) 2019 Motorola Mobility LLC
+#
+#  Redistribution and use in source and binary forms, with or without modification, are permitted provided that
+#  the following conditions are met:
+#
+#  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the
+#     following disclaimer.
+#
+#  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
+#     the following disclaimer in the documentation and/or other materials provided with the distribution.
+#
+#  3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or
+#     promote products derived from this software without specific prior written permission.
+#
+#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+#  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+#  PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+#  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+#  TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+#  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+#  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+#  POSSIBILITY OF SUCH DAMAGE.
+#
+###############################################################################################################
+###############################################################################################################
+#
+#                             Bluetooth Virtual Sniffing for Android
+#
+#  This script supports Bluetooth Virtual Sniffing via Live Import Feature of Frontline Bluetooth Sniffer(FTS)
+#
+#  It extracts the HCI packets from Snoop logs and redirect it to FTS for live HCI sniffing.
+#
+#  It uses liveimport.ini and LiveImportAPI.dll from FTS path to communicate with FTS sniffer software.
+#
+#  It works on both Windows and Ubuntu. For Ubuntu, both FTS and Python should be installed on Wine.
+#
+#  FTS_INI_PATH & FTS_DLL_PATH should be set to absolute path of liveimport.ini and LiveImportAPI.dll of FTS
+#
+#  Example below - This may change per machine per FTS version in Windows vs Ubuntu (Wine), set accordingly.
+#  For Windows
+#    FTS_INI_PATH = 'C:\\Program Files (x86)\\Frontline Test System II\\Frontline 13.2\\'
+#    FTS_DLL_PATH = 'C:\\Program Files (x86)\\Frontline Test System II\\Frontline 13.2\\Executables\\Core\\'
+#
+#  For Ubuntu - FTS path recognized by Wine (not Ubuntu path)
+#    FTS_INI_PATH = 'C:\\Program Files\\Frontline Test System II\\Frontline 13.2\\'
+#    FTS_DLL_PATH = 'C:\\Program Files\\Frontline Test System II\\Frontline 13.2\\Executables\\Core\\'
+#
+###############################################################################################################
+
+import os
+import platform
+import socket
+import struct
+import subprocess
+import sys
+import time
+if sys.version_info[0] >= 3:
+    import configparser
+else:
+    import ConfigParser as configparser
+
+from calendar import timegm
+from ctypes import byref, c_bool, c_longlong, CDLL
+from _ctypes import FreeLibrary
+from datetime import datetime
+
+# Update below to right path corresponding to your machine, FTS version and OS used.
+FTS_INI_PATH = 'C:\\Program Files (x86)\\Frontline Test System II\\Frontline 15.12\\'
+FTS_DLL_PATH = 'C:\\Program Files (x86)\\Frontline Test System II\\Frontline 15.12\\Executables\\Core\\'
+
+iniName = 'liveimport.ini'
+if (platform.architecture()[0] == '32bit'):
+    dllName = 'LiveImportAPI.dll'
+else:
+    dllName = 'LiveImportAPI_x64.dll'
+
+launchFtsCmd = '\"' + FTS_DLL_PATH + 'fts.exe\"' + ' \"/ComProbe Protocol Analysis System=Generic\"' + ' \"/oemkey=Virtual\"'
+
+# Unix Epoch delta since 01/01/1970
+FILETIME_EPOCH_DELTA = 116444736000000000
+HUNDREDS_OF_NANOSECONDS = 10000000
+
+HOST = 'localhost'
+PORT = 8872
+SNOOP_ID = 16
+SNOOP_HDR = 24
+
+
+def get_file_time():
+    """
+    Obtain current time in file time format for display
+    """
+    date_time = datetime.now()
+    file_time = FILETIME_EPOCH_DELTA + (
+        timegm(date_time.timetuple()) * HUNDREDS_OF_NANOSECONDS)
+    file_time = file_time + (date_time.microsecond * 10)
+    return file_time
+
+
+def get_connection_string():
+    """
+    Read ConnectionString from liveimport.ini
+    """
+    config = configparser.ConfigParser()
+    config.read(FTS_INI_PATH + iniName)
+    try:
+        conn_str = config.get('General', 'ConnectionString')
+    except (configparser.NoSectionError, configparser.NoOptionError):
+        return None
+
+    return conn_str
+
+
+def get_configuration_string():
+    """
+    Read Configuration string from liveimport.ini
+    """
+    config_str = ''
+    config = configparser.ConfigParser()
+    config.read(FTS_INI_PATH + iniName)
+    try:
+        config_items = config.items('Configuration')
+    except (configparser.NoSectionError, configparser.NoOptionError):
+        return None
+
+    if config_items is not None:
+        for item in config_items:
+            key, value = item
+            config_str += ("%s=%s\n" % (key, value))
+        return config_str
+    else:
+        return None
+
+
+def check_live_import_connection(live_import):
+    """
+    Launch FTS app in Virtual Sniffing Mode
+    Check if FTS App is ready to start receiving the data.
+    If not, wait until 1 min and exit if FTS didn't start.
+    """
+    is_connection_running = c_bool()
+    count = 0
+
+    status = live_import.IsAppReady(byref(is_connection_running))
+    if (is_connection_running.value == True):
+        print("FTS is already launched, Start capture if not already started")
+        return True
+
+    print("Launching FTS Virtual Sniffing")
+    try:
+        ftsProcess = subprocess.Popen((launchFtsCmd), stdout=subprocess.PIPE)
+    except:
+        print("Error in Launching FTS.. exiting")
+        return False
+
+    while (is_connection_running.value == False and count < 12):
+        status = live_import.IsAppReady(byref(is_connection_running))
+        if (status < 0):
+            print("Live Import Internal Error %d" % (status))
+            return False
+        if (is_connection_running.value == False):
+            print("Waiting for 5 sec.. Open FTS Virtual Sniffing")
+            time.sleep(5)
+            count += 1
+    if (is_connection_running.value == True):
+        print("FTS is ready to receive the data, Start capture now")
+        return True
+    else:
+        print("FTS Virtual Sniffing didn't start until 1 min.. exiting")
+        return False
+
+
+def init_live_import(conn_str, config_str):
+    """
+    Load DLL and Initialize the LiveImport module for FTS.
+    """
+    success = c_bool()
+    try:
+        live_import = CDLL(FTS_DLL_PATH + dllName)
+    except:
+        return None
+
+    if live_import is None:
+        print("Error: Path to LiveImportAPI.dll is incorrect.. exiting")
+        return None
+
+    print(dllName + " loaded successfully")
+    result = live_import.InitializeLiveImport(
+        conn_str.encode('ascii', 'ignore'), config_str.encode(
+            'ascii', 'ignore'), byref(success))
+    if (result < 0):
+        print("Live Import Init failed")
+        return None
+    else:
+        print("Live Import Init success")
+        return live_import
+
+
+def release_live_import(live_import):
+    """
+    Cleanup and exit live import module.
+    """
+    if live_import is not None:
+        live_import.ReleaseLiveImport()
+        FreeLibrary(live_import._handle)
+
+
+def main():
+
+    print("Bluetooth Virtual Sniffing for Fluoride")
+    connection_str = get_connection_string()
+    if connection_str is None:
+        print("Error: path to liveimport.ini is incorrect.. exiting")
+        exit(0)
+
+    configuration_str = get_configuration_string()
+    if configuration_str is None:
+        print("Error: path to liveimport.ini is incorrect.. exiting")
+        exit(0)
+
+    live_import = init_live_import(connection_str, configuration_str)
+    if live_import is None:
+        print("Error: Path to LiveImportAPI.dll is incorrect.. exiting")
+        exit(0)
+
+    if (check_live_import_connection(live_import) == False):
+        release_live_import(live_import)
+        exit(0)
+
+    # Wait until the forward socket is ready
+    print("Waiting until adb is ready")
+    os.system('adb wait-for-device forward tcp:8872 tcp:8872')
+
+    btsnoop_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+    btsnoop_sock.connect((HOST, PORT))
+    snoop_id = btsnoop_sock.recv(SNOOP_ID)
+    if not snoop_id.startswith(b"btsnoop"):
+        print("Error: Snoop ID wasn't received.. exiting")
+        release_live_import(live_import)
+        exit(0)
+
+    while True:
+        try:
+            snoop_hdr = btsnoop_sock.recv(SNOOP_HDR)
+            if snoop_hdr is not None:
+                try:
+                    olen, ilen, flags = struct.unpack(">LLL", snoop_hdr[0:12])
+                except struct.error:
+                    print("Error: Invalid data", repr(snoop_hdr))
+                    continue
+
+                file_time = get_file_time()
+                timestamp = c_longlong(file_time)
+
+                snoop_data = b''
+                while (len(snoop_data) < olen):
+                    data_frag = btsnoop_sock.recv(olen - len(snoop_data))
+                    if data_frag is not None:
+                        snoop_data += data_frag
+
+                print("Bytes received %d Olen %d ilen %d flags %d" %
+                      (len(snoop_data), olen, ilen, flags))
+                packet_type = struct.unpack(">B", snoop_data[0:1])[0]
+                if packet_type == 1:
+                    drf = 1
+                    isend = 0
+                elif packet_type == 2:
+                    drf = 2
+                    if (flags & 0x01):
+                        isend = 1
+                    else:
+                        isend = 0
+                elif packet_type == 3:
+                    drf = 4
+                    if (flags & 0x01):
+                        isend = 1
+                    else:
+                        isend = 0
+                elif packet_type == 4:
+                    drf = 8
+                    isend = 1
+
+                result = live_import.SendFrame(olen - 1, olen - 1,
+                                               snoop_data[1:olen], drf, isend,
+                                               timestamp)
+                if (result < 0):
+                    print("Send frame failed")
+        except KeyboardInterrupt:
+            print("Cleanup and exit")
+            release_live_import(live_import)
+            btsnoop_sock.close()
+            exit(0)
+
+
+if __name__ == '__main__':
+    main()
diff --git a/tools/scripts/btsnooz.py b/tools/scripts/btsnooz.py
index 6e0e112..a29e718 100755
--- a/tools/scripts/btsnooz.py
+++ b/tools/scripts/btsnooz.py
@@ -21,14 +21,12 @@
 the btsnoop headers.
 """
 
-
 import base64
 import fileinput
 import struct
 import sys
 import zlib
 
-
 # Enumeration of the values the 'type' field can take in a btsnooz
 # header. These values come from the Bluetooth stack's internal
 # representation of packet types.
@@ -41,127 +39,135 @@
 
 
 def type_to_direction(type):
-  """
+    """
   Returns the inbound/outbound direction of a packet given its type.
   0 = sent packet
   1 = received packet
   """
-  if type in [TYPE_IN_EVT, TYPE_IN_ACL, TYPE_IN_SCO]:
-    return 1
-  return 0
+    if type in [TYPE_IN_EVT, TYPE_IN_ACL, TYPE_IN_SCO]:
+        return 1
+    return 0
 
 
 def type_to_hci(type):
-  """
+    """
   Returns the HCI type of a packet given its btsnooz type.
   """
-  if type == TYPE_OUT_CMD:
-    return '\x01'
-  if type == TYPE_IN_ACL or type == TYPE_OUT_ACL:
-    return '\x02'
-  if type == TYPE_IN_SCO or type == TYPE_OUT_SCO:
-    return '\x03'
-  if type == TYPE_IN_EVT:
-    return '\x04'
+    if type == TYPE_OUT_CMD:
+        return '\x01'
+    if type == TYPE_IN_ACL or type == TYPE_OUT_ACL:
+        return '\x02'
+    if type == TYPE_IN_SCO or type == TYPE_OUT_SCO:
+        return '\x03'
+    if type == TYPE_IN_EVT:
+        return '\x04'
 
 
 def decode_snooz(snooz):
-  """
+    """
   Decodes all known versions of a btsnooz file into a btsnoop file.
   """
-  version, last_timestamp_ms = struct.unpack_from('=bQ', snooz)
+    version, last_timestamp_ms = struct.unpack_from('=bQ', snooz)
 
-  if version != 1 and version != 2:
-    sys.stderr.write('Unsupported btsnooz version: %s\n' % version)
-    exit(1)
+    if version != 1 and version != 2:
+        sys.stderr.write('Unsupported btsnooz version: %s\n' % version)
+        exit(1)
 
-  # Oddly, the file header (9 bytes) is not compressed, but the rest is.
-  decompressed = zlib.decompress(snooz[9:])
+    # Oddly, the file header (9 bytes) is not compressed, but the rest is.
+    decompressed = zlib.decompress(snooz[9:])
 
-  sys.stdout.write('btsnoop\x00\x00\x00\x00\x01\x00\x00\x03\xea')
+    sys.stdout.write('btsnoop\x00\x00\x00\x00\x01\x00\x00\x03\xea')
 
-  if version == 1:
-    decode_snooz_v1(decompressed, last_timestamp_ms)
-  elif version == 2:
-    decode_snooz_v2(decompressed, last_timestamp_ms)
+    if version == 1:
+        decode_snooz_v1(decompressed, last_timestamp_ms)
+    elif version == 2:
+        decode_snooz_v2(decompressed, last_timestamp_ms)
 
 
 def decode_snooz_v1(decompressed, last_timestamp_ms):
-  """
+    """
   Decodes btsnooz v1 files into a btsnoop file.
   """
-  # An unfortunate consequence of the file format design: we have to do a
-  # pass of the entire file to determine the timestamp of the first packet.
-  first_timestamp_ms = last_timestamp_ms + 0x00dcddb30f2f8000
-  offset = 0
-  while offset < len(decompressed):
-    length, delta_time_ms, type = struct.unpack_from('=HIb', decompressed, offset)
-    offset += 7 + length - 1
-    first_timestamp_ms -= delta_time_ms
+    # An unfortunate consequence of the file format design: we have to do a
+    # pass of the entire file to determine the timestamp of the first packet.
+    first_timestamp_ms = last_timestamp_ms + 0x00dcddb30f2f8000
+    offset = 0
+    while offset < len(decompressed):
+        length, delta_time_ms, type = struct.unpack_from(
+            '=HIb', decompressed, offset)
+        offset += 7 + length - 1
+        first_timestamp_ms -= delta_time_ms
 
-  # Second pass does the actual writing out to stdout.
-  offset = 0
-  while offset < len(decompressed):
-    length, delta_time_ms, type = struct.unpack_from('=HIb', decompressed, offset)
-    first_timestamp_ms += delta_time_ms
-    offset += 7
-    sys.stdout.write(struct.pack('>II', length, length))
-    sys.stdout.write(struct.pack('>II', type_to_direction(type), 0))
-    sys.stdout.write(struct.pack('>II', (first_timestamp_ms >> 32), (first_timestamp_ms & 0xFFFFFFFF)))
-    sys.stdout.write(type_to_hci(type))
-    sys.stdout.write(decompressed[offset : offset + length - 1])
-    offset += length - 1
+    # Second pass does the actual writing out to stdout.
+    offset = 0
+    while offset < len(decompressed):
+        length, delta_time_ms, type = struct.unpack_from(
+            '=HIb', decompressed, offset)
+        first_timestamp_ms += delta_time_ms
+        offset += 7
+        sys.stdout.write(struct.pack('>II', length, length))
+        sys.stdout.write(struct.pack('>II', type_to_direction(type), 0))
+        sys.stdout.write(
+            struct.pack('>II', (first_timestamp_ms >> 32),
+                        (first_timestamp_ms & 0xFFFFFFFF)))
+        sys.stdout.write(type_to_hci(type))
+        sys.stdout.write(decompressed[offset:offset + length - 1])
+        offset += length - 1
 
 
 def decode_snooz_v2(decompressed, last_timestamp_ms):
-  """
+    """
   Decodes btsnooz v2 files into a btsnoop file.
   """
-  # An unfortunate consequence of the file format design: we have to do a
-  # pass of the entire file to determine the timestamp of the first packet.
-  first_timestamp_ms = last_timestamp_ms + 0x00dcddb30f2f8000
-  offset = 0
-  while offset < len(decompressed):
-    length, packet_length, delta_time_ms, snooz_type = struct.unpack_from('=HHIb', decompressed, offset)
-    offset += 9 + length - 1
-    first_timestamp_ms -= delta_time_ms
+    # An unfortunate consequence of the file format design: we have to do a
+    # pass of the entire file to determine the timestamp of the first packet.
+    first_timestamp_ms = last_timestamp_ms + 0x00dcddb30f2f8000
+    offset = 0
+    while offset < len(decompressed):
+        length, packet_length, delta_time_ms, snooz_type = struct.unpack_from(
+            '=HHIb', decompressed, offset)
+        offset += 9 + length - 1
+        first_timestamp_ms -= delta_time_ms
 
-  # Second pass does the actual writing out to stdout.
-  offset = 0
-  while offset < len(decompressed):
-    length, packet_length, delta_time_ms, snooz_type = struct.unpack_from('=HHIb', decompressed, offset)
-    first_timestamp_ms += delta_time_ms
-    offset += 9
-    sys.stdout.write(struct.pack('>II', packet_length, length))
-    sys.stdout.write(struct.pack('>II', type_to_direction(snooz_type), 0))
-    sys.stdout.write(struct.pack('>II', (first_timestamp_ms >> 32), (first_timestamp_ms & 0xFFFFFFFF)))
-    sys.stdout.write(type_to_hci(snooz_type))
-    sys.stdout.write(decompressed[offset : offset + length - 1])
-    offset += length - 1
+    # Second pass does the actual writing out to stdout.
+    offset = 0
+    while offset < len(decompressed):
+        length, packet_length, delta_time_ms, snooz_type = struct.unpack_from(
+            '=HHIb', decompressed, offset)
+        first_timestamp_ms += delta_time_ms
+        offset += 9
+        sys.stdout.write(struct.pack('>II', packet_length, length))
+        sys.stdout.write(struct.pack('>II', type_to_direction(snooz_type), 0))
+        sys.stdout.write(
+            struct.pack('>II', (first_timestamp_ms >> 32),
+                        (first_timestamp_ms & 0xFFFFFFFF)))
+        sys.stdout.write(type_to_hci(snooz_type))
+        sys.stdout.write(decompressed[offset:offset + length - 1])
+        offset += length - 1
 
 
 def main():
-  if len(sys.argv) > 2:
-    sys.stderr.write('Usage: %s [bugreport]\n' % sys.argv[0])
-    exit(1)
+    if len(sys.argv) > 2:
+        sys.stderr.write('Usage: %s [bugreport]\n' % sys.argv[0])
+        exit(1)
 
-  iterator = fileinput.input()
-  found = False
-  base64_string = ""
-  for line in iterator:
-    if found:
-      if line.find('--- END:BTSNOOP_LOG_SUMMARY') != -1:
-        decode_snooz(base64.standard_b64decode(base64_string))
-        sys.exit(0)
-      base64_string += line.strip()
+    iterator = fileinput.input()
+    found = False
+    base64_string = ""
+    for line in iterator:
+        if found:
+            if line.find('--- END:BTSNOOP_LOG_SUMMARY') != -1:
+                decode_snooz(base64.standard_b64decode(base64_string))
+                sys.exit(0)
+            base64_string += line.strip()
 
-    if line.find('--- BEGIN:BTSNOOP_LOG_SUMMARY') != -1:
-      found = True
+        if line.find('--- BEGIN:BTSNOOP_LOG_SUMMARY') != -1:
+            found = True
 
-  if not found:
-    sys.stderr.write('No btsnooz section found in bugreport.\n')
-    sys.exit(1)
+    if not found:
+        sys.stderr.write('No btsnooz section found in bugreport.\n')
+        sys.exit(1)
 
 
 if __name__ == '__main__':
-  main()
+    main()
diff --git a/tools/scripts/dump_hearingaid_audio.py b/tools/scripts/dump_hearingaid_audio.py
index 32d9a13..81cd78d 100755
--- a/tools/scripts/dump_hearingaid_audio.py
+++ b/tools/scripts/dump_hearingaid_audio.py
@@ -20,7 +20,10 @@
 # Generates a valid audio file which can be played using player like smplayer.
 #
 # Audio File Name Format:
-# [PEER_ADDRESS]-[START TIMESTAMP]-[AUDIO_TYPE]-[SAMPLE_RATE].[CODEC]
+# [PEER_ADDRESS]-[START_TIMESTAMP]-[AUDIO_TYPE]-[SAMPLE_RATE].[CODEC]
+#
+# Debug Infomation File Name Format:
+# debug_ver_[DEBUG_VERSION]-[PEER_ADDRESS]-[START_TIMESTAMP]-[AUDIO_TYPE]-[SAMPLE_RATE].txt
 #
 # Player:
 # smplayer
@@ -41,15 +44,31 @@
 CONNECTION_HANDLE = "CONNECTION_HANDLE"
 AUDIO_CONTROL_ATTR_HANDLE = "AUDIO_CONTROL_ATTR_HANDLE"
 START = "START"
-TIMESTAMP = "TIMESTAMP"
+TIMESTAMP_STR_FORMAT = "TIMESTAMP_STR_FORMAT"
+TIMESTAMP_TIME_FORMAT = "TIMESTAMP_TIME_FORMAT"
 CODEC = "CODEC"
 SAMPLE_RATE = "SAMPLE_RATE"
 AUDIO_TYPE = "AUDIO_TYPE"
+DEBUG_VERSION = "DEBUG_VERSION"
+DEBUG_DATA = "DEBUG_DATA"
 AUDIO_DATA_B = "AUDIO_DATA_B"
 
+# Debug packet header struct
+header_list_str = [
+    "Event Processed", "Number Packet Nacked By Slave",
+    "Number Packet Nacked By Master"
+]
+# Debug frame information structs
+data_list_str = [
+    "Event Number", "Overrun", "Underrun", "Skips", "Rendered Audio Frame",
+    "First PDU Option", "Second PDU Option", "Third PDU Option"
+]
+
 AUDIO_CONTROL_POINT_UUID = "f0d4de7e4a88476c9d9f1937b0996cc0"
 SEC_CONVERT = 1000000
 folder = None
+full_debug = False
+simple_debug = False
 
 force_audio_control_attr_handle = None
 default_audio_control_attr_handle = 0x0079
@@ -65,134 +84,183 @@
 #-----------------------------------------------------------------------
 
 
+def parse_acl_ha_debug_buffer(data, result):
+    """This function extracts HA debug buffer"""
+    if len(data) < 5:
+        return
+
+    version, data = unpack_data(data, 1)
+    update_audio_data(CONNECTION_HANDLE, result[CONNECTION_HANDLE],
+                      DEBUG_VERSION, str(version))
+
+    debug_str = result[TIMESTAMP_TIME_FORMAT]
+    for p in range(3):
+        byte_data, data = unpack_data(data, 1)
+        debug_str = debug_str + ", " + header_list_str[p] + "=" + str(
+            byte_data).rjust(3)
+
+    if full_debug:
+        debug_str = debug_str + "\n" + "|".join(data_list_str) + "\n"
+        while True:
+            if len(data) < 7:
+                break
+            base = 0
+            data_list_content = []
+            for counter in range(6):
+                p = base + counter
+                byte_data, data = unpack_data(data, 1)
+                if p == 1:
+                    data_list_content.append(
+                        str(byte_data & 0x03).rjust(len(data_list_str[p])))
+                    data_list_content.append(
+                        str((byte_data >> 2) & 0x03).rjust(
+                            len(data_list_str[p + 1])))
+                    data_list_content.append(
+                        str((byte_data >> 4) & 0x0f).rjust(
+                            len(data_list_str[p + 2])))
+                    base = 2
+                else:
+                    data_list_content.append(
+                        str(byte_data).rjust(len(data_list_str[p])))
+            debug_str = debug_str + "|".join(data_list_content) + "\n"
+
+    update_audio_data(CONNECTION_HANDLE, result[CONNECTION_HANDLE], DEBUG_DATA,
+                      debug_str)
+
+
 def parse_acl_ha_audio_data(data, result):
-  """This function extracts HA audio data."""
-  if len(data) < 2:
-    return
-  # Remove audio packet number
-  audio_data_b = data[1:]
-  update_audio_data(CONNECTION_HANDLE, result[CONNECTION_HANDLE],
-                    AUDIO_DATA_B, audio_data_b)
+    """This function extracts HA audio data."""
+    if len(data) < 2:
+        return
+    # Remove audio packet number
+    audio_data_b = data[1:]
+    update_audio_data(CONNECTION_HANDLE, result[CONNECTION_HANDLE],
+                      AUDIO_DATA_B, audio_data_b)
 
 
 def parse_acl_ha_audio_type(data, result):
-  """This function parses HA audio control cmd audio type."""
-  audio_type, data = unpack_data(data, 1)
-  if audio_type is None:
-    return
-  elif audio_type == 0x01:
-    audio_type = "Ringtone"
-  elif audio_type == 0x02:
-    audio_type = "Phonecall"
-  elif audio_type == 0x03:
-    audio_type = "Media"
-  else:
-    audio_type = "Unknown"
-  update_audio_data(CONNECTION_HANDLE, result[CONNECTION_HANDLE],
-                    AUDIO_TYPE, audio_type)
+    """This function parses HA audio control cmd audio type."""
+    audio_type, data = unpack_data(data, 1)
+    if audio_type is None:
+        return
+    elif audio_type == 0x01:
+        audio_type = "Ringtone"
+    elif audio_type == 0x02:
+        audio_type = "Phonecall"
+    elif audio_type == 0x03:
+        audio_type = "Media"
+    else:
+        audio_type = "Unknown"
+    update_audio_data(CONNECTION_HANDLE, result[CONNECTION_HANDLE], AUDIO_TYPE,
+                      audio_type)
 
 
 def parse_acl_ha_codec(data, result):
-  """This function parses HA audio control cmd codec and sample rate."""
-  codec, data = unpack_data(data, 1)
-  if codec == 0x01:
-    codec = "G722"
-    sample_rate = "16KHZ"
-  elif codec == 0x02:
-    codec = "G722"
-    sample_rate = "24KHZ"
-  else:
-    codec = "Unknown"
-    sample_rate = "Unknown"
-  update_audio_data(CONNECTION_HANDLE, result[CONNECTION_HANDLE],
-                    CODEC, codec)
-  update_audio_data(CONNECTION_HANDLE, result[CONNECTION_HANDLE],
-                    SAMPLE_RATE, sample_rate)
-  parse_acl_ha_audio_type(data, result)
+    """This function parses HA audio control cmd codec and sample rate."""
+    codec, data = unpack_data(data, 1)
+    if codec == 0x01:
+        codec = "G722"
+        sample_rate = "16KHZ"
+    elif codec == 0x02:
+        codec = "G722"
+        sample_rate = "24KHZ"
+    else:
+        codec = "Unknown"
+        sample_rate = "Unknown"
+    update_audio_data(CONNECTION_HANDLE, result[CONNECTION_HANDLE], CODEC,
+                      codec)
+    update_audio_data(CONNECTION_HANDLE, result[CONNECTION_HANDLE], SAMPLE_RATE,
+                      sample_rate)
+    parse_acl_ha_audio_type(data, result)
 
 
 def parse_acl_ha_audio_control_cmd(data, result):
-  """This function parses HA audio control cmd is start/stop."""
-  control_cmd, data = unpack_data(data, 1)
-  if control_cmd == 0x01:
-    update_audio_data(CONNECTION_HANDLE, result[CONNECTION_HANDLE],
-                      START, True)
-    update_audio_data(CONNECTION_HANDLE, result[CONNECTION_HANDLE],
-                      TIMESTAMP, result[TIMESTAMP])
-    parse_acl_ha_codec(data, result)
-  elif control_cmd == 0x02:
-    update_audio_data(CONNECTION_HANDLE, result[CONNECTION_HANDLE],
-                      START, False)
+    """This function parses HA audio control cmd is start/stop."""
+    control_cmd, data = unpack_data(data, 1)
+    if control_cmd == 0x01:
+        update_audio_data(CONNECTION_HANDLE, result[CONNECTION_HANDLE], START,
+                          True)
+        update_audio_data(CONNECTION_HANDLE, result[CONNECTION_HANDLE],
+                          TIMESTAMP_STR_FORMAT, result[TIMESTAMP_STR_FORMAT])
+        parse_acl_ha_codec(data, result)
+    elif control_cmd == 0x02:
+        update_audio_data(CONNECTION_HANDLE, result[CONNECTION_HANDLE], START,
+                          False)
 
 
 #-----------------------------------------------------------------------
 # Parse ACL Packet
 #-----------------------------------------------------------------------
 
+
 def parse_acl_att_long_uuid(data, result):
-  """This function parses ATT long UUID to get attr_handle."""
-  # len (1 byte) + start_attr_handle (2 bytes) + properties (1 byte) +
-  # attr_handle (2 bytes) + long_uuid (16 bytes) = 22 bytes
-  if len(data) < 22:
-    return
-  # skip unpack len, start_attr_handle, properties.
-  data = data[4:]
-  attr_handle, data = unpack_data(data, 2)
-  long_uuid_list = []
-  for p in range(0, 16):
-    long_uuid_list.append("{0:02x}".format(struct.unpack(">B", data[p])[0]))
-  long_uuid_list.reverse()
-  long_uuid = "".join(long_uuid_list)
-  # Check long_uuid is AUDIO_CONTROL_POINT uuid to get the attr_handle.
-  if long_uuid == AUDIO_CONTROL_POINT_UUID:
-    update_audio_data(CONNECTION_HANDLE, result[CONNECTION_HANDLE],
-                      AUDIO_CONTROL_ATTR_HANDLE, attr_handle)
+    """This function parses ATT long UUID to get attr_handle."""
+    # len (1 byte) + start_attr_handle (2 bytes) + properties (1 byte) +
+    # attr_handle (2 bytes) + long_uuid (16 bytes) = 22 bytes
+    if len(data) < 22:
+        return
+    # skip unpack len, start_attr_handle, properties.
+    data = data[4:]
+    attr_handle, data = unpack_data(data, 2)
+    long_uuid_list = []
+    for p in range(0, 16):
+        long_uuid_list.append("{0:02x}".format(struct.unpack(">B", data[p])[0]))
+    long_uuid_list.reverse()
+    long_uuid = "".join(long_uuid_list)
+    # Check long_uuid is AUDIO_CONTROL_POINT uuid to get the attr_handle.
+    if long_uuid == AUDIO_CONTROL_POINT_UUID:
+        update_audio_data(CONNECTION_HANDLE, result[CONNECTION_HANDLE],
+                          AUDIO_CONTROL_ATTR_HANDLE, attr_handle)
 
 
 def parse_acl_opcode(data, result):
-  """This function parses acl data opcode."""
-  # opcode (1 byte) = 1 bytes
-  if len(data) < 1:
-    return
-  opcode, data = unpack_data(data, 1)
-  # Check opcode is 0x12 (write request) and attr_handle is
-  # audio_control_attr_handle for check it is HA audio control cmd.
-  if result[IS_SENT] and opcode == 0x12:
-    if len(data) < 2:
-      return
-    attr_handle, data = unpack_data(data, 2)
-    if attr_handle == \
-        get_audio_control_attr_handle(result[CONNECTION_HANDLE]):
-          parse_acl_ha_audio_control_cmd(data, result)
-  # Check opcode is 0x09 (read response) to parse ATT long UUID.
-  elif not result[IS_SENT] and opcode == 0x09:
-    parse_acl_att_long_uuid(data, result)
+    """This function parses acl data opcode."""
+    # opcode (1 byte) = 1 bytes
+    if len(data) < 1:
+        return
+    opcode, data = unpack_data(data, 1)
+    # Check opcode is 0x12 (write request) and attr_handle is
+    # audio_control_attr_handle for check it is HA audio control cmd.
+    if result[IS_SENT] and opcode == 0x12:
+        if len(data) < 2:
+            return
+        attr_handle, data = unpack_data(data, 2)
+        if attr_handle == \
+            get_audio_control_attr_handle(result[CONNECTION_HANDLE]):
+            parse_acl_ha_audio_control_cmd(data, result)
+    # Check opcode is 0x09 (read response) to parse ATT long UUID.
+    elif not result[IS_SENT] and opcode == 0x09:
+        parse_acl_att_long_uuid(data, result)
 
 
 def parse_acl_handle(data, result):
-  """This function parses acl data handle."""
-  # connection_handle (2 bytes) + total_len (2 bytes) + pdu (2 bytes)
-  # + channel_id (2 bytes) = 8 bytes
-  if len(data) < 8:
-    return
-  connection_handle, data = unpack_data(data, 2)
-  connection_handle = connection_handle & 0x0FFF
-  # skip unpack total_len
-  data = data[2:]
-  pdu, data = unpack_data(data, 2)
-  channel_id, data = unpack_data(data, 2)
+    """This function parses acl data handle."""
+    # connection_handle (2 bytes) + total_len (2 bytes) + pdu (2 bytes)
+    # + channel_id (2 bytes) = 8 bytes
+    if len(data) < 8:
+        return
+    connection_handle, data = unpack_data(data, 2)
+    connection_handle = connection_handle & 0x0FFF
+    # skip unpack total_len
+    data = data[2:]
+    pdu, data = unpack_data(data, 2)
+    channel_id, data = unpack_data(data, 2)
 
-  # Check ATT packet or "Coc Data Packet" to get ATT information and audio
-  # data.
-  if connection_handle <= 0x0EFF:
-    if channel_id <= 0x003F:
-      result[CONNECTION_HANDLE] = connection_handle
-      parse_acl_opcode(data, result)
-    elif result[IS_SENT] and channel_id >= 0x0040 and channel_id <= 0x007F:
-      result[CONNECTION_HANDLE] = connection_handle
-      sdu, data = unpack_data(data, 2)
-      if pdu - 2 == sdu:
-        parse_acl_ha_audio_data(data, result)
+    # Check ATT packet or "Coc Data Packet" to get ATT information and audio
+    # data.
+    if connection_handle <= 0x0EFF:
+        if channel_id <= 0x003F:
+            result[CONNECTION_HANDLE] = connection_handle
+            parse_acl_opcode(data, result)
+        elif channel_id >= 0x0040 and channel_id <= 0x007F:
+            result[CONNECTION_HANDLE] = connection_handle
+            sdu, data = unpack_data(data, 2)
+            if pdu - 2 == sdu:
+                if result[IS_SENT]:
+                    parse_acl_ha_audio_data(data, result)
+                else:
+                    if simple_debug:
+                        parse_acl_ha_debug_buffer(data, result)
 
 
 #=======================================================================
@@ -201,48 +269,48 @@
 
 
 def parse_hci_evt_peer_address(data, result):
-  """This function parses peer address from hci event."""
-  peer_address_list = []
-  address_empty_list = ["00", "00", "00", "00", "00", "00"]
-  for n in range(0, 3):
-    if len(data) < 6:
-      return
-    for p in range(0, 6):
-      peer_address_list.append("{0:02x}".format(struct.unpack(">B",
-                                                              data[p])[0]))
-    # Check the address is empty or not.
-    if peer_address_list == address_empty_list:
-      del peer_address_list[:]
-      data = data[6:]
-    else:
-      break
-  peer_address_list.reverse()
-  peer_address = "_".join(peer_address_list)
-  update_audio_data("", "", PEER_ADDRESS, peer_address)
-  update_audio_data(PEER_ADDRESS, peer_address, CONNECTION_HANDLE,
-                    result[CONNECTION_HANDLE])
+    """This function parses peer address from hci event."""
+    peer_address_list = []
+    address_empty_list = ["00", "00", "00", "00", "00", "00"]
+    for n in range(0, 3):
+        if len(data) < 6:
+            return
+        for p in range(0, 6):
+            peer_address_list.append("{0:02x}".format(
+                struct.unpack(">B", data[p])[0]))
+        # Check the address is empty or not.
+        if peer_address_list == address_empty_list:
+            del peer_address_list[:]
+            data = data[6:]
+        else:
+            break
+    peer_address_list.reverse()
+    peer_address = "_".join(peer_address_list)
+    update_audio_data("", "", PEER_ADDRESS, peer_address)
+    update_audio_data(PEER_ADDRESS, peer_address, CONNECTION_HANDLE,
+                      result[CONNECTION_HANDLE])
 
 
 def parse_hci_evt_code(data, result):
-  """This function parses hci event content."""
-  # hci_evt (1 byte) + param_total_len (1 byte) + sub_event (1 byte)
-  # + status (1 byte) + connection_handle (2 bytes) + role (1 byte)
-  # + address_type (1 byte) = 8 bytes
-  if len(data) < 8:
-    return
-  hci_evt, data = unpack_data(data, 1)
-  # skip unpack param_total_len.
-  data = data[1:]
-  sub_event, data = unpack_data(data, 1)
-  status, data = unpack_data(data, 1)
-  connection_handle, data = unpack_data(data, 2)
-  connection_handle = connection_handle & 0x0FFF
-  # skip unpack role, address_type.
-  data = data[2:]
-  # We will directly check it is LE Enhanced Connection Complete or not
-  # for get Connection Handle and Address.
-  if not result[IS_SENT] and hci_evt == 0x3E and sub_event == 0x0A \
-      and status == 0x00 and connection_handle <= 0x0EFF:
+    """This function parses hci event content."""
+    # hci_evt (1 byte) + param_total_len (1 byte) + sub_event (1 byte)
+    # + status (1 byte) + connection_handle (2 bytes) + role (1 byte)
+    # + address_type (1 byte) = 8 bytes
+    if len(data) < 8:
+        return
+    hci_evt, data = unpack_data(data, 1)
+    # skip unpack param_total_len.
+    data = data[1:]
+    sub_event, data = unpack_data(data, 1)
+    status, data = unpack_data(data, 1)
+    connection_handle, data = unpack_data(data, 2)
+    connection_handle = connection_handle & 0x0FFF
+    # skip unpack role, address_type.
+    data = data[2:]
+    # We will directly check it is LE Enhanced Connection Complete or not
+    # for get Connection Handle and Address.
+    if not result[IS_SENT] and hci_evt == 0x3E and sub_event == 0x0A \
+        and status == 0x00 and connection_handle <= 0x0EFF:
         result[CONNECTION_HANDLE] = connection_handle
         parse_hci_evt_peer_address(data, result)
 
@@ -253,41 +321,42 @@
 
 
 def parse_packet_data(data, result):
-  """This function parses packet type."""
-  packet_type, data = unpack_data(data, 1)
-  if packet_type == 0x02:
-    # Try to check HearingAid audio control packet and data packet.
-    parse_acl_handle(data, result)
-  elif packet_type == 0x04:
-    # Try to check HearingAid connection successful packet.
-    parse_hci_evt_code(data, result)
+    """This function parses packet type."""
+    packet_type, data = unpack_data(data, 1)
+    if packet_type == 0x02:
+        # Try to check HearingAid audio control packet and data packet.
+        parse_acl_handle(data, result)
+    elif packet_type == 0x04:
+        # Try to check HearingAid connection successful packet.
+        parse_hci_evt_code(data, result)
 
 
 def parse_packet(btsnoop_file):
-  """This function parses packet len, timestamp."""
-  packet_result = {}
+    """This function parses packet len, timestamp."""
+    packet_result = {}
 
-  # ori_len (4 bytes) + include_len (4 bytes) + packet_flag (4 bytes)
-  # + drop (4 bytes) + timestamp (8 bytes) = 24 bytes
-  packet_header = btsnoop_file.read(24)
-  if len(packet_header) != 24:
-    return False
+    # ori_len (4 bytes) + include_len (4 bytes) + packet_flag (4 bytes)
+    # + drop (4 bytes) + timestamp (8 bytes) = 24 bytes
+    packet_header = btsnoop_file.read(24)
+    if len(packet_header) != 24:
+        return False
 
-  ori_len, include_len, packet_flag, drop, timestamp = \
-      struct.unpack(">IIIIq", packet_header)
+    ori_len, include_len, packet_flag, drop, timestamp = \
+        struct.unpack(">IIIIq", packet_header)
 
-  if ori_len == include_len:
-    packet_data = btsnoop_file.read(ori_len)
-    if len(packet_data) != ori_len:
-      return False
-    if packet_flag != 2 and drop == 0:
-      packet_result[IS_SENT] = (packet_flag == 0)
-      packet_result[TIMESTAMP] = convert_time_str(timestamp)
-      parse_packet_data(packet_data, packet_result)
-  else:
-    return False
+    if ori_len == include_len:
+        packet_data = btsnoop_file.read(ori_len)
+        if len(packet_data) != ori_len:
+            return False
+        if packet_flag != 2 and drop == 0:
+            packet_result[IS_SENT] = (packet_flag == 0)
+            packet_result[TIMESTAMP_STR_FORMAT], packet_result[
+                TIMESTAMP_TIME_FORMAT] = convert_time_str(timestamp)
+            parse_packet_data(packet_data, packet_result)
+    else:
+        return False
 
-  return True
+    return True
 
 
 #=======================================================================
@@ -296,31 +365,54 @@
 
 
 def dump_audio_data(data):
-  """This function dumps audio data into file."""
-  file_type = "." + data[CODEC]
-  file_name_list = []
-  file_name_list.append(data[PEER_ADDRESS])
-  file_name_list.append(data[TIMESTAMP])
-  file_name_list.append(data[AUDIO_TYPE])
-  file_name_list.append(data[SAMPLE_RATE])
-  if folder is not None:
-    if not os.path.exists(folder):
-      os.makedirs(folder)
-    file_name = os.path.join(folder, "-".join(file_name_list) + file_type)
-  else:
-    file_name = "-".join(file_name_list) + file_type
-  sys.stdout.write("Start to dump audio file : " + file_name + "\n")
-  if data.has_key(AUDIO_DATA_B):
-    with open(file_name, "wb+") as g722_file:
-      g722_file.write(data[AUDIO_DATA_B])
-      sys.stdout.write("Finished to dump Audio File: %s\n\n" % file_name)
-  else:
-    sys.stdout.write("Fail to dump Audio File: %s\n" % file_name)
-    sys.stdout.write("There isn't any Hearing Aid audio data.\n\n")
+    """This function dumps audio data into file."""
+    file_type = "." + data[CODEC]
+    file_name_list = []
+    file_name_list.append(data[PEER_ADDRESS])
+    file_name_list.append(data[TIMESTAMP_STR_FORMAT])
+    file_name_list.append(data[AUDIO_TYPE])
+    file_name_list.append(data[SAMPLE_RATE])
+    if folder is not None:
+        if not os.path.exists(folder):
+            os.makedirs(folder)
+        audio_file_name = os.path.join(folder,
+                                       "-".join(file_name_list) + file_type)
+        if data.has_key(DEBUG_VERSION):
+            file_prefix = "debug_ver_" + data[DEBUG_VERSION] + "-"
+            debug_file_name = os.path.join(
+                folder, file_prefix + "-".join(file_name_list) + ".txt")
+    else:
+        audio_file_name = "-".join(file_name_list) + file_type
+        if data.has_key(DEBUG_VERSION):
+            file_prefix = "debug_ver_" + data[DEBUG_VERSION] + "-"
+            debug_file_name = file_prefix + "-".join(file_name_list) + ".txt"
+
+    sys.stdout.write("Start to dump Audio File : %s\n" % audio_file_name)
+    if data.has_key(AUDIO_DATA_B):
+        with open(audio_file_name, "wb+") as audio_file:
+            audio_file.write(data[AUDIO_DATA_B])
+            sys.stdout.write(
+                "Finished to dump Audio File: %s\n\n" % audio_file_name)
+    else:
+        sys.stdout.write("Fail to dump Audio File: %s\n" % audio_file_name)
+        sys.stdout.write("There isn't any Hearing Aid audio data.\n\n")
+
+    if simple_debug:
+        sys.stdout.write(
+            "Start to dump audio %s Debug File\n" % audio_file_name)
+        if data.has_key(DEBUG_DATA):
+            with open(debug_file_name, "wb+") as debug_file:
+                debug_file.write(data[DEBUG_DATA])
+                sys.stdout.write(
+                    "Finished to dump Debug File: %s\n\n" % debug_file_name)
+        else:
+            sys.stdout.write(
+                "Fail to dump audio %s Debug File\n" % audio_file_name)
+            sys.stdout.write("There isn't any Hearing Aid debug data.\n\n")
 
 
 def update_audio_data(relate_key, relate_value, key, value):
-  """
+    """
   This function records the dump audio file related information.
   audio_data = {
     PEER_ADDRESS:{
@@ -328,10 +420,12 @@
       CONNECTION_HANDLE: CONNECTION_HANDLE,
       AUDIO_CONTROL_ATTR_HANDLE: AUDIO_CONTROL_ATTR_HANDLE,
       START: True or False,
-      TIMESTAMP: START_TIMESTAMP,
+      TIMESTAMP_STR_FORMAT: START_TIMESTAMP_STR_FORMAT,
       CODEC: CODEC,
       SAMPLE_RATE: SAMPLE_RATE,
       AUDIO_TYPE: AUDIO_TYPE,
+      DEBUG_VERSION: DEBUG_VERSION,
+      DEBUG_DATA: DEBUG_DATA,
       AUDIO_DATA_B: AUDIO_DATA_B
     },
     PEER_ADDRESS_2:{
@@ -339,50 +433,54 @@
       CONNECTION_HANDLE: CONNECTION_HANDLE,
       AUDIO_CONTROL_ATTR_HANDLE: AUDIO_CONTROL_ATTR_HANDLE,
       START: True or False,
-      TIMESTAMP: START_TIMESTAMP,
+      TIMESTAMP_STR_FORMAT: START_TIMESTAMP_STR_FORMAT,
       CODEC: CODEC,
       SAMPLE_RATE: SAMPLE_RATE,
       AUDIO_TYPE: AUDIO_TYPE,
+      DEBUG_VERSION: DEBUG_VERSION,
+      DEBUG_DATA: DEBUG_DATA,
       AUDIO_DATA_B: AUDIO_DATA_B
     }
   }
   """
-  if key == PEER_ADDRESS:
-    if audio_data.has_key(value):
-      # Dump audio data and clear previous data.
-      update_audio_data(key, value, START, False)
-      # Extra clear CONNECTION_HANDLE due to new connection create.
-      if audio_data[value].has_key(CONNECTION_HANDLE):
-        audio_data[value].pop(CONNECTION_HANDLE, "")
-    else:
-      device_audio_data = {key: value}
-      temp_audio_data = {value: device_audio_data}
-      audio_data.update(temp_audio_data)
-  else:
-    for i in audio_data:
-      if audio_data[i].has_key(relate_key) \
-          and audio_data[i][relate_key] == relate_value:
-            if key == START:
-              if audio_data[i].has_key(key) and audio_data[i][key]:
-                dump_audio_data(audio_data[i])
-              # Clear data except PEER_ADDRESS, CONNECTION_HANDLE and
-              # AUDIO_CONTROL_ATTR_HANDLE.
-              audio_data[i].pop(key, "")
-              audio_data[i].pop(TIMESTAMP, "")
-              audio_data[i].pop(CODEC, "")
-              audio_data[i].pop(SAMPLE_RATE, "")
-              audio_data[i].pop(AUDIO_TYPE, "")
-              audio_data[i].pop(AUDIO_DATA_B, "")
-            elif key == AUDIO_DATA_B:
-              if audio_data[i].has_key(START) and audio_data[i][START]:
-                if audio_data[i].has_key(AUDIO_DATA_B):
-                  ori_audio_data = audio_data[i].pop(AUDIO_DATA_B, "")
-                  value = ori_audio_data + value
-              else:
-                # Audio doesn't start, don't record.
-                return
+    if key == PEER_ADDRESS:
+        if audio_data.has_key(value):
+            # Dump audio data and clear previous data.
+            update_audio_data(key, value, START, False)
+            # Extra clear CONNECTION_HANDLE due to new connection create.
+            if audio_data[value].has_key(CONNECTION_HANDLE):
+                audio_data[value].pop(CONNECTION_HANDLE, "")
+        else:
             device_audio_data = {key: value}
-            audio_data[i].update(device_audio_data)
+            temp_audio_data = {value: device_audio_data}
+            audio_data.update(temp_audio_data)
+    else:
+        for i in audio_data:
+            if audio_data[i].has_key(relate_key) \
+                and audio_data[i][relate_key] == relate_value:
+                if key == START:
+                    if audio_data[i].has_key(key) and audio_data[i][key]:
+                        dump_audio_data(audio_data[i])
+                    # Clear data except PEER_ADDRESS, CONNECTION_HANDLE and
+                    # AUDIO_CONTROL_ATTR_HANDLE.
+                    audio_data[i].pop(key, "")
+                    audio_data[i].pop(TIMESTAMP_STR_FORMAT, "")
+                    audio_data[i].pop(CODEC, "")
+                    audio_data[i].pop(SAMPLE_RATE, "")
+                    audio_data[i].pop(AUDIO_TYPE, "")
+                    audio_data[i].pop(DEBUG_VERSION, "")
+                    audio_data[i].pop(DEBUG_DATA, "")
+                    audio_data[i].pop(AUDIO_DATA_B, "")
+                elif key == AUDIO_DATA_B or key == DEBUG_DATA:
+                    if audio_data[i].has_key(START) and audio_data[i][START]:
+                        if audio_data[i].has_key(key):
+                            ori_data = audio_data[i].pop(key, "")
+                            value = ori_data + value
+                    else:
+                        # Audio doesn't start, don't record.
+                        return
+                device_audio_data = {key: value}
+                audio_data[i].update(device_audio_data)
 
 
 #=======================================================================
@@ -391,141 +489,188 @@
 
 
 def get_audio_control_attr_handle(connection_handle):
-  """This function gets audio_control_attr_handle."""
-  # If force_audio_control_attr_handle is set, will use it first.
-  if force_audio_control_attr_handle is not None:
-    return force_audio_control_attr_handle
+    """This function gets audio_control_attr_handle."""
+    # If force_audio_control_attr_handle is set, will use it first.
+    if force_audio_control_attr_handle is not None:
+        return force_audio_control_attr_handle
 
-  # Try to check the audio_control_attr_handle is record into audio_data.
-  for i in audio_data:
-    if audio_data[i].has_key(CONNECTION_HANDLE) \
-        and audio_data[i][CONNECTION_HANDLE] == connection_handle:
-          if audio_data[i].has_key(AUDIO_CONTROL_ATTR_HANDLE):
-            return audio_data[i][AUDIO_CONTROL_ATTR_HANDLE]
+    # Try to check the audio_control_attr_handle is record into audio_data.
+    for i in audio_data:
+        if audio_data[i].has_key(CONNECTION_HANDLE) \
+            and audio_data[i][CONNECTION_HANDLE] == connection_handle:
+            if audio_data[i].has_key(AUDIO_CONTROL_ATTR_HANDLE):
+                return audio_data[i][AUDIO_CONTROL_ATTR_HANDLE]
 
-  # Return default attr_handle if audio_data doesn't record it.
-  return default_audio_control_attr_handle
+    # Return default attr_handle if audio_data doesn't record it.
+    return default_audio_control_attr_handle
 
 
 def unpack_data(data, byte):
-  """This function unpacks data."""
-  if byte == 1:
-    value = struct.unpack(">B", data[0])[0]
-  elif byte == 2:
-    value = struct.unpack(">H", data[1]+data[0])[0]
-  else:
-    value = ""
-  data = data[byte:]
-  return value, data
+    """This function unpacks data."""
+    if byte == 1:
+        value = struct.unpack(">B", data[0])[0]
+    elif byte == 2:
+        value = struct.unpack(">H", data[1] + data[0])[0]
+    else:
+        value = ""
+    data = data[byte:]
+    return value, data
 
 
 def convert_time_str(timestamp):
-  """This function converts time to string format."""
-  really_timestamp = float(timestamp) / SEC_CONVERT
-  local_timestamp = time.localtime(really_timestamp)
-  time_str = time.strftime("%m_%d__%H_%M_%S", local_timestamp)
-  dt = really_timestamp - long(really_timestamp)
-  ms_str = "{0:06}".format(int(round(dt * 1000000)))
-  full_time_str = time_str + "_" + ms_str
-  return full_time_str
+    """This function converts time to string format."""
+    really_timestamp = float(timestamp) / SEC_CONVERT
+    local_timestamp = time.localtime(really_timestamp)
+    dt = really_timestamp - long(really_timestamp)
+    ms_str = "{0:06}".format(int(round(dt * 1000000)))
+
+    str_format = time.strftime("%m_%d__%H_%M_%S", local_timestamp)
+    full_str_format = str_format + "_" + ms_str
+
+    time_format = time.strftime("%m-%d %H:%M:%S", local_timestamp)
+    full_time_format = time_format + "." + ms_str
+    return full_str_format, full_time_format
 
 
 def set_config():
-  """This function is for set config by flag and check the argv is correct."""
-  argv_parser = argparse.ArgumentParser(
-      description="Extracts Hearing Aid audio data from BTSNOOP.")
-  argv_parser.add_argument("BTSNOOP", help="BLUETOOTH BTSNOOP file.")
-  argv_parser.add_argument("-f", "--folder", help="select output folder.",
-                           dest="folder")
-  argv_parser.add_argument("-c1", "--connection-handle1",
-                           help="set a fake connection handle 1 to capture \
-                           audio dump.", dest="connection_handle1", type=int)
-  argv_parser.add_argument("-c2", "--connection-handle2",
-                           help="set a fake connection handle 2 to capture \
-                           audio dump.", dest="connection_handle2", type=int)
-  argv_parser.add_argument("-ns", "--no-start", help="No audio 'Start' cmd is \
+    """This function is for set config by flag and check the argv is correct."""
+    argv_parser = argparse.ArgumentParser(
+        description="Extracts Hearing Aid audio data from BTSNOOP.")
+    argv_parser.add_argument("BTSNOOP", help="BLUETOOTH BTSNOOP file.")
+    argv_parser.add_argument(
+        "-f", "--folder", help="select output folder.", dest="folder")
+    argv_parser.add_argument(
+        "-c1",
+        "--connection-handle1",
+        help="set a fake connection handle 1 to capture \
+                           audio dump.",
+        dest="connection_handle1",
+        type=int)
+    argv_parser.add_argument(
+        "-c2",
+        "--connection-handle2",
+        help="set a fake connection handle 2 to capture \
+                           audio dump.",
+        dest="connection_handle2",
+        type=int)
+    argv_parser.add_argument(
+        "-ns",
+        "--no-start",
+        help="No audio 'Start' cmd is \
                            needed before extracting audio data.",
-                           dest="no_start", default="False")
-  argv_parser.add_argument("-dc", "--default-codec", help="set a default \
-                           codec.", dest="codec", default="G722")
-  argv_parser.add_argument("-a", "--attr-handle",
-                           help="force to select audio control attr handle.",
-                           dest="audio_control_attr_handle", type=int)
-  arg = argv_parser.parse_args()
+        dest="no_start",
+        default="False")
+    argv_parser.add_argument(
+        "-dc",
+        "--default-codec",
+        help="set a default \
+                           codec.",
+        dest="codec",
+        default="G722")
+    argv_parser.add_argument(
+        "-a",
+        "--attr-handle",
+        help="force to select audio control attr handle.",
+        dest="audio_control_attr_handle",
+        type=int)
+    argv_parser.add_argument(
+        "-d",
+        "--debug",
+        help="dump full debug buffer content.",
+        dest="full_debug",
+        default="False")
+    argv_parser.add_argument(
+        "-sd",
+        "--simple-debug",
+        help="dump debug buffer header content.",
+        dest="simple_debug",
+        default="False")
+    arg = argv_parser.parse_args()
 
-  if arg.folder is not None:
-    global folder
-    folder = arg.folder
+    if arg.folder is not None:
+        global folder
+        folder = arg.folder
 
-  if arg.connection_handle1 is not None and arg.connection_handle2 is not None \
-      and arg.connection_handle1 == arg.connection_handle2:
+    if arg.connection_handle1 is not None and arg.connection_handle2 is not None \
+        and arg.connection_handle1 == arg.connection_handle2:
         argv_parser.error("connection_handle1 can't be same with \
                           connection_handle2")
         exit(1)
 
-  if not (arg.no_start.lower() == "true" or arg.no_start.lower() == "false"):
-    argv_parser.error("-ns/--no-start arg is invalid, it should be true/false.")
-    exit(1)
+    if not (arg.no_start.lower() == "true" or arg.no_start.lower() == "false"):
+        argv_parser.error(
+            "-ns/--no-start arg is invalid, it should be true/false.")
+        exit(1)
 
-  if arg.connection_handle1 is not None:
-    fake_name = "ConnectionHandle" + str(arg.connection_handle1)
-    update_audio_data("", "", PEER_ADDRESS, fake_name)
-    update_audio_data(PEER_ADDRESS, fake_name, CONNECTION_HANDLE,
-                      arg.connection_handle1)
-    if arg.no_start.lower() == "true":
-      update_audio_data(PEER_ADDRESS, fake_name, START, True)
-      update_audio_data(PEER_ADDRESS, fake_name, TIMESTAMP, "Unknown")
-      update_audio_data(PEER_ADDRESS, fake_name, CODEC, arg.codec)
-      update_audio_data(PEER_ADDRESS, fake_name, SAMPLE_RATE, "Unknown")
-      update_audio_data(PEER_ADDRESS, fake_name, AUDIO_TYPE, "Unknown")
+    if arg.connection_handle1 is not None:
+        fake_name = "ConnectionHandle" + str(arg.connection_handle1)
+        update_audio_data("", "", PEER_ADDRESS, fake_name)
+        update_audio_data(PEER_ADDRESS, fake_name, CONNECTION_HANDLE,
+                          arg.connection_handle1)
+        if arg.no_start.lower() == "true":
+            update_audio_data(PEER_ADDRESS, fake_name, START, True)
+            update_audio_data(PEER_ADDRESS, fake_name, TIMESTAMP_STR_FORMAT,
+                              "Unknown")
+            update_audio_data(PEER_ADDRESS, fake_name, CODEC, arg.codec)
+            update_audio_data(PEER_ADDRESS, fake_name, SAMPLE_RATE, "Unknown")
+            update_audio_data(PEER_ADDRESS, fake_name, AUDIO_TYPE, "Unknown")
 
-  if arg.connection_handle2 is not None:
-    fake_name = "ConnectionHandle" + str(arg.connection_handle2)
-    update_audio_data("", "", PEER_ADDRESS, fake_name)
-    update_audio_data(PEER_ADDRESS, fake_name, CONNECTION_HANDLE,
-                      arg.connection_handle2)
-    if arg.no_start.lower() == "true":
-      update_audio_data(PEER_ADDRESS, fake_name, START, True)
-      update_audio_data(PEER_ADDRESS, fake_name, TIMESTAMP, "Unknown")
-      update_audio_data(PEER_ADDRESS, fake_name, CODEC, arg.codec)
-      update_audio_data(PEER_ADDRESS, fake_name, SAMPLE_RATE, "Unknown")
-      update_audio_data(PEER_ADDRESS, fake_name, AUDIO_TYPE, "Unknown")
+    if arg.connection_handle2 is not None:
+        fake_name = "ConnectionHandle" + str(arg.connection_handle2)
+        update_audio_data("", "", PEER_ADDRESS, fake_name)
+        update_audio_data(PEER_ADDRESS, fake_name, CONNECTION_HANDLE,
+                          arg.connection_handle2)
+        if arg.no_start.lower() == "true":
+            update_audio_data(PEER_ADDRESS, fake_name, START, True)
+            update_audio_data(PEER_ADDRESS, fake_name, TIMESTAMP_STR_FORMAT,
+                              "Unknown")
+            update_audio_data(PEER_ADDRESS, fake_name, CODEC, arg.codec)
+            update_audio_data(PEER_ADDRESS, fake_name, SAMPLE_RATE, "Unknown")
+            update_audio_data(PEER_ADDRESS, fake_name, AUDIO_TYPE, "Unknown")
 
-  if arg.audio_control_attr_handle is not None:
-    global force_audio_control_attr_handle
-    force_audio_control_attr_handle = arg.audio_control_attr_handle
+    if arg.audio_control_attr_handle is not None:
+        global force_audio_control_attr_handle
+        force_audio_control_attr_handle = arg.audio_control_attr_handle
 
-  if os.path.isfile(arg.BTSNOOP):
-    return arg.BTSNOOP
-  else:
-    argv_parser.error("BTSNOOP file not found: %s" % arg.BTSNOOP)
-    exit(1)
+    global full_debug
+    global simple_debug
+    if arg.full_debug.lower() == "true":
+        full_debug = True
+        simple_debug = True
+    elif arg.simple_debug.lower() == "true":
+        simple_debug = True
+
+    if os.path.isfile(arg.BTSNOOP):
+        return arg.BTSNOOP
+    else:
+        argv_parser.error("BTSNOOP file not found: %s" % arg.BTSNOOP)
+        exit(1)
 
 
 def main():
-  btsnoop_file_name = set_config()
+    btsnoop_file_name = set_config()
 
-  with open(btsnoop_file_name, "rb") as btsnoop_file:
-    identification = btsnoop_file.read(8)
-    if identification != "btsnoop\0":
-      sys.stderr.write(
-          "Check identification fail. It is not correct btsnoop file.")
-      exit(1)
+    with open(btsnoop_file_name, "rb") as btsnoop_file:
+        identification = btsnoop_file.read(8)
+        if identification != "btsnoop\0":
+            sys.stderr.write(
+                "Check identification fail. It is not correct btsnoop file.")
+            exit(1)
 
-    ver, data_link = struct.unpack(">II", btsnoop_file.read(4 + 4))
-    if (ver != 1) or (data_link != 1002):
-      sys.stderr.write(
-          "Check ver or dataLink fail. It is not correct btsnoop file.")
-      exit(1)
+        ver, data_link = struct.unpack(">II", btsnoop_file.read(4 + 4))
+        if (ver != 1) or (data_link != 1002):
+            sys.stderr.write(
+                "Check ver or dataLink fail. It is not correct btsnoop file.")
+            exit(1)
 
-    while True:
-      if not parse_packet(btsnoop_file):
-        break
+        while True:
+            if not parse_packet(btsnoop_file):
+                break
 
-    for i in audio_data:
-      if audio_data[i].get(START, False):
-        dump_audio_data(audio_data[i])
+        for i in audio_data:
+            if audio_data[i].get(START, False):
+                dump_audio_data(audio_data[i])
 
 
 if __name__ == "__main__":
-  main()
+    main()
diff --git a/tools/scripts/dump_metrics_ascii.py b/tools/scripts/dump_metrics_ascii.py
index c24d299..20d4132 100755
--- a/tools/scripts/dump_metrics_ascii.py
+++ b/tools/scripts/dump_metrics_ascii.py
@@ -23,6 +23,7 @@
 import google.protobuf.text_format as text_format
 from importlib import import_module
 
+
 def compile_proto(proto_path, output_dir):
     """Invoke Protocol Compiler to generate python from given source .proto."""
     # Find compiler path
@@ -48,15 +49,15 @@
     if not os.path.exists(output_dir):
         os.mkdirs(output_dir)
     elif not os.path.isdir(output_dir):
-        logging.error("Output path is not a valid directory: %s" %
-                      (output_dir))
+        logging.error("Output path is not a valid directory: %s" % (output_dir))
         return None
     input_dir = os.path.dirname(proto_path)
     output_filename = os.path.basename(proto_path).replace('.proto', '_pb2.py')
     output_path = os.path.join(output_dir, output_filename)
     protoc_command = [
-        protoc, '-I=%s' % (input_dir), '--python_out=%s' % (output_dir),
-        proto_path
+        protoc,
+        '-I=%s' % (input_dir),
+        '--python_out=%s' % (output_dir), proto_path
     ]
     if subprocess.call(protoc_command, stderr=subprocess.STDOUT) != 0:
         logging.error("Fail to compile proto")
@@ -81,8 +82,8 @@
     try:
         output_module = import_module(output_module_name)
     except ImportError:
-        logging.error("Cannot import generated py-proto %s" %
-                      (output_module_name))
+        logging.error(
+            "Cannot import generated py-proto %s" % (output_module_name))
     return output_module
 
 
@@ -94,26 +95,31 @@
     """
     return text_format.MessageToString(binary_proto_msg)
 
+
 def dump_metrics():
     os.system('adb wait-for-device')
-    p = subprocess.Popen("adb shell dumpsys bluetooth_manager --proto-bin",
-        shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
+    p = subprocess.Popen(
+        "adb shell dumpsys bluetooth_manager --proto-bin",
+        shell=True,
+        stdout=subprocess.PIPE,
+        stderr=subprocess.PIPE,
         stdin=subprocess.PIPE)
     return p.communicate()
 
+
 def get_bluetooth_metrics(proto_native_str_64, bluetooth_proto_module):
     bluetooth_log = bluetooth_proto_module.BluetoothLog()
     proto_native_str = base64.b64decode(proto_native_str_64)
     bluetooth_log.MergeFromString(proto_native_str)
     return bluetooth_log
 
+
 def main():
     root = logging.getLogger()
     root.setLevel(logging.DEBUG)
     log_handler = logging.StreamHandler(sys.stderr)
     log_handler.setLevel(logging.DEBUG)
-    formatter = logging.Formatter(
-        "%(asctime)s %(levelname)s %(message)s")
+    formatter = logging.Formatter("%(asctime)s %(levelname)s %(message)s")
     log_handler.setFormatter(formatter)
     root.addHandler(log_handler)
     if len(sys.argv) < 2:
@@ -125,7 +131,7 @@
         logging.info("Requires Protobuf compiler, protoc, version >=3.0.0")
         sys.exit(0)
     bluetooth_proto_module = compile_import_proto(tempfile.gettempdir(),
-        sys.argv[1])
+                                                  sys.argv[1])
     if not bluetooth_proto_module:
         logging.error("Cannot compile " + sys.argv[1])
         sys.exit(1)
@@ -136,5 +142,6 @@
     bluetooth_log_ascii = parse_proto_to_ascii(bluetooth_log)
     print(bluetooth_log_ascii)
 
+
 if __name__ == "__main__":
     main()
diff --git a/tools/scripts/yapf_checker.py b/tools/scripts/yapf_checker.py
new file mode 100755
index 0000000..86f1f39
--- /dev/null
+++ b/tools/scripts/yapf_checker.py
@@ -0,0 +1,89 @@
+#!/usr/bin/env python3
+#
+#   Copyright 2019 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import logging
+import os
+import subprocess
+import sys
+
+PYTHONPATH_KEY = 'PYTHONPATH'
+COMMIT_ID_ENV_KEY = 'PREUPLOAD_COMMIT'
+ANDROID_BUILD_TOP_KEY = 'ANDROID_BUILD_TOP'
+DEFAULT_YAPF_DIR = 'external/yapf'
+GIT_COMMAND = ['git', 'diff-tree', '--no-commit-id', '--name-only']
+
+
+def main():
+    """
+    A Python commit formatter using YAPF
+
+    Caveat: if you modify a file, the entire file will be formatted, instead of
+            the diff
+    :return:
+    """
+    if COMMIT_ID_ENV_KEY not in os.environ:
+        logging.error('Missing PREUPLOAD_COMMIT in environment.')
+        exit(1)
+
+    if ANDROID_BUILD_TOP_KEY not in os.environ:
+        logging.error('Missing ANDROID_BUILD_TOP in environment.')
+        exit(1)
+
+    # Gather changed Python files
+    commit_id = os.environ[COMMIT_ID_ENV_KEY]
+    full_git_command = GIT_COMMAND + ['-r', commit_id]
+    files = subprocess.check_output(full_git_command).decode(
+        'utf-8').splitlines()
+    full_files = [os.path.abspath(f) for f in files if f.endswith('.py')]
+    if not full_files:
+        return
+
+    # Find yapf in Android code tree
+    yapf_dir = os.path.join(os.environ[ANDROID_BUILD_TOP_KEY], DEFAULT_YAPF_DIR)
+    yapf_binary = os.path.join(yapf_dir, 'yapf')
+
+    # Run YAPF
+    full_yapf_command = [
+        "%s=$%s:%s" % (PYTHONPATH_KEY, PYTHONPATH_KEY, yapf_dir), 'python3',
+        yapf_binary, '-d', '-p'
+    ] + full_files
+    environment = os.environ.copy()
+    environment[PYTHONPATH_KEY] = environment[PYTHONPATH_KEY] + ":" + yapf_dir
+    result = subprocess.run(
+        full_yapf_command[1:],
+        env=environment,
+        stderr=subprocess.STDOUT,
+        stdout=subprocess.PIPE)
+
+    if result.returncode != 0 or result.stdout:
+        logging.error(result.stdout.decode('utf-8').strip())
+        logging.error('INVALID FORMATTING, return code %d', result.returncode)
+        logging.error('To re-run the format command:\n\n'
+                      '    %s\n' % ' '.join(full_yapf_command))
+        yapf_inplace_format = ' '.join([
+            "%s=$%s:%s" % (PYTHONPATH_KEY, PYTHONPATH_KEY,
+                           yapf_dir), 'python3', yapf_binary, '-p', '-i'
+        ] + full_files)
+        logging.error('If this is a legitimate format error, please run:\n\n'
+                      '    %s\n' % yapf_inplace_format)
+        logging.error(
+            'CAVEAT: Currently, this format the entire Python file if you modify even part of it'
+        )
+        exit(1)
+
+
+if __name__ == '__main__':
+    main()
diff --git a/types/Android.bp b/types/Android.bp
index 444b6ff..bdd319d 100644
--- a/types/Android.bp
+++ b/types/Android.bp
@@ -4,6 +4,16 @@
     export_include_dirs: ["./"],
     vendor_available: true,
     host_supported: true,
+    // TODO(b/153609531): remove when no longer needed.
+    native_bridge_supported: true,
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.media",
+        "com.android.media.swcodec",
+        "com.android.bluetooth.updatable"
+    ],
+    // As part of updatable mainline modules(media, swcodec), it should support at least 29(Q)
+    min_sdk_version: "29",
 }
 
 cc_library_static {
diff --git a/types/class_of_device.h b/types/class_of_device.h
index 8c2ab37..4c37ebf 100644
--- a/types/class_of_device.h
+++ b/types/class_of_device.h
@@ -20,6 +20,9 @@
 
 #include <string>
 
+namespace bluetooth {
+namespace types {
+
 /** Bluetooth Class of Device */
 class ClassOfDevice final {
  public:
@@ -52,3 +55,8 @@
   os << c.ToString();
   return os;
 }
+
+}  // namespace types
+}  // namespace bluetooth
+
+using ::bluetooth::types::ClassOfDevice;  // TODO, remove
\ No newline at end of file
diff --git a/types/raw_address.h b/types/raw_address.h
index 49443a8..b0035c9 100644
--- a/types/raw_address.h
+++ b/types/raw_address.h
@@ -18,6 +18,7 @@
 
 #pragma once
 
+#include <cstring>
 #include <string>
 
 /** Bluetooth Address */
@@ -64,3 +65,14 @@
   os << a.ToString();
   return os;
 }
+
+template <>
+struct std::hash<RawAddress> {
+  std::size_t operator()(const RawAddress& val) const {
+    static_assert(sizeof(uint64_t) >= RawAddress::kLength);
+    uint64_t int_addr = 0;
+    memcpy(reinterpret_cast<uint8_t*>(&int_addr), val.address,
+           RawAddress::kLength);
+    return std::hash<uint64_t>{}(int_addr);
+  }
+};
diff --git a/udrv/Android.bp b/udrv/Android.bp
index 9bf1d83..4605c53 100644
--- a/udrv/Android.bp
+++ b/udrv/Android.bp
@@ -5,15 +5,15 @@
         "ulinux/uipc.cc",
     ],
     include_dirs: [
-      "system/bt",
-      "system/bt/internal_include",
-      "system/bt/utils/include",
-      "system/bt/stack/include",
+        "system/bt",
+        "system/bt/internal_include",
+        "system/bt/utils/include",
+        "system/bt/stack/include",
     ],
     local_include_dirs: [
-      "include",
+        "include",
     ],
     shared_libs: [
-      "liblog",
+        "liblog",
     ],
 }
diff --git a/vendor_libs/linux/interface/Android.bp b/vendor_libs/linux/interface/Android.bp
index 8d6caa4..e7d161e 100644
--- a/vendor_libs/linux/interface/Android.bp
+++ b/vendor_libs/linux/interface/Android.bp
@@ -14,7 +14,7 @@
 // limitations under the License.
 
 cc_binary {
-    name: "android.hardware.bluetooth@1.0-service.btlinux",
+    name: "android.hardware.bluetooth@1.1-service.btlinux",
     proprietary: true,
     relative_install_path: "hw",
     srcs: [
@@ -22,22 +22,23 @@
         "h4_protocol.cc",
         "bluetooth_hci.cc",
         "async_fd_watcher.cc",
-        "service.cc"
+        "service.cc",
     ],
-    cflags: ["-Wall", "-Werror"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
     header_libs: ["libbluetooth_headers"],
     shared_libs: [
-        "android.hardware.bluetooth@1.0",
+        "android.hardware.bluetooth@1.1",
         "libbase",
         "libcutils",
         "libhidlbase",
-        "libhidltransport",
         "liblog",
         "libutils",
     ],
     conlyflags: [
         "-std=c99",
     ],
-    init_rc: ["android.hardware.bluetooth@1.0-service.btlinux.rc"],
+    init_rc: ["android.hardware.bluetooth@1.1-service.btlinux.rc"],
 }
-
diff --git a/vendor_libs/linux/interface/android.hardware.bluetooth@1.0-service.btlinux.rc b/vendor_libs/linux/interface/android.hardware.bluetooth@1.0-service.btlinux.rc
deleted file mode 100644
index 36fbc2c..0000000
--- a/vendor_libs/linux/interface/android.hardware.bluetooth@1.0-service.btlinux.rc
+++ /dev/null
@@ -1,5 +0,0 @@
-service btlinux-1.0 /vendor/bin/hw/android.hardware.bluetooth@1.0-service.btlinux
-    class hal
-    user bluetooth
-    group bluetooth net_admin net_bt_admin
-    capabilities NET_ADMIN
diff --git a/vendor_libs/linux/interface/android.hardware.bluetooth@1.1-service.btlinux.rc b/vendor_libs/linux/interface/android.hardware.bluetooth@1.1-service.btlinux.rc
new file mode 100644
index 0000000..cb03de2
--- /dev/null
+++ b/vendor_libs/linux/interface/android.hardware.bluetooth@1.1-service.btlinux.rc
@@ -0,0 +1,5 @@
+service btlinux-1.1 /vendor/bin/hw/android.hardware.bluetooth@1.1-service.btlinux
+    class hal
+    user bluetooth
+    group bluetooth net_admin net_bt_admin
+    capabilities NET_ADMIN
diff --git a/vendor_libs/linux/interface/bluetooth_hci.cc b/vendor_libs/linux/interface/bluetooth_hci.cc
index 8d7e3f7..bd987d8 100644
--- a/vendor_libs/linux/interface/bluetooth_hci.cc
+++ b/vendor_libs/linux/interface/bluetooth_hci.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#define LOG_TAG "android.hardware.bluetooth@1.0-btlinux"
+#define LOG_TAG "android.hardware.bluetooth@1.1-btlinux"
 #include <errno.h>
 #include <fcntl.h>
 #include <poll.h>
@@ -66,7 +66,7 @@
 namespace android {
 namespace hardware {
 namespace bluetooth {
-namespace V1_0 {
+namespace V1_1 {
 namespace btlinux {
 
 int BluetoothHci::openBtHci() {
@@ -245,9 +245,9 @@
  public:
   BluetoothDeathRecipient(const sp<IBluetoothHci> hci) : mHci(hci) {}
 
-  virtual void serviceDied(
+  void serviceDied(
       uint64_t /*cookie*/,
-      const wp<::android::hidl::base::V1_0::IBase>& /*who*/) {
+      const wp<::android::hidl::base::V1_0::IBase>& /*who*/) override {
     ALOGE("BluetoothDeathRecipient::serviceDied - Bluetooth service died");
     has_died_ = true;
     mHci->close();
@@ -264,7 +264,18 @@
     : death_recipient_(new BluetoothDeathRecipient(this)) {}
 
 Return<void> BluetoothHci::initialize(
-    const ::android::sp<IBluetoothHciCallbacks>& cb) {
+    const ::android::sp<V1_0::IBluetoothHciCallbacks>& cb) {
+  return initialize_impl(cb, nullptr);
+}
+
+Return<void> BluetoothHci::initialize_1_1(
+    const ::android::sp<V1_1::IBluetoothHciCallbacks>& cb) {
+  return initialize_impl(cb, cb);
+}
+
+Return<void> BluetoothHci::initialize_impl(
+    const ::android::sp<V1_0::IBluetoothHciCallbacks>& cb,
+    const ::android::sp<V1_1::IBluetoothHciCallbacks>& cb_1_1) {
   ALOGI("BluetoothHci::initialize()");
   if (cb == nullptr) {
     ALOGE("cb == nullptr! -> Unable to call initializationComplete(ERR)");
@@ -275,7 +286,7 @@
   cb->linkToDeath(death_recipient_, 0);
   int hci_fd = openBtHci();
   auto hidl_status = cb->initializationComplete(
-          hci_fd > 0 ? Status::SUCCESS : Status::INITIALIZATION_ERROR);
+      hci_fd > 0 ? V1_0::Status::SUCCESS : V1_0::Status::INITIALIZATION_ERROR);
   if (!hidl_status.isOk()) {
       ALOGE("VendorInterface -> Unable to call initializationComplete(ERR)");
   }
@@ -283,7 +294,10 @@
       hci_fd,
       [cb](const hidl_vec<uint8_t>& packet) { cb->hciEventReceived(packet); },
       [cb](const hidl_vec<uint8_t>& packet) { cb->aclDataReceived(packet); },
-      [cb](const hidl_vec<uint8_t>& packet) { cb->scoDataReceived(packet); });
+      [cb](const hidl_vec<uint8_t>& packet) { cb->scoDataReceived(packet); },
+      [cb_1_1](const hidl_vec<uint8_t>& packet) {
+        cb_1_1->isoDataReceived(packet);
+      });
 
   fd_watcher_.WatchFdForNonBlockingReads(
           hci_fd, [h4_hci](int fd) { h4_hci->OnDataReady(fd); });
@@ -327,6 +341,11 @@
   return Void();
 }
 
+Return<void> BluetoothHci::sendIsoData(const hidl_vec<uint8_t>& data) {
+  sendDataToController(HCI_DATA_TYPE_ISO, data);
+  return Void();
+}
+
 void BluetoothHci::sendDataToController(const uint8_t type,
                                         const hidl_vec<uint8_t>& data) {
   hci_handle_->Send(type, data.data(), data.size());
@@ -337,7 +356,7 @@
 }
 
 }  // namespace btlinux
-}  // namespace V1_0
+}  // namespace V1_1
 }  // namespace bluetooth
 }  // namespace hardware
 }  // namespace android
diff --git a/vendor_libs/linux/interface/bluetooth_hci.h b/vendor_libs/linux/interface/bluetooth_hci.h
index 5dc1c0a..095c10c 100644
--- a/vendor_libs/linux/interface/bluetooth_hci.h
+++ b/vendor_libs/linux/interface/bluetooth_hci.h
@@ -14,10 +14,11 @@
 // limitations under the License.
 //
 
-#ifndef HIDL_GENERATED_android_hardware_bluetooth_V1_0_BluetoothHci_H_
-#define HIDL_GENERATED_android_hardware_bluetooth_V1_0_BluetoothHci_H_
+#ifndef HIDL_GENERATED_android_hardware_bluetooth_V1_1_BluetoothHci_H_
+#define HIDL_GENERATED_android_hardware_bluetooth_V1_1_BluetoothHci_H_
 
 #include <android/hardware/bluetooth/1.0/IBluetoothHci.h>
+#include <android/hardware/bluetooth/1.1/IBluetoothHci.h>
 
 #include <hidl/MQDescriptor.h>
 
@@ -28,7 +29,7 @@
 namespace android {
 namespace hardware {
 namespace bluetooth {
-namespace V1_0 {
+namespace V1_1 {
 namespace btlinux {
 
 using ::android::hardware::Return;
@@ -40,13 +41,20 @@
  public:
   BluetoothHci();
   Return<void> initialize(
-      const ::android::sp<IBluetoothHciCallbacks>& cb) override;
+      const ::android::sp<V1_0::IBluetoothHciCallbacks>& cb) override;
+  Return<void> initialize_1_1(
+      const ::android::sp<V1_1::IBluetoothHciCallbacks>& cb) override;
   Return<void> sendHciCommand(const hidl_vec<uint8_t>& packet) override;
   Return<void> sendAclData(const hidl_vec<uint8_t>& data) override;
   Return<void> sendScoData(const hidl_vec<uint8_t>& data) override;
+  Return<void> sendIsoData(const hidl_vec<uint8_t>& data) override;
   Return<void> close() override;
 
  private:
+  Return<void> initialize_impl(
+      const ::android::sp<V1_0::IBluetoothHciCallbacks>& cb,
+      const ::android::sp<V1_1::IBluetoothHciCallbacks>& cb_1_1);
+
   async::AsyncFdWatcher fd_watcher_;
   hci::H4Protocol* hci_handle_;
   int bt_soc_fd_;
@@ -55,6 +63,7 @@
   const uint8_t HCI_DATA_TYPE_COMMAND = 1;
   const uint8_t HCI_DATA_TYPE_ACL = 2;
   const uint8_t HCI_DATA_TYPE_SCO = 3;
+  const uint8_t HCI_DATA_TYPE_ISO = 5;
 
   int waitHciDev(int hci_interface);
   int findRfKill(void);
@@ -70,9 +79,9 @@
 extern "C" IBluetoothHci* HIDL_FETCH_IBluetoothHci(const char* name);
 
 }  // namespace btlinux
-}  // namespace V1_0
+}  // namespace V1_1
 }  // namespace bluetooth
 }  // namespace hardware
 }  // namespace android
 
-#endif  // HIDL_GENERATED_android_hardware_bluetooth_V1_0_BluetoothHci_H_
+#endif  // HIDL_GENERATED_android_hardware_bluetooth_V1_1_BluetoothHci_H_
diff --git a/vendor_libs/linux/interface/h4_protocol.cc b/vendor_libs/linux/interface/h4_protocol.cc
index e7acff4..e7c0f62 100644
--- a/vendor_libs/linux/interface/h4_protocol.cc
+++ b/vendor_libs/linux/interface/h4_protocol.cc
@@ -67,6 +67,9 @@
     case HCI_PACKET_TYPE_SCO_DATA:
       sco_cb_(hci_packetizer_.GetPacket());
       break;
+    case HCI_PACKET_TYPE_ISO_DATA:
+      iso_cb_(hci_packetizer_.GetPacket());
+      break;
     default: {
       bool bad_packet_type = true;
       CHECK(!bad_packet_type);
diff --git a/vendor_libs/linux/interface/h4_protocol.h b/vendor_libs/linux/interface/h4_protocol.h
index 612b0db..92d6698 100644
--- a/vendor_libs/linux/interface/h4_protocol.h
+++ b/vendor_libs/linux/interface/h4_protocol.h
@@ -33,11 +33,12 @@
 class H4Protocol {
  public:
   H4Protocol(int fd, PacketReadCallback event_cb, PacketReadCallback acl_cb,
-             PacketReadCallback sco_cb)
+             PacketReadCallback sco_cb, PacketReadCallback iso_cb)
       : uart_fd_(fd),
         event_cb_(event_cb),
         acl_cb_(acl_cb),
         sco_cb_(sco_cb),
+        iso_cb_(iso_cb),
         hci_packetizer_([this]() { OnPacketReady(); }) {}
 
   size_t Send(uint8_t type, const uint8_t* data, size_t length);
@@ -52,6 +53,7 @@
   PacketReadCallback event_cb_;
   PacketReadCallback acl_cb_;
   PacketReadCallback sco_cb_;
+  PacketReadCallback iso_cb_;
 
   HciPacketType hci_packet_type_{HCI_PACKET_TYPE_UNKNOWN};
   HciPacketizer hci_packetizer_;
diff --git a/vendor_libs/linux/interface/hci_internals.h b/vendor_libs/linux/interface/hci_internals.h
index 1e1f300..24e944f 100644
--- a/vendor_libs/linux/interface/hci_internals.h
+++ b/vendor_libs/linux/interface/hci_internals.h
@@ -24,7 +24,8 @@
   HCI_PACKET_TYPE_COMMAND = 1,
   HCI_PACKET_TYPE_ACL_DATA = 2,
   HCI_PACKET_TYPE_SCO_DATA = 3,
-  HCI_PACKET_TYPE_EVENT = 4
+  HCI_PACKET_TYPE_EVENT = 4,
+  HCI_PACKET_TYPE_ISO_DATA = 5,
 };
 
 // 2 bytes for opcode, 1 byte for parameter length (Volume 2, Part E, 5.4.1)
diff --git a/vendor_libs/linux/interface/service.cc b/vendor_libs/linux/interface/service.cc
index fac9ce0..4efd5e7 100644
--- a/vendor_libs/linux/interface/service.cc
+++ b/vendor_libs/linux/interface/service.cc
@@ -14,20 +14,20 @@
 // limitations under the License.
 //
 
-#define LOG_TAG "android.hardware.bluetooth@1.0-service.btlinux"
+#define LOG_TAG "android.hardware.bluetooth@1.1-service.btlinux"
 
-#include <android/hardware/bluetooth/1.0/IBluetoothHci.h>
+#include <android/hardware/bluetooth/1.1/IBluetoothHci.h>
 #include <hidl/HidlSupport.h>
 #include <hidl/HidlTransportSupport.h>
 #include <utils/Log.h>
 
 #include "bluetooth_hci.h"
 
-using ::android::hardware::configureRpcThreadpool;
-using ::android::hardware::bluetooth::V1_0::IBluetoothHci;
-using ::android::hardware::bluetooth::V1_0::btlinux::BluetoothHci;
-using ::android::hardware::joinRpcThreadpool;
 using ::android::sp;
+using ::android::hardware::configureRpcThreadpool;
+using ::android::hardware::joinRpcThreadpool;
+using ::android::hardware::bluetooth::V1_1::IBluetoothHci;
+using ::android::hardware::bluetooth::V1_1::btlinux::BluetoothHci;
 
 int main(int /* argc */, char** /* argv */) {
   sp<IBluetoothHci> bluetooth = new BluetoothHci;
diff --git a/vendor_libs/test_vendor_lib/Android.bp b/vendor_libs/test_vendor_lib/Android.bp
index aded1c3..4523be5 100644
--- a/vendor_libs/test_vendor_lib/Android.bp
+++ b/vendor_libs/test_vendor_lib/Android.bp
@@ -2,7 +2,8 @@
 // ========================================================
 cc_library_static {
     name: "libbt-rootcanal",
-    defaults: ["libchrome_support_defaults"],
+    defaults: ["gd_defaults",],
+    header_libs: ["jni_headers"],
     host_supported: true,
     proprietary: true,
     srcs: [
@@ -28,6 +29,7 @@
         "model/devices/loopback.cc",
         "model/devices/polled_socket.cc",
         "model/devices/remote_loopback_device.cc",
+        "model/devices/scripted_beacon.cc",
         "model/devices/sniffer.cc",
         "model/setup/async_manager.cc",
         "model/setup/device_boutique.cc",
@@ -35,6 +37,8 @@
         "model/setup/test_channel_transport.cc",
         "model/setup/test_command_handler.cc",
         "model/setup/test_model.cc",
+        ":BluetoothPacketSources",
+        ":BluetoothHciClassSources",
     ],
     cflags: [
         "-fvisibility=hidden",
@@ -47,28 +51,36 @@
         "include",
         ".",
     ],
-    header_libs: [
-        "libbluetooth_headers",
+    generated_headers: [
+        "RootCanalGeneratedPackets_h",
+        "BluetoothGeneratedPackets_h",
     ],
     include_dirs: [
         "system/bt",
-        "system/bt/utils/include",
-        "system/bt/hci/include",
-        "system/bt/internal_include",
-        "system/bt/stack/include",
+        "system/bt/gd",
     ],
     shared_libs: [
         "libbase",
+        "libchrome",
         "liblog",
     ],
-    whole_static_libs: [
-        "libbt-rootcanal-packets",
-    ],
     static_libs: [
         "libbt-rootcanal-types",
+        "libscriptedbeaconpayload-protos-lite",
     ],
 }
 
+cc_library_static {
+    name: "libscriptedbeaconpayload-protos-lite",
+    host_supported: true,
+    proprietary: true,
+    proto: {
+        export_proto_headers: true,
+        type: "lite",
+    },
+    srcs: ["model/devices/scripted_beacon_ble_payload.proto"],
+}
+
 // test-vendor unit tests for host
 // ========================================================
 cc_test_host {
@@ -90,9 +102,7 @@
     ],
     include_dirs: [
         "system/bt",
-        "system/bt/utils/include",
-        "system/bt/hci/include",
-        "system/bt/stack/include",
+        "system/bt/gd",
     ],
     shared_libs: [
         "liblog",
@@ -126,15 +136,33 @@
     ],
     include_dirs: [
         "system/bt",
-        "system/bt/utils/include",
-        "system/bt/hci/include",
-        "system/bt/stack/include",
+        "system/bt/gd",
+    ],
+    generated_headers: [
+        "RootCanalGeneratedPackets_h",
+        "BluetoothGeneratedPackets_h",
     ],
     shared_libs: [
         "liblog",
     ],
     static_libs: [
         "libbt-rootcanal-types",
+        "libprotobuf-cpp-lite",
+        "libscriptedbeaconpayload-protos-lite",
         "libbt-rootcanal",
     ],
 }
+
+genrule {
+    name: "RootCanalGeneratedPackets_h",
+    tools: [
+        "bluetooth_packetgen",
+    ],
+    cmd: "$(location bluetooth_packetgen) --root_namespace=model --include=system/bt/vendor_libs/test_vendor_lib --out=$(genDir) $(in)",
+    srcs: [
+        "packets/link_layer_packets.pdl",
+    ],
+    out: [
+        "packets/link_layer_packets.h",
+    ],
+}
diff --git a/vendor_libs/test_vendor_lib/data/controller_properties.json b/vendor_libs/test_vendor_lib/data/controller_properties.json
index 32adc52..814efd3 100644
--- a/vendor_libs/test_vendor_lib/data/controller_properties.json
+++ b/vendor_libs/test_vendor_lib/data/controller_properties.json
@@ -1,5 +1,6 @@
 {
   "AclDataPacketSize": "1024",
+  "EncryptionKeySize": "10",
   "ScoDataPacketSize": "255",
   "NumAclDataPackets": "10",
   "NumScoDataPackets": "10",
diff --git a/vendor_libs/test_vendor_lib/desktop/root_canal_main.cc b/vendor_libs/test_vendor_lib/desktop/root_canal_main.cc
index 5432292..95bbfd3 100644
--- a/vendor_libs/test_vendor_lib/desktop/root_canal_main.cc
+++ b/vendor_libs/test_vendor_lib/desktop/root_canal_main.cc
@@ -14,15 +14,11 @@
 // limitations under the License.
 //
 
-#define LOG_TAG "root_canal"
-
 #include "test_environment.h"
 
-#include <base/logging.h>
-#include <utils/Log.h>
 #include <future>
 
-#include "hci_internals.h"
+#include "os/log.h"
 
 using ::android::bluetooth::root_canal::TestEnvironment;
 
@@ -31,16 +27,16 @@
 constexpr uint16_t kLinkServerPort = 6403;
 
 int main(int argc, char** argv) {
-  ALOGI("main");
+  LOG_INFO("main");
   uint16_t test_port = kTestPort;
   uint16_t hci_server_port = kHciServerPort;
   uint16_t link_server_port = kLinkServerPort;
 
   for (int arg = 0; arg < argc; arg++) {
     int port = atoi(argv[arg]);
-    ALOGI("%d: %s (%d)", arg, argv[arg], port);
+    LOG_INFO("%d: %s (%d)", arg, argv[arg], port);
     if (port < 0 || port > 0xffff) {
-      ALOGW("%s out of range", argv[arg]);
+      LOG_WARN("%s out of range", argv[arg]);
     } else {
       switch (arg) {
         case 0:  // executable name
@@ -55,7 +51,7 @@
           link_server_port = port;
           break;
         default:
-          ALOGW("Ignored option %s", argv[arg]);
+          LOG_WARN("Ignored option %s", argv[arg]);
       }
     }
   }
diff --git a/vendor_libs/test_vendor_lib/desktop/test_environment.cc b/vendor_libs/test_vendor_lib/desktop/test_environment.cc
index 542738d..1ca3b8c 100644
--- a/vendor_libs/test_vendor_lib/desktop/test_environment.cc
+++ b/vendor_libs/test_vendor_lib/desktop/test_environment.cc
@@ -14,17 +14,16 @@
 // limitations under the License.
 //
 
-#define LOG_TAG "root_canal"
-
 #include "test_environment.h"
 
-#include <base/logging.h>
+#include <fcntl.h>
 #include <netdb.h>
 #include <netinet/in.h>
+#include <signal.h>
 #include <string.h>
-#include <utils/Log.h>
+#include <unistd.h>
 
-#include "hci_internals.h"
+#include "os/log.h"
 
 namespace android {
 namespace bluetooth {
@@ -35,7 +34,7 @@
 using test_vendor_lib::TaskCallback;
 
 void TestEnvironment::initialize(std::promise<void> barrier) {
-  ALOGI("%s", __func__);
+  LOG_INFO("%s", __func__);
 
   barrier_ = std::move(barrier);
 
@@ -50,34 +49,39 @@
   SetUpHciServer([this](int fd) { test_model_.IncomingHciConnection(fd); });
   SetUpLinkLayerServer([this](int fd) { test_model_.IncomingLinkLayerConnection(fd); });
 
-  ALOGI("%s: Finished", __func__);
+  // In case the client socket is closed, and rootcanal doesn't detect it due to
+  // TimerTick not fired, writing to the socket causes a SIGPIPE and we need to
+  // catch it to prevent rootcanal from crash
+  signal(SIGPIPE, SIG_IGN);
+
+  LOG_INFO("%s: Finished", __func__);
 }
 
 void TestEnvironment::close() {
-  ALOGI("%s", __func__);
+  LOG_INFO("%s", __func__);
 }
 
 void TestEnvironment::SetUpHciServer(const std::function<void(int)>& connection_callback) {
   int socket_fd = remote_hci_transport_.SetUp(hci_server_port_);
 
   test_channel_.RegisterSendResponse(
-      [](const std::string& response) { ALOGI("No HCI Response channel: %s", response.c_str()); });
+      [](const std::string& response) { LOG_INFO("No HCI Response channel: %s", response.c_str()); });
 
   if (socket_fd == -1) {
-    ALOGE("Remote HCI channel SetUp(%d) failed.", hci_server_port_);
+    LOG_ERROR("Remote HCI channel SetUp(%d) failed.", hci_server_port_);
     return;
   }
 
   async_manager_.WatchFdForNonBlockingReads(socket_fd, [this, connection_callback](int socket_fd) {
     int conn_fd = remote_hci_transport_.Accept(socket_fd);
     if (conn_fd < 0) {
-      ALOGE("Error watching remote HCI channel fd.");
+      LOG_ERROR("Error watching remote HCI channel fd.");
       return;
     }
     int flags = fcntl(conn_fd, F_GETFL, NULL);
     int ret;
     ret = fcntl(conn_fd, F_SETFL, flags | O_NONBLOCK);
-    CHECK(ret != -1) << "Error setting O_NONBLOCK " << strerror(errno);
+    ASSERT_LOG(ret != -1, "Error setting O_NONBLOCK %s", strerror(errno));
 
     connection_callback(conn_fd);
   });
@@ -87,22 +91,22 @@
   int socket_fd = remote_link_layer_transport_.SetUp(link_server_port_);
 
   test_channel_.RegisterSendResponse(
-      [](const std::string& response) { ALOGI("No LinkLayer Response channel: %s", response.c_str()); });
+      [](const std::string& response) { LOG_INFO("No LinkLayer Response channel: %s", response.c_str()); });
 
   if (socket_fd == -1) {
-    ALOGE("Remote LinkLayer channel SetUp(%d) failed.", link_server_port_);
+    LOG_ERROR("Remote LinkLayer channel SetUp(%d) failed.", link_server_port_);
     return;
   }
 
   async_manager_.WatchFdForNonBlockingReads(socket_fd, [this, connection_callback](int socket_fd) {
     int conn_fd = remote_link_layer_transport_.Accept(socket_fd);
     if (conn_fd < 0) {
-      ALOGE("Error watching remote LinkLayer channel fd.");
+      LOG_ERROR("Error watching remote LinkLayer channel fd.");
       return;
     }
     int flags = fcntl(conn_fd, F_GETFL, NULL);
     int ret = fcntl(conn_fd, F_SETFL, flags | O_NONBLOCK);
-    CHECK(ret != -1) << "Error setting O_NONBLOCK " << strerror(errno);
+    ASSERT_LOG(ret != -1, "Error setting O_NONBLOCK %s", strerror(errno));
 
     connection_callback(conn_fd);
   });
@@ -111,14 +115,14 @@
 int TestEnvironment::ConnectToRemoteServer(const std::string& server, int port) {
   int socket_fd = socket(AF_INET, SOCK_STREAM, 0);
   if (socket_fd < 1) {
-    ALOGI("socket() call failed: %s", strerror(errno));
+    LOG_INFO("socket() call failed: %s", strerror(errno));
     return -1;
   }
 
   struct hostent* host;
   host = gethostbyname(server.c_str());
   if (host == NULL) {
-    ALOGI("gethostbyname() failed for %s: %s", server.c_str(), strerror(errno));
+    LOG_INFO("gethostbyname() failed for %s: %s", server.c_str(), strerror(errno));
     return -1;
   }
 
@@ -130,37 +134,41 @@
 
   int result = connect(socket_fd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
   if (result < 0) {
-    ALOGI("connect() failed for %s@%d: %s", server.c_str(), port, strerror(errno));
+    LOG_INFO("connect() failed for %s@%d: %s", server.c_str(), port, strerror(errno));
     return -1;
   }
 
   int flags = fcntl(socket_fd, F_GETFL, NULL);
   int ret = fcntl(socket_fd, F_SETFL, flags | O_NONBLOCK);
-  CHECK(ret != -1) << "Error setting O_NONBLOCK " << strerror(errno);
+  ASSERT_LOG(ret != -1, "Error setting O_NONBLOCK %s", strerror(errno));
 
   return socket_fd;
 }
 
 void TestEnvironment::SetUpTestChannel() {
   int socket_fd = test_channel_transport_.SetUp(test_port_);
+  test_channel_.AddPhy({"BR_EDR"});
+  test_channel_.AddPhy({"LOW_ENERGY"});
+  test_channel_.SetTimerPeriod({"10"});
+  test_channel_.StartTimer({});
 
   test_channel_.RegisterSendResponse(
-      [](const std::string& response) { ALOGI("No test channel: %s", response.c_str()); });
+      [](const std::string& response) { LOG_INFO("No test channel: %s", response.c_str()); });
 
   if (socket_fd == -1) {
-    ALOGE("Test channel SetUp(%d) failed.", test_port_);
+    LOG_ERROR("Test channel SetUp(%d) failed.", test_port_);
     return;
   }
 
-  ALOGI("Test channel SetUp() successful");
+  LOG_INFO("Test channel SetUp() successful");
   async_manager_.WatchFdForNonBlockingReads(socket_fd, [this](int socket_fd) {
     int conn_fd = test_channel_transport_.Accept(socket_fd);
     if (conn_fd < 0) {
-      ALOGE("Error watching test channel fd.");
+      LOG_ERROR("Error watching test channel fd.");
       barrier_.set_value();
       return;
     }
-    ALOGI("Test channel connection accepted.");
+    LOG_INFO("Test channel connection accepted.");
     test_channel_.RegisterSendResponse(
         [this, conn_fd](const std::string& response) { test_channel_transport_.SendResponse(conn_fd, response); });
 
diff --git a/vendor_libs/test_vendor_lib/include/hci.h b/vendor_libs/test_vendor_lib/include/hci.h
index 11eb029..eaa74c0 100644
--- a/vendor_libs/test_vendor_lib/include/hci.h
+++ b/vendor_libs/test_vendor_lib/include/hci.h
@@ -17,11 +17,6 @@
 #pragma once
 #include <cstdint>
 
-#include "include/hci/event_code.h"
-#include "include/hci/le_sub_event_code.h"
-#include "include/hci/op_code.h"
-#include "include/hci/status.h"
-
 namespace test_vendor_lib {
 namespace hci {
 
@@ -33,31 +28,5 @@
   EVENT = 4,
 };
 
-enum class LinkType : uint8_t {
-  SCO = 0x00,
-  ACL = 0x01,
-  ESCO = 0x02,
-};
-
-enum class LoopbackMode : uint8_t {
-  NO = 0x00,
-  LOCAL = 0x01,
-  REMOTE = 0x02,
-};
-
-/* HCI, PAL, and LMP Version numbers are the same */
-enum class Version : uint8_t {
-  V1_0 = 0,
-  V1_1 = 1,
-  V1_2 = 2,
-  V2_0 = 3,
-  V2_1 = 4,
-  V3_0 = 5,
-  V4_0 = 6,
-  V4_1 = 7,
-  V4_2 = 8,
-  V5_0 = 9,
-};
-
 }  // namespace hci
 }  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/include/hci/event_code.h b/vendor_libs/test_vendor_lib/include/hci/event_code.h
deleted file mode 100644
index c0bdcbc..0000000
--- a/vendor_libs/test_vendor_lib/include/hci/event_code.h
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-#include <cstdint>
-
-namespace test_vendor_lib {
-namespace hci {
-
-enum class EventCode : uint8_t {
-  INQUIRY_COMPLETE = 0x01,
-  INQUIRY_RESULT = 0x02,
-  CONNECTION_COMPLETE = 0x03,
-  CONNECTION_REQUEST = 0x04,
-  DISCONNECTION_COMPLETE = 0x05,
-  AUTHENTICATION_COMPLETE = 0x06,
-  REMOTE_NAME_REQUEST_COMPLETE = 0x07,
-  ENCRYPTION_CHANGE = 0x08,
-  CHANGE_CONNECTION_LINK_KEY_COMPLETE = 0x09,
-  MASTER_LINK_KEY_COMPLETE = 0x0A,
-  READ_REMOTE_SUPPORTED_FEATURES_COMPLETE = 0x0B,
-  READ_REMOTE_VERSION_INFORMATION_COMPLETE = 0x0C,
-  QOS_SETUP_COMPLETE = 0x0D,
-  COMMAND_COMPLETE = 0x0E,
-  COMMAND_STATUS = 0x0F,
-  HARDWARE_ERROR = 0x10,
-  FLUSH_OCCURED = 0x11,
-  ROLE_CHANGE = 0x12,
-  NUMBER_OF_COMPLETED_PACKETS = 0x13,
-  MODE_CHANGE = 0x14,
-  RETURN_LINK_KEYS = 0x15,
-  PIN_CODE_REQUEST = 0x16,
-  LINK_KEY_REQUEST = 0x17,
-  LINK_KEY_NOTIFICATION = 0x18,
-  LOOPBACK_COMMAND = 0x19,
-  DATA_BUFFER_OVERFLOW = 0x1A,
-  MAX_SLOTS_CHANGE = 0x1B,
-  READ_CLOCK_OFFSET_COMPLETE = 0x1C,
-  CONNECTION_PACKET_TYPE_CHANGE = 0x1D,
-  QOS_VIOLATION = 0x1E,
-  PAGE_SCAN_REPETITION_MODE_CHANGE = 0x20,
-  FLOW_SPECIFICATION_COMPLETE = 0x21,
-  INQUIRY_RESULT_WITH_RSSI = 0x22,
-  READ_REMOTE_EXTENDED_FEATURES_COMPLETE = 0x23,
-  SYNCHRONOUS_CONNECTION_COMPLETE = 0x2C,
-  SYNCHRONOUS_CONNECTION_CHANGED = 0x2D,
-  SNIFF_SUBRATING = 0x2E,
-  EXTENDED_INQUIRY_RESULT = 0x2F,
-  ENCRYPTION_KEY_REFRESH_COMPLETE = 0x30,
-  IO_CAPABILITY_REQUEST = 0x31,
-  IO_CAPABILITY_RESPONSE = 0x32,
-  USER_CONFIRMATION_REQUEST = 0x33,
-  USER_PASSKEY_REQUEST = 0x34,
-  REMOTE_OOB_DATA_REQUEST = 0x35,
-  SIMPLE_PAIRING_COMPLETE = 0x36,
-  LINK_SUPERVISION_TIMEOUT_CHANGED = 0x38,
-  ENHANCED_FLUSH_COMPLETE = 0x39,
-  USER_PASSKEY_NOTIFICATION = 0x3B,
-  KEYPRESS_NOTIFICATION = 0x3C,
-  REMOTE_HOST_SUPPORTED_FEATURES_NOTIFICATION = 0x3D,
-  LE_META_EVENT = 0x3e,
-  PHYSICAL_LINK_COMPLETE = 0x40,
-  CHANNEL_SELECTED = 0x41,
-  DISCONNECTION_PHYSICAL_LINK_COMPLETE = 0x42,
-  PHYSICAL_LINK_LOSS_EARLY_WARNING = 0x43,
-  PHYSICAL_LINK_RECOVERY = 0x44,
-  LOGICAL_LINK_COMPLETE = 0x45,
-  DISCONNECTION_LOGICAL_LINK_COMPLETE = 0x46,
-  FLOW_SPEC_MODIFY_COMPLETE = 0x47,
-  NUMBER_OF_COMPLETED_DATA_BLOCKS = 0x48,
-  SHORT_RANGE_MODE_CHANGE_COMPLETE = 0x4C,
-};
-}
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/include/hci/le_sub_event_code.h b/vendor_libs/test_vendor_lib/include/hci/le_sub_event_code.h
deleted file mode 100644
index 42edecd..0000000
--- a/vendor_libs/test_vendor_lib/include/hci/le_sub_event_code.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-#include <cstdint>
-
-namespace test_vendor_lib {
-namespace hci {
-
-enum class LeSubEventCode : uint8_t {
-  CONNECTION_COMPLETE = 0x01,
-  ADVERTISING_REPORT = 0x02,
-  CONNECTION_UPDATE_COMPLETE = 0x03,
-  READ_REMOTE_FEATURES_COMPLETE = 0x04,
-  LONG_TERM_KEY_REQUEST = 0x05,
-  REMOTE_CONNECTION_PARAMETER_REQUEST = 0x06,
-  DATA_LENGTH_CHANGE = 0x07,
-  ENHANCED_CONNECTION_COMPLETE = 0x0a,
-  DIRECTED_ADVERTISING_REPORT = 0x0b,
-  PHY_UPDATE_COMPLETE = 0x0c,
-  EXTENDED_ADVERTISING_REPORT = 0x0D,
-  PERIODIC_ADVERTISING_SYNC_ESTABLISHED = 0x0E,
-  PERIODIC_ADVERTISING_REPORT = 0x0F,
-  PERIODIC_ADVERTISING_SYNC_LOST = 0x10,
-  SCAN_TIMEOUT = 0x11,
-  ADVERTISING_SET_TERMINATED = 0x12,
-  SCAN_REQUEST_RECEIVED = 0x13,
-};
-}
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/include/hci/op_code.h b/vendor_libs/test_vendor_lib/include/hci/op_code.h
deleted file mode 100644
index 931145d..0000000
--- a/vendor_libs/test_vendor_lib/include/hci/op_code.h
+++ /dev/null
@@ -1,281 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-#include <cstdint>
-
-namespace test_vendor_lib {
-namespace hci {
-
-using CommandGroups = enum {
-  LINK_CONTROL = 0x01 << 10,             /* 0x0400 */
-  LINK_POLICY = 0x02 << 10,              /* 0x0800 */
-  CONTROLLER_AND_BASEBAND = 0x03 << 10,  /* 0x0C00 */
-  INFORMATIONAL_PARAMETERS = 0x04 << 10, /* 0x1000 */
-  STATUS_PARAMETERS = 0x05 << 10,        /* 0x1400 */
-  TESTING = 0x06 << 10,                  /* 0x1800 */
-  LE_CONTROLLER = 0x08 << 10,            /* 0x2000 */
-  VENDOR_SPECIFIC = 0x3F << 10,          /* 0xFC00 */
-};
-
-enum class OpCode : uint16_t {
-  NONE = 0x0000,
-
-  /* LINK_CONTROL */
-  INQUIRY = LINK_CONTROL | 0x0001,
-  INQUIRY_CANCEL = LINK_CONTROL | 0x0002,
-  PERIODIC_INQUIRY_MODE = LINK_CONTROL | 0x0003,
-  EXIT_PERIODIC_INQUIRY_MODE = LINK_CONTROL | 0x0004,
-  CREATE_CONNECTION = LINK_CONTROL | 0x0005,
-  DISCONNECT = LINK_CONTROL | 0x0006,
-  CREATE_CONNECTION_CANCEL = LINK_CONTROL | 0x0008,
-  ACCEPT_CONNECTION_REQUEST = LINK_CONTROL | 0x0009,
-  REJECT_CONNECTION_REQUEST = LINK_CONTROL | 0x000A,
-  LINK_KEY_REQUEST_REPLY = LINK_CONTROL | 0x000B,
-  LINK_KEY_REQUEST_NEGATIVE_REPLY = LINK_CONTROL | 0x000C,
-  PIN_CODE_REQUEST_REPLY = LINK_CONTROL | 0x000D,
-  PIN_CODE_REQUEST_NEGATIVE_REPLY = LINK_CONTROL | 0x000E,
-  CHANGE_CONNECTION_PACKET_TYPE = LINK_CONTROL | 0x000F,
-  AUTHENTICATION_REQUESTED = LINK_CONTROL | 0x0011,
-  SET_CONNECTION_ENCRYPTION = LINK_CONTROL | 0x0013,
-  CHANGE_CONNECTION_LINK_KEY = LINK_CONTROL | 0x0015,
-  MASTER_LINK_KEY = LINK_CONTROL | 0x0017,
-  REMOTE_NAME_REQUEST = LINK_CONTROL | 0x0019,
-  REMOTE_NAME_REQUEST_CANCEL = LINK_CONTROL | 0x001A,
-  READ_REMOTE_SUPPORTED_FEATURES = LINK_CONTROL | 0x001B,
-  READ_REMOTE_EXTENDED_FEATURES = LINK_CONTROL | 0x001C,
-  READ_REMOTE_VERSION_INFORMATION = LINK_CONTROL | 0x001D,
-  READ_CLOCK_OFFSET = LINK_CONTROL | 0x001F,
-  READ_LMP_HANDLE = LINK_CONTROL | 0x0020,
-  SETUP_SYNCHRONOUS_CONNECTION = LINK_CONTROL | 0x0028,
-  ACCEPT_SYNCHRONOUS_CONNECTION = LINK_CONTROL | 0x0029,
-  REJECT_SYNCHRONOUS_CONNECTION = LINK_CONTROL | 0x002A,
-  IO_CAPABILITY_REQUEST_REPLY = LINK_CONTROL | 0x002B,
-  USER_CONFIRMATION_REQUEST_REPLY = LINK_CONTROL | 0x002C,
-  USER_CONFIRMATION_REQUEST_NEGATIVE_REPLY = LINK_CONTROL | 0x002D,
-  USER_PASSKEY_REQUEST_REPLY = LINK_CONTROL | 0x002E,
-  USER_PASSKEY_REQUEST_NEGATIVE_REPLY = LINK_CONTROL | 0x002F,
-  REMOTE_OOB_DATA_REQUEST_REPLY = LINK_CONTROL | 0x0030,
-  REMOTE_OOB_DATA_REQUEST_NEGATIVE_REPLY = LINK_CONTROL | 0x0033,
-  IO_CAPABILITY_REQUEST_NEGATIVE_REPLY = LINK_CONTROL | 0x0034,
-  ENHANCED_SETUP_SYNCHRONOUS_CONNECTION = LINK_CONTROL | 0x003D,
-  ENHANCED_ACCEPT_SYNCHRONOUS_CONNECTION = LINK_CONTROL | 0x003E,
-
-  /* LINK_POLICY */
-  HOLD_MODE = LINK_POLICY | 0x0001,
-  SNIFF_MODE = LINK_POLICY | 0x0003,
-  EXIT_SNIFF_MODE = LINK_POLICY | 0x0004,
-  QOS_SETUP = LINK_POLICY | 0x0007,
-  ROLE_DISCOVERY = LINK_POLICY | 0x0009,
-  SWITCH_ROLE = LINK_POLICY | 0x000B,
-  READ_LINK_POLICY_SETTINGS = LINK_POLICY | 0x000C,
-  WRITE_LINK_POLICY_SETTINGS = LINK_POLICY | 0x000D,
-  READ_DEFAULT_LINK_POLICY_SETTINGS = LINK_POLICY | 0x000E,
-  WRITE_DEFAULT_LINK_POLICY_SETTINGS = LINK_POLICY | 0x000F,
-  FLOW_SPECIFICATION = LINK_POLICY | 0x0010,
-  SNIFF_SUBRATING = LINK_POLICY | 0x0011,
-
-  /* CONTROLLER_AND_BASEBAND */
-  SET_EVENT_MASK = CONTROLLER_AND_BASEBAND | 0x0001,
-  RESET = CONTROLLER_AND_BASEBAND | 0x0003,
-  SET_EVENT_FILTER = CONTROLLER_AND_BASEBAND | 0x0005,
-  FLUSH = CONTROLLER_AND_BASEBAND | 0x0008,
-  READ_PIN_TYPE = CONTROLLER_AND_BASEBAND | 0x0009,
-  WRITE_PIN_TYPE = CONTROLLER_AND_BASEBAND | 0x000A,
-  CREATE_NEW_UNIT_KEY = CONTROLLER_AND_BASEBAND | 0x000B,
-  READ_STORED_LINK_KEY = CONTROLLER_AND_BASEBAND | 0x000D,
-  WRITE_STORED_LINK_KEY = CONTROLLER_AND_BASEBAND | 0x0011,
-  DELETE_STORED_LINK_KEY = CONTROLLER_AND_BASEBAND | 0x0012,
-  WRITE_LOCAL_NAME = CONTROLLER_AND_BASEBAND | 0x0013,
-  READ_LOCAL_NAME = CONTROLLER_AND_BASEBAND | 0x0014,
-  READ_CONNECTION_ACCEPT_TIMEOUT = CONTROLLER_AND_BASEBAND | 0x0015,
-  WRITE_CONNECTION_ACCEPT_TIMEOUT = CONTROLLER_AND_BASEBAND | 0x0016,
-  READ_PAGE_TIMEOUT = CONTROLLER_AND_BASEBAND | 0x0017,
-  WRITE_PAGE_TIMEOUT = CONTROLLER_AND_BASEBAND | 0x0018,
-  READ_SCAN_ENABLE = CONTROLLER_AND_BASEBAND | 0x0019,
-  WRITE_SCAN_ENABLE = CONTROLLER_AND_BASEBAND | 0x001A,
-  READ_PAGE_SCAN_ACTIVITY = CONTROLLER_AND_BASEBAND | 0x001B,
-  WRITE_PAGE_SCAN_ACTIVITY = CONTROLLER_AND_BASEBAND | 0x001C,
-  READ_INQUIRY_SCAN_ACTIVITY = CONTROLLER_AND_BASEBAND | 0x001D,
-  WRITE_INQUIRY_SCAN_ACTIVITY = CONTROLLER_AND_BASEBAND | 0x001E,
-  READ_AUTHENTICATION_ENABLE = CONTROLLER_AND_BASEBAND | 0x001F,
-  WRITE_AUTHENTICATION_ENABLE = CONTROLLER_AND_BASEBAND | 0x0020,
-  READ_CLASS_OF_DEVICE = CONTROLLER_AND_BASEBAND | 0x0023,
-  WRITE_CLASS_OF_DEVICE = CONTROLLER_AND_BASEBAND | 0x0024,
-  READ_VOICE_SETTING = CONTROLLER_AND_BASEBAND | 0x0025,
-  WRITE_VOICE_SETTING = CONTROLLER_AND_BASEBAND | 0x0026,
-  READ_AUTOMATIC_FLUSH_TIMEOUT = CONTROLLER_AND_BASEBAND | 0x0027,
-  WRITE_AUTOMATIC_FLUSH_TIMEOUT = CONTROLLER_AND_BASEBAND | 0x0028,
-  READ_NUM_BROADCAST_RETRANSMITS = CONTROLLER_AND_BASEBAND | 0x0029,
-  WRITE_NUM_BROADCAST_RETRANSMITS = CONTROLLER_AND_BASEBAND | 0x002A,
-  READ_HOLD_MODE_ACTIVITY = CONTROLLER_AND_BASEBAND | 0x002B,
-  WRITE_HOLD_MODE_ACTIVITY = CONTROLLER_AND_BASEBAND | 0x002C,
-  READ_TRANSMIT_POWER_LEVEL = CONTROLLER_AND_BASEBAND | 0x002D,
-  READ_SYNCHRONOUS_FLOW_CONTROL_ENABLE = CONTROLLER_AND_BASEBAND | 0x002E,
-  WRITE_SYNCHRONOUS_FLOW_CONTROL_ENABLE = CONTROLLER_AND_BASEBAND | 0x002F,
-  SET_CONTROLLER_TO_HOST_FLOW_CONTROL = CONTROLLER_AND_BASEBAND | 0x0031,
-  HOST_BUFFER_SIZE = CONTROLLER_AND_BASEBAND | 0x0033,
-  HOST_NUM_COMPLETED_PACKETS = CONTROLLER_AND_BASEBAND | 0x0035,
-  READ_LINK_SUPERVISION_TIMEOUT = CONTROLLER_AND_BASEBAND | 0x0036,
-  WRITE_LINK_SUPERVISION_TIMEOUT = CONTROLLER_AND_BASEBAND | 0x0037,
-  READ_NUMBER_OF_SUPPORTED_IAC = CONTROLLER_AND_BASEBAND | 0x0038,
-  READ_CURRENT_IAC_LAP = CONTROLLER_AND_BASEBAND | 0x0039,
-  WRITE_CURRENT_IAC_LAP = CONTROLLER_AND_BASEBAND | 0x003A,
-  SET_AFH_HOST_CHANNEL_CLASSIFICATION = CONTROLLER_AND_BASEBAND | 0x003F,
-  READ_LE_HOST_SUPPORT = CONTROLLER_AND_BASEBAND | 0x6C,
-  WRITE_LE_HOST_SUPPORT = CONTROLLER_AND_BASEBAND | 0x6D,
-  READ_INQUIRY_SCAN_TYPE = CONTROLLER_AND_BASEBAND | 0x0042,
-  WRITE_INQUIRY_SCAN_TYPE = CONTROLLER_AND_BASEBAND | 0x0043,
-  READ_INQUIRY_MODE = CONTROLLER_AND_BASEBAND | 0x0044,
-  WRITE_INQUIRY_MODE = CONTROLLER_AND_BASEBAND | 0x0045,
-  READ_PAGE_SCAN_TYPE = CONTROLLER_AND_BASEBAND | 0x0046,
-  WRITE_PAGE_SCAN_TYPE = CONTROLLER_AND_BASEBAND | 0x0047,
-  READ_AFH_CHANNEL_ASSESSMENT_MODE = CONTROLLER_AND_BASEBAND | 0x0048,
-  WRITE_AFH_CHANNEL_ASSESSMENT_MODE = CONTROLLER_AND_BASEBAND | 0x0049,
-  READ_EXTENDED_INQUIRY_RESPONSE = CONTROLLER_AND_BASEBAND | 0x0051,
-  WRITE_EXTENDED_INQUIRY_RESPONSE = CONTROLLER_AND_BASEBAND | 0x0052,
-  REFRESH_ENCRYPTION_KEY = CONTROLLER_AND_BASEBAND | 0x0053,
-  READ_SIMPLE_PAIRING_MODE = CONTROLLER_AND_BASEBAND | 0x0055,
-  WRITE_SIMPLE_PAIRING_MODE = CONTROLLER_AND_BASEBAND | 0x0056,
-  READ_LOCAL_OOB_DATA = CONTROLLER_AND_BASEBAND | 0x0057,
-  READ_INQUIRY_RESPONSE_TRANSMIT_POWER_LEVEL = CONTROLLER_AND_BASEBAND | 0x0058,
-  WRITE_INQUIRY_TRANSMIT_POWER_LEVEL = CONTROLLER_AND_BASEBAND | 0x0059,
-  SEND_KEYPRESS_NOTIFICATION = CONTROLLER_AND_BASEBAND | 0x0060,
-
-  READ_SECURE_CONNECTIONS_HOST_SUPPORT = CONTROLLER_AND_BASEBAND | 0x0079,
-  WRITE_SECURE_CONNECTIONS_HOST_SUPPORT = CONTROLLER_AND_BASEBAND | 0x007A,
-
-  /* INFORMATIONAL_PARAMETERS */
-  READ_LOCAL_VERSION_INFORMATION = INFORMATIONAL_PARAMETERS | 0x0001,
-  READ_LOCAL_SUPPORTED_COMMANDS = INFORMATIONAL_PARAMETERS | 0x0002,
-  READ_LOCAL_SUPPORTED_FEATURES = INFORMATIONAL_PARAMETERS | 0x0003,
-  READ_LOCAL_EXTENDED_FEATURES = INFORMATIONAL_PARAMETERS | 0x0004,
-  READ_BUFFER_SIZE = INFORMATIONAL_PARAMETERS | 0x0005,
-  READ_BD_ADDR = INFORMATIONAL_PARAMETERS | 0x0009,
-  READ_DATA_BLOCK_SIZE = INFORMATIONAL_PARAMETERS | 0x000A,
-  READ_LOCAL_SUPPORTED_CODECS = INFORMATIONAL_PARAMETERS | 0x000B,
-
-  /* STATUS_PARAMETERS */
-  READ_FAILED_CONTACT_COUNTER = STATUS_PARAMETERS | 0x0001,
-  RESET_FAILED_CONTACT_COUNTER = STATUS_PARAMETERS | 0x0002,
-  READ_LINK_QUALITY = STATUS_PARAMETERS | 0x0003,
-  READ_RSSI = STATUS_PARAMETERS | 0x0005,
-  READ_AFH_CHANNEL_MAP = STATUS_PARAMETERS | 0x0006,
-  READ_CLOCK = STATUS_PARAMETERS | 0x0007,
-  READ_ENCRYPTION_KEY_SIZE = STATUS_PARAMETERS | 0x0008,
-
-  /* TESTING */
-  READ_LOOPBACK_MODE = TESTING | 0x0001,
-  WRITE_LOOPBACK_MODE = TESTING | 0x0002,
-  ENABLE_DEVICE_UNDER_TEST_MODE = TESTING | 0x0003,
-  WRITE_SIMPLE_PAIRING_DEBUG_MODE = TESTING | 0x0004,
-  WRITE_SECURE_CONNECTIONS_TEST_MODE = TESTING | 0x000A,
-
-  /* LE_CONTROLLER */
-  LE_SET_EVENT_MASK = LE_CONTROLLER | 0x0001,
-  LE_READ_BUFFER_SIZE = LE_CONTROLLER | 0x0002,
-  LE_READ_LOCAL_SUPPORTED_FEATURES = LE_CONTROLLER | 0x0003,
-  LE_WRITE_LOCAL_SUPPORTED_FEATURES = LE_CONTROLLER | 0x0004,
-  LE_SET_RANDOM_ADDRESS = LE_CONTROLLER | 0x0005,
-  LE_SET_ADVERTISING_PARAMETERS = LE_CONTROLLER | 0x0006,
-  LE_READ_ADVERTISING_CHANNEL_TX_POWER = LE_CONTROLLER | 0x0007,
-  LE_SET_ADVERTISING_DATA = LE_CONTROLLER | 0x0008,
-  LE_SET_SCAN_RSPONSE_DATA = LE_CONTROLLER | 0x0009,
-  LE_SET_ADVERTISING_ENABLE = LE_CONTROLLER | 0x000A,
-  LE_SET_SCAN_PARAMETERS = LE_CONTROLLER | 0x000B,
-  LE_SET_SCAN_ENABLE = LE_CONTROLLER | 0x000C,
-  LE_CREATE_CONNECTION = LE_CONTROLLER | 0x000D,
-  LE_CREATE_CONNECTION_CANCEL = LE_CONTROLLER | 0x000E,
-  LE_READ_WHITE_LIST_SIZE = LE_CONTROLLER | 0x000F,
-  LE_CLEAR_WHITE_LIST = LE_CONTROLLER | 0x0010,
-  LE_ADD_DEVICE_TO_WHITE_LIST = LE_CONTROLLER | 0x0011,
-  LE_REMOVE_DEVICE_FROM_WHITE_LIST = LE_CONTROLLER | 0x0012,
-  LE_CONNECTION_UPDATE = LE_CONTROLLER | 0x0013,
-  LE_SET_HOST_CHANNEL_CLASSIFICATION = LE_CONTROLLER | 0x0014,
-  LE_READ_CHANNEL_MAP = LE_CONTROLLER | 0x0015,
-  LE_READ_REMOTE_FEATURES = LE_CONTROLLER | 0x0016,
-  LE_ENCRYPT = LE_CONTROLLER | 0x0017,
-  LE_RAND = LE_CONTROLLER | 0x0018,
-  LE_START_ENCRYPTION = LE_CONTROLLER | 0x0019,
-  LE_LONG_TERM_KEY_REQUEST_REPLY = LE_CONTROLLER | 0x001A,
-  LE_LONG_TERM_KEY_REQUEST_NEGATIVE_REPLY = LE_CONTROLLER | 0x001B,
-  LE_READ_SUPPORTED_STATES = LE_CONTROLLER | 0x001C,
-  LE_RECEIVER_TEST = LE_CONTROLLER | 0x001D,
-  LE_TRANSMITTER_TEST = LE_CONTROLLER | 0x001E,
-  LE_TEST_END = LE_CONTROLLER | 0x001F,
-  LE_REMOTE_CONNECTION_PARAMETER_REQUEST_REPLY = LE_CONTROLLER | 0x0020,
-  LE_REMOTE_CONNECTION_PARAMETER_REQUEST_NEGATIVE_REPLY = LE_CONTROLLER | 0x0021,
-
-  LE_SET_DATA_LENGTH = LE_CONTROLLER | 0x0022,
-  LE_READ_SUGGESTED_DEFAULT_DATA_LENGTH = LE_CONTROLLER | 0x0023,
-  LE_WRITE_SUGGESTED_DEFAULT_DATA_LENGTH = LE_CONTROLLER | 0x0024,
-  LE_READ_LOCAL_P_256_PUBLIC_KEY_COMMAND = LE_CONTROLLER | 0x0025,
-  LE_GENERATE_DHKEY_COMMAND = LE_CONTROLLER | 0x0026,
-  LE_ADD_DEVICE_TO_RESOLVING_LIST = LE_CONTROLLER | 0x0027,
-  LE_REMOVE_DEVICE_FROM_RESOLVING_LIST = LE_CONTROLLER | 0x0028,
-  LE_CLEAR_RESOLVING_LIST = LE_CONTROLLER | 0x0029,
-  LE_READ_RESOLVING_LIST_SIZE = LE_CONTROLLER | 0x002A,
-  LE_READ_PEER_RESOLVABLE_ADDRESS = LE_CONTROLLER | 0x002B,
-  LE_READ_LOCAL_RESOLVABLE_ADDRESS = LE_CONTROLLER | 0x002C,
-  LE_SET_ADDRESS_RESOLUTION_ENABLE = LE_CONTROLLER | 0x002D,
-  LE_SET_RESOLVABLE_PRIVATE_ADDRESS_TIMEOUT = LE_CONTROLLER | 0x002E,
-  LE_READ_MAXIMUM_DATA_LENGTH = LE_CONTROLLER | 0x002F,
-  LE_READ_PHY = LE_CONTROLLER | 0x0030,
-  LE_SET_DEFAULT_PHY = LE_CONTROLLER | 0x0031,
-  LE_SET_PHY = LE_CONTROLLER | 0x0032,
-  LE_ENHANCED_RECEIVER_TEST = LE_CONTROLLER | 0x0033,
-  LE_ENHANCED_TRANSMITTER_TEST = LE_CONTROLLER | 0x0034,
-  LE_SET_EXTENDED_ADVERTISING_RANDOM_ADDRESS = LE_CONTROLLER | 0x35,
-  LE_SET_EXTENDED_ADVERTISING_PARAMETERS = LE_CONTROLLER | 0x36,
-  LE_SET_EXTENDED_ADVERTISING_DATA = LE_CONTROLLER | 0x37,
-  LE_SET_EXTENDED_ADVERTISING_SCAN_RESPONSE = LE_CONTROLLER | 0x38,
-  LE_SET_EXTENDED_ADVERTISING_ENABLE = LE_CONTROLLER | 0x39,
-  LE_READ_MAXIMUM_ADVERTISING_DATA_LENGTH = LE_CONTROLLER | 0x003A,
-  LE_READ_NUMBER_OF_SUPPORTED_ADVERTISING_SETS = LE_CONTROLLER | 0x003B,
-  LE_REMOVE_ADVERTISING_SET = LE_CONTROLLER | 0x003C,
-  LE_CLEAR_ADVERTISING_SETS = LE_CONTROLLER | 0x003D,
-  LE_SET_PERIODIC_ADVERTISING_PARAM = LE_CONTROLLER | 0x003E,
-  LE_SET_PERIODIC_ADVERTISING_DATA = LE_CONTROLLER | 0x003F,
-  LE_SET_PERIODIC_ADVERTISING_ENABLE = LE_CONTROLLER | 0x0040,
-  LE_SET_EXTENDED_SCAN_PARAMETERS = LE_CONTROLLER | 0x0041,
-  LE_SET_EXTENDED_SCAN_ENABLE = LE_CONTROLLER | 0x0042,
-  LE_EXTENDED_CREATE_CONNECTION = LE_CONTROLLER | 0x0043,
-  LE_PERIODIC_ADVERTISING_CREATE_SYNC = LE_CONTROLLER | 0x0044,
-  LE_PERIODIC_ADVERTISING_CREATE_SYNC_CANCEL = LE_CONTROLLER | 0x0045,
-  LE_PERIODIC_ADVERTISING_TERMINATE_SYNC = LE_CONTROLLER | 0x0046,
-  LE_ADD_DEVICE_TO_PERIODIC_ADVERTISING_LIST = LE_CONTROLLER | 0x0047,
-  LE_REMOVE_DEVICE_FROM_PERIODIC_ADVERTISING_LIST = LE_CONTROLLER | 0x0048,
-  LE_CLEAR_PERIODIC_ADVERTISING_LIST = LE_CONTROLLER | 0x0049,
-  LE_READ_PERIODIC_ADVERTISING_LIST_SIZE = LE_CONTROLLER | 0x004A,
-  LE_READ_TRANSMIT_POWER = LE_CONTROLLER | 0x004B,
-  LE_READ_RF_PATH_COMPENSATION_POWER = LE_CONTROLLER | 0x004C,
-  LE_WRITE_RF_PATH_COMPENSATION_POWER = LE_CONTROLLER | 0x004D,
-  LE_SET_PRIVACY_MODE = LE_CONTROLLER | 0x004E,
-
-  /* VENDOR_SPECIFIC */
-  LE_GET_VENDOR_CAPABILITIES = VENDOR_SPECIFIC | 0x0153,
-  LE_MULTI_ADVT = VENDOR_SPECIFIC | 0x0154,
-  LE_BATCH_SCAN = VENDOR_SPECIFIC | 0x0156,
-  LE_ADV_FILTER = VENDOR_SPECIFIC | 0x0157,
-  LE_TRACK_ADV = VENDOR_SPECIFIC | 0x0158,
-  LE_ENERGY_INFO = VENDOR_SPECIFIC | 0x0159,
-  LE_EXTENDED_SCAN_PARAMS = VENDOR_SPECIFIC | 0x015A,
-  CONTROLLER_DEBUG_INFO = VENDOR_SPECIFIC | 0x015B,
-  CONTROLLER_A2DP_OPCODE = VENDOR_SPECIFIC | 0x015D,
-};
-
-}  // namespace hci
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/include/hci/status.h b/vendor_libs/test_vendor_lib/include/hci/status.h
deleted file mode 100644
index 4452cfa..0000000
--- a/vendor_libs/test_vendor_lib/include/hci/status.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-#include <cstdint>
-
-namespace test_vendor_lib {
-namespace hci {
-
-enum class Status : uint8_t {
-  SUCCESS = 0,
-  UNKNOWN_COMMAND = 1,
-  UNKNOWN_CONNECTION = 2,
-  HARDWARE_FAILURE = 3,
-  PAGE_TIMEOUT = 4,
-  AUTHENTICATION_FAILURE = 5,
-  PIN_OR_KEY_MISSING = 6,
-  MEMORY_CAPACITY_EXCEEDED = 7,
-  CONNECTION_TIMEOUT = 8,
-  COMMAND_DISALLOWED = 0x0c,
-  CONNECTION_REJECTED_LIMITED_RESOURCES = 0x0d,
-  CONNECTION_REJECTED_SECURITY = 0x0e,
-  CONNECTION_REJECTED_UNACCEPTABLE_BD_ADDR = 0x0f,
-  INVALID_HCI_COMMAND_PARAMETERS = 0x12,
-  REMOTE_USER_TERMINATED_CONNECTION = 0x13,
-  CONNECTION_TERMINATED_BY_LOCAL_HOST = 0x16,
-  UNSPECIFIED_ERROR = 0x1f,
-  ENCRYPTION_MODE_NOT_ACCEPTABLE = 0x25,
-  HOST_BUSY_PAIRING = 0x38,
-  CONTROLLER_BUSY = 0x3a,
-};
-}
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/include/link.h b/vendor_libs/test_vendor_lib/include/link.h
deleted file mode 100644
index 1c60e3d..0000000
--- a/vendor_libs/test_vendor_lib/include/link.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-namespace test_vendor_lib {
-class Link {
- public:
-  static constexpr size_t kSizeBytes = sizeof(uint32_t);
-  static constexpr size_t kTypeBytes = sizeof(uint8_t);
-
-  enum class PacketType : uint8_t {
-    UNKNOWN,
-    ACL,
-    COMMAND,
-    DISCONNECT,
-    ENCRYPT_CONNECTION,
-    ENCRYPT_CONNECTION_RESPONSE,
-    EVENT,
-    INQUIRY,
-    INQUIRY_RESPONSE,
-    IO_CAPABILITY_REQUEST,
-    IO_CAPABILITY_RESPONSE,
-    IO_CAPABILITY_NEGATIVE_RESPONSE,
-    LE_ADVERTISEMENT,
-    LE_SCAN,
-    LE_SCAN_RESPONSE,
-    PAGE,
-    PAGE_RESPONSE,
-    RESPONSE,
-    SCO,
-  };
-};
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/model/controller/acl_connection.cc b/vendor_libs/test_vendor_lib/model/controller/acl_connection.cc
index 460f550..4f0f132 100644
--- a/vendor_libs/test_vendor_lib/model/controller/acl_connection.cc
+++ b/vendor_libs/test_vendor_lib/model/controller/acl_connection.cc
@@ -14,14 +14,29 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "acl_connection"
-
 #include "acl_connection.h"
 
-#include "base/logging.h"
-
-#include "osi/include/log.h"
-
 using std::shared_ptr;
 
-namespace test_vendor_lib {}  // namespace test_vendor_lib
+namespace test_vendor_lib {
+AclConnection::AclConnection(AddressWithType addr, AddressWithType own_addr,
+                             Phy::Type phy_type)
+    : address_(addr), own_address_(own_addr), type_(phy_type) {}
+
+void AclConnection::Encrypt() { encrypted_ = true; };
+
+bool AclConnection::IsEncrypted() const { return encrypted_; };
+
+AddressWithType AclConnection::GetAddress() const { return address_; }
+
+void AclConnection::SetAddress(AddressWithType address) { address_ = address; }
+
+AddressWithType AclConnection::GetOwnAddress() const { return own_address_; }
+
+void AclConnection::SetOwnAddress(AddressWithType own_addr) {
+  own_address_ = own_addr;
+}
+
+Phy::Type AclConnection::GetPhyType() const { return type_; }
+
+}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/model/controller/acl_connection.h b/vendor_libs/test_vendor_lib/model/controller/acl_connection.h
index 796d04e..12e1779 100644
--- a/vendor_libs/test_vendor_lib/model/controller/acl_connection.h
+++ b/vendor_libs/test_vendor_lib/model/controller/acl_connection.h
@@ -18,44 +18,42 @@
 
 #include <cstdint>
 
-#include "types/address.h"
+#include "hci/address_with_type.h"
+#include "phy.h"
 
 namespace test_vendor_lib {
 
+using ::bluetooth::hci::AddressWithType;
+
 // Model the connection of a device to the controller.
 class AclConnection {
  public:
-  AclConnection(const Address& addr) : address_(addr), connected_(false), encrypted_(false) {}
+  AclConnection(AddressWithType addr, AddressWithType own_addr,
+                Phy::Type phy_type);
 
   virtual ~AclConnection() = default;
 
-  void SetConnected(bool connected) {
-    connected_ = connected;
-  };
-  bool IsConnected() const {
-    return connected_;
-  };
+  void Encrypt();
 
-  void Encrypt() {
-    encrypted_ = true;
-  };
-  bool IsEncrypted() const {
-    return encrypted_;
-  };
+  bool IsEncrypted() const;
 
-  const Address& GetAddress() const {
-    return address_;
-  }
-  void SetAddress(const Address& address) {
-    address_ = address;
-  }
+  AddressWithType GetAddress() const;
+
+  void SetAddress(AddressWithType address);
+
+  AddressWithType GetOwnAddress() const;
+
+  void SetOwnAddress(AddressWithType own_addr);
+
+  Phy::Type GetPhyType() const;
 
  private:
-  Address address_;
+  AddressWithType address_;
+  AddressWithType own_address_;
+  Phy::Type type_{Phy::Type::BR_EDR};
 
   // State variables
-  bool connected_;
-  bool encrypted_;
+  bool encrypted_{false};
 };
 
 }  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/model/controller/acl_connection_handler.cc b/vendor_libs/test_vendor_lib/model/controller/acl_connection_handler.cc
index 2d5ff97..d798e78 100644
--- a/vendor_libs/test_vendor_lib/model/controller/acl_connection_handler.cc
+++ b/vendor_libs/test_vendor_lib/model/controller/acl_connection_handler.cc
@@ -14,19 +14,20 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "acl_connection_handler"
-
 #include "acl_connection_handler.h"
 
-#include "base/logging.h"
+#include "os/log.h"
 
-#include "osi/include/log.h"
-#include "types/address.h"
+#include "hci/address.h"
 
 using std::shared_ptr;
 
 namespace test_vendor_lib {
 
+using ::bluetooth::hci::Address;
+using ::bluetooth::hci::AddressType;
+using ::bluetooth::hci::AddressWithType;
+
 bool AclConnectionHandler::HasHandle(uint16_t handle) const {
   if (acl_connections_.count(handle) == 0) {
     return false;
@@ -35,40 +36,98 @@
 }
 
 uint16_t AclConnectionHandler::GetUnusedHandle() {
-  static uint16_t sNextHandle = acl::kReservedHandle - 2;
-  while (acl_connections_.count(sNextHandle) == 1) {
-    sNextHandle = (sNextHandle + 1) % acl::kReservedHandle;
+  while (acl_connections_.count(last_handle_) == 1) {
+    last_handle_ = (last_handle_ + 1) % acl::kReservedHandle;
   }
-  uint16_t unused_handle = sNextHandle;
-  sNextHandle = (sNextHandle + 1) % acl::kReservedHandle;
+  uint16_t unused_handle = last_handle_;
+  last_handle_ = (last_handle_ + 1) % acl::kReservedHandle;
   return unused_handle;
 }
 
-bool AclConnectionHandler::CreatePendingConnection(const Address& addr) {
-  if ((pending_connections_.size() + 1 > max_pending_connections_) || HasPendingConnection(addr)) {
+bool AclConnectionHandler::CreatePendingConnection(
+    Address addr, bool authenticate_on_connect) {
+  if (classic_connection_pending_) {
     return false;
   }
-  pending_connections_.insert(addr);
+  classic_connection_pending_ = true;
+  pending_connection_address_ = addr;
+  authenticate_pending_classic_connection_ = authenticate_on_connect;
   return true;
 }
 
-bool AclConnectionHandler::HasPendingConnection(const Address& addr) {
-  return pending_connections_.count(addr) == 1;
+bool AclConnectionHandler::HasPendingConnection(Address addr) const {
+  return classic_connection_pending_ && pending_connection_address_ == addr;
 }
 
-bool AclConnectionHandler::CancelPendingConnection(const Address& addr) {
-  if (!HasPendingConnection(addr)) {
+bool AclConnectionHandler::AuthenticatePendingConnection() const {
+  return authenticate_pending_classic_connection_;
+}
+
+bool AclConnectionHandler::CancelPendingConnection(Address addr) {
+  if (!classic_connection_pending_ || pending_connection_address_ != addr) {
     return false;
   }
-  pending_connections_.erase(addr);
+  classic_connection_pending_ = false;
+  pending_connection_address_ = Address::kEmpty;
   return true;
 }
 
-uint16_t AclConnectionHandler::CreateConnection(const Address& addr) {
+bool AclConnectionHandler::CreatePendingLeConnection(AddressWithType addr) {
+  bool device_connected = false;
+  for (auto pair : acl_connections_) {
+    auto connection = std::get<AclConnection>(pair);
+    if (connection.GetAddress() == addr) {
+      device_connected = true;
+    }
+  }
+  if (device_connected) {
+    LOG_INFO("%s: %s is already connected", __func__, addr.ToString().c_str());
+    return false;
+  }
+  if (le_connection_pending_) {
+    LOG_INFO("%s: connection already pending", __func__);
+    return false;
+  }
+  le_connection_pending_ = true;
+  pending_le_connection_address_ = addr;
+  return true;
+}
+
+bool AclConnectionHandler::HasPendingLeConnection(AddressWithType addr) const {
+  return le_connection_pending_ && pending_le_connection_address_ == addr;
+}
+
+bool AclConnectionHandler::CancelPendingLeConnection(AddressWithType addr) {
+  if (!le_connection_pending_ || pending_le_connection_address_ != addr) {
+    return false;
+  }
+  le_connection_pending_ = false;
+  pending_le_connection_address_ =
+      AddressWithType{Address::kEmpty, AddressType::PUBLIC_DEVICE_ADDRESS};
+  return true;
+}
+
+uint16_t AclConnectionHandler::CreateConnection(Address addr,
+                                                Address own_addr) {
   if (CancelPendingConnection(addr)) {
     uint16_t handle = GetUnusedHandle();
-    acl_connections_.emplace(handle, addr);
-    SetConnected(handle, true);
+    acl_connections_.emplace(
+        handle,
+        AclConnection{
+            AddressWithType{addr, AddressType::PUBLIC_DEVICE_ADDRESS},
+            AddressWithType{own_addr, AddressType::PUBLIC_DEVICE_ADDRESS},
+            Phy::Type::BR_EDR});
+    return handle;
+  }
+  return acl::kReservedHandle;
+}
+
+uint16_t AclConnectionHandler::CreateLeConnection(AddressWithType addr,
+                                                  AddressWithType own_addr) {
+  if (CancelPendingLeConnection(addr)) {
+    uint16_t handle = GetUnusedHandle();
+    acl_connections_.emplace(
+        handle, AclConnection{addr, own_addr, Phy::Type::LOW_ENERGY});
     return handle;
   }
   return acl::kReservedHandle;
@@ -78,7 +137,7 @@
   return acl_connections_.erase(handle) > 0;
 }
 
-uint16_t AclConnectionHandler::GetHandle(const Address& addr) const {
+uint16_t AclConnectionHandler::GetHandle(AddressWithType addr) const {
   for (auto pair : acl_connections_) {
     if (std::get<AclConnection>(pair).GetAddress() == addr) {
       return std::get<0>(pair);
@@ -87,23 +146,24 @@
   return acl::kReservedHandle;
 }
 
-const Address& AclConnectionHandler::GetAddress(uint16_t handle) const {
-  CHECK(HasHandle(handle)) << "Handle unknown " << handle;
+uint16_t AclConnectionHandler::GetHandleOnlyAddress(
+    bluetooth::hci::Address addr) const {
+  for (auto pair : acl_connections_) {
+    if (std::get<AclConnection>(pair).GetAddress().GetAddress() == addr) {
+      return std::get<0>(pair);
+    }
+  }
+  return acl::kReservedHandle;
+}
+
+AddressWithType AclConnectionHandler::GetAddress(uint16_t handle) const {
+  ASSERT_LOG(HasHandle(handle), "Handle unknown %hd", handle);
   return acl_connections_.at(handle).GetAddress();
 }
 
-void AclConnectionHandler::SetConnected(uint16_t handle, bool connected) {
-  if (!HasHandle(handle)) {
-    return;
-  }
-  acl_connections_.at(handle).SetConnected(connected);
-}
-
-bool AclConnectionHandler::IsConnected(uint16_t handle) const {
-  if (!HasHandle(handle)) {
-    return false;
-  }
-  return acl_connections_.at(handle).IsConnected();
+AddressWithType AclConnectionHandler::GetOwnAddress(uint16_t handle) const {
+  ASSERT_LOG(HasHandle(handle), "Handle unknown %hd", handle);
+  return acl_connections_.at(handle).GetOwnAddress();
 }
 
 void AclConnectionHandler::Encrypt(uint16_t handle) {
@@ -120,11 +180,20 @@
   return acl_connections_.at(handle).IsEncrypted();
 }
 
-void AclConnectionHandler::SetAddress(uint16_t handle, const Address& address) {
+void AclConnectionHandler::SetAddress(uint16_t handle,
+                                      AddressWithType address) {
   if (!HasHandle(handle)) {
     return;
   }
-  acl_connections_.at(handle).SetAddress(address);
+  auto connection = acl_connections_.at(handle);
+  connection.SetAddress(address);
+}
+
+Phy::Type AclConnectionHandler::GetPhyType(uint16_t handle) const {
+  if (!HasHandle(handle)) {
+    return Phy::Type::BR_EDR;
+  }
+  return acl_connections_.at(handle).GetPhyType();
 }
 
 }  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/model/controller/acl_connection_handler.h b/vendor_libs/test_vendor_lib/model/controller/acl_connection_handler.h
index fda1d3d..0f0b5a7 100644
--- a/vendor_libs/test_vendor_lib/model/controller/acl_connection_handler.h
+++ b/vendor_libs/test_vendor_lib/model/controller/acl_connection_handler.h
@@ -21,41 +21,61 @@
 #include <unordered_map>
 
 #include "acl_connection.h"
+#include "hci/address.h"
+#include "hci/address_with_type.h"
 #include "include/acl.h"
-#include "types/address.h"
+#include "phy.h"
 
 namespace test_vendor_lib {
 
 class AclConnectionHandler {
  public:
-  AclConnectionHandler(size_t max_pending_connections = 1) : max_pending_connections_(max_pending_connections) {}
+  AclConnectionHandler() = default;
 
   virtual ~AclConnectionHandler() = default;
 
-  bool CreatePendingConnection(const Address& addr);
-  bool HasPendingConnection(const Address& addr);
-  bool CancelPendingConnection(const Address& addr);
+  bool CreatePendingConnection(bluetooth::hci::Address addr,
+                               bool authenticate_on_connect);
+  bool HasPendingConnection(bluetooth::hci::Address addr) const;
+  bool CancelPendingConnection(bluetooth::hci::Address addr);
+  bool AuthenticatePendingConnection() const;
 
-  uint16_t CreateConnection(const Address& addr);
+  bool CreatePendingLeConnection(bluetooth::hci::AddressWithType addr);
+  bool HasPendingLeConnection(bluetooth::hci::AddressWithType addr) const;
+  bool CancelPendingLeConnection(bluetooth::hci::AddressWithType addr);
+
+  uint16_t CreateConnection(bluetooth::hci::Address addr,
+                            bluetooth::hci::Address own_addr);
+  uint16_t CreateLeConnection(bluetooth::hci::AddressWithType addr,
+                              bluetooth::hci::AddressWithType own_addr);
   bool Disconnect(uint16_t handle);
   bool HasHandle(uint16_t handle) const;
 
-  uint16_t GetHandle(const Address& addr) const;
-  const Address& GetAddress(uint16_t handle) const;
-
-  void SetConnected(uint16_t handle, bool connected);
-  bool IsConnected(uint16_t handle) const;
+  uint16_t GetHandle(bluetooth::hci::AddressWithType addr) const;
+  uint16_t GetHandleOnlyAddress(bluetooth::hci::Address addr) const;
+  bluetooth::hci::AddressWithType GetAddress(uint16_t handle) const;
+  bluetooth::hci::AddressWithType GetOwnAddress(uint16_t handle) const;
 
   void Encrypt(uint16_t handle);
   bool IsEncrypted(uint16_t handle) const;
 
-  void SetAddress(uint16_t handle, const Address& address);
+  void SetAddress(uint16_t handle, bluetooth::hci::AddressWithType address);
+
+  Phy::Type GetPhyType(uint16_t handle) const;
 
  private:
   std::unordered_map<uint16_t, AclConnection> acl_connections_;
-  size_t max_pending_connections_;
-  std::set<Address> pending_connections_;
+  bool classic_connection_pending_{false};
+  bluetooth::hci::Address pending_connection_address_{
+      bluetooth::hci::Address::kEmpty};
+  bool authenticate_pending_classic_connection_{false};
+  bool le_connection_pending_{false};
+  bluetooth::hci::AddressWithType pending_le_connection_address_{
+      bluetooth::hci::Address::kEmpty,
+      bluetooth::hci::AddressType::PUBLIC_DEVICE_ADDRESS};
+
   uint16_t GetUnusedHandle();
+  uint16_t last_handle_{acl::kReservedHandle - 2};
 };
 
 }  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/model/controller/dual_mode_controller.cc b/vendor_libs/test_vendor_lib/model/controller/dual_mode_controller.cc
index 273dd09..5aed73d 100644
--- a/vendor_libs/test_vendor_lib/model/controller/dual_mode_controller.cc
+++ b/vendor_libs/test_vendor_lib/model/controller/dual_mode_controller.cc
@@ -14,60 +14,46 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "dual_mode_controller"
-
 #include "dual_mode_controller.h"
 
 #include <memory>
 
 #include <base/files/file_util.h>
 #include <base/json/json_reader.h>
-#include <base/logging.h>
 #include <base/values.h>
 
-#include "osi/include/log.h"
-#include "osi/include/osi.h"
+#include "os/log.h"
+#include "packet/raw_builder.h"
 
-#include "hci.h"
-#include "packets/hci/acl_packet_view.h"
-#include "packets/hci/command_packet_view.h"
-#include "packets/hci/event_packet_builder.h"
-#include "packets/hci/sco_packet_view.h"
-
+namespace gd_hci = ::bluetooth::hci;
+using gd_hci::ErrorCode;
+using gd_hci::LoopbackMode;
+using gd_hci::OpCode;
 using std::vector;
-using test_vendor_lib::hci::EventCode;
-using test_vendor_lib::hci::OpCode;
-
-namespace {
-
-size_t LastNonZero(test_vendor_lib::packets::PacketView<true> view) {
-  for (size_t i = view.size() - 1; i > 0; i--) {
-    if (view[i] != 0) {
-      return i;
-    }
-  }
-  return 0;
-}
-
-}  // namespace
 
 namespace test_vendor_lib {
 constexpr char DualModeController::kControllerPropertiesFile[];
 constexpr uint16_t DualModeController::kSecurityManagerNumKeys;
+constexpr uint16_t kNumCommandPackets = 0x01;
 
 // Device methods.
 void DualModeController::Initialize(const std::vector<std::string>& args) {
   if (args.size() < 2) return;
 
   Address addr;
-  if (Address::FromString(args[1], addr)) properties_.SetAddress(addr);
+  if (Address::FromString(args[1], addr)) {
+    properties_.SetAddress(addr);
+  } else {
+    LOG_ALWAYS_FATAL("Invalid address: %s", args[1].c_str());
+  }
 };
 
 std::string DualModeController::GetTypeString() const {
   return "Simulated Bluetooth Controller";
 }
 
-void DualModeController::IncomingPacket(packets::LinkLayerPacketView incoming) {
+void DualModeController::IncomingPacket(
+    model::packets::LinkLayerPacketView incoming) {
   link_layer_controller_.IncomingPacket(incoming);
 }
 
@@ -75,157 +61,190 @@
   link_layer_controller_.TimerTick();
 }
 
-void DualModeController::SendLinkLayerPacket(std::shared_ptr<packets::LinkLayerPacketBuilder> to_send,
-                                             Phy::Type phy_type) {
-  for (auto phy_pair : phy_layers_) {
-    auto phy_list = std::get<1>(phy_pair);
-    if (phy_type != std::get<0>(phy_pair)) {
-      continue;
-    }
-    for (auto phy : phy_list) {
-      phy->Send(to_send);
-    }
-  }
-}
-
-/*
-void DualModeController::AddConnectionAction(const TaskCallback& task,
-                                             uint16_t handle) {
-  for (size_t i = 0; i < connections_.size(); i++)
-    if (connections_[i]->GetHandle() == handle)
-connections_[i]->AddAction(task);
-}
-*/
-
-void DualModeController::SendCommandCompleteSuccess(OpCode command_opcode) const {
-  send_event_(packets::EventPacketBuilder::CreateCommandCompleteOnlyStatusEvent(command_opcode, hci::Status::SUCCESS)
-                  ->ToVector());
-}
-
 void DualModeController::SendCommandCompleteUnknownOpCodeEvent(uint16_t command_opcode) const {
-  send_event_(packets::EventPacketBuilder::CreateCommandCompleteUnknownOpCodeEvent(command_opcode)->ToVector());
-}
+  std::unique_ptr<bluetooth::packet::RawBuilder> raw_builder_ptr =
+      std::make_unique<bluetooth::packet::RawBuilder>();
+  raw_builder_ptr->AddOctets1(kNumCommandPackets);
+  raw_builder_ptr->AddOctets2(command_opcode);
+  raw_builder_ptr->AddOctets1(
+      static_cast<uint8_t>(ErrorCode::UNKNOWN_HCI_COMMAND));
 
-void DualModeController::SendCommandCompleteOnlyStatus(OpCode command_opcode, hci::Status status) const {
-  send_event_(packets::EventPacketBuilder::CreateCommandCompleteOnlyStatusEvent(command_opcode, status)->ToVector());
-}
-
-void DualModeController::SendCommandCompleteStatusAndAddress(OpCode command_opcode, hci::Status status,
-                                                             const Address& address) const {
-  send_event_(packets::EventPacketBuilder::CreateCommandCompleteStatusAndAddressEvent(command_opcode, status, address)
-                  ->ToVector());
-}
-
-void DualModeController::SendCommandStatus(hci::Status status, OpCode command_opcode) const {
-  send_event_(packets::EventPacketBuilder::CreateCommandStatusEvent(status, command_opcode)->ToVector());
-}
-
-void DualModeController::SendCommandStatusSuccess(OpCode command_opcode) const {
-  SendCommandStatus(hci::Status::SUCCESS, command_opcode);
+  auto packet = gd_hci::EventPacketBuilder::Create(
+      gd_hci::EventCode::COMMAND_COMPLETE, std::move(raw_builder_ptr));
+  send_event_(std::move(packet));
 }
 
 DualModeController::DualModeController(const std::string& properties_filename, uint16_t num_keys)
     : Device(properties_filename), security_manager_(num_keys) {
-  loopback_mode_ = hci::LoopbackMode::NO;
+  loopback_mode_ = LoopbackMode::NO_LOOPBACK;
 
   Address public_address;
-  CHECK(Address::FromString("3C:5A:B4:04:05:06", public_address));
+  ASSERT(Address::FromString("3C:5A:B4:04:05:06", public_address));
   properties_.SetAddress(public_address);
 
   link_layer_controller_.RegisterRemoteChannel(
-      [this](std::shared_ptr<packets::LinkLayerPacketBuilder> packet, Phy::Type phy_type) {
+      [this](std::shared_ptr<model::packets::LinkLayerPacketBuilder> packet,
+             Phy::Type phy_type) {
         DualModeController::SendLinkLayerPacket(packet, phy_type);
       });
 
-#define SET_HANDLER(opcode, method) \
-  active_hci_commands_[static_cast<uint16_t>(opcode)] = [this](packets::PacketView<true> param) { method(param); };
-  SET_HANDLER(OpCode::RESET, HciReset);
-  SET_HANDLER(OpCode::READ_BUFFER_SIZE, HciReadBufferSize);
-  SET_HANDLER(OpCode::HOST_BUFFER_SIZE, HciHostBufferSize);
-  SET_HANDLER(OpCode::SNIFF_SUBRATING, HciSniffSubrating);
-  SET_HANDLER(OpCode::READ_LOCAL_VERSION_INFORMATION, HciReadLocalVersionInformation);
-  SET_HANDLER(OpCode::READ_BD_ADDR, HciReadBdAddr);
-  SET_HANDLER(OpCode::READ_LOCAL_SUPPORTED_COMMANDS, HciReadLocalSupportedCommands);
-  SET_HANDLER(OpCode::READ_LOCAL_SUPPORTED_CODECS, HciReadLocalSupportedCodecs);
-  SET_HANDLER(OpCode::READ_LOCAL_EXTENDED_FEATURES, HciReadLocalExtendedFeatures);
-  SET_HANDLER(OpCode::READ_REMOTE_EXTENDED_FEATURES, HciReadRemoteExtendedFeatures);
-  SET_HANDLER(OpCode::READ_REMOTE_SUPPORTED_FEATURES, HciReadRemoteSupportedFeatures);
-  SET_HANDLER(OpCode::READ_CLOCK_OFFSET, HciReadClockOffset);
-  SET_HANDLER(OpCode::IO_CAPABILITY_REQUEST_REPLY, HciIoCapabilityRequestReply);
-  SET_HANDLER(OpCode::USER_CONFIRMATION_REQUEST_REPLY, HciUserConfirmationRequestReply);
-  SET_HANDLER(OpCode::USER_CONFIRMATION_REQUEST_NEGATIVE_REPLY, HciUserConfirmationRequestNegativeReply);
-  SET_HANDLER(OpCode::IO_CAPABILITY_REQUEST_NEGATIVE_REPLY, HciIoCapabilityRequestNegativeReply);
-  SET_HANDLER(OpCode::WRITE_SIMPLE_PAIRING_MODE, HciWriteSimplePairingMode);
-  SET_HANDLER(OpCode::WRITE_LE_HOST_SUPPORT, HciWriteLeHostSupport);
-  SET_HANDLER(OpCode::SET_EVENT_MASK, HciSetEventMask);
-  SET_HANDLER(OpCode::WRITE_INQUIRY_MODE, HciWriteInquiryMode);
-  SET_HANDLER(OpCode::WRITE_PAGE_SCAN_TYPE, HciWritePageScanType);
-  SET_HANDLER(OpCode::WRITE_INQUIRY_SCAN_TYPE, HciWriteInquiryScanType);
-  SET_HANDLER(OpCode::AUTHENTICATION_REQUESTED, HciAuthenticationRequested);
-  SET_HANDLER(OpCode::SET_CONNECTION_ENCRYPTION, HciSetConnectionEncryption);
-  SET_HANDLER(OpCode::WRITE_AUTHENTICATION_ENABLE, HciWriteAuthenticationEnable);
-  SET_HANDLER(OpCode::READ_AUTHENTICATION_ENABLE, HciReadAuthenticationEnable);
-  SET_HANDLER(OpCode::WRITE_CLASS_OF_DEVICE, HciWriteClassOfDevice);
-  SET_HANDLER(OpCode::WRITE_PAGE_TIMEOUT, HciWritePageTimeout);
-  SET_HANDLER(OpCode::WRITE_LINK_SUPERVISION_TIMEOUT, HciWriteLinkSupervisionTimeout);
-  SET_HANDLER(OpCode::WRITE_DEFAULT_LINK_POLICY_SETTINGS, HciWriteDefaultLinkPolicySettings);
-  SET_HANDLER(OpCode::WRITE_LINK_POLICY_SETTINGS, HciWriteLinkPolicySettings);
-  SET_HANDLER(OpCode::CHANGE_CONNECTION_PACKET_TYPE, HciChangeConnectionPacketType);
-  SET_HANDLER(OpCode::WRITE_LOCAL_NAME, HciWriteLocalName);
-  SET_HANDLER(OpCode::READ_LOCAL_NAME, HciReadLocalName);
-  SET_HANDLER(OpCode::WRITE_EXTENDED_INQUIRY_RESPONSE, HciWriteExtendedInquiryResponse);
-  SET_HANDLER(OpCode::WRITE_VOICE_SETTING, HciWriteVoiceSetting);
-  SET_HANDLER(OpCode::WRITE_CURRENT_IAC_LAP, HciWriteCurrentIacLap);
-  SET_HANDLER(OpCode::WRITE_INQUIRY_SCAN_ACTIVITY, HciWriteInquiryScanActivity);
-  SET_HANDLER(OpCode::WRITE_SCAN_ENABLE, HciWriteScanEnable);
-  SET_HANDLER(OpCode::SET_EVENT_FILTER, HciSetEventFilter);
-  SET_HANDLER(OpCode::INQUIRY, HciInquiry);
-  SET_HANDLER(OpCode::INQUIRY_CANCEL, HciInquiryCancel);
-  SET_HANDLER(OpCode::ACCEPT_CONNECTION_REQUEST, HciAcceptConnectionRequest);
-  SET_HANDLER(OpCode::REJECT_CONNECTION_REQUEST, HciRejectConnectionRequest);
-  SET_HANDLER(OpCode::LINK_KEY_REQUEST_REPLY, HciLinkKeyRequestReply);
-  SET_HANDLER(OpCode::LINK_KEY_REQUEST_NEGATIVE_REPLY, HciLinkKeyRequestNegativeReply);
-  SET_HANDLER(OpCode::DELETE_STORED_LINK_KEY, HciDeleteStoredLinkKey);
-  SET_HANDLER(OpCode::REMOTE_NAME_REQUEST, HciRemoteNameRequest);
-  SET_HANDLER(OpCode::LE_SET_EVENT_MASK, HciLeSetEventMask);
-  SET_HANDLER(OpCode::LE_READ_BUFFER_SIZE, HciLeReadBufferSize);
-  SET_HANDLER(OpCode::LE_READ_LOCAL_SUPPORTED_FEATURES, HciLeReadLocalSupportedFeatures);
-  SET_HANDLER(OpCode::LE_SET_RANDOM_ADDRESS, HciLeSetRandomAddress);
-  SET_HANDLER(OpCode::LE_SET_ADVERTISING_DATA, HciLeSetAdvertisingData);
-  SET_HANDLER(OpCode::LE_SET_ADVERTISING_PARAMETERS, HciLeSetAdvertisingParameters);
-  SET_HANDLER(OpCode::LE_SET_SCAN_PARAMETERS, HciLeSetScanParameters);
-  SET_HANDLER(OpCode::LE_SET_SCAN_ENABLE, HciLeSetScanEnable);
-  SET_HANDLER(OpCode::LE_CREATE_CONNECTION, HciLeCreateConnection);
-  SET_HANDLER(OpCode::CREATE_CONNECTION, HciCreateConnection);
-  SET_HANDLER(OpCode::DISCONNECT, HciDisconnect);
-  SET_HANDLER(OpCode::LE_CREATE_CONNECTION_CANCEL, HciLeConnectionCancel);
-  SET_HANDLER(OpCode::LE_READ_WHITE_LIST_SIZE, HciLeReadWhiteListSize);
-  SET_HANDLER(OpCode::LE_CLEAR_WHITE_LIST, HciLeClearWhiteList);
-  SET_HANDLER(OpCode::LE_ADD_DEVICE_TO_WHITE_LIST, HciLeAddDeviceToWhiteList);
-  SET_HANDLER(OpCode::LE_REMOVE_DEVICE_FROM_WHITE_LIST, HciLeRemoveDeviceFromWhiteList);
-  SET_HANDLER(OpCode::LE_RAND, HciLeRand);
-  SET_HANDLER(OpCode::LE_READ_SUPPORTED_STATES, HciLeReadSupportedStates);
-  SET_HANDLER(OpCode::LE_GET_VENDOR_CAPABILITIES, HciLeVendorCap);
-  SET_HANDLER(OpCode::LE_MULTI_ADVT, HciLeVendorMultiAdv);
-  SET_HANDLER(OpCode::LE_ADV_FILTER, HciLeAdvertisingFilter);
-  SET_HANDLER(OpCode::LE_ENERGY_INFO, HciLeEnergyInfo);
-  SET_HANDLER(OpCode::LE_EXTENDED_SCAN_PARAMS, HciLeExtendedScanParams);
-  SET_HANDLER(OpCode::LE_READ_REMOTE_FEATURES, HciLeReadRemoteFeatures);
-  SET_HANDLER(OpCode::READ_REMOTE_VERSION_INFORMATION, HciReadRemoteVersionInformation);
-  SET_HANDLER(OpCode::LE_CONNECTION_UPDATE, HciLeConnectionUpdate);
-  SET_HANDLER(OpCode::LE_START_ENCRYPTION, HciLeStartEncryption);
+#define SET_HANDLER(opcode, method)                                \
+  active_hci_commands_[opcode] = [this](CommandPacketView param) { \
+    method(param);                                                 \
+  };
+  SET_HANDLER(OpCode::RESET, Reset);
+  SET_HANDLER(OpCode::READ_BUFFER_SIZE, ReadBufferSize);
+  SET_HANDLER(OpCode::HOST_BUFFER_SIZE, HostBufferSize);
+  SET_HANDLER(OpCode::SNIFF_SUBRATING, SniffSubrating);
+  SET_HANDLER(OpCode::READ_ENCRYPTION_KEY_SIZE, ReadEncryptionKeySize);
+  SET_HANDLER(OpCode::READ_LOCAL_VERSION_INFORMATION,
+              ReadLocalVersionInformation);
+  SET_HANDLER(OpCode::READ_BD_ADDR, ReadBdAddr);
+  SET_HANDLER(OpCode::READ_LOCAL_SUPPORTED_COMMANDS,
+              ReadLocalSupportedCommands);
+  SET_HANDLER(OpCode::READ_LOCAL_SUPPORTED_FEATURES,
+              ReadLocalSupportedFeatures);
+  SET_HANDLER(OpCode::READ_LOCAL_SUPPORTED_CODECS, ReadLocalSupportedCodecs);
+  SET_HANDLER(OpCode::READ_LOCAL_EXTENDED_FEATURES, ReadLocalExtendedFeatures);
+  SET_HANDLER(OpCode::READ_REMOTE_EXTENDED_FEATURES,
+              ReadRemoteExtendedFeatures);
+  SET_HANDLER(OpCode::SWITCH_ROLE, SwitchRole);
+  SET_HANDLER(OpCode::READ_REMOTE_SUPPORTED_FEATURES,
+              ReadRemoteSupportedFeatures);
+  SET_HANDLER(OpCode::READ_CLOCK_OFFSET, ReadClockOffset);
+  SET_HANDLER(OpCode::IO_CAPABILITY_REQUEST_REPLY, IoCapabilityRequestReply);
+  SET_HANDLER(OpCode::USER_CONFIRMATION_REQUEST_REPLY,
+              UserConfirmationRequestReply);
+  SET_HANDLER(OpCode::USER_CONFIRMATION_REQUEST_NEGATIVE_REPLY,
+              UserConfirmationRequestNegativeReply);
+  SET_HANDLER(OpCode::IO_CAPABILITY_REQUEST_NEGATIVE_REPLY,
+              IoCapabilityRequestNegativeReply);
+  SET_HANDLER(OpCode::READ_INQUIRY_RESPONSE_TRANSMIT_POWER_LEVEL,
+              ReadInquiryResponseTransmitPowerLevel);
+  SET_HANDLER(OpCode::WRITE_SIMPLE_PAIRING_MODE, WriteSimplePairingMode);
+  SET_HANDLER(OpCode::WRITE_LE_HOST_SUPPORT, WriteLeHostSupport);
+  SET_HANDLER(OpCode::WRITE_SECURE_CONNECTIONS_HOST_SUPPORT,
+              WriteSecureConnectionsHostSupport);
+  SET_HANDLER(OpCode::SET_EVENT_MASK, SetEventMask);
+  SET_HANDLER(OpCode::READ_INQUIRY_MODE, ReadInquiryMode);
+  SET_HANDLER(OpCode::WRITE_INQUIRY_MODE, WriteInquiryMode);
+  SET_HANDLER(OpCode::READ_PAGE_SCAN_TYPE, ReadPageScanType);
+  SET_HANDLER(OpCode::WRITE_PAGE_SCAN_TYPE, WritePageScanType);
+  SET_HANDLER(OpCode::WRITE_INQUIRY_SCAN_TYPE, WriteInquiryScanType);
+  SET_HANDLER(OpCode::READ_INQUIRY_SCAN_TYPE, ReadInquiryScanType);
+  SET_HANDLER(OpCode::AUTHENTICATION_REQUESTED, AuthenticationRequested);
+  SET_HANDLER(OpCode::SET_CONNECTION_ENCRYPTION, SetConnectionEncryption);
+  SET_HANDLER(OpCode::CHANGE_CONNECTION_LINK_KEY, ChangeConnectionLinkKey);
+  SET_HANDLER(OpCode::MASTER_LINK_KEY, MasterLinkKey);
+  SET_HANDLER(OpCode::WRITE_AUTHENTICATION_ENABLE, WriteAuthenticationEnable);
+  SET_HANDLER(OpCode::READ_AUTHENTICATION_ENABLE, ReadAuthenticationEnable);
+  SET_HANDLER(OpCode::WRITE_CLASS_OF_DEVICE, WriteClassOfDevice);
+  SET_HANDLER(OpCode::READ_PAGE_TIMEOUT, ReadPageTimeout);
+  SET_HANDLER(OpCode::WRITE_PAGE_TIMEOUT, WritePageTimeout);
+  SET_HANDLER(OpCode::WRITE_LINK_SUPERVISION_TIMEOUT,
+              WriteLinkSupervisionTimeout);
+  SET_HANDLER(OpCode::HOLD_MODE, HoldMode);
+  SET_HANDLER(OpCode::SNIFF_MODE, SniffMode);
+  SET_HANDLER(OpCode::EXIT_SNIFF_MODE, ExitSniffMode);
+  SET_HANDLER(OpCode::QOS_SETUP, QosSetup);
+  SET_HANDLER(OpCode::WRITE_DEFAULT_LINK_POLICY_SETTINGS,
+              WriteDefaultLinkPolicySettings);
+  SET_HANDLER(OpCode::FLOW_SPECIFICATION, FlowSpecification);
+  SET_HANDLER(OpCode::WRITE_LINK_POLICY_SETTINGS, WriteLinkPolicySettings);
+  SET_HANDLER(OpCode::CHANGE_CONNECTION_PACKET_TYPE,
+              ChangeConnectionPacketType);
+  SET_HANDLER(OpCode::WRITE_LOCAL_NAME, WriteLocalName);
+  SET_HANDLER(OpCode::READ_LOCAL_NAME, ReadLocalName);
+  SET_HANDLER(OpCode::WRITE_EXTENDED_INQUIRY_RESPONSE,
+              WriteExtendedInquiryResponse);
+  SET_HANDLER(OpCode::REFRESH_ENCRYPTION_KEY, RefreshEncryptionKey);
+  SET_HANDLER(OpCode::WRITE_VOICE_SETTING, WriteVoiceSetting);
+  SET_HANDLER(OpCode::READ_NUMBER_OF_SUPPORTED_IAC, ReadNumberOfSupportedIac);
+  SET_HANDLER(OpCode::READ_CURRENT_IAC_LAP, ReadCurrentIacLap);
+  SET_HANDLER(OpCode::WRITE_CURRENT_IAC_LAP, WriteCurrentIacLap);
+  SET_HANDLER(OpCode::READ_PAGE_SCAN_ACTIVITY, ReadPageScanActivity);
+  SET_HANDLER(OpCode::WRITE_PAGE_SCAN_ACTIVITY, WritePageScanActivity);
+  SET_HANDLER(OpCode::READ_INQUIRY_SCAN_ACTIVITY, ReadInquiryScanActivity);
+  SET_HANDLER(OpCode::WRITE_INQUIRY_SCAN_ACTIVITY, WriteInquiryScanActivity);
+  SET_HANDLER(OpCode::READ_SCAN_ENABLE, ReadScanEnable);
+  SET_HANDLER(OpCode::WRITE_SCAN_ENABLE, WriteScanEnable);
+  SET_HANDLER(OpCode::SET_EVENT_FILTER, SetEventFilter);
+  SET_HANDLER(OpCode::INQUIRY, Inquiry);
+  SET_HANDLER(OpCode::INQUIRY_CANCEL, InquiryCancel);
+  SET_HANDLER(OpCode::ACCEPT_CONNECTION_REQUEST, AcceptConnectionRequest);
+  SET_HANDLER(OpCode::REJECT_CONNECTION_REQUEST, RejectConnectionRequest);
+  SET_HANDLER(OpCode::LINK_KEY_REQUEST_REPLY, LinkKeyRequestReply);
+  SET_HANDLER(OpCode::LINK_KEY_REQUEST_NEGATIVE_REPLY,
+              LinkKeyRequestNegativeReply);
+  SET_HANDLER(OpCode::DELETE_STORED_LINK_KEY, DeleteStoredLinkKey);
+  SET_HANDLER(OpCode::REMOTE_NAME_REQUEST, RemoteNameRequest);
+  SET_HANDLER(OpCode::LE_SET_EVENT_MASK, LeSetEventMask);
+  SET_HANDLER(OpCode::LE_READ_BUFFER_SIZE, LeReadBufferSize);
+  SET_HANDLER(OpCode::LE_READ_LOCAL_SUPPORTED_FEATURES,
+              LeReadLocalSupportedFeatures);
+  SET_HANDLER(OpCode::LE_SET_RANDOM_ADDRESS, LeSetRandomAddress);
+  SET_HANDLER(OpCode::LE_SET_ADVERTISING_PARAMETERS,
+              LeSetAdvertisingParameters);
+  SET_HANDLER(OpCode::LE_SET_ADVERTISING_DATA, LeSetAdvertisingData);
+  SET_HANDLER(OpCode::LE_SET_SCAN_RESPONSE_DATA, LeSetScanResponseData);
+  SET_HANDLER(OpCode::LE_SET_ADVERTISING_ENABLE, LeSetAdvertisingEnable);
+  SET_HANDLER(OpCode::LE_SET_SCAN_PARAMETERS, LeSetScanParameters);
+  SET_HANDLER(OpCode::LE_SET_SCAN_ENABLE, LeSetScanEnable);
+  SET_HANDLER(OpCode::LE_CREATE_CONNECTION, LeCreateConnection);
+  SET_HANDLER(OpCode::CREATE_CONNECTION, CreateConnection);
+  SET_HANDLER(OpCode::DISCONNECT, Disconnect);
+  SET_HANDLER(OpCode::LE_CREATE_CONNECTION_CANCEL, LeConnectionCancel);
+  SET_HANDLER(OpCode::LE_READ_WHITE_LIST_SIZE, LeReadWhiteListSize);
+  SET_HANDLER(OpCode::LE_CLEAR_WHITE_LIST, LeClearWhiteList);
+  SET_HANDLER(OpCode::LE_ADD_DEVICE_TO_WHITE_LIST, LeAddDeviceToWhiteList);
+  SET_HANDLER(OpCode::LE_REMOVE_DEVICE_FROM_WHITE_LIST,
+              LeRemoveDeviceFromWhiteList);
+  SET_HANDLER(OpCode::LE_RAND, LeRand);
+  SET_HANDLER(OpCode::LE_READ_SUPPORTED_STATES, LeReadSupportedStates);
+  SET_HANDLER(OpCode::LE_GET_VENDOR_CAPABILITIES, LeVendorCap);
+  SET_HANDLER(OpCode::LE_MULTI_ADVT, LeVendorMultiAdv);
+  SET_HANDLER(OpCode::LE_ADV_FILTER, LeAdvertisingFilter);
+  SET_HANDLER(OpCode::LE_ENERGY_INFO, LeEnergyInfo);
+  SET_HANDLER(OpCode::LE_SET_EXTENDED_ADVERTISING_RANDOM_ADDRESS,
+              LeSetExtendedAdvertisingRandomAddress);
+  SET_HANDLER(OpCode::LE_SET_EXTENDED_ADVERTISING_PARAMETERS,
+              LeSetExtendedAdvertisingParameters);
+  SET_HANDLER(OpCode::LE_SET_EXTENDED_ADVERTISING_DATA,
+              LeSetExtendedAdvertisingData);
+  SET_HANDLER(OpCode::LE_SET_EXTENDED_ADVERTISING_SCAN_RESPONSE,
+              LeSetExtendedAdvertisingScanResponse);
+  SET_HANDLER(OpCode::LE_SET_EXTENDED_ADVERTISING_ENABLE,
+              LeSetExtendedAdvertisingEnable);
+  SET_HANDLER(OpCode::LE_READ_REMOTE_FEATURES, LeReadRemoteFeatures);
+  SET_HANDLER(OpCode::READ_REMOTE_VERSION_INFORMATION,
+              ReadRemoteVersionInformation);
+  SET_HANDLER(OpCode::LE_CONNECTION_UPDATE, LeConnectionUpdate);
+  SET_HANDLER(OpCode::LE_START_ENCRYPTION, LeStartEncryption);
+  SET_HANDLER(OpCode::LE_ADD_DEVICE_TO_RESOLVING_LIST,
+              LeAddDeviceToResolvingList);
+  SET_HANDLER(OpCode::LE_REMOVE_DEVICE_FROM_RESOLVING_LIST,
+              LeRemoveDeviceFromResolvingList);
+  SET_HANDLER(OpCode::LE_CLEAR_RESOLVING_LIST, LeClearResolvingList);
+  SET_HANDLER(OpCode::LE_SET_EXTENDED_SCAN_PARAMETERS,
+              LeSetExtendedScanParameters);
+  SET_HANDLER(OpCode::LE_SET_EXTENDED_SCAN_ENABLE, LeSetExtendedScanEnable);
+  SET_HANDLER(OpCode::LE_EXTENDED_CREATE_CONNECTION,
+              LeExtendedCreateConnection);
+  SET_HANDLER(OpCode::LE_SET_PRIVACY_MODE, LeSetPrivacyMode);
   // Testing Commands
-  SET_HANDLER(OpCode::READ_LOOPBACK_MODE, HciReadLoopbackMode);
-  SET_HANDLER(OpCode::WRITE_LOOPBACK_MODE, HciWriteLoopbackMode);
+  SET_HANDLER(OpCode::READ_LOOPBACK_MODE, ReadLoopbackMode);
+  SET_HANDLER(OpCode::WRITE_LOOPBACK_MODE, WriteLoopbackMode);
 #undef SET_HANDLER
 }
 
-void DualModeController::HciSniffSubrating(packets::PacketView<true> args) {
-  CHECK(args.size() == 8) << __func__ << " size=" << args.size();
+void DualModeController::SniffSubrating(CommandPacketView command) {
+  auto command_view = gd_hci::SniffSubratingView::Create(
+      gd_hci::ConnectionManagementCommandView::Create(command));
+  ASSERT(command_view.IsValid());
 
-  uint16_t handle = args.begin().extract<uint16_t>();
-
-  send_event_(packets::EventPacketBuilder::CreateSniffSubratingEvent(hci::Status::SUCCESS, handle)->ToVector());
+  send_event_(gd_hci::SniffSubratingCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS,
+      command_view.GetConnectionHandle()));
 }
 
 void DualModeController::RegisterTaskScheduler(
@@ -244,11 +263,19 @@
 }
 
 void DualModeController::HandleAcl(std::shared_ptr<std::vector<uint8_t>> packet) {
-  auto acl_packet = packets::AclPacketView::Create(packet);
-  if (loopback_mode_ == hci::LoopbackMode::LOCAL) {
+  bluetooth::hci::PacketView<bluetooth::hci::kLittleEndian> raw_packet(packet);
+  auto acl_packet = bluetooth::hci::AclPacketView::Create(raw_packet);
+  ASSERT(acl_packet.IsValid());
+  if (loopback_mode_ == LoopbackMode::ENABLE_LOCAL) {
     uint16_t handle = acl_packet.GetHandle();
-    send_acl_(packet);
-    send_event_(packets::EventPacketBuilder::CreateNumberOfCompletedPacketsEvent(handle, 1)->ToVector());
+
+    std::vector<bluetooth::hci::CompletedPackets> completed_packets;
+    bluetooth::hci::CompletedPackets cp;
+    cp.connection_handle_ = handle;
+    cp.host_num_of_completed_packets_ = kNumCommandPackets;
+    completed_packets.push_back(cp);
+    send_event_(bluetooth::hci::NumberOfCompletedPacketsBuilder::Create(
+        completed_packets));
     return;
   }
 
@@ -256,44 +283,80 @@
 }
 
 void DualModeController::HandleSco(std::shared_ptr<std::vector<uint8_t>> packet) {
-  auto sco_packet = packets::ScoPacketView::Create(packet);
-  if (loopback_mode_ == hci::LoopbackMode::LOCAL) {
+  bluetooth::hci::PacketView<bluetooth::hci::kLittleEndian> raw_packet(packet);
+  auto sco_packet = bluetooth::hci::ScoPacketView::Create(raw_packet);
+  if (loopback_mode_ == LoopbackMode::ENABLE_LOCAL) {
     uint16_t handle = sco_packet.GetHandle();
     send_sco_(packet);
-    send_event_(packets::EventPacketBuilder::CreateNumberOfCompletedPacketsEvent(handle, 1)->ToVector());
+    std::vector<bluetooth::hci::CompletedPackets> completed_packets;
+    bluetooth::hci::CompletedPackets cp;
+    cp.connection_handle_ = handle;
+    cp.host_num_of_completed_packets_ = kNumCommandPackets;
+    completed_packets.push_back(cp);
+    send_event_(bluetooth::hci::NumberOfCompletedPacketsBuilder::Create(
+        completed_packets));
     return;
   }
 }
 
-void DualModeController::HandleCommand(std::shared_ptr<std::vector<uint8_t>> packet) {
-  auto command_packet = packets::CommandPacketView::Create(packet);
-  uint16_t opcode = command_packet.GetOpcode();
-  hci::OpCode op = static_cast<hci::OpCode>(opcode);
+void DualModeController::HandleIso(
+    std::shared_ptr<std::vector<uint8_t>> /* packet */) {
+  // TODO: implement handling similar to HandleSco
+}
 
-  if (loopback_mode_ == hci::LoopbackMode::LOCAL &&
+void DualModeController::HandleCommand(std::shared_ptr<std::vector<uint8_t>> packet) {
+  bluetooth::hci::PacketView<bluetooth::hci::kLittleEndian> raw_packet(packet);
+  auto command_packet = bluetooth::hci::CommandPacketView::Create(raw_packet);
+  ASSERT(command_packet.IsValid());
+  auto op = command_packet.GetOpCode();
+
+  if (loopback_mode_ == LoopbackMode::ENABLE_LOCAL &&
       // Loopback exceptions.
-      op != OpCode::RESET && op != OpCode::SET_CONTROLLER_TO_HOST_FLOW_CONTROL && op != OpCode::HOST_BUFFER_SIZE &&
-      op != OpCode::HOST_NUM_COMPLETED_PACKETS && op != OpCode::READ_BUFFER_SIZE && op != OpCode::READ_LOOPBACK_MODE &&
+      op != OpCode::RESET &&
+      op != OpCode::SET_CONTROLLER_TO_HOST_FLOW_CONTROL &&
+      op != OpCode::HOST_BUFFER_SIZE &&
+      op != OpCode::HOST_NUM_COMPLETED_PACKETS &&
+      op != OpCode::READ_BUFFER_SIZE && op != OpCode::READ_LOOPBACK_MODE &&
       op != OpCode::WRITE_LOOPBACK_MODE) {
-    send_event_(packets::EventPacketBuilder::CreateLoopbackCommandEvent(op, command_packet.GetPayload())->ToVector());
-  } else if (active_hci_commands_.count(opcode) > 0) {
-    active_hci_commands_[opcode](command_packet.GetPayload());
+    std::unique_ptr<bluetooth::packet::RawBuilder> raw_builder_ptr =
+        std::make_unique<bluetooth::packet::RawBuilder>(255);
+    raw_builder_ptr->AddOctets(*packet);
+    send_event_(bluetooth::hci::LoopbackCommandBuilder::Create(
+        std::move(raw_builder_ptr)));
+  } else if (active_hci_commands_.count(op) > 0) {
+    active_hci_commands_[op](command_packet);
   } else {
+    uint16_t opcode = static_cast<uint16_t>(op);
     SendCommandCompleteUnknownOpCodeEvent(opcode);
-    LOG_INFO(LOG_TAG, "Command opcode: 0x%04X, OGF: 0x%04X, OCF: 0x%04X", opcode, opcode & 0xFC00, opcode & 0x03FF);
+    LOG_INFO("Unknown command, opcode: 0x%04X, OGF: 0x%04X, OCF: 0x%04X",
+             opcode, (opcode & 0xFC00) >> 10, opcode & 0x03FF);
   }
 }
 
 void DualModeController::RegisterEventChannel(
     const std::function<void(std::shared_ptr<std::vector<uint8_t>>)>& callback) {
-  link_layer_controller_.RegisterEventChannel(callback);
-  send_event_ = callback;
+  send_event_ =
+      [callback](std::shared_ptr<bluetooth::hci::EventPacketBuilder> event) {
+        auto bytes = std::make_shared<std::vector<uint8_t>>();
+        bluetooth::packet::BitInserter bit_inserter(*bytes);
+        bytes->reserve(event->size());
+        event->Serialize(bit_inserter);
+        callback(std::move(bytes));
+      };
+  link_layer_controller_.RegisterEventChannel(send_event_);
 }
 
 void DualModeController::RegisterAclChannel(
     const std::function<void(std::shared_ptr<std::vector<uint8_t>>)>& callback) {
-  link_layer_controller_.RegisterAclChannel(callback);
-  send_acl_ = callback;
+  send_acl_ =
+      [callback](std::shared_ptr<bluetooth::hci::AclPacketBuilder> acl_data) {
+        auto bytes = std::make_shared<std::vector<uint8_t>>();
+        bluetooth::packet::BitInserter bit_inserter(*bytes);
+        bytes->reserve(acl_data->size());
+        acl_data->Serialize(bit_inserter);
+        callback(std::move(bytes));
+      };
+  link_layer_controller_.RegisterAclChannel(send_acl_);
 }
 
 void DualModeController::RegisterScoChannel(
@@ -302,792 +365,1659 @@
   send_sco_ = callback;
 }
 
-void DualModeController::HciReset(packets::PacketView<true> args) {
-  CHECK(args.size() == 0) << __func__ << " size=" << args.size();
+void DualModeController::RegisterIsoChannel(
+    const std::function<void(std::shared_ptr<std::vector<uint8_t>>)>&
+        callback) {
+  link_layer_controller_.RegisterIsoChannel(callback);
+  send_iso_ = callback;
+}
+
+void DualModeController::Reset(CommandPacketView command) {
+  auto command_view = gd_hci::ResetView::Create(command);
+  ASSERT(command_view.IsValid());
   link_layer_controller_.Reset();
-
-  SendCommandCompleteSuccess(OpCode::RESET);
-}
-
-void DualModeController::HciReadBufferSize(packets::PacketView<true> args) {
-  CHECK(args.size() == 0) << __func__ << " size=" << args.size();
-  std::shared_ptr<packets::EventPacketBuilder> command_complete =
-      packets::EventPacketBuilder::CreateCommandCompleteReadBufferSize(
-          hci::Status::SUCCESS, properties_.GetAclDataPacketSize(), properties_.GetSynchronousDataPacketSize(),
-          properties_.GetTotalNumAclDataPackets(), properties_.GetTotalNumSynchronousDataPackets());
-
-  send_event_(command_complete->ToVector());
-}
-
-void DualModeController::HciHostBufferSize(packets::PacketView<true> args) {
-  CHECK(args.size() == 7) << __func__ << " size=" << args.size();
-  SendCommandCompleteSuccess(OpCode::HOST_BUFFER_SIZE);
-}
-
-void DualModeController::HciReadLocalVersionInformation(packets::PacketView<true> args) {
-  CHECK(args.size() == 0) << __func__ << " size=" << args.size();
-  std::shared_ptr<packets::EventPacketBuilder> command_complete =
-      packets::EventPacketBuilder::CreateCommandCompleteReadLocalVersionInformation(
-          hci::Status::SUCCESS, properties_.GetVersion(), properties_.GetRevision(), properties_.GetLmpPalVersion(),
-          properties_.GetManufacturerName(), properties_.GetLmpPalSubversion());
-  send_event_(command_complete->ToVector());
-}
-
-void DualModeController::HciReadRemoteVersionInformation(packets::PacketView<true> args) {
-  CHECK(args.size() == 2) << __func__ << " size=" << args.size();
-
-  uint16_t handle = args.begin().extract<uint16_t>();
-
-  hci::Status status =
-      link_layer_controller_.SendCommandToRemoteByHandle(OpCode::READ_REMOTE_VERSION_INFORMATION, args, handle);
-
-  SendCommandStatus(status, OpCode::READ_REMOTE_VERSION_INFORMATION);
-}
-
-void DualModeController::HciReadBdAddr(packets::PacketView<true> args) {
-  CHECK(args.size() == 0) << __func__ << " size=" << args.size();
-  std::shared_ptr<packets::EventPacketBuilder> command_complete =
-      packets::EventPacketBuilder::CreateCommandCompleteReadBdAddr(hci::Status::SUCCESS, properties_.GetAddress());
-  send_event_(command_complete->ToVector());
-}
-
-void DualModeController::HciReadLocalSupportedCommands(packets::PacketView<true> args) {
-  CHECK(args.size() == 0) << __func__ << " size=" << args.size();
-  std::shared_ptr<packets::EventPacketBuilder> command_complete =
-      packets::EventPacketBuilder::CreateCommandCompleteReadLocalSupportedCommands(hci::Status::SUCCESS,
-                                                                                   properties_.GetSupportedCommands());
-  send_event_(command_complete->ToVector());
-}
-
-void DualModeController::HciReadLocalSupportedCodecs(packets::PacketView<true> args) {
-  CHECK(args.size() == 0) << __func__ << " size=" << args.size();
-  std::shared_ptr<packets::EventPacketBuilder> command_complete =
-      packets::EventPacketBuilder::CreateCommandCompleteReadLocalSupportedCodecs(
-          hci::Status::SUCCESS, properties_.GetSupportedCodecs(), properties_.GetVendorSpecificCodecs());
-  send_event_(command_complete->ToVector());
-}
-
-void DualModeController::HciReadLocalExtendedFeatures(packets::PacketView<true> args) {
-  CHECK(args.size() == 1) << __func__ << " size=" << args.size();
-  uint8_t page_number = args.begin().extract<uint8_t>();
-  send_event_(packets::EventPacketBuilder::CreateCommandCompleteReadLocalExtendedFeatures(
-                  hci::Status::SUCCESS, page_number, properties_.GetExtendedFeaturesMaximumPageNumber(),
-                  properties_.GetExtendedFeatures(page_number))
-                  ->ToVector());
-}
-
-void DualModeController::HciReadRemoteExtendedFeatures(packets::PacketView<true> args) {
-  CHECK(args.size() == 3) << __func__ << " size=" << args.size();
-
-  uint16_t handle = args.begin().extract<uint16_t>();
-
-  hci::Status status =
-      link_layer_controller_.SendCommandToRemoteByHandle(OpCode::READ_REMOTE_EXTENDED_FEATURES, args, handle);
-
-  SendCommandStatus(status, OpCode::READ_REMOTE_EXTENDED_FEATURES);
-}
-
-void DualModeController::HciReadRemoteSupportedFeatures(packets::PacketView<true> args) {
-  CHECK(args.size() == 2) << __func__ << " size=" << args.size();
-
-  uint16_t handle = args.begin().extract<uint16_t>();
-
-  hci::Status status =
-      link_layer_controller_.SendCommandToRemoteByHandle(OpCode::READ_REMOTE_SUPPORTED_FEATURES, args, handle);
-
-  SendCommandStatus(status, OpCode::READ_REMOTE_SUPPORTED_FEATURES);
-}
-
-void DualModeController::HciReadClockOffset(packets::PacketView<true> args) {
-  CHECK(args.size() == 2) << __func__ << " size=" << args.size();
-
-  uint16_t handle = args.begin().extract<uint16_t>();
-
-  hci::Status status = link_layer_controller_.SendCommandToRemoteByHandle(OpCode::READ_CLOCK_OFFSET, args, handle);
-
-  SendCommandStatus(status, OpCode::READ_CLOCK_OFFSET);
-}
-
-void DualModeController::HciIoCapabilityRequestReply(packets::PacketView<true> args) {
-  CHECK(args.size() == 9) << __func__ << " size=" << args.size();
-
-  auto args_itr = args.begin();
-  Address peer = args_itr.extract<Address>();
-  uint8_t io_capability = args_itr.extract<uint8_t>();
-  uint8_t oob_data_present_flag = args_itr.extract<uint8_t>();
-  uint8_t authentication_requirements = args_itr.extract<uint8_t>();
-
-  hci::Status status = link_layer_controller_.IoCapabilityRequestReply(peer, io_capability, oob_data_present_flag,
-                                                                       authentication_requirements);
-
-  SendCommandCompleteStatusAndAddress(OpCode::IO_CAPABILITY_REQUEST_REPLY, status, peer);
-}
-
-void DualModeController::HciUserConfirmationRequestReply(packets::PacketView<true> args) {
-  CHECK(args.size() == 6) << __func__ << " size=" << args.size();
-
-  Address peer = args.begin().extract<Address>();
-
-  hci::Status status = link_layer_controller_.UserConfirmationRequestReply(peer);
-
-  SendCommandCompleteStatusAndAddress(OpCode::USER_CONFIRMATION_REQUEST_REPLY, status, peer);
-}
-
-void DualModeController::HciUserConfirmationRequestNegativeReply(packets::PacketView<true> args) {
-  CHECK(args.size() == 6) << __func__ << " size=" << args.size();
-
-  Address peer = args.begin().extract<Address>();
-
-  hci::Status status = link_layer_controller_.UserConfirmationRequestNegativeReply(peer);
-
-  SendCommandCompleteStatusAndAddress(OpCode::USER_CONFIRMATION_REQUEST_NEGATIVE_REPLY, status, peer);
-}
-
-void DualModeController::HciUserPasskeyRequestReply(packets::PacketView<true> args) {
-  CHECK(args.size() == 10) << __func__ << " size=" << args.size();
-
-  auto args_itr = args.begin();
-  Address peer = args_itr.extract<Address>();
-  uint32_t numeric_value = args_itr.extract<uint32_t>();
-
-  hci::Status status = link_layer_controller_.UserPasskeyRequestReply(peer, numeric_value);
-
-  SendCommandCompleteStatusAndAddress(OpCode::USER_PASSKEY_REQUEST_REPLY, status, peer);
-}
-
-void DualModeController::HciUserPasskeyRequestNegativeReply(packets::PacketView<true> args) {
-  CHECK(args.size() == 6) << __func__ << " size=" << args.size();
-
-  Address peer = args.begin().extract<Address>();
-
-  hci::Status status = link_layer_controller_.UserPasskeyRequestNegativeReply(peer);
-
-  SendCommandCompleteStatusAndAddress(OpCode::USER_PASSKEY_REQUEST_NEGATIVE_REPLY, status, peer);
-}
-
-void DualModeController::HciRemoteOobDataRequestReply(packets::PacketView<true> args) {
-  CHECK(args.size() == 38) << __func__ << " size=" << args.size();
-
-  auto args_itr = args.begin();
-  Address peer = args_itr.extract<Address>();
-  std::vector<uint8_t> c;
-  std::vector<uint8_t> r;
-  for (size_t i = 0; i < 16; i++) {
-    c.push_back(args_itr.extract<uint8_t>());
+  if (loopback_mode_ == LoopbackMode::ENABLE_LOCAL) {
+    loopback_mode_ = LoopbackMode::NO_LOOPBACK;
   }
-  for (size_t i = 0; i < 16; i++) {
-    r.push_back(args_itr.extract<uint8_t>());
+
+  send_event_(bluetooth::hci::ResetCompleteBuilder::Create(kNumCommandPackets,
+                                                           ErrorCode::SUCCESS));
+}
+
+void DualModeController::ReadBufferSize(CommandPacketView command) {
+  auto command_view = gd_hci::ReadBufferSizeView::Create(command);
+  ASSERT(command_view.IsValid());
+
+  auto packet = bluetooth::hci::ReadBufferSizeCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS,
+      properties_.GetAclDataPacketSize(),
+      properties_.GetSynchronousDataPacketSize(),
+      properties_.GetTotalNumAclDataPackets(),
+      properties_.GetTotalNumSynchronousDataPackets());
+  send_event_(std::move(packet));
+}
+
+void DualModeController::ReadEncryptionKeySize(CommandPacketView command) {
+  auto command_view = gd_hci::ReadEncryptionKeySizeView::Create(
+      gd_hci::SecurityCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+
+  auto packet = bluetooth::hci::ReadEncryptionKeySizeCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS,
+      command_view.GetConnectionHandle(), properties_.GetEncryptionKeySize());
+  send_event_(std::move(packet));
+}
+
+void DualModeController::HostBufferSize(CommandPacketView command) {
+  auto command_view = gd_hci::HostBufferSizeView::Create(command);
+  ASSERT(command_view.IsValid());
+  auto packet = bluetooth::hci::HostBufferSizeCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
+}
+
+void DualModeController::ReadLocalVersionInformation(
+    CommandPacketView command) {
+  auto command_view = gd_hci::ReadLocalVersionInformationView::Create(command);
+  ASSERT(command_view.IsValid());
+
+  bluetooth::hci::LocalVersionInformation local_version_information;
+  local_version_information.hci_version_ =
+      static_cast<bluetooth::hci::HciVersion>(properties_.GetVersion());
+  local_version_information.hci_revision_ = properties_.GetRevision();
+  local_version_information.lmp_version_ =
+      static_cast<bluetooth::hci::LmpVersion>(properties_.GetLmpPalVersion());
+  local_version_information.manufacturer_name_ =
+      properties_.GetManufacturerName();
+  local_version_information.lmp_subversion_ = properties_.GetLmpPalSubversion();
+  auto packet =
+      bluetooth::hci::ReadLocalVersionInformationCompleteBuilder::Create(
+          kNumCommandPackets, ErrorCode::SUCCESS, local_version_information);
+  send_event_(std::move(packet));
+}
+
+void DualModeController::ReadRemoteVersionInformation(
+    CommandPacketView command) {
+  auto command_view = gd_hci::ReadRemoteVersionInformationView::Create(
+      gd_hci::DiscoveryCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+
+  auto status = link_layer_controller_.SendCommandToRemoteByHandle(
+      OpCode::READ_REMOTE_VERSION_INFORMATION, command.GetPayload(),
+      command_view.GetConnectionHandle());
+
+  auto packet =
+      bluetooth::hci::ReadRemoteVersionInformationStatusBuilder::Create(
+          status, kNumCommandPackets);
+  send_event_(std::move(packet));
+}
+
+void DualModeController::ReadBdAddr(CommandPacketView command) {
+  auto command_view = gd_hci::ReadBdAddrView::Create(command);
+  ASSERT(command_view.IsValid());
+  auto packet = bluetooth::hci::ReadBdAddrCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS, properties_.GetAddress());
+  send_event_(std::move(packet));
+}
+
+void DualModeController::ReadLocalSupportedCommands(CommandPacketView command) {
+  auto command_view = gd_hci::ReadLocalSupportedCommandsView::Create(command);
+  ASSERT(command_view.IsValid());
+
+  std::array<uint8_t, 64> supported_commands;
+  supported_commands.fill(0x00);
+  size_t len = properties_.GetSupportedCommands().size();
+  if (len > 64) {
+    len = 64;
   }
-  hci::Status status = link_layer_controller_.RemoteOobDataRequestReply(peer, c, r);
+  std::copy_n(properties_.GetSupportedCommands().begin(), len,
+              supported_commands.begin());
 
-  SendCommandCompleteStatusAndAddress(OpCode::REMOTE_OOB_DATA_REQUEST_REPLY, status, peer);
+  auto packet =
+      bluetooth::hci::ReadLocalSupportedCommandsCompleteBuilder::Create(
+          kNumCommandPackets, ErrorCode::SUCCESS, supported_commands);
+  send_event_(std::move(packet));
 }
 
-void DualModeController::HciRemoteOobDataRequestNegativeReply(packets::PacketView<true> args) {
-  CHECK(args.size() == 6) << __func__ << " size=" << args.size();
-
-  Address peer = args.begin().extract<Address>();
-
-  hci::Status status = link_layer_controller_.RemoteOobDataRequestNegativeReply(peer);
-
-  SendCommandCompleteStatusAndAddress(OpCode::REMOTE_OOB_DATA_REQUEST_NEGATIVE_REPLY, status, peer);
+void DualModeController::ReadLocalSupportedFeatures(CommandPacketView command) {
+  auto command_view = gd_hci::ReadLocalSupportedFeaturesView::Create(command);
+  ASSERT(command_view.IsValid());
+  auto packet =
+      bluetooth::hci::ReadLocalSupportedFeaturesCompleteBuilder::Create(
+          kNumCommandPackets, ErrorCode::SUCCESS,
+          properties_.GetSupportedFeatures());
+  send_event_(std::move(packet));
 }
 
-void DualModeController::HciIoCapabilityRequestNegativeReply(packets::PacketView<true> args) {
-  CHECK(args.size() == 7) << __func__ << " size=" << args.size();
-
-  auto args_itr = args.begin();
-  Address peer = args_itr.extract<Address>();
-  hci::Status reason = args_itr.extract<hci::Status>();
-
-  hci::Status status = link_layer_controller_.IoCapabilityRequestNegativeReply(peer, reason);
-
-  SendCommandCompleteStatusAndAddress(OpCode::IO_CAPABILITY_REQUEST_NEGATIVE_REPLY, status, peer);
+void DualModeController::ReadLocalSupportedCodecs(CommandPacketView command) {
+  auto command_view = gd_hci::ReadLocalSupportedCodecsView::Create(command);
+  ASSERT(command_view.IsValid());
+  auto packet = bluetooth::hci::ReadLocalSupportedCodecsCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS, properties_.GetSupportedCodecs(),
+      properties_.GetVendorSpecificCodecs());
+  send_event_(std::move(packet));
 }
 
-void DualModeController::HciWriteSimplePairingMode(packets::PacketView<true> args) {
-  CHECK(args.size() == 1) << __func__ << " size=" << args.size();
-  CHECK(args[0] == 1 || args[0] == 0);
-  link_layer_controller_.WriteSimplePairingMode(args[0] == 1);
-  SendCommandCompleteSuccess(OpCode::WRITE_SIMPLE_PAIRING_MODE);
+void DualModeController::ReadLocalExtendedFeatures(CommandPacketView command) {
+  auto command_view = gd_hci::ReadLocalExtendedFeaturesView::Create(command);
+  ASSERT(command_view.IsValid());
+  uint8_t page_number = command_view.GetPageNumber();
+
+  auto pakcet =
+      bluetooth::hci::ReadLocalExtendedFeaturesCompleteBuilder::Create(
+          kNumCommandPackets, ErrorCode::SUCCESS, page_number,
+          properties_.GetExtendedFeaturesMaximumPageNumber(),
+          properties_.GetExtendedFeatures(page_number));
+  send_event_(std::move(pakcet));
 }
 
-void DualModeController::HciChangeConnectionPacketType(packets::PacketView<true> args) {
-  CHECK(args.size() == 4) << __func__ << " size=" << args.size();
-  auto args_itr = args.begin();
-  uint16_t handle = args_itr.extract<uint16_t>();
-  uint16_t packet_type = args_itr.extract<uint16_t>();
+void DualModeController::ReadRemoteExtendedFeatures(CommandPacketView command) {
+  auto command_view = gd_hci::ReadRemoteExtendedFeaturesView::Create(
+      gd_hci::DiscoveryCommandView::Create(command));
+  ASSERT(command_view.IsValid());
 
-  hci::Status status = link_layer_controller_.ChangeConnectionPacketType(handle, packet_type);
+  auto status = link_layer_controller_.SendCommandToRemoteByHandle(
+      OpCode::READ_REMOTE_EXTENDED_FEATURES, command_view.GetPayload(),
+      command_view.GetConnectionHandle());
 
-  SendCommandStatus(status, OpCode::CHANGE_CONNECTION_PACKET_TYPE);
+  auto packet = bluetooth::hci::ReadRemoteExtendedFeaturesStatusBuilder::Create(
+      status, kNumCommandPackets);
+  send_event_(std::move(packet));
 }
 
-void DualModeController::HciWriteLeHostSupport(packets::PacketView<true> args) {
-  CHECK(args.size() == 2) << __func__ << " size=" << args.size();
-  SendCommandCompleteSuccess(OpCode::WRITE_LE_HOST_SUPPORT);
+void DualModeController::SwitchRole(CommandPacketView command) {
+  auto command_view = gd_hci::SwitchRoleView::Create(
+      gd_hci::ConnectionManagementCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+
+  auto status = link_layer_controller_.SwitchRole(
+      command_view.GetBdAddr(), static_cast<uint8_t>(command_view.GetRole()));
+
+  auto packet = bluetooth::hci::SwitchRoleStatusBuilder::Create(
+      status, kNumCommandPackets);
+  send_event_(std::move(packet));
 }
 
-void DualModeController::HciSetEventMask(packets::PacketView<true> args) {
-  CHECK(args.size() == 8) << __func__ << " size=" << args.size();
-  SendCommandCompleteSuccess(OpCode::SET_EVENT_MASK);
+void DualModeController::ReadRemoteSupportedFeatures(
+    CommandPacketView command) {
+  auto command_view = gd_hci::ReadRemoteSupportedFeaturesView::Create(
+      gd_hci::DiscoveryCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+
+  auto status = link_layer_controller_.SendCommandToRemoteByHandle(
+      OpCode::READ_REMOTE_SUPPORTED_FEATURES, command_view.GetPayload(),
+      command_view.GetConnectionHandle());
+
+  auto packet =
+      bluetooth::hci::ReadRemoteSupportedFeaturesStatusBuilder::Create(
+          status, kNumCommandPackets);
+  send_event_(std::move(packet));
 }
 
-void DualModeController::HciWriteInquiryMode(packets::PacketView<true> args) {
-  CHECK(args.size() == 1) << __func__ << " size=" << args.size();
-  link_layer_controller_.SetInquiryMode(args[0]);
-  SendCommandCompleteSuccess(OpCode::WRITE_INQUIRY_MODE);
+void DualModeController::ReadClockOffset(CommandPacketView command) {
+  auto command_view = gd_hci::ReadClockOffsetView::Create(
+      gd_hci::ConnectionManagementCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+
+  uint16_t handle = command_view.GetConnectionHandle();
+
+  auto status = link_layer_controller_.SendCommandToRemoteByHandle(
+      OpCode::READ_CLOCK_OFFSET, command_view.GetPayload(), handle);
+
+  auto packet = bluetooth::hci::ReadClockOffsetStatusBuilder::Create(
+      status, kNumCommandPackets);
+  send_event_(std::move(packet));
 }
 
-void DualModeController::HciWritePageScanType(packets::PacketView<true> args) {
-  CHECK(args.size() == 1) << __func__ << " size=" << args.size();
-  SendCommandCompleteSuccess(OpCode::WRITE_PAGE_SCAN_TYPE);
+void DualModeController::IoCapabilityRequestReply(CommandPacketView command) {
+  auto command_view = gd_hci::IoCapabilityRequestReplyView::Create(
+      gd_hci::SecurityCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+
+  Address peer = command_view.GetBdAddr();
+  uint8_t io_capability = static_cast<uint8_t>(command_view.GetIoCapability());
+  uint8_t oob_data_present_flag =
+      static_cast<uint8_t>(command_view.GetOobPresent());
+  uint8_t authentication_requirements =
+      static_cast<uint8_t>(command_view.GetAuthenticationRequirements());
+
+  auto status = link_layer_controller_.IoCapabilityRequestReply(
+      peer, io_capability, oob_data_present_flag, authentication_requirements);
+  auto packet = bluetooth::hci::IoCapabilityRequestReplyCompleteBuilder::Create(
+      kNumCommandPackets, status, peer);
+
+  send_event_(std::move(packet));
 }
 
-void DualModeController::HciWriteInquiryScanType(packets::PacketView<true> args) {
-  CHECK(args.size() == 1) << __func__ << " size=" << args.size();
-  SendCommandCompleteSuccess(OpCode::WRITE_INQUIRY_SCAN_TYPE);
+void DualModeController::UserConfirmationRequestReply(
+    CommandPacketView command) {
+  auto command_view = gd_hci::UserConfirmationRequestReplyView::Create(
+      gd_hci::SecurityCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+
+  Address peer = command_view.GetBdAddr();
+
+  auto status = link_layer_controller_.UserConfirmationRequestReply(peer);
+  auto packet =
+      bluetooth::hci::UserConfirmationRequestReplyCompleteBuilder::Create(
+          kNumCommandPackets, status, peer);
+
+  send_event_(std::move(packet));
 }
 
-void DualModeController::HciAuthenticationRequested(packets::PacketView<true> args) {
-  CHECK(args.size() == 2) << __func__ << " size=" << args.size();
-  uint16_t handle = args.begin().extract<uint16_t>();
-  hci::Status status = link_layer_controller_.AuthenticationRequested(handle);
+void DualModeController::UserConfirmationRequestNegativeReply(
+    CommandPacketView command) {
+  auto command_view = gd_hci::UserConfirmationRequestNegativeReplyView::Create(
+      gd_hci::SecurityCommandView::Create(command));
+  ASSERT(command_view.IsValid());
 
-  SendCommandStatus(status, OpCode::AUTHENTICATION_REQUESTED);
+  Address peer = command_view.GetBdAddr();
+
+  auto status =
+      link_layer_controller_.UserConfirmationRequestNegativeReply(peer);
+  auto packet =
+      bluetooth::hci::UserConfirmationRequestNegativeReplyCompleteBuilder::
+          Create(kNumCommandPackets, status, peer);
+
+  send_event_(std::move(packet));
 }
 
-void DualModeController::HciSetConnectionEncryption(packets::PacketView<true> args) {
-  CHECK(args.size() == 3) << __func__ << " size=" << args.size();
-  auto args_itr = args.begin();
-  uint16_t handle = args_itr.extract<uint16_t>();
-  uint8_t encryption_enable = args_itr.extract<uint8_t>();
-  hci::Status status = link_layer_controller_.SetConnectionEncryption(handle, encryption_enable);
+void DualModeController::UserPasskeyRequestReply(CommandPacketView command) {
+  auto command_view = gd_hci::UserPasskeyRequestReplyView::Create(
+      gd_hci::SecurityCommandView::Create(command));
+  ASSERT(command_view.IsValid());
 
-  SendCommandStatus(status, OpCode::SET_CONNECTION_ENCRYPTION);
+  Address peer = command_view.GetBdAddr();
+  uint32_t numeric_value = command_view.GetNumericValue();
+
+  auto status =
+      link_layer_controller_.UserPasskeyRequestReply(peer, numeric_value);
+  auto packet = bluetooth::hci::UserPasskeyRequestReplyCompleteBuilder::Create(
+      kNumCommandPackets, status, peer);
+
+  send_event_(std::move(packet));
 }
 
-void DualModeController::HciWriteAuthenticationEnable(packets::PacketView<true> args) {
-  CHECK(args.size() == 1) << __func__ << " size=" << args.size();
-  properties_.SetAuthenticationEnable(args[0]);
-  SendCommandCompleteSuccess(OpCode::WRITE_AUTHENTICATION_ENABLE);
+void DualModeController::UserPasskeyRequestNegativeReply(
+    CommandPacketView command) {
+  auto command_view = gd_hci::UserPasskeyRequestNegativeReplyView::Create(
+      gd_hci::SecurityCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+
+  Address peer = command_view.GetBdAddr();
+
+  auto status = link_layer_controller_.UserPasskeyRequestNegativeReply(peer);
+  auto packet =
+      bluetooth::hci::UserPasskeyRequestNegativeReplyCompleteBuilder::Create(
+          kNumCommandPackets, status, peer);
+
+  send_event_(std::move(packet));
 }
 
-void DualModeController::HciReadAuthenticationEnable(packets::PacketView<true> args) {
-  CHECK(args.size() == 0) << __func__ << " size=" << args.size();
-  std::shared_ptr<packets::EventPacketBuilder> command_complete =
-      packets::EventPacketBuilder::CreateCommandCompleteReadAuthenticationEnable(hci::Status::SUCCESS,
-                                                                                 properties_.GetAuthenticationEnable());
-  send_event_(command_complete->ToVector());
+void DualModeController::RemoteOobDataRequestReply(CommandPacketView command) {
+  auto command_view = gd_hci::RemoteOobDataRequestReplyView::Create(
+      gd_hci::SecurityCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+
+  Address peer = command_view.GetBdAddr();
+  std::array<uint8_t, 16> c = command_view.GetC();
+  std::array<uint8_t, 16> r = command_view.GetR();
+
+  auto status = link_layer_controller_.RemoteOobDataRequestReply(
+      peer, std::vector<uint8_t>(c.begin(), c.end()),
+      std::vector<uint8_t>(r.begin(), r.end()));
+  auto packet =
+      bluetooth::hci::RemoteOobDataRequestReplyCompleteBuilder::Create(
+          kNumCommandPackets, status, peer);
+
+  send_event_(std::move(packet));
 }
 
-void DualModeController::HciWriteClassOfDevice(packets::PacketView<true> args) {
-  CHECK(args.size() == 3) << __func__ << " size=" << args.size();
-  properties_.SetClassOfDevice(args[0], args[1], args[2]);
-  SendCommandCompleteSuccess(OpCode::WRITE_CLASS_OF_DEVICE);
+void DualModeController::RemoteOobDataRequestNegativeReply(
+    CommandPacketView command) {
+  auto command_view = gd_hci::RemoteOobDataRequestNegativeReplyView::Create(
+      gd_hci::SecurityCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+
+  Address peer = command_view.GetBdAddr();
+
+  auto status = link_layer_controller_.RemoteOobDataRequestNegativeReply(peer);
+  auto packet =
+      bluetooth::hci::RemoteOobDataRequestNegativeReplyCompleteBuilder::Create(
+          kNumCommandPackets, status, peer);
+
+  send_event_(std::move(packet));
 }
 
-void DualModeController::HciWritePageTimeout(packets::PacketView<true> args) {
-  CHECK(args.size() == 2) << __func__ << " size=" << args.size();
-  SendCommandCompleteSuccess(OpCode::WRITE_PAGE_TIMEOUT);
+void DualModeController::IoCapabilityRequestNegativeReply(
+    CommandPacketView command) {
+  auto command_view = gd_hci::IoCapabilityRequestNegativeReplyView::Create(
+      gd_hci::SecurityCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+
+  Address peer = command_view.GetBdAddr();
+  ErrorCode reason = command_view.GetReason();
+
+  auto status =
+      link_layer_controller_.IoCapabilityRequestNegativeReply(peer, reason);
+  auto packet =
+      bluetooth::hci::IoCapabilityRequestNegativeReplyCompleteBuilder::Create(
+          kNumCommandPackets, status, peer);
+
+  send_event_(std::move(packet));
 }
 
-void DualModeController::HciWriteDefaultLinkPolicySettings(packets::PacketView<true> args) {
-  CHECK(args.size() == 2) << __func__ << " size=" << args.size();
-  SendCommandCompleteSuccess(OpCode::WRITE_DEFAULT_LINK_POLICY_SETTINGS);
+void DualModeController::ReadInquiryResponseTransmitPowerLevel(
+    CommandPacketView command) {
+  auto command_view = gd_hci::ReadInquiryResponseTransmitPowerLevelView::Create(
+      gd_hci::DiscoveryCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+
+  uint8_t tx_power = 20;  // maximum
+  auto packet =
+      bluetooth::hci::ReadInquiryResponseTransmitPowerLevelCompleteBuilder::
+          Create(kNumCommandPackets, ErrorCode::SUCCESS, tx_power);
+  send_event_(std::move(packet));
 }
 
-void DualModeController::HciWriteLinkPolicySettings(packets::PacketView<true> args) {
-  CHECK(args.size() == 4) << __func__ << " size=" << args.size();
+void DualModeController::WriteSimplePairingMode(CommandPacketView command) {
+  auto command_view = gd_hci::WriteSimplePairingModeView::Create(
+      gd_hci::SecurityCommandView::Create(command));
+  ASSERT(command_view.IsValid());
 
-  auto args_itr = args.begin();
-  uint16_t handle = args_itr.extract<uint16_t>();
-  uint16_t settings = args_itr.extract<uint16_t>();
-
-  hci::Status status = link_layer_controller_.WriteLinkPolicySettings(handle, settings);
-
-  send_event_(packets::EventPacketBuilder::CreateCommandCompleteWriteLinkPolicySettings(status, handle)->ToVector());
+  link_layer_controller_.WriteSimplePairingMode(
+      command_view.GetSimplePairingMode() == gd_hci::Enable::ENABLED);
+  auto packet = bluetooth::hci::WriteSimplePairingModeCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
 }
 
-void DualModeController::HciWriteLinkSupervisionTimeout(packets::PacketView<true> args) {
-  CHECK(args.size() == 4) << __func__ << " size=" << args.size();
+void DualModeController::ChangeConnectionPacketType(CommandPacketView command) {
+  auto command_view = gd_hci::ChangeConnectionPacketTypeView::Create(
+      gd_hci::ConnectionManagementCommandView::Create(command));
+  ASSERT(command_view.IsValid());
 
-  auto args_itr = args.begin();
-  uint16_t handle = args_itr.extract<uint16_t>();
-  uint16_t timeout = args_itr.extract<uint16_t>();
+  uint16_t handle = command_view.GetConnectionHandle();
+  uint16_t packet_type = static_cast<uint16_t>(command_view.GetPacketType());
 
-  hci::Status status = link_layer_controller_.WriteLinkSupervisionTimeout(handle, timeout);
+  auto status =
+      link_layer_controller_.ChangeConnectionPacketType(handle, packet_type);
 
-  send_event_(
-      packets::EventPacketBuilder::CreateCommandCompleteWriteLinkSupervisionTimeout(status, handle)->ToVector());
+  auto packet = bluetooth::hci::ChangeConnectionPacketTypeStatusBuilder::Create(
+      status, kNumCommandPackets);
+  send_event_(std::move(packet));
 }
 
-void DualModeController::HciReadLocalName(packets::PacketView<true> args) {
-  CHECK(args.size() == 0) << __func__ << " size=" << args.size();
-  std::shared_ptr<packets::EventPacketBuilder> command_complete =
-      packets::EventPacketBuilder::CreateCommandCompleteReadLocalName(hci::Status::SUCCESS, properties_.GetName());
-  send_event_(command_complete->ToVector());
+void DualModeController::WriteLeHostSupport(CommandPacketView command) {
+  auto command_view = gd_hci::WriteLeHostSupportView::Create(command);
+  ASSERT(command_view.IsValid());
+  auto packet = bluetooth::hci::WriteLeHostSupportCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
 }
 
-void DualModeController::HciWriteLocalName(packets::PacketView<true> args) {
-  CHECK(args.size() == 248) << __func__ << " size=" << args.size();
-  std::vector<uint8_t> clipped(args.begin(), args.begin() + LastNonZero(args) + 1);
-  properties_.SetName(clipped);
-  SendCommandCompleteSuccess(OpCode::WRITE_LOCAL_NAME);
+void DualModeController::WriteSecureConnectionsHostSupport(
+    CommandPacketView command) {
+  auto command_view = gd_hci::WriteSecureConnectionsHostSupportView::Create(
+      gd_hci::SecurityCommandView::Create(command));
+  properties_.SetExtendedFeatures(properties_.GetExtendedFeatures(1) | 0x8, 1);
+  auto packet =
+      bluetooth::hci::WriteSecureConnectionsHostSupportCompleteBuilder::Create(
+          kNumCommandPackets, ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
 }
 
-void DualModeController::HciWriteExtendedInquiryResponse(packets::PacketView<true> args) {
-  CHECK(args.size() == 241) << __func__ << " size=" << args.size();
-  // Strip FEC byte and trailing zeros
-  std::vector<uint8_t> clipped(args.begin() + 1, args.begin() + LastNonZero(args) + 1);
-  properties_.SetExtendedInquiryData(clipped);
-  LOG_WARN(LOG_TAG, "Write EIR Inquiry - Size = %d (%d)", static_cast<int>(properties_.GetExtendedInquiryData().size()),
-           static_cast<int>(clipped.size()));
-  SendCommandCompleteSuccess(OpCode::WRITE_EXTENDED_INQUIRY_RESPONSE);
+void DualModeController::SetEventMask(CommandPacketView command) {
+  auto command_view = gd_hci::SetEventMaskView::Create(command);
+  ASSERT(command_view.IsValid());
+  auto packet = bluetooth::hci::SetEventMaskCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
 }
 
-void DualModeController::HciWriteVoiceSetting(packets::PacketView<true> args) {
-  CHECK(args.size() == 2) << __func__ << " size=" << args.size();
-  SendCommandCompleteSuccess(OpCode::WRITE_VOICE_SETTING);
+void DualModeController::ReadInquiryMode(CommandPacketView command) {
+  auto command_view = gd_hci::ReadInquiryModeView::Create(
+      gd_hci::DiscoveryCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  gd_hci::InquiryMode inquiry_mode = gd_hci::InquiryMode::STANDARD;
+  auto packet = bluetooth::hci::ReadInquiryModeCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS, inquiry_mode);
+  send_event_(std::move(packet));
 }
 
-void DualModeController::HciWriteCurrentIacLap(packets::PacketView<true> args) {
-  CHECK(args.size() > 0);
-  CHECK(args.size() == 1 + (3 * args[0]));  // count + 3-byte IACs
-
-  SendCommandCompleteSuccess(OpCode::WRITE_CURRENT_IAC_LAP);
+void DualModeController::WriteInquiryMode(CommandPacketView command) {
+  auto command_view = gd_hci::WriteInquiryModeView::Create(
+      gd_hci::DiscoveryCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  link_layer_controller_.SetInquiryMode(
+      static_cast<uint8_t>(command_view.GetInquiryMode()));
+  auto packet = bluetooth::hci::WriteInquiryModeCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
 }
 
-void DualModeController::HciWriteInquiryScanActivity(packets::PacketView<true> args) {
-  CHECK(args.size() == 4) << __func__ << " size=" << args.size();
-  SendCommandCompleteSuccess(OpCode::WRITE_INQUIRY_SCAN_ACTIVITY);
+void DualModeController::ReadPageScanType(CommandPacketView command) {
+  auto command_view = gd_hci::ReadPageScanTypeView::Create(
+      gd_hci::DiscoveryCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  gd_hci::PageScanType page_scan_type = gd_hci::PageScanType::STANDARD;
+  auto packet = bluetooth::hci::ReadPageScanTypeCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS, page_scan_type);
+  send_event_(std::move(packet));
 }
 
-void DualModeController::HciWriteScanEnable(packets::PacketView<true> args) {
-  CHECK(args.size() == 1) << __func__ << " size=" << args.size();
-  link_layer_controller_.SetInquiryScanEnable(args[0] & 0x1);
-  link_layer_controller_.SetPageScanEnable(args[0] & 0x2);
-  SendCommandCompleteSuccess(OpCode::WRITE_SCAN_ENABLE);
+void DualModeController::WritePageScanType(CommandPacketView command) {
+  auto command_view = gd_hci::WritePageScanTypeView::Create(
+      gd_hci::DiscoveryCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  auto packet = bluetooth::hci::WritePageScanTypeCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
 }
 
-void DualModeController::HciSetEventFilter(packets::PacketView<true> args) {
-  CHECK(args.size() > 0);
-  SendCommandCompleteSuccess(OpCode::SET_EVENT_FILTER);
+void DualModeController::ReadInquiryScanType(CommandPacketView command) {
+  auto command_view = gd_hci::ReadInquiryScanTypeView::Create(
+      gd_hci::DiscoveryCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  gd_hci::InquiryScanType inquiry_scan_type = gd_hci::InquiryScanType::STANDARD;
+  auto packet = bluetooth::hci::ReadInquiryScanTypeCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS, inquiry_scan_type);
+  send_event_(std::move(packet));
 }
 
-void DualModeController::HciInquiry(packets::PacketView<true> args) {
-  CHECK(args.size() == 5) << __func__ << " size=" << args.size();
-  link_layer_controller_.SetInquiryLAP(args[0] | (args[1] << 8) | (args[2] << 16));
-  link_layer_controller_.SetInquiryMaxResponses(args[4]);
-  link_layer_controller_.StartInquiry(std::chrono::milliseconds(args[3] * 1280));
-
-  SendCommandStatusSuccess(OpCode::INQUIRY);
+void DualModeController::WriteInquiryScanType(CommandPacketView command) {
+  auto command_view = gd_hci::WriteInquiryScanTypeView::Create(
+      gd_hci::DiscoveryCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  auto packet = bluetooth::hci::WriteInquiryScanTypeCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
 }
 
-void DualModeController::HciInquiryCancel(packets::PacketView<true> args) {
-  CHECK(args.size() == 0) << __func__ << " size=" << args.size();
+void DualModeController::AuthenticationRequested(CommandPacketView command) {
+  auto command_view = gd_hci::AuthenticationRequestedView::Create(
+      gd_hci::ConnectionManagementCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  uint16_t handle = command_view.GetConnectionHandle();
+  auto status = link_layer_controller_.AuthenticationRequested(handle);
+
+  auto packet = bluetooth::hci::AuthenticationRequestedStatusBuilder::Create(
+      status, kNumCommandPackets);
+  send_event_(std::move(packet));
+}
+
+void DualModeController::SetConnectionEncryption(CommandPacketView command) {
+  auto command_view = gd_hci::SetConnectionEncryptionView::Create(
+      gd_hci::ConnectionManagementCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  uint16_t handle = command_view.GetConnectionHandle();
+  uint8_t encryption_enable =
+      static_cast<uint8_t>(command_view.GetEncryptionEnable());
+  auto status =
+      link_layer_controller_.SetConnectionEncryption(handle, encryption_enable);
+
+  auto packet = bluetooth::hci::SetConnectionEncryptionStatusBuilder::Create(
+      status, kNumCommandPackets);
+  send_event_(std::move(packet));
+}
+
+void DualModeController::ChangeConnectionLinkKey(CommandPacketView command) {
+  auto command_view = gd_hci::ChangeConnectionLinkKeyView::Create(
+      gd_hci::ConnectionManagementCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  uint16_t handle = command_view.GetConnectionHandle();
+
+  auto status = link_layer_controller_.ChangeConnectionLinkKey(handle);
+
+  auto packet = bluetooth::hci::ChangeConnectionLinkKeyStatusBuilder::Create(
+      status, kNumCommandPackets);
+  send_event_(std::move(packet));
+}
+
+void DualModeController::MasterLinkKey(CommandPacketView command) {
+  auto command_view = gd_hci::MasterLinkKeyView::Create(
+      gd_hci::ConnectionManagementCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  uint8_t key_flag = static_cast<uint8_t>(command_view.GetKeyFlag());
+
+  auto status = link_layer_controller_.MasterLinkKey(key_flag);
+
+  auto packet = bluetooth::hci::MasterLinkKeyStatusBuilder::Create(
+      status, kNumCommandPackets);
+  send_event_(std::move(packet));
+}
+
+void DualModeController::WriteAuthenticationEnable(CommandPacketView command) {
+  auto command_view = gd_hci::WriteAuthenticationEnableView::Create(
+      gd_hci::SecurityCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  properties_.SetAuthenticationEnable(
+      static_cast<uint8_t>(command_view.GetAuthenticationEnable()));
+  auto packet =
+      bluetooth::hci::WriteAuthenticationEnableCompleteBuilder::Create(
+          kNumCommandPackets, ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
+}
+
+void DualModeController::ReadAuthenticationEnable(CommandPacketView command) {
+  auto command_view = gd_hci::ReadAuthenticationEnableView::Create(command);
+  ASSERT(command_view.IsValid());
+  auto packet = bluetooth::hci::ReadAuthenticationEnableCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS,
+      static_cast<bluetooth::hci::AuthenticationEnable>(
+          properties_.GetAuthenticationEnable()));
+  send_event_(std::move(packet));
+}
+
+void DualModeController::WriteClassOfDevice(CommandPacketView command) {
+  auto command_view = gd_hci::WriteClassOfDeviceView::Create(
+      gd_hci::DiscoveryCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  ClassOfDevice class_of_device = command_view.GetClassOfDevice();
+  properties_.SetClassOfDevice(class_of_device.cod[0], class_of_device.cod[1],
+                               class_of_device.cod[2]);
+  auto packet = bluetooth::hci::WriteClassOfDeviceCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
+}
+
+void DualModeController::ReadPageTimeout(CommandPacketView command) {
+  auto command_view = gd_hci::ReadPageTimeoutView::Create(
+      gd_hci::DiscoveryCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  uint16_t page_timeout = 0x2000;
+  auto packet = bluetooth::hci::ReadPageTimeoutCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS, page_timeout);
+  send_event_(std::move(packet));
+}
+
+void DualModeController::WritePageTimeout(CommandPacketView command) {
+  auto command_view = gd_hci::WritePageTimeoutView::Create(
+      gd_hci::DiscoveryCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  auto packet = bluetooth::hci::WritePageTimeoutCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
+}
+
+void DualModeController::HoldMode(CommandPacketView command) {
+  auto command_view = gd_hci::HoldModeView::Create(
+      gd_hci::ConnectionManagementCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  uint16_t handle = command_view.GetConnectionHandle();
+  uint16_t hold_mode_max_interval = command_view.GetHoldModeMaxInterval();
+  uint16_t hold_mode_min_interval = command_view.GetHoldModeMinInterval();
+
+  auto status = link_layer_controller_.HoldMode(handle, hold_mode_max_interval,
+                                                hold_mode_min_interval);
+
+  auto packet =
+      bluetooth::hci::HoldModeStatusBuilder::Create(status, kNumCommandPackets);
+  send_event_(std::move(packet));
+}
+
+void DualModeController::SniffMode(CommandPacketView command) {
+  auto command_view = gd_hci::SniffModeView::Create(
+      gd_hci::ConnectionManagementCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  uint16_t handle = command_view.GetConnectionHandle();
+  uint16_t sniff_max_interval = command_view.GetSniffMaxInterval();
+  uint16_t sniff_min_interval = command_view.GetSniffMinInterval();
+  uint16_t sniff_attempt = command_view.GetSniffAttempt();
+  uint16_t sniff_timeout = command_view.GetSniffTimeout();
+
+  auto status = link_layer_controller_.SniffMode(handle, sniff_max_interval,
+                                                 sniff_min_interval,
+                                                 sniff_attempt, sniff_timeout);
+
+  auto packet = bluetooth::hci::SniffModeStatusBuilder::Create(
+      status, kNumCommandPackets);
+  send_event_(std::move(packet));
+}
+
+void DualModeController::ExitSniffMode(CommandPacketView command) {
+  auto command_view = gd_hci::ExitSniffModeView::Create(
+      gd_hci::ConnectionManagementCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+
+  auto status =
+      link_layer_controller_.ExitSniffMode(command_view.GetConnectionHandle());
+
+  auto packet = bluetooth::hci::ExitSniffModeStatusBuilder::Create(
+      status, kNumCommandPackets);
+  send_event_(std::move(packet));
+}
+
+void DualModeController::QosSetup(CommandPacketView command) {
+  auto command_view = gd_hci::QosSetupView::Create(
+      gd_hci::ConnectionManagementCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  uint16_t handle = command_view.GetConnectionHandle();
+  uint8_t service_type = static_cast<uint8_t>(command_view.GetServiceType());
+  uint32_t token_rate = command_view.GetTokenRate();
+  uint32_t peak_bandwidth = command_view.GetPeakBandwidth();
+  uint32_t latency = command_view.GetLatency();
+  uint32_t delay_variation = command_view.GetDelayVariation();
+
+  auto status =
+      link_layer_controller_.QosSetup(handle, service_type, token_rate,
+                                      peak_bandwidth, latency, delay_variation);
+
+  auto packet =
+      bluetooth::hci::QosSetupStatusBuilder::Create(status, kNumCommandPackets);
+  send_event_(std::move(packet));
+}
+
+void DualModeController::WriteDefaultLinkPolicySettings(
+    CommandPacketView command) {
+  auto command_view = gd_hci::WriteDefaultLinkPolicySettingsView::Create(
+      gd_hci::ConnectionManagementCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  auto packet =
+      bluetooth::hci::WriteDefaultLinkPolicySettingsCompleteBuilder::Create(
+          kNumCommandPackets, ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
+}
+
+void DualModeController::FlowSpecification(CommandPacketView command) {
+  auto command_view = gd_hci::FlowSpecificationView::Create(
+      gd_hci::ConnectionManagementCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  uint16_t handle = command_view.GetConnectionHandle();
+  uint8_t flow_direction =
+      static_cast<uint8_t>(command_view.GetFlowDirection());
+  uint8_t service_type = static_cast<uint8_t>(command_view.GetServiceType());
+  uint32_t token_rate = command_view.GetTokenRate();
+  uint32_t token_bucket_size = command_view.GetTokenBucketSize();
+  uint32_t peak_bandwidth = command_view.GetPeakBandwidth();
+  uint32_t access_latency = command_view.GetAccessLatency();
+
+  auto status = link_layer_controller_.FlowSpecification(
+      handle, flow_direction, service_type, token_rate, token_bucket_size,
+      peak_bandwidth, access_latency);
+
+  auto packet = bluetooth::hci::FlowSpecificationStatusBuilder::Create(
+      status, kNumCommandPackets);
+  send_event_(std::move(packet));
+}
+
+void DualModeController::WriteLinkPolicySettings(CommandPacketView command) {
+  auto command_view = gd_hci::WriteLinkPolicySettingsView::Create(
+      gd_hci::ConnectionManagementCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+
+  uint16_t handle = command_view.GetConnectionHandle();
+  uint16_t settings = command_view.GetLinkPolicySettings();
+
+  auto status =
+      link_layer_controller_.WriteLinkPolicySettings(handle, settings);
+
+  auto packet = bluetooth::hci::WriteLinkPolicySettingsCompleteBuilder::Create(
+      kNumCommandPackets, status, handle);
+  send_event_(std::move(packet));
+}
+
+void DualModeController::WriteLinkSupervisionTimeout(
+    CommandPacketView command) {
+  auto command_view = gd_hci::WriteLinkSupervisionTimeoutView::Create(
+      gd_hci::ConnectionManagementCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+
+  uint16_t handle = command_view.GetHandle();
+  uint16_t timeout = command_view.GetLinkSupervisionTimeout();
+
+  auto status =
+      link_layer_controller_.WriteLinkSupervisionTimeout(handle, timeout);
+  auto packet =
+      bluetooth::hci::WriteLinkSupervisionTimeoutCompleteBuilder::Create(
+          kNumCommandPackets, status, handle);
+  send_event_(std::move(packet));
+}
+
+void DualModeController::ReadLocalName(CommandPacketView command) {
+  auto command_view = gd_hci::ReadLocalNameView::Create(command);
+  ASSERT(command_view.IsValid());
+
+  std::array<uint8_t, 248> local_name;
+  local_name.fill(0x00);
+  size_t len = properties_.GetName().size();
+  if (len > 247) {
+    len = 247;  // one byte for NULL octet (0x00)
+  }
+  std::copy_n(properties_.GetName().begin(), len, local_name.begin());
+
+  auto packet = bluetooth::hci::ReadLocalNameCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS, local_name);
+  send_event_(std::move(packet));
+}
+
+void DualModeController::WriteLocalName(CommandPacketView command) {
+  auto command_view = gd_hci::WriteLocalNameView::Create(command);
+  ASSERT(command_view.IsValid());
+  const auto local_name = command_view.GetLocalName();
+  std::vector<uint8_t> name_vec(248);
+  for (size_t i = 0; i < 248; i++) {
+    name_vec[i] = local_name[i];
+  }
+  properties_.SetName(name_vec);
+  auto packet = bluetooth::hci::WriteLocalNameCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
+}
+
+void DualModeController::WriteExtendedInquiryResponse(
+    CommandPacketView command) {
+  auto command_view = gd_hci::WriteExtendedInquiryResponseView::Create(command);
+  ASSERT(command_view.IsValid());
+  properties_.SetExtendedInquiryData(std::vector<uint8_t>(
+      command_view.GetPayload().begin() + 1, command_view.GetPayload().end()));
+  auto packet =
+      bluetooth::hci::WriteExtendedInquiryResponseCompleteBuilder::Create(
+          kNumCommandPackets, ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
+}
+
+void DualModeController::RefreshEncryptionKey(CommandPacketView command) {
+  auto command_view = gd_hci::RefreshEncryptionKeyView::Create(
+      gd_hci::SecurityCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  uint16_t handle = command_view.GetConnectionHandle();
+  auto status_packet =
+      bluetooth::hci::RefreshEncryptionKeyStatusBuilder::Create(
+          ErrorCode::SUCCESS, kNumCommandPackets);
+  send_event_(std::move(status_packet));
+  // TODO: Support this in the link layer
+  auto complete_packet =
+      bluetooth::hci::EncryptionKeyRefreshCompleteBuilder::Create(
+          ErrorCode::SUCCESS, handle);
+  send_event_(std::move(complete_packet));
+}
+
+void DualModeController::WriteVoiceSetting(CommandPacketView command) {
+  auto command_view = gd_hci::WriteVoiceSettingView::Create(command);
+  ASSERT(command_view.IsValid());
+  auto packet = bluetooth::hci::WriteVoiceSettingCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
+}
+
+void DualModeController::ReadNumberOfSupportedIac(CommandPacketView command) {
+  auto command_view = gd_hci::ReadNumberOfSupportedIacView::Create(
+      gd_hci::DiscoveryCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  uint8_t num_support_iac = 0x1;
+  auto packet = bluetooth::hci::ReadNumberOfSupportedIacCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS, num_support_iac);
+  send_event_(std::move(packet));
+}
+
+void DualModeController::ReadCurrentIacLap(CommandPacketView command) {
+  auto command_view = gd_hci::ReadCurrentIacLapView::Create(
+      gd_hci::DiscoveryCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  gd_hci::Lap lap;
+  lap.lap_ = 0x30;
+  auto packet = bluetooth::hci::ReadCurrentIacLapCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS, {lap});
+  send_event_(std::move(packet));
+}
+
+void DualModeController::WriteCurrentIacLap(CommandPacketView command) {
+  auto command_view = gd_hci::WriteCurrentIacLapView::Create(
+      gd_hci::DiscoveryCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  auto packet = bluetooth::hci::WriteCurrentIacLapCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
+}
+
+void DualModeController::ReadPageScanActivity(CommandPacketView command) {
+  auto command_view = gd_hci::ReadPageScanActivityView::Create(
+      gd_hci::DiscoveryCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  uint16_t interval = 0x1000;
+  uint16_t window = 0x0012;
+  auto packet = bluetooth::hci::ReadPageScanActivityCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS, interval, window);
+  send_event_(std::move(packet));
+}
+
+void DualModeController::WritePageScanActivity(CommandPacketView command) {
+  auto command_view = gd_hci::WritePageScanActivityView::Create(
+      gd_hci::DiscoveryCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  auto packet = bluetooth::hci::WritePageScanActivityCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
+}
+
+void DualModeController::ReadInquiryScanActivity(CommandPacketView command) {
+  auto command_view = gd_hci::ReadInquiryScanActivityView::Create(
+      gd_hci::DiscoveryCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  uint16_t interval = 0x1000;
+  uint16_t window = 0x0012;
+  auto packet = bluetooth::hci::ReadInquiryScanActivityCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS, interval, window);
+  send_event_(std::move(packet));
+}
+
+void DualModeController::WriteInquiryScanActivity(CommandPacketView command) {
+  auto command_view = gd_hci::WriteInquiryScanActivityView::Create(
+      gd_hci::DiscoveryCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  auto packet = bluetooth::hci::WriteInquiryScanActivityCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
+}
+
+void DualModeController::ReadScanEnable(CommandPacketView command) {
+  auto command_view = gd_hci::ReadScanEnableView::Create(
+      gd_hci::DiscoveryCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  auto packet = bluetooth::hci::ReadScanEnableCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS, gd_hci::ScanEnable::NO_SCANS);
+  send_event_(std::move(packet));
+}
+
+void DualModeController::WriteScanEnable(CommandPacketView command) {
+  auto command_view = gd_hci::WriteScanEnableView::Create(
+      gd_hci::DiscoveryCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  link_layer_controller_.SetInquiryScanEnable(
+      command_view.GetScanEnable() ==
+          gd_hci::ScanEnable::INQUIRY_AND_PAGE_SCAN ||
+      command_view.GetScanEnable() == gd_hci::ScanEnable::INQUIRY_SCAN_ONLY);
+  link_layer_controller_.SetPageScanEnable(
+      command_view.GetScanEnable() ==
+          gd_hci::ScanEnable::INQUIRY_AND_PAGE_SCAN ||
+      command_view.GetScanEnable() == gd_hci::ScanEnable::PAGE_SCAN_ONLY);
+  auto packet = bluetooth::hci::WriteScanEnableCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
+}
+
+void DualModeController::SetEventFilter(CommandPacketView command) {
+  auto command_view = gd_hci::SetEventFilterView::Create(command);
+  ASSERT(command_view.IsValid());
+  auto packet = bluetooth::hci::SetEventFilterCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
+}
+
+void DualModeController::Inquiry(CommandPacketView command) {
+  auto command_view = gd_hci::InquiryView::Create(
+      gd_hci::DiscoveryCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  link_layer_controller_.SetInquiryLAP(command_view.GetLap().lap_);
+  link_layer_controller_.SetInquiryMaxResponses(command_view.GetNumResponses());
+  link_layer_controller_.StartInquiry(
+      std::chrono::milliseconds(command_view.GetInquiryLength() * 1280));
+
+  auto packet = bluetooth::hci::InquiryStatusBuilder::Create(
+      ErrorCode::SUCCESS, kNumCommandPackets);
+  send_event_(std::move(packet));
+}
+
+void DualModeController::InquiryCancel(CommandPacketView command) {
+  auto command_view = gd_hci::InquiryCancelView::Create(
+      gd_hci::DiscoveryCommandView::Create(command));
+  ASSERT(command_view.IsValid());
   link_layer_controller_.InquiryCancel();
-  SendCommandCompleteSuccess(OpCode::INQUIRY_CANCEL);
+  auto packet = bluetooth::hci::InquiryCancelCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
 }
 
-void DualModeController::HciAcceptConnectionRequest(packets::PacketView<true> args) {
-  CHECK(args.size() == 7) << __func__ << " size=" << args.size();
-  Address addr = args.begin().extract<Address>();
-  bool try_role_switch = args[6] == 0;
-  hci::Status status = link_layer_controller_.AcceptConnectionRequest(addr, try_role_switch);
-  SendCommandStatus(status, OpCode::ACCEPT_CONNECTION_REQUEST);
+void DualModeController::AcceptConnectionRequest(CommandPacketView command) {
+  auto command_view = gd_hci::AcceptConnectionRequestView::Create(
+      gd_hci::ConnectionManagementCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  Address addr = command_view.GetBdAddr();
+  bool try_role_switch = command_view.GetRole() ==
+                         gd_hci::AcceptConnectionRequestRole::BECOME_MASTER;
+  auto status =
+      link_layer_controller_.AcceptConnectionRequest(addr, try_role_switch);
+  auto packet = bluetooth::hci::AcceptConnectionRequestStatusBuilder::Create(
+      status, kNumCommandPackets);
+  send_event_(std::move(packet));
 }
 
-void DualModeController::HciRejectConnectionRequest(packets::PacketView<true> args) {
-  CHECK(args.size() == 7) << __func__ << " size=" << args.size();
-  auto args_itr = args.begin();
-  Address addr = args_itr.extract<Address>();
-  uint8_t reason = args_itr.extract<uint8_t>();
-  hci::Status status = link_layer_controller_.RejectConnectionRequest(addr, reason);
-  SendCommandStatus(status, OpCode::REJECT_CONNECTION_REQUEST);
+void DualModeController::RejectConnectionRequest(CommandPacketView command) {
+  auto command_view = gd_hci::RejectConnectionRequestView::Create(
+      gd_hci::ConnectionManagementCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  Address addr = command_view.GetBdAddr();
+  uint8_t reason = static_cast<uint8_t>(command_view.GetReason());
+  auto status = link_layer_controller_.RejectConnectionRequest(addr, reason);
+  auto packet = bluetooth::hci::RejectConnectionRequestStatusBuilder::Create(
+      status, kNumCommandPackets);
+  send_event_(std::move(packet));
 }
 
-void DualModeController::HciLinkKeyRequestReply(packets::PacketView<true> args) {
-  CHECK(args.size() == 22) << __func__ << " size=" << args.size();
-  Address addr = args.begin().extract<Address>();
-  packets::PacketView<true> key = args.SubViewLittleEndian(6, 22);
-  hci::Status status = link_layer_controller_.LinkKeyRequestReply(addr, key);
-  send_event_(packets::EventPacketBuilder::CreateCommandCompleteLinkKeyRequestReply(status, addr)->ToVector());
+void DualModeController::LinkKeyRequestReply(CommandPacketView command) {
+  auto command_view = gd_hci::LinkKeyRequestReplyView::Create(
+      gd_hci::SecurityCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  Address addr = command_view.GetBdAddr();
+  auto key = command_view.GetLinkKey();
+  auto status = link_layer_controller_.LinkKeyRequestReply(addr, key);
+  auto packet = bluetooth::hci::LinkKeyRequestReplyCompleteBuilder::Create(
+      kNumCommandPackets, status);
+  send_event_(std::move(packet));
 }
 
-void DualModeController::HciLinkKeyRequestNegativeReply(packets::PacketView<true> args) {
-  CHECK(args.size() == 6) << __func__ << " size=" << args.size();
-  Address addr = args.begin().extract<Address>();
-  hci::Status status = link_layer_controller_.LinkKeyRequestNegativeReply(addr);
-  send_event_(packets::EventPacketBuilder::CreateCommandCompleteLinkKeyRequestNegativeReply(status, addr)->ToVector());
+void DualModeController::LinkKeyRequestNegativeReply(
+    CommandPacketView command) {
+  auto command_view = gd_hci::LinkKeyRequestNegativeReplyView::Create(
+      gd_hci::SecurityCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  Address addr = command_view.GetBdAddr();
+  auto status = link_layer_controller_.LinkKeyRequestNegativeReply(addr);
+  auto packet =
+      bluetooth::hci::LinkKeyRequestNegativeReplyCompleteBuilder::Create(
+          kNumCommandPackets, status, addr);
+  send_event_(std::move(packet));
 }
 
-void DualModeController::HciDeleteStoredLinkKey(packets::PacketView<true> args) {
-  CHECK(args.size() == 7) << __func__ << " size=" << args.size();
+void DualModeController::DeleteStoredLinkKey(CommandPacketView command) {
+  auto command_view = gd_hci::DeleteStoredLinkKeyView::Create(
+      gd_hci::SecurityCommandView::Create(command));
+  ASSERT(command_view.IsValid());
 
   uint16_t deleted_keys = 0;
 
-  if (args[6] == 0) {
-    Address addr = args.begin().extract<Address>();
+  auto flag = command_view.GetDeleteAllFlag();
+  if (flag == gd_hci::DeleteStoredLinkKeyDeleteAllFlag::SPECIFIED_BD_ADDR) {
+    Address addr = command_view.GetBdAddr();
     deleted_keys = security_manager_.DeleteKey(addr);
   }
 
-  if (args[6] == 1) {
+  if (flag == gd_hci::DeleteStoredLinkKeyDeleteAllFlag::ALL) {
     security_manager_.DeleteAllKeys();
   }
 
-  send_event_(packets::EventPacketBuilder::CreateCommandCompleteDeleteStoredLinkKey(hci::Status::SUCCESS, deleted_keys)
-                  ->ToVector());
+  auto packet = bluetooth::hci::DeleteStoredLinkKeyCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS, deleted_keys);
+
+  send_event_(std::move(packet));
 }
 
-void DualModeController::HciRemoteNameRequest(packets::PacketView<true> args) {
-  CHECK(args.size() == 10) << __func__ << " size=" << args.size();
+void DualModeController::RemoteNameRequest(CommandPacketView command) {
+  auto command_view = gd_hci::RemoteNameRequestView::Create(
+      gd_hci::DiscoveryCommandView::Create(command));
+  ASSERT(command_view.IsValid());
 
-  Address remote_addr = args.begin().extract<Address>();
+  Address remote_addr = command_view.GetBdAddr();
 
-  hci::Status status =
-      link_layer_controller_.SendCommandToRemoteByAddress(OpCode::REMOTE_NAME_REQUEST, args, remote_addr, false);
+  auto status = link_layer_controller_.SendCommandToRemoteByAddress(
+      OpCode::REMOTE_NAME_REQUEST, command_view.GetPayload(), remote_addr);
 
-  SendCommandStatus(status, OpCode::REMOTE_NAME_REQUEST);
+  auto packet = bluetooth::hci::RemoteNameRequestStatusBuilder::Create(
+      status, kNumCommandPackets);
+  send_event_(std::move(packet));
 }
 
-void DualModeController::HciLeSetEventMask(packets::PacketView<true> args) {
-  CHECK(args.size() == 8) << __func__ << " size=" << args.size();
+void DualModeController::LeSetEventMask(CommandPacketView command) {
+  auto command_view = gd_hci::LeSetEventMaskView::Create(command);
+  ASSERT(command_view.IsValid());
   /*
-    uint64_t mask = args.begin().extract<uint64_t>();
-    link_layer_controller_.SetLeEventMask(mask);
-  */
-  SendCommandCompleteSuccess(OpCode::LE_SET_EVENT_MASK);
+  uint64_t mask = args.begin().extract<uint64_t>();
+  link_layer_controller_.SetLeEventMask(mask);
+*/
+  auto packet = bluetooth::hci::LeSetEventMaskCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
 }
 
-void DualModeController::HciLeReadBufferSize(packets::PacketView<true> args) {
-  CHECK(args.size() == 0) << __func__ << " size=" << args.size();
-  std::shared_ptr<packets::EventPacketBuilder> command_complete =
-      packets::EventPacketBuilder::CreateCommandCompleteLeReadBufferSize(
-          hci::Status::SUCCESS, properties_.GetLeDataPacketLength(), properties_.GetTotalNumLeDataPackets());
-  send_event_(command_complete->ToVector());
+void DualModeController::LeReadBufferSize(CommandPacketView command) {
+  auto command_view = gd_hci::LeReadBufferSizeView::Create(command);
+  ASSERT(command_view.IsValid());
+
+  bluetooth::hci::LeBufferSize le_buffer_size;
+  le_buffer_size.le_data_packet_length_ = properties_.GetLeDataPacketLength();
+  le_buffer_size.total_num_le_packets_ = properties_.GetTotalNumLeDataPackets();
+
+  auto packet = bluetooth::hci::LeReadBufferSizeCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS, le_buffer_size);
+  send_event_(std::move(packet));
 }
 
-void DualModeController::HciLeReadLocalSupportedFeatures(packets::PacketView<true> args) {
-  CHECK(args.size() == 0) << __func__ << " size=" << args.size();
-  std::shared_ptr<packets::EventPacketBuilder> command_complete =
-      packets::EventPacketBuilder::CreateCommandCompleteLeReadLocalSupportedFeatures(
-          hci::Status::SUCCESS, properties_.GetLeSupportedFeatures());
-  send_event_(command_complete->ToVector());
+void DualModeController::LeReadLocalSupportedFeatures(
+    CommandPacketView command) {
+  auto command_view = gd_hci::LeReadLocalSupportedFeaturesView::Create(command);
+  ASSERT(command_view.IsValid());
+  auto packet =
+      bluetooth::hci::LeReadLocalSupportedFeaturesCompleteBuilder::Create(
+          kNumCommandPackets, ErrorCode::SUCCESS,
+          properties_.GetLeSupportedFeatures());
+  send_event_(std::move(packet));
 }
 
-void DualModeController::HciLeSetRandomAddress(packets::PacketView<true> args) {
-  CHECK(args.size() == 6) << __func__ << " size=" << args.size();
-  properties_.SetLeAddress(args.begin().extract<Address>());
-  SendCommandCompleteSuccess(OpCode::LE_SET_RANDOM_ADDRESS);
+void DualModeController::LeSetRandomAddress(CommandPacketView command) {
+  auto command_view = gd_hci::LeSetRandomAddressView::Create(
+      gd_hci::LeAdvertisingCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  properties_.SetLeAddress(command_view.GetRandomAddress());
+  auto packet = bluetooth::hci::LeSetRandomAddressCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
 }
 
-void DualModeController::HciLeSetAdvertisingParameters(packets::PacketView<true> args) {
-  CHECK(args.size() == 15) << __func__ << " size=" << args.size();
+void DualModeController::LeSetAdvertisingParameters(CommandPacketView command) {
+  auto command_view = gd_hci::LeSetAdvertisingParametersView::Create(
+      gd_hci::LeAdvertisingCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  properties_.SetLeAdvertisingParameters(
+      command_view.GetIntervalMin(), command_view.GetIntervalMax(),
+      static_cast<uint8_t>(command_view.GetType()),
+      static_cast<uint8_t>(command_view.GetOwnAddressType()),
+      static_cast<uint8_t>(command_view.GetPeerAddressType()),
+      command_view.GetPeerAddress(), command_view.GetChannelMap(),
+      static_cast<uint8_t>(command_view.GetFilterPolicy()));
 
-  SendCommandCompleteSuccess(OpCode::LE_SET_ADVERTISING_PARAMETERS);
+  auto packet =
+      bluetooth::hci::LeSetAdvertisingParametersCompleteBuilder::Create(
+          kNumCommandPackets, ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
 }
 
-void DualModeController::HciLeSetAdvertisingData(packets::PacketView<true> args) {
-  CHECK(args.size() > 0);
-  SendCommandCompleteSuccess(OpCode::LE_SET_ADVERTISING_DATA);
+void DualModeController::LeSetAdvertisingData(CommandPacketView command) {
+  auto command_view = gd_hci::LeSetAdvertisingDataView::Create(
+      gd_hci::LeAdvertisingCommandView::Create(command));
+  auto payload = command.GetPayload();
+  std::vector<uint8_t> payload_bytes{payload.begin() + 1,
+                                     payload.begin() + *payload.begin()};
+  ASSERT_LOG(command_view.IsValid(), "%s command.size() = %zu",
+             gd_hci::OpCodeText(command.GetOpCode()).c_str(), command.size());
+  ASSERT(command_view.GetPayload().size() == 32);
+  properties_.SetLeAdvertisement(payload_bytes);
+  auto packet = bluetooth::hci::LeSetAdvertisingDataCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
 }
 
-void DualModeController::HciLeSetScanParameters(packets::PacketView<true> args) {
-  CHECK(args.size() == 7) << __func__ << " size=" << args.size();
-  link_layer_controller_.SetLeScanType(args[0]);
-  link_layer_controller_.SetLeScanInterval(args[1] | (args[2] << 8));
-  link_layer_controller_.SetLeScanWindow(args[3] | (args[4] << 8));
-  link_layer_controller_.SetLeAddressType(args[5]);
-  link_layer_controller_.SetLeScanFilterPolicy(args[6]);
-  SendCommandCompleteSuccess(OpCode::LE_SET_SCAN_PARAMETERS);
+void DualModeController::LeSetScanResponseData(CommandPacketView command) {
+  auto command_view = gd_hci::LeSetScanResponseDataView::Create(
+      gd_hci::LeAdvertisingCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  ASSERT(command_view.GetPayload().size() == 32);
+  properties_.SetLeScanResponse(std::vector<uint8_t>(
+      command_view.GetPayload().begin() + 1, command_view.GetPayload().end()));
+  auto packet = bluetooth::hci::LeSetScanResponseDataCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
 }
 
-void DualModeController::HciLeSetScanEnable(packets::PacketView<true> args) {
-  CHECK(args.size() == 2) << __func__ << " size=" << args.size();
-  LOG_INFO(LOG_TAG, "SetScanEnable: %d %d", args[0], args[1]);
-  link_layer_controller_.SetLeScanEnable(args[0]);
-  link_layer_controller_.SetLeFilterDuplicates(args[1]);
-  SendCommandCompleteSuccess(OpCode::LE_SET_SCAN_ENABLE);
+void DualModeController::LeSetAdvertisingEnable(CommandPacketView command) {
+  auto command_view = gd_hci::LeSetAdvertisingEnableView::Create(
+      gd_hci::LeAdvertisingCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  auto status = link_layer_controller_.SetLeAdvertisingEnable(
+      command_view.GetAdvertisingEnable() == gd_hci::Enable::ENABLED);
+  auto packet = bluetooth::hci::LeSetAdvertisingEnableCompleteBuilder::Create(
+      kNumCommandPackets, status);
+  send_event_(std::move(packet));
 }
 
-void DualModeController::HciLeCreateConnection(packets::PacketView<true> args) {
-  CHECK(args.size() == 25) << __func__ << " size=" << args.size();
-  auto args_itr = args.begin();
-  link_layer_controller_.SetLeScanInterval(args_itr.extract<uint16_t>());
-  link_layer_controller_.SetLeScanWindow(args_itr.extract<uint16_t>());
-  uint8_t initiator_filter_policy = args_itr.extract<uint8_t>();
+void DualModeController::LeSetScanParameters(CommandPacketView command) {
+  auto command_view = gd_hci::LeSetScanParametersView::Create(
+      gd_hci::LeScanningCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  link_layer_controller_.SetLeScanType(
+      static_cast<uint8_t>(command_view.GetLeScanType()));
+  link_layer_controller_.SetLeScanInterval(command_view.GetLeScanInterval());
+  link_layer_controller_.SetLeScanWindow(command_view.GetLeScanWindow());
+  link_layer_controller_.SetLeAddressType(
+      static_cast<uint8_t>(command_view.GetOwnAddressType()));
+  link_layer_controller_.SetLeScanFilterPolicy(
+      static_cast<uint8_t>(command_view.GetScanningFilterPolicy()));
+  auto packet = bluetooth::hci::LeSetScanParametersCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
+}
+
+void DualModeController::LeSetScanEnable(CommandPacketView command) {
+  auto command_view = gd_hci::LeSetScanEnableView::Create(
+      gd_hci::LeScanningCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  if (command_view.GetLeScanEnable() == gd_hci::Enable::ENABLED) {
+    link_layer_controller_.SetLeScanEnable(gd_hci::OpCode::LE_SET_SCAN_ENABLE);
+  } else {
+    link_layer_controller_.SetLeScanEnable(gd_hci::OpCode::NONE);
+  }
+  link_layer_controller_.SetLeFilterDuplicates(
+      command_view.GetFilterDuplicates() == gd_hci::Enable::ENABLED);
+  auto packet = bluetooth::hci::LeSetScanEnableCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
+}
+
+void DualModeController::LeCreateConnection(CommandPacketView command) {
+  auto command_view = gd_hci::LeCreateConnectionView::Create(
+      gd_hci::LeConnectionManagementCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  link_layer_controller_.SetLeScanInterval(command_view.GetLeScanInterval());
+  link_layer_controller_.SetLeScanWindow(command_view.GetLeScanWindow());
+  uint8_t initiator_filter_policy =
+      static_cast<uint8_t>(command_view.GetInitiatorFilterPolicy());
   link_layer_controller_.SetLeInitiatorFilterPolicy(initiator_filter_policy);
 
   if (initiator_filter_policy == 0) {  // White list not used
-    uint8_t peer_address_type = args_itr.extract<uint8_t>();
-    Address peer_address = args_itr.extract<Address>();
+    uint8_t peer_address_type =
+        static_cast<uint8_t>(command_view.GetPeerAddressType());
+    Address peer_address = command_view.GetPeerAddress();
     link_layer_controller_.SetLePeerAddressType(peer_address_type);
     link_layer_controller_.SetLePeerAddress(peer_address);
   }
-  link_layer_controller_.SetLeAddressType(args_itr.extract<uint8_t>());
-  link_layer_controller_.SetLeConnectionIntervalMin(args_itr.extract<uint16_t>());
-  link_layer_controller_.SetLeConnectionIntervalMax(args_itr.extract<uint16_t>());
-  link_layer_controller_.SetLeConnectionLatency(args_itr.extract<uint16_t>());
-  link_layer_controller_.SetLeSupervisionTimeout(args_itr.extract<uint16_t>());
-  link_layer_controller_.SetLeMinimumCeLength(args_itr.extract<uint16_t>());
-  link_layer_controller_.SetLeMaximumCeLength(args_itr.extract<uint16_t>());
+  link_layer_controller_.SetLeAddressType(
+      static_cast<uint8_t>(command_view.GetOwnAddressType()));
+  link_layer_controller_.SetLeConnectionIntervalMin(
+      command_view.GetConnIntervalMin());
+  link_layer_controller_.SetLeConnectionIntervalMax(
+      command_view.GetConnIntervalMax());
+  link_layer_controller_.SetLeConnectionLatency(command_view.GetConnLatency());
+  link_layer_controller_.SetLeSupervisionTimeout(
+      command_view.GetSupervisionTimeout());
+  link_layer_controller_.SetLeMinimumCeLength(
+      command_view.GetMinimumCeLength());
+  link_layer_controller_.SetLeMaximumCeLength(
+      command_view.GetMaximumCeLength());
 
-  hci::Status status = link_layer_controller_.SetLeConnect(true);
+  auto status = link_layer_controller_.SetLeConnect(true);
 
-  SendCommandStatus(status, OpCode::LE_CREATE_CONNECTION);
+  auto packet = bluetooth::hci::LeCreateConnectionStatusBuilder::Create(
+      status, kNumCommandPackets);
+  send_event_(std::move(packet));
 }
 
-void DualModeController::HciLeConnectionUpdate(packets::PacketView<true> args) {
-  CHECK(args.size() == 14) << __func__ << " size=" << args.size();
+void DualModeController::LeConnectionUpdate(CommandPacketView command) {
+  auto command_view = gd_hci::LeConnectionUpdateView::Create(
+      gd_hci::LeConnectionManagementCommandView::Create(command));
+  ASSERT(command_view.IsValid());
 
-  SendCommandStatus(hci::Status::CONNECTION_REJECTED_UNACCEPTABLE_BD_ADDR, OpCode::LE_CONNECTION_UPDATE);
+  auto status_packet = bluetooth::hci::LeConnectionUpdateStatusBuilder::Create(
+      ErrorCode::CONNECTION_REJECTED_UNACCEPTABLE_BD_ADDR, kNumCommandPackets);
+  send_event_(std::move(status_packet));
 
-  send_event_(packets::EventPacketBuilder::CreateLeConnectionUpdateCompleteEvent(hci::Status::SUCCESS, 0x0002, 0x0006,
-                                                                                 0x0000, 0x01f4)
-                  ->ToVector());
+  auto complete_packet =
+      bluetooth::hci::LeConnectionUpdateCompleteBuilder::Create(
+          ErrorCode::SUCCESS, 0x0002, 0x0006, 0x0000, 0x01f4);
+  send_event_(std::move(complete_packet));
 }
 
-void DualModeController::HciCreateConnection(packets::PacketView<true> args) {
-  CHECK(args.size() == 13) << __func__ << " size=" << args.size();
+void DualModeController::CreateConnection(CommandPacketView command) {
+  auto command_view = gd_hci::CreateConnectionView::Create(
+      gd_hci::ConnectionManagementCommandView::Create(command));
+  ASSERT(command_view.IsValid());
 
-  auto args_itr = args.begin();
-  Address address = args_itr.extract<Address>();
-  uint16_t packet_type = args_itr.extract<uint16_t>();
-  uint8_t page_scan_mode = args_itr.extract<uint8_t>();
-  uint16_t clock_offset = args_itr.extract<uint16_t>();
-  uint8_t allow_role_switch = args_itr.extract<uint8_t>();
+  Address address = command_view.GetBdAddr();
+  uint16_t packet_type = command_view.GetPacketType();
+  uint8_t page_scan_mode =
+      static_cast<uint8_t>(command_view.GetPageScanRepetitionMode());
+  uint16_t clock_offset =
+      (command_view.GetClockOffsetValid() == gd_hci::ClockOffsetValid::VALID
+           ? command_view.GetClockOffset()
+           : 0);
+  uint8_t allow_role_switch =
+      static_cast<uint8_t>(command_view.GetAllowRoleSwitch());
 
-  hci::Status status =
-      link_layer_controller_.CreateConnection(address, packet_type, page_scan_mode, clock_offset, allow_role_switch);
+  auto status = link_layer_controller_.CreateConnection(
+      address, packet_type, page_scan_mode, clock_offset, allow_role_switch);
 
-  SendCommandStatus(status, OpCode::CREATE_CONNECTION);
+  auto packet = bluetooth::hci::CreateConnectionStatusBuilder::Create(
+      status, kNumCommandPackets);
+  send_event_(std::move(packet));
 }
 
-void DualModeController::HciDisconnect(packets::PacketView<true> args) {
-  CHECK(args.size() == 3) << __func__ << " size=" << args.size();
+void DualModeController::Disconnect(CommandPacketView command) {
+  auto command_view = gd_hci::DisconnectView::Create(
+      gd_hci::ConnectionManagementCommandView::Create(command));
+  ASSERT(command_view.IsValid());
 
-  auto args_itr = args.begin();
-  uint16_t handle = args_itr.extract<uint16_t>();
-  uint8_t reason = args_itr.extract<uint8_t>();
+  uint16_t handle = command_view.GetConnectionHandle();
+  uint8_t reason = static_cast<uint8_t>(command_view.GetReason());
 
-  hci::Status status = link_layer_controller_.Disconnect(handle, reason);
+  auto status = link_layer_controller_.Disconnect(handle, reason);
 
-  SendCommandStatus(status, OpCode::DISCONNECT);
+  auto packet = bluetooth::hci::DisconnectStatusBuilder::Create(
+      status, kNumCommandPackets);
+  send_event_(std::move(packet));
 }
 
-void DualModeController::HciLeConnectionCancel(packets::PacketView<true> args) {
-  CHECK(args.size() == 0) << __func__ << " size=" << args.size();
+void DualModeController::LeConnectionCancel(CommandPacketView command) {
+  auto command_view = gd_hci::LeCreateConnectionCancelView::Create(
+      gd_hci::LeConnectionManagementCommandView::Create(command));
+  ASSERT(command_view.IsValid());
   link_layer_controller_.SetLeConnect(false);
-  SendCommandStatusSuccess(OpCode::LE_CREATE_CONNECTION_CANCEL);
+  auto packet = bluetooth::hci::LeCreateConnectionCancelStatusBuilder::Create(
+      ErrorCode::SUCCESS, kNumCommandPackets);
+  send_event_(std::move(packet));
   /* For testing Jakub's patch:  Figure out a neat way to call this without
      recompiling.  I'm thinking about a bad device. */
   /*
   SendCommandCompleteOnlyStatus(OpCode::LE_CREATE_CONNECTION_CANCEL,
-                                Status::ERR_COMMAND_DISALLOWED);
+                                ErrorCode::COMMAND_DISALLOWED);
   */
 }
 
-void DualModeController::HciLeReadWhiteListSize(packets::PacketView<true> args) {
-  CHECK(args.size() == 0) << __func__ << " size=" << args.size();
-  std::shared_ptr<packets::EventPacketBuilder> command_complete =
-      packets::EventPacketBuilder::CreateCommandCompleteLeReadWhiteListSize(hci::Status::SUCCESS,
-                                                                            properties_.GetLeWhiteListSize());
-  send_event_(command_complete->ToVector());
+void DualModeController::LeReadWhiteListSize(CommandPacketView command) {
+  auto command_view = gd_hci::LeReadWhiteListSizeView::Create(
+      gd_hci::LeConnectionManagementCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  auto packet = bluetooth::hci::LeReadWhiteListSizeCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS, properties_.GetLeWhiteListSize());
+  send_event_(std::move(packet));
 }
 
-void DualModeController::HciLeClearWhiteList(packets::PacketView<true> args) {
-  CHECK(args.size() == 0) << __func__ << " size=" << args.size();
+void DualModeController::LeClearWhiteList(CommandPacketView command) {
+  auto command_view = gd_hci::LeClearWhiteListView::Create(
+      gd_hci::LeConnectionManagementCommandView::Create(command));
+  ASSERT(command_view.IsValid());
   link_layer_controller_.LeWhiteListClear();
-  SendCommandCompleteSuccess(OpCode::LE_CLEAR_WHITE_LIST);
+  auto packet = bluetooth::hci::LeClearWhiteListCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
 }
 
-void DualModeController::HciLeAddDeviceToWhiteList(packets::PacketView<true> args) {
-  CHECK(args.size() == 7) << __func__ << " size=" << args.size();
+void DualModeController::LeAddDeviceToWhiteList(CommandPacketView command) {
+  auto command_view = gd_hci::LeAddDeviceToWhiteListView::Create(
+      gd_hci::LeConnectionManagementCommandView::Create(command));
+  ASSERT(command_view.IsValid());
 
   if (link_layer_controller_.LeWhiteListFull()) {
-    SendCommandCompleteOnlyStatus(OpCode::LE_ADD_DEVICE_TO_WHITE_LIST, hci::Status::MEMORY_CAPACITY_EXCEEDED);
+    auto packet = bluetooth::hci::LeAddDeviceToWhiteListCompleteBuilder::Create(
+        kNumCommandPackets, ErrorCode::MEMORY_CAPACITY_EXCEEDED);
+    send_event_(std::move(packet));
     return;
   }
-  auto args_itr = args.begin();
-  uint8_t addr_type = args_itr.extract<uint8_t>();
-  Address address = args_itr.extract<Address>();
+  uint8_t addr_type = static_cast<uint8_t>(command_view.GetAddressType());
+  Address address = command_view.GetAddress();
   link_layer_controller_.LeWhiteListAddDevice(address, addr_type);
-  SendCommandCompleteSuccess(OpCode::LE_ADD_DEVICE_TO_WHITE_LIST);
+  auto packet = bluetooth::hci::LeAddDeviceToWhiteListCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
 }
 
-void DualModeController::HciLeRemoveDeviceFromWhiteList(packets::PacketView<true> args) {
-  CHECK(args.size() == 7) << __func__ << " size=" << args.size();
+void DualModeController::LeRemoveDeviceFromWhiteList(
+    CommandPacketView command) {
+  auto command_view = gd_hci::LeRemoveDeviceFromWhiteListView::Create(
+      gd_hci::LeConnectionManagementCommandView::Create(command));
+  ASSERT(command_view.IsValid());
 
-  auto args_itr = args.begin();
-  uint8_t addr_type = args_itr.extract<uint8_t>();
-  Address address = args_itr.extract<Address>();
+  uint8_t addr_type = static_cast<uint8_t>(command_view.GetAddressType());
+  Address address = command_view.GetAddress();
   link_layer_controller_.LeWhiteListRemoveDevice(address, addr_type);
-  SendCommandCompleteSuccess(OpCode::LE_REMOVE_DEVICE_FROM_WHITE_LIST);
+  auto packet =
+      bluetooth::hci::LeRemoveDeviceFromWhiteListCompleteBuilder::Create(
+          kNumCommandPackets, ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
 }
 
-/*
-void DualModeController::HciLeReadRemoteUsedFeaturesRsp(uint16_t handle,
-                                                        uint64_t features) {
-  std::shared_ptr<packets::EventPacketBuilder> event =
-      packets::EventPacketBuilder::CreateLeRemoteUsedFeaturesEvent(
-          hci::Status::SUCCESS, handle, features);
-  send_event_(event->ToVector());
-}
-*/
-
-void DualModeController::HciLeReadRemoteFeatures(packets::PacketView<true> args) {
-  CHECK(args.size() == 2) << __func__ << " size=" << args.size();
-
-  uint16_t handle = args.begin().extract<uint16_t>();
-
-  hci::Status status =
-      link_layer_controller_.SendCommandToRemoteByHandle(OpCode::LE_READ_REMOTE_FEATURES, args, handle);
-
-  SendCommandStatus(status, OpCode::LE_READ_REMOTE_FEATURES);
+void DualModeController::LeClearResolvingList(CommandPacketView command) {
+  auto command_view = gd_hci::LeClearResolvingListView::Create(
+      gd_hci::LeSecurityCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  link_layer_controller_.LeResolvingListClear();
+  auto packet = bluetooth::hci::LeClearResolvingListCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
 }
 
-void DualModeController::HciLeRand(packets::PacketView<true> args) {
-  CHECK(args.size() == 0) << __func__ << " size=" << args.size();
+void DualModeController::LeAddDeviceToResolvingList(CommandPacketView command) {
+  auto command_view = gd_hci::LeAddDeviceToResolvingListView::Create(
+      gd_hci::LeSecurityCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+
+  if (link_layer_controller_.LeResolvingListFull()) {
+    auto packet =
+        bluetooth::hci::LeAddDeviceToResolvingListCompleteBuilder::Create(
+            kNumCommandPackets, ErrorCode::MEMORY_CAPACITY_EXCEEDED);
+    send_event_(std::move(packet));
+    return;
+  }
+  uint8_t addr_type =
+      static_cast<uint8_t>(command_view.GetPeerIdentityAddressType());
+  Address address = command_view.GetPeerIdentityAddress();
+  std::array<uint8_t, LinkLayerController::kIrk_size> peerIrk =
+      command_view.GetPeerIrk();
+  std::array<uint8_t, LinkLayerController::kIrk_size> localIrk =
+      command_view.GetLocalIrk();
+
+  link_layer_controller_.LeResolvingListAddDevice(address, addr_type, peerIrk,
+                                                  localIrk);
+  auto packet =
+      bluetooth::hci::LeAddDeviceToResolvingListCompleteBuilder::Create(
+          kNumCommandPackets, ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
+}
+
+void DualModeController::LeRemoveDeviceFromResolvingList(
+    CommandPacketView command) {
+  auto command_view = gd_hci::LeRemoveDeviceFromResolvingListView::Create(
+      gd_hci::LeSecurityCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+
+  uint8_t addr_type =
+      static_cast<uint8_t>(command_view.GetPeerIdentityAddressType());
+  Address address = command_view.GetPeerIdentityAddress();
+  link_layer_controller_.LeResolvingListRemoveDevice(address, addr_type);
+  auto packet =
+      bluetooth::hci::LeRemoveDeviceFromResolvingListCompleteBuilder::Create(
+          kNumCommandPackets, ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
+}
+
+void DualModeController::LeSetExtendedScanParameters(
+    CommandPacketView command) {
+  auto command_view = gd_hci::LeSetExtendedScanParametersView::Create(
+      gd_hci::LeScanningCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  auto parameters = command_view.GetParameters();
+  // Multiple phys are not supported.
+  ASSERT(command_view.GetScanningPhys() == 1);
+  ASSERT(parameters.size() == 1);
+
+  link_layer_controller_.SetLeScanType(
+      static_cast<uint8_t>(parameters[0].le_scan_type_));
+  link_layer_controller_.SetLeScanInterval(parameters[0].le_scan_interval_);
+  link_layer_controller_.SetLeScanWindow(parameters[0].le_scan_window_);
+  link_layer_controller_.SetLeAddressType(
+      static_cast<uint8_t>(command_view.GetOwnAddressType()));
+  link_layer_controller_.SetLeScanFilterPolicy(
+      static_cast<uint8_t>(command_view.GetScanningFilterPolicy()));
+  auto packet =
+      bluetooth::hci::LeSetExtendedScanParametersCompleteBuilder::Create(
+          kNumCommandPackets, ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
+}
+
+void DualModeController::LeSetExtendedScanEnable(CommandPacketView command) {
+  auto command_view = gd_hci::LeSetExtendedScanEnableView::Create(
+      gd_hci::LeScanningCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  if (command_view.GetEnable() == gd_hci::Enable::ENABLED) {
+    link_layer_controller_.SetLeScanEnable(
+        gd_hci::OpCode::LE_SET_EXTENDED_SCAN_ENABLE);
+  } else {
+    link_layer_controller_.SetLeScanEnable(gd_hci::OpCode::NONE);
+  }
+  link_layer_controller_.SetLeFilterDuplicates(
+      command_view.GetFilterDuplicates() == gd_hci::FilterDuplicates::ENABLED);
+  auto packet = bluetooth::hci::LeSetExtendedScanEnableCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
+}
+
+void DualModeController::LeExtendedCreateConnection(CommandPacketView command) {
+  auto command_view = gd_hci::LeExtendedCreateConnectionView::Create(
+      gd_hci::LeConnectionManagementCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  ASSERT_LOG(command_view.GetInitiatingPhys() == 1, "Only LE_1M is supported");
+  auto params = command_view.GetPhyScanParameters();
+  link_layer_controller_.SetLeScanInterval(params[0].scan_interval_);
+  link_layer_controller_.SetLeScanWindow(params[0].scan_window_);
+  auto initiator_filter_policy = command_view.GetInitiatorFilterPolicy();
+  link_layer_controller_.SetLeInitiatorFilterPolicy(
+      static_cast<uint8_t>(initiator_filter_policy));
+
+  if (initiator_filter_policy ==
+      gd_hci::InitiatorFilterPolicy::USE_PEER_ADDRESS) {
+    link_layer_controller_.SetLePeerAddressType(
+        static_cast<uint8_t>(command_view.GetPeerAddressType()));
+    link_layer_controller_.SetLePeerAddress(command_view.GetPeerAddress());
+  }
+  link_layer_controller_.SetLeAddressType(
+      static_cast<uint8_t>(command_view.GetOwnAddressType()));
+  link_layer_controller_.SetLeConnectionIntervalMin(
+      params[0].conn_interval_min_);
+  link_layer_controller_.SetLeConnectionIntervalMax(
+      params[0].conn_interval_max_);
+  link_layer_controller_.SetLeConnectionLatency(params[0].conn_latency_);
+  link_layer_controller_.SetLeSupervisionTimeout(
+      params[0].supervision_timeout_);
+  link_layer_controller_.SetLeMinimumCeLength(params[0].min_ce_length_);
+  link_layer_controller_.SetLeMaximumCeLength(params[0].max_ce_length_);
+
+  auto status = link_layer_controller_.SetLeConnect(true);
+
+  send_event_(bluetooth::hci::LeExtendedCreateConnectionStatusBuilder::Create(
+      status, kNumCommandPackets));
+}
+
+void DualModeController::LeSetPrivacyMode(CommandPacketView command) {
+  auto command_view = gd_hci::LeSetPrivacyModeView::Create(
+      gd_hci::LeSecurityCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+
+  uint8_t peer_identity_address_type =
+      static_cast<uint8_t>(command_view.GetPeerIdentityAddressType());
+  Address peer_identity_address = command_view.GetPeerIdentityAddress();
+  uint8_t privacy_mode = static_cast<uint8_t>(command_view.GetPrivacyMode());
+
+  if (link_layer_controller_.LeResolvingListContainsDevice(
+          peer_identity_address, peer_identity_address_type)) {
+    link_layer_controller_.LeSetPrivacyMode(
+        peer_identity_address_type, peer_identity_address, privacy_mode);
+  }
+
+  auto packet = bluetooth::hci::LeSetPrivacyModeCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
+}
+
+void DualModeController::LeReadRemoteFeatures(CommandPacketView command) {
+  auto command_view = gd_hci::LeReadRemoteFeaturesView::Create(
+      gd_hci::LeConnectionManagementCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+
+  uint16_t handle = command_view.GetConnectionHandle();
+
+  auto status = link_layer_controller_.SendCommandToRemoteByHandle(
+      OpCode::LE_READ_REMOTE_FEATURES, command_view.GetPayload(), handle);
+
+  auto packet = bluetooth::hci::LeConnectionUpdateStatusBuilder::Create(
+      status, kNumCommandPackets);
+  send_event_(std::move(packet));
+}
+
+void DualModeController::LeRand(CommandPacketView command) {
+  auto command_view = gd_hci::LeRandView::Create(
+      gd_hci::LeSecurityCommandView::Create(command));
+  ASSERT(command_view.IsValid());
   uint64_t random_val = 0;
   for (size_t rand_bytes = 0; rand_bytes < sizeof(uint64_t); rand_bytes += sizeof(RAND_MAX)) {
     random_val = (random_val << (8 * sizeof(RAND_MAX))) | random();
   }
-  std::shared_ptr<packets::EventPacketBuilder> command_complete =
-      packets::EventPacketBuilder::CreateCommandCompleteLeRand(hci::Status::SUCCESS, random_val);
-  send_event_(command_complete->ToVector());
+
+  auto packet = bluetooth::hci::LeRandCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS, random_val);
+  send_event_(std::move(packet));
 }
 
-void DualModeController::HciLeReadSupportedStates(packets::PacketView<true> args) {
-  CHECK(args.size() == 0) << __func__ << " size=" << args.size();
-  std::shared_ptr<packets::EventPacketBuilder> command_complete =
-      packets::EventPacketBuilder::CreateCommandCompleteLeReadSupportedStates(hci::Status::SUCCESS,
-                                                                              properties_.GetLeSupportedStates());
-  send_event_(command_complete->ToVector());
+void DualModeController::LeReadSupportedStates(CommandPacketView command) {
+  auto command_view = gd_hci::LeReadSupportedStatesView::Create(command);
+  ASSERT(command_view.IsValid());
+  auto packet = bluetooth::hci::LeReadSupportedStatesCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS,
+      properties_.GetLeSupportedStates());
+  send_event_(std::move(packet));
 }
 
-void DualModeController::HciLeVendorCap(packets::PacketView<true> args) {
-  CHECK(args.size() == 0) << __func__ << " size=" << args.size();
+void DualModeController::LeVendorCap(CommandPacketView command) {
+  auto command_view = gd_hci::LeGetVendorCapabilitiesView::Create(
+      gd_hci::VendorCommandView::Create(command));
+  ASSERT(command_view.IsValid());
   vector<uint8_t> caps = properties_.GetLeVendorCap();
   if (caps.size() == 0) {
-    SendCommandCompleteOnlyStatus(OpCode::LE_GET_VENDOR_CAPABILITIES, hci::Status::UNKNOWN_COMMAND);
+    SendCommandCompleteUnknownOpCodeEvent(
+        static_cast<uint16_t>(OpCode::LE_GET_VENDOR_CAPABILITIES));
     return;
   }
 
-  std::shared_ptr<packets::EventPacketBuilder> command_complete =
-      packets::EventPacketBuilder::CreateCommandCompleteLeGetVendorCapabilities(hci::Status::SUCCESS,
-                                                                                properties_.GetLeVendorCap());
-  send_event_(command_complete->ToVector());
+  std::unique_ptr<bluetooth::packet::RawBuilder> raw_builder_ptr =
+      std::make_unique<bluetooth::packet::RawBuilder>();
+  raw_builder_ptr->AddOctets1(static_cast<uint8_t>(ErrorCode::SUCCESS));
+  raw_builder_ptr->AddOctets(properties_.GetLeVendorCap());
+
+  auto packet = bluetooth::hci::CommandCompleteBuilder::Create(
+      kNumCommandPackets, OpCode::LE_GET_VENDOR_CAPABILITIES,
+      std::move(raw_builder_ptr));
+  send_event_(std::move(packet));
 }
 
-void DualModeController::HciLeVendorMultiAdv(packets::PacketView<true> args) {
-  CHECK(args.size() > 0);
-  SendCommandCompleteOnlyStatus(OpCode::LE_MULTI_ADVT, hci::Status::UNKNOWN_COMMAND);
+void DualModeController::LeVendorMultiAdv(CommandPacketView command) {
+  auto command_view = gd_hci::LeMultiAdvtView::Create(
+      gd_hci::LeAdvertisingCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  SendCommandCompleteUnknownOpCodeEvent(
+      static_cast<uint16_t>(OpCode::LE_MULTI_ADVT));
 }
 
-void DualModeController::HciLeAdvertisingFilter(packets::PacketView<true> args) {
-  CHECK(args.size() > 0);
-  SendCommandCompleteOnlyStatus(OpCode::LE_ADV_FILTER, hci::Status::UNKNOWN_COMMAND);
+void DualModeController::LeAdvertisingFilter(CommandPacketView command) {
+  auto command_view = gd_hci::LeAdvFilterView::Create(
+      gd_hci::LeScanningCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  SendCommandCompleteUnknownOpCodeEvent(
+      static_cast<uint16_t>(OpCode::LE_ADV_FILTER));
 }
 
-void DualModeController::HciLeEnergyInfo(packets::PacketView<true> args) {
-  CHECK(args.size() > 0);
-  SendCommandCompleteOnlyStatus(OpCode::LE_ENERGY_INFO, hci::Status::UNKNOWN_COMMAND);
+void DualModeController::LeEnergyInfo(CommandPacketView command) {
+  auto command_view = gd_hci::LeEnergyInfoView::Create(
+      gd_hci::VendorCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  SendCommandCompleteUnknownOpCodeEvent(
+      static_cast<uint16_t>(OpCode::LE_ENERGY_INFO));
 }
 
-void DualModeController::HciLeExtendedScanParams(packets::PacketView<true> args) {
-  CHECK(args.size() > 0);
-  SendCommandCompleteOnlyStatus(OpCode::LE_EXTENDED_SCAN_PARAMS, hci::Status::UNKNOWN_COMMAND);
+void DualModeController::LeSetExtendedAdvertisingRandomAddress(
+    CommandPacketView command) {
+  auto command_view = gd_hci::LeSetExtendedAdvertisingRandomAddressView::Create(
+      gd_hci::LeAdvertisingCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  properties_.SetLeAddress(command_view.GetAdvertisingRandomAddress());
+  send_event_(
+      bluetooth::hci::LeSetExtendedAdvertisingRandomAddressCompleteBuilder::
+          Create(kNumCommandPackets, ErrorCode::SUCCESS));
 }
 
-void DualModeController::HciLeStartEncryption(packets::PacketView<true> args) {
-  CHECK(args.size() == 28) << __func__ << " size=" << args.size();
+void DualModeController::LeSetExtendedAdvertisingParameters(
+    CommandPacketView command) {
+  auto command_view =
+      gd_hci::LeSetExtendedAdvertisingLegacyParametersView::Create(
+          gd_hci::LeAdvertisingCommandView::Create(command));
+  // TODO: Support non-legacy parameters
+  ASSERT(command_view.IsValid());
+  properties_.SetLeAdvertisingParameters(
+      command_view.GetPrimaryAdvertisingIntervalMin(),
+      command_view.GetPrimaryAdvertisingIntervalMax(),
+      static_cast<uint8_t>(bluetooth::hci::AdvertisingEventType::ADV_IND),
+      static_cast<uint8_t>(command_view.GetOwnAddressType()),
+      static_cast<uint8_t>(command_view.GetPeerAddressType()),
+      command_view.GetPeerAddress(),
+      command_view.GetPrimaryAdvertisingChannelMap(),
+      static_cast<uint8_t>(command_view.GetAdvertisingFilterPolicy()));
 
-  auto args_itr = args.begin();
-  uint16_t handle = args_itr.extract<uint16_t>();
-  // uint64_t random_number = args_itr.extract<uint64_t>();
-  // uint16_t encrypted_diversifier = args_itr.extract<uint16_t>();
-  // std::vector<uint8_t> long_term_key;
-  // for (size_t i = 0; i < 16; i++) {
-  //   long_term_key.push_back(args_itr.extract<uint18_t>();
-  // }
-  SendCommandStatus(hci::Status::SUCCESS, OpCode::LE_START_ENCRYPTION);
-
-  send_event_(packets::EventPacketBuilder::CreateEncryptionChange(hci::Status::SUCCESS, handle, 0x01)->ToVector());
-#if 0
-
-  std::shared_ptr<packets::AclPacketBuilder> encryption_information =
-      std::make_shared<packets::AclPacketBuilder>(
-          0x0002, Acl::FIRST_AUTOMATICALLY_FLUSHABLE, Acl::POINT_TO_POINT,
-          std::vector<uint8_t>({}));
-
-  encryption_information->AddPayloadOctets2(0x0011);
-  encryption_information->AddPayloadOctets2(0x0006);
-  encryption_information->AddPayloadOctets1(0x06);
-  encryption_information->AddPayloadOctets8(0x0706050403020100);
-  encryption_information->AddPayloadOctets8(0x0F0E0D0C0B0A0908);
-
-  send_acl_(encryption_information);
-
-  encryption_information = std::make_shared<packets::AclPacketBuilder>(
-      0x0002, Acl::FIRST_AUTOMATICALLY_FLUSHABLE, Acl::POINT_TO_POINT,
-      std::vector<uint8_t>({}));
-
-  encryption_information->AddPayloadOctets2(0x000B);
-  encryption_information->AddPayloadOctets2(0x0006);
-  encryption_information->AddPayloadOctets1(0x07);
-  encryption_information->AddPayloadOctets2(0xBEEF);
-  encryption_information->AddPayloadOctets8(0x0706050403020100);
-
-  send_acl_(encryption_information);
-
-  encryption_information = std::make_shared<packets::AclPacketBuilder>(
-      0x0002, Acl::FIRST_AUTOMATICALLY_FLUSHABLE, Acl::POINT_TO_POINT,
-      std::vector<uint8_t>({}));
-
-  encryption_information->AddPayloadOctets2(0x0011);
-  encryption_information->AddPayloadOctets2(0x0006);
-  encryption_information->AddPayloadOctets1(0x08);
-  encryption_information->AddPayloadOctets8(0x0F0E0D0C0B0A0908);
-  encryption_information->AddPayloadOctets8(0x0706050403020100);
-
-  send_acl_(encryption_information);
-
-  encryption_information = std::make_shared<packets::AclPacketBuilder>(
-      0x0002, Acl::FIRST_AUTOMATICALLY_FLUSHABLE, Acl::POINT_TO_POINT,
-      std::vector<uint8_t>({}));
-
-  encryption_information->AddPayloadOctets2(0x0008);
-  encryption_information->AddPayloadOctets2(0x0006);
-  encryption_information->AddPayloadOctets1(0x09);
-  encryption_information->AddPayloadOctets1(0x01);
-  encryption_information->AddPayloadOctets6(0xDEADBEEFF00D);
-  send_acl_(encryption_information);
-  // send_event_(packets::EventPacketBuilder::CreateLeStartEncryption()->ToVector());
-
-#endif
+  send_event_(
+      bluetooth::hci::LeSetExtendedAdvertisingParametersCompleteBuilder::Create(
+          kNumCommandPackets, ErrorCode::SUCCESS, 0xa5));
 }
 
-void DualModeController::HciReadLoopbackMode(packets::PacketView<true> args) {
-  CHECK(args.size() == 0) << __func__ << " size=" << args.size();
-  send_event_(packets::EventPacketBuilder::CreateCommandCompleteReadLoopbackMode(hci::Status::SUCCESS, loopback_mode_)
-                  ->ToVector());
+void DualModeController::LeSetExtendedAdvertisingData(
+    CommandPacketView command) {
+  auto command_view = gd_hci::LeSetExtendedAdvertisingDataView::Create(
+      gd_hci::LeAdvertisingCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  auto raw_command_view = gd_hci::LeSetExtendedAdvertisingDataRawView::Create(
+      gd_hci::LeAdvertisingCommandView::Create(command));
+  ASSERT(raw_command_view.IsValid());
+  properties_.SetLeAdvertisement(raw_command_view.GetAdvertisingData());
+  auto packet =
+      bluetooth::hci::LeSetExtendedAdvertisingDataCompleteBuilder::Create(
+          kNumCommandPackets, ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
 }
 
-void DualModeController::HciWriteLoopbackMode(packets::PacketView<true> args) {
-  CHECK(args.size() == 1) << __func__ << " size=" << args.size();
-  loopback_mode_ = static_cast<hci::LoopbackMode>(args[0]);
+void DualModeController::LeSetExtendedAdvertisingScanResponse(
+    CommandPacketView command) {
+  auto command_view = gd_hci::LeSetExtendedAdvertisingScanResponseView::Create(
+      gd_hci::LeAdvertisingCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  properties_.SetLeScanResponse(std::vector<uint8_t>(
+      command_view.GetPayload().begin() + 1, command_view.GetPayload().end()));
+  send_event_(
+      bluetooth::hci::LeSetExtendedAdvertisingScanResponseCompleteBuilder::
+          Create(kNumCommandPackets, ErrorCode::SUCCESS));
+}
+
+void DualModeController::LeSetExtendedAdvertisingEnable(
+    CommandPacketView command) {
+  auto command_view = gd_hci::LeSetExtendedAdvertisingEnableView::Create(
+      gd_hci::LeAdvertisingCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  auto status = link_layer_controller_.SetLeAdvertisingEnable(
+      command_view.GetEnable() == gd_hci::Enable::ENABLED);
+  send_event_(
+      bluetooth::hci::LeSetExtendedAdvertisingEnableCompleteBuilder::Create(
+          kNumCommandPackets, status));
+}
+
+void DualModeController::LeExtendedScanParams(CommandPacketView command) {
+  auto command_view = gd_hci::LeExtendedScanParamsView::Create(
+      gd_hci::LeScanningCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+  SendCommandCompleteUnknownOpCodeEvent(
+      static_cast<uint16_t>(OpCode::LE_EXTENDED_SCAN_PARAMS));
+}
+
+void DualModeController::LeStartEncryption(CommandPacketView command) {
+  auto command_view = gd_hci::LeStartEncryptionView::Create(
+      gd_hci::LeSecurityCommandView::Create(command));
+  ASSERT(command_view.IsValid());
+
+  uint16_t handle = command_view.GetConnectionHandle();
+
+  auto status_packet = bluetooth::hci::LeStartEncryptionStatusBuilder::Create(
+      ErrorCode::SUCCESS, kNumCommandPackets);
+  send_event_(std::move(status_packet));
+
+  auto complete_packet = bluetooth::hci::EncryptionChangeBuilder::Create(
+      ErrorCode::SUCCESS, handle, bluetooth::hci::EncryptionEnabled::OFF);
+  send_event_(std::move(complete_packet));
+}
+
+void DualModeController::ReadLoopbackMode(CommandPacketView command) {
+  auto command_view = gd_hci::ReadLoopbackModeView::Create(command);
+  ASSERT(command_view.IsValid());
+  auto packet = bluetooth::hci::ReadLoopbackModeCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS,
+      static_cast<LoopbackMode>(loopback_mode_));
+  send_event_(std::move(packet));
+}
+
+void DualModeController::WriteLoopbackMode(CommandPacketView command) {
+  auto command_view = gd_hci::WriteLoopbackModeView::Create(command);
+  ASSERT(command_view.IsValid());
+  loopback_mode_ = command_view.GetLoopbackMode();
   // ACL channel
   uint16_t acl_handle = 0x123;
-  send_event_(packets::EventPacketBuilder::CreateConnectionCompleteEvent(
-                  hci::Status::SUCCESS, acl_handle, properties_.GetAddress(), hci::LinkType::ACL, false)
-                  ->ToVector());
+  auto packet_acl = bluetooth::hci::ConnectionCompleteBuilder::Create(
+      ErrorCode::SUCCESS, acl_handle, properties_.GetAddress(),
+      bluetooth::hci::LinkType::ACL, bluetooth::hci::Enable::DISABLED);
+  send_event_(std::move(packet_acl));
   // SCO channel
   uint16_t sco_handle = 0x345;
-  send_event_(packets::EventPacketBuilder::CreateConnectionCompleteEvent(
-                  hci::Status::SUCCESS, sco_handle, properties_.GetAddress(), hci::LinkType::SCO, false)
-                  ->ToVector());
-  SendCommandCompleteSuccess(OpCode::WRITE_LOOPBACK_MODE);
+  auto packet_sco = bluetooth::hci::ConnectionCompleteBuilder::Create(
+      ErrorCode::SUCCESS, sco_handle, properties_.GetAddress(),
+      bluetooth::hci::LinkType::SCO, bluetooth::hci::Enable::DISABLED);
+  send_event_(std::move(packet_sco));
+  auto packet = bluetooth::hci::WriteLoopbackModeCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
+}
+
+void DualModeController::SetAddress(Address address) {
+  properties_.SetAddress(address);
 }
 
 }  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/model/controller/dual_mode_controller.h b/vendor_libs/test_vendor_lib/model/controller/dual_mode_controller.h
index f33bbb8..696d24a 100644
--- a/vendor_libs/test_vendor_lib/model/controller/dual_mode_controller.h
+++ b/vendor_libs/test_vendor_lib/model/controller/dual_mode_controller.h
@@ -24,14 +24,18 @@
 #include <vector>
 
 #include "base/time/time.h"
+#include "hci/address.h"
+#include "hci/hci_packets.h"
 #include "link_layer_controller.h"
 #include "model/devices/device.h"
 #include "model/setup/async_manager.h"
 #include "security_manager.h"
-#include "types/address.h"
 
 namespace test_vendor_lib {
 
+using ::bluetooth::hci::Address;
+using ::bluetooth::hci::CommandPacketView;
+
 // Emulates a dual mode BR/EDR + LE controller by maintaining the link layer
 // state machine detailed in the Bluetooth Core Specification Version 4.2,
 // Volume 6, Part B, Section 1.1 (page 30). Provides methods corresponding to
@@ -60,17 +64,16 @@
 
   virtual std::string GetTypeString() const override;
 
-  virtual void IncomingPacket(packets::LinkLayerPacketView incoming) override;
+  virtual void IncomingPacket(
+      model::packets::LinkLayerPacketView incoming) override;
 
   virtual void TimerTick() override;
 
-  // Send packets to remote devices
-  void SendLinkLayerPacket(std::shared_ptr<packets::LinkLayerPacketBuilder> to_send, Phy::Type phy_type);
-
   // Route commands and data from the stack.
   void HandleAcl(std::shared_ptr<std::vector<uint8_t>> acl_packet);
   void HandleCommand(std::shared_ptr<std::vector<uint8_t>> command_packet);
   void HandleSco(std::shared_ptr<std::vector<uint8_t>> sco_packet);
+  void HandleIso(std::shared_ptr<std::vector<uint8_t>> iso_packet);
 
   // Set the callbacks for scheduling tasks.
   void RegisterTaskScheduler(std::function<AsyncTaskId(std::chrono::milliseconds, const TaskCallback&)> evtScheduler);
@@ -82,12 +85,21 @@
   void RegisterTaskCancel(std::function<void(AsyncTaskId)> cancel);
 
   // Set the callbacks for sending packets to the HCI.
-  void RegisterEventChannel(const std::function<void(std::shared_ptr<std::vector<uint8_t>>)>& send_event);
+  void RegisterEventChannel(
+      const std::function<void(std::shared_ptr<std::vector<uint8_t>>)>&
+          send_event);
 
   void RegisterAclChannel(const std::function<void(std::shared_ptr<std::vector<uint8_t>>)>& send_acl);
 
   void RegisterScoChannel(const std::function<void(std::shared_ptr<std::vector<uint8_t>>)>& send_sco);
 
+  void RegisterIsoChannel(
+      const std::function<void(std::shared_ptr<std::vector<uint8_t>>)>&
+          send_iso);
+
+  // Set the device's address.
+  void SetAddress(Address address) override;
+
   // Controller commands. For error codes, see the Bluetooth Core Specification,
   // Version 4.2, Volume 2, Part D (page 370).
 
@@ -95,261 +107,372 @@
   // Bluetooth Core Specification Version 4.2 Volume 2 Part E 7.1
 
   // 7.1.1
-  void HciInquiry(packets::PacketView<true> args);
+  void Inquiry(CommandPacketView args);
 
   // 7.1.2
-  void HciInquiryCancel(packets::PacketView<true> args);
+  void InquiryCancel(CommandPacketView args);
 
   // 7.1.5
-  void HciCreateConnection(packets::PacketView<true> args);
+  void CreateConnection(CommandPacketView args);
 
   // 7.1.6
-  void HciDisconnect(packets::PacketView<true> args);
+  void Disconnect(CommandPacketView args);
 
   // 7.1.8
-  void HciAcceptConnectionRequest(packets::PacketView<true> args);
+  void AcceptConnectionRequest(CommandPacketView args);
 
   // 7.1.9
-  void HciRejectConnectionRequest(packets::PacketView<true> args);
+  void RejectConnectionRequest(CommandPacketView args);
 
   // 7.1.10
-  void HciLinkKeyRequestReply(packets::PacketView<true> args);
+  void LinkKeyRequestReply(CommandPacketView args);
 
   // 7.1.11
-  void HciLinkKeyRequestNegativeReply(packets::PacketView<true> args);
+  void LinkKeyRequestNegativeReply(CommandPacketView args);
 
   // 7.1.14
-  void HciChangeConnectionPacketType(packets::PacketView<true> args);
+  void ChangeConnectionPacketType(CommandPacketView args);
 
   // 7.1.15
-  void HciAuthenticationRequested(packets::PacketView<true> args);
+  void AuthenticationRequested(CommandPacketView args);
 
   // 7.1.16
-  void HciSetConnectionEncryption(packets::PacketView<true> args);
+  void SetConnectionEncryption(CommandPacketView args);
+
+  // 7.1.17
+  void ChangeConnectionLinkKey(CommandPacketView args);
+
+  // 7.1.18
+  void MasterLinkKey(CommandPacketView args);
 
   // 7.1.19
-  void HciRemoteNameRequest(packets::PacketView<true> args);
+  void RemoteNameRequest(CommandPacketView args);
+
+  // 7.2.8
+  void SwitchRole(CommandPacketView args);
 
   // 7.1.21
-  void HciReadRemoteSupportedFeatures(packets::PacketView<true> args);
+  void ReadRemoteSupportedFeatures(CommandPacketView args);
 
   // 7.1.22
-  void HciReadRemoteExtendedFeatures(packets::PacketView<true> args);
+  void ReadRemoteExtendedFeatures(CommandPacketView args);
 
   // 7.1.23
-  void HciReadRemoteVersionInformation(packets::PacketView<true> args);
+  void ReadRemoteVersionInformation(CommandPacketView args);
 
   // 7.1.24
-  void HciReadClockOffset(packets::PacketView<true> args);
+  void ReadClockOffset(CommandPacketView args);
 
   // 7.1.29
-  void HciIoCapabilityRequestReply(packets::PacketView<true> args);
+  void IoCapabilityRequestReply(CommandPacketView args);
 
   // 7.1.30
-  void HciUserConfirmationRequestReply(packets::PacketView<true> args);
+  void UserConfirmationRequestReply(CommandPacketView args);
 
   // 7.1.31
-  void HciUserConfirmationRequestNegativeReply(packets::PacketView<true> args);
+  void UserConfirmationRequestNegativeReply(CommandPacketView args);
 
   // 7.1.32
-  void HciUserPasskeyRequestReply(packets::PacketView<true> args);
+  void UserPasskeyRequestReply(CommandPacketView args);
 
   // 7.1.33
-  void HciUserPasskeyRequestNegativeReply(packets::PacketView<true> args);
+  void UserPasskeyRequestNegativeReply(CommandPacketView args);
 
   // 7.1.34
-  void HciRemoteOobDataRequestReply(packets::PacketView<true> args);
+  void RemoteOobDataRequestReply(CommandPacketView args);
 
   // 7.1.35
-  void HciRemoteOobDataRequestNegativeReply(packets::PacketView<true> args);
+  void RemoteOobDataRequestNegativeReply(CommandPacketView args);
 
   // 7.1.36
-  void HciIoCapabilityRequestNegativeReply(packets::PacketView<true> args);
+  void IoCapabilityRequestNegativeReply(CommandPacketView args);
 
   // Link Policy Commands
   // Bluetooth Core Specification Version 4.2 Volume 2 Part E 7.2
 
+  // 7.2.1
+  void HoldMode(CommandPacketView args);
+
+  // 7.2.2
+  void SniffMode(CommandPacketView args);
+
+  // 7.2.3
+  void ExitSniffMode(CommandPacketView args);
+
+  // 7.2.6
+  void QosSetup(CommandPacketView args);
+
   // 7.2.10
-  void HciWriteLinkPolicySettings(packets::PacketView<true> args);
+  void WriteLinkPolicySettings(CommandPacketView args);
 
   // 7.2.12
-  void HciWriteDefaultLinkPolicySettings(packets::PacketView<true> args);
+  void WriteDefaultLinkPolicySettings(CommandPacketView args);
+
+  // 7.2.13
+  void FlowSpecification(CommandPacketView args);
 
   // 7.2.14
-  void HciSniffSubrating(packets::PacketView<true> args);
+  void SniffSubrating(CommandPacketView args);
 
   // Link Controller Commands
   // Bluetooth Core Specification Version 4.2 Volume 2 Part E 7.3
 
   // 7.3.1
-  void HciSetEventMask(packets::PacketView<true> args);
+  void SetEventMask(CommandPacketView args);
 
   // 7.3.2
-  void HciReset(packets::PacketView<true> args);
+  void Reset(CommandPacketView args);
 
   // 7.3.3
-  void HciSetEventFilter(packets::PacketView<true> args);
+  void SetEventFilter(CommandPacketView args);
 
   // 7.3.10
-  void HciDeleteStoredLinkKey(packets::PacketView<true> args);
+  void DeleteStoredLinkKey(CommandPacketView args);
 
   // 7.3.11
-  void HciWriteLocalName(packets::PacketView<true> args);
+  void WriteLocalName(CommandPacketView args);
 
   // 7.3.12
-  void HciReadLocalName(packets::PacketView<true> args);
+  void ReadLocalName(CommandPacketView args);
+
+  // 7.3.15
+  void ReadPageTimeout(CommandPacketView args);
 
   // 7.3.16
-  void HciWritePageTimeout(packets::PacketView<true> args);
+  void WritePageTimeout(CommandPacketView args);
+
+  // 7.3.17
+  void ReadScanEnable(CommandPacketView args);
 
   // 7.3.18
-  void HciWriteScanEnable(packets::PacketView<true> args);
+  void WriteScanEnable(CommandPacketView args);
+
+  // 7.3.19
+  void ReadPageScanActivity(CommandPacketView args);
+
+  // 7.3.20
+  void WritePageScanActivity(CommandPacketView args);
+
+  // 7.3.21
+  void ReadInquiryScanActivity(CommandPacketView args);
 
   // 7.3.22
-  void HciWriteInquiryScanActivity(packets::PacketView<true> args);
+  void WriteInquiryScanActivity(CommandPacketView args);
 
   // 7.3.23
-  void HciReadAuthenticationEnable(packets::PacketView<true> args);
+  void ReadAuthenticationEnable(CommandPacketView args);
 
   // 7.3.24
-  void HciWriteAuthenticationEnable(packets::PacketView<true> args);
+  void WriteAuthenticationEnable(CommandPacketView args);
 
   // 7.3.26
-  void HciWriteClassOfDevice(packets::PacketView<true> args);
+  void WriteClassOfDevice(CommandPacketView args);
 
   // 7.3.28
-  void HciWriteVoiceSetting(packets::PacketView<true> args);
+  void WriteVoiceSetting(CommandPacketView args);
 
   // 7.3.39
-  void HciHostBufferSize(packets::PacketView<true> args);
+  void HostBufferSize(CommandPacketView args);
 
   // 7.3.42
-  void HciWriteLinkSupervisionTimeout(packets::PacketView<true> args);
+  void WriteLinkSupervisionTimeout(CommandPacketView args);
+
+  // 7.3.43
+  void ReadNumberOfSupportedIac(CommandPacketView args);
+
+  // 7.3.44
+  void ReadCurrentIacLap(CommandPacketView args);
 
   // 7.3.45
-  void HciWriteCurrentIacLap(packets::PacketView<true> args);
+  void WriteCurrentIacLap(CommandPacketView args);
+
+  // 7.3.47
+  void ReadInquiryScanType(CommandPacketView args);
 
   // 7.3.48
-  void HciWriteInquiryScanType(packets::PacketView<true> args);
+  void WriteInquiryScanType(CommandPacketView args);
+
+  // 7.3.49
+  void ReadInquiryMode(CommandPacketView args);
 
   // 7.3.50
-  void HciWriteInquiryMode(packets::PacketView<true> args);
+  void WriteInquiryMode(CommandPacketView args);
 
   // 7.3.52
-  void HciWritePageScanType(packets::PacketView<true> args);
+  void ReadPageScanType(CommandPacketView args);
+
+  // 7.3.52
+  void WritePageScanType(CommandPacketView args);
 
   // 7.3.56
-  void HciWriteExtendedInquiryResponse(packets::PacketView<true> args);
+  void WriteExtendedInquiryResponse(CommandPacketView args);
+
+  // 7.3.57
+  void RefreshEncryptionKey(CommandPacketView args);
 
   // 7.3.59
-  void HciWriteSimplePairingMode(packets::PacketView<true> args);
+  void WriteSimplePairingMode(CommandPacketView args);
+
+  // 7.3.61
+  void ReadInquiryResponseTransmitPowerLevel(CommandPacketView args);
 
   // 7.3.79
-  void HciWriteLeHostSupport(packets::PacketView<true> args);
+  void WriteLeHostSupport(CommandPacketView args);
+
+  // 7.3.92
+  void WriteSecureConnectionsHostSupport(CommandPacketView args);
 
   // Informational Parameters Commands
   // Bluetooth Core Specification Version 4.2 Volume 2 Part E 7.4
 
   // 7.4.5
-  void HciReadBufferSize(packets::PacketView<true> args);
+  void ReadBufferSize(CommandPacketView args);
 
   // 7.4.1
-  void HciReadLocalVersionInformation(packets::PacketView<true> args);
+  void ReadLocalVersionInformation(CommandPacketView args);
 
   // 7.4.6
-  void HciReadBdAddr(packets::PacketView<true> args);
+  void ReadBdAddr(CommandPacketView args);
 
   // 7.4.2
-  void HciReadLocalSupportedCommands(packets::PacketView<true> args);
+  void ReadLocalSupportedCommands(CommandPacketView args);
+
+  // 7.4.3
+  void ReadLocalSupportedFeatures(CommandPacketView args);
 
   // 7.4.4
-  void HciReadLocalExtendedFeatures(packets::PacketView<true> args);
+  void ReadLocalExtendedFeatures(CommandPacketView args);
 
   // 7.4.8
-  void HciReadLocalSupportedCodecs(packets::PacketView<true> args);
+  void ReadLocalSupportedCodecs(CommandPacketView args);
 
   // Status Parameters Commands
   // Bluetooth Core Specification Version 4.2 Volume 2 Part E 7.5
 
+  // 7.5.7
+  void ReadEncryptionKeySize(CommandPacketView args);
+
   // Test Commands
   // Bluetooth Core Specification Version 4.2 Volume 2 Part E 7.7
 
   // 7.7.1
-  void HciReadLoopbackMode(packets::PacketView<true> args);
+  void ReadLoopbackMode(CommandPacketView args);
 
   // 7.7.2
-  void HciWriteLoopbackMode(packets::PacketView<true> args);
+  void WriteLoopbackMode(CommandPacketView args);
 
   // LE Controller Commands
   // Bluetooth Core Specification Version 4.2 Volume 2 Part E 7.8
 
   // 7.8.1
-  void HciLeSetEventMask(packets::PacketView<true> args);
+  void LeSetEventMask(CommandPacketView args);
 
   // 7.8.2
-  void HciLeReadBufferSize(packets::PacketView<true> args);
+  void LeReadBufferSize(CommandPacketView args);
 
   // 7.8.3
-  void HciLeReadLocalSupportedFeatures(packets::PacketView<true> args);
+  void LeReadLocalSupportedFeatures(CommandPacketView args);
 
   // 7.8.4
-  void HciLeSetRandomAddress(packets::PacketView<true> args);
+  void LeSetRandomAddress(CommandPacketView args);
 
   // 7.8.5
-  void HciLeSetAdvertisingParameters(packets::PacketView<true> args);
+  void LeSetAdvertisingParameters(CommandPacketView args);
 
   // 7.8.7
-  void HciLeSetAdvertisingData(packets::PacketView<true> args);
+  void LeSetAdvertisingData(CommandPacketView args);
+
+  // 7.8.8
+  void LeSetScanResponseData(CommandPacketView args);
+
+  // 7.8.9
+  void LeSetAdvertisingEnable(CommandPacketView args);
 
   // 7.8.10
-  void HciLeSetScanParameters(packets::PacketView<true> args);
+  void LeSetScanParameters(CommandPacketView args);
 
   // 7.8.11
-  void HciLeSetScanEnable(packets::PacketView<true> args);
+  void LeSetScanEnable(CommandPacketView args);
 
   // 7.8.12
-  void HciLeCreateConnection(packets::PacketView<true> args);
+  void LeCreateConnection(CommandPacketView args);
 
   // 7.8.18
-  void HciLeConnectionUpdate(packets::PacketView<true> args);
+  void LeConnectionUpdate(CommandPacketView args);
 
   // 7.8.13
-  void HciLeConnectionCancel(packets::PacketView<true> args);
+  void LeConnectionCancel(CommandPacketView args);
 
   // 7.8.14
-  void HciLeReadWhiteListSize(packets::PacketView<true> args);
+  void LeReadWhiteListSize(CommandPacketView args);
 
   // 7.8.15
-  void HciLeClearWhiteList(packets::PacketView<true> args);
+  void LeClearWhiteList(CommandPacketView args);
 
   // 7.8.16
-  void HciLeAddDeviceToWhiteList(packets::PacketView<true> args);
+  void LeAddDeviceToWhiteList(CommandPacketView args);
 
   // 7.8.17
-  void HciLeRemoveDeviceFromWhiteList(packets::PacketView<true> args);
+  void LeRemoveDeviceFromWhiteList(CommandPacketView args);
 
   // 7.8.21
-  void HciLeReadRemoteFeatures(packets::PacketView<true> args);
+  void LeReadRemoteFeatures(CommandPacketView args);
 
   // 7.8.23
-  void HciLeRand(packets::PacketView<true> args);
+  void LeRand(CommandPacketView args);
 
   // 7.8.24
-  void HciLeStartEncryption(packets::PacketView<true> args);
+  void LeStartEncryption(CommandPacketView args);
 
   // 7.8.27
-  void HciLeReadSupportedStates(packets::PacketView<true> args);
+  void LeReadSupportedStates(CommandPacketView args);
+
+  // 7.8.38
+  void LeAddDeviceToResolvingList(CommandPacketView args);
+
+  // 7.8.39
+  void LeRemoveDeviceFromResolvingList(CommandPacketView args);
+
+  // 7.8.40
+  void LeClearResolvingList(CommandPacketView args);
+
+  // 7.8.52
+  void LeSetExtendedAdvertisingRandomAddress(CommandPacketView args);
+
+  // 7.8.53
+  void LeSetExtendedAdvertisingParameters(CommandPacketView args);
+
+  // 7.8.54
+  void LeSetExtendedAdvertisingData(CommandPacketView args);
+
+  // 7.8.55
+  void LeSetExtendedAdvertisingScanResponse(CommandPacketView args);
+
+  // 7.8.56
+  void LeSetExtendedAdvertisingEnable(CommandPacketView args);
+
+  // 7.8.64
+  void LeSetExtendedScanParameters(CommandPacketView args);
+
+  // 7.8.65
+  void LeSetExtendedScanEnable(CommandPacketView args);
+
+  // 7.8.66
+  void LeExtendedCreateConnection(CommandPacketView args);
+
+  // 7.8.77
+  void LeSetPrivacyMode(CommandPacketView args);
 
   // Vendor-specific Commands
 
-  void HciLeVendorSleepMode(packets::PacketView<true> args);
-  void HciLeVendorCap(packets::PacketView<true> args);
-  void HciLeVendorMultiAdv(packets::PacketView<true> args);
-  void HciLeVendor155(packets::PacketView<true> args);
-  void HciLeVendor157(packets::PacketView<true> args);
-  void HciLeEnergyInfo(packets::PacketView<true> args);
-  void HciLeAdvertisingFilter(packets::PacketView<true> args);
-  void HciLeExtendedScanParams(packets::PacketView<true> args);
+  void LeVendorSleepMode(CommandPacketView args);
+  void LeVendorCap(CommandPacketView args);
+  void LeVendorMultiAdv(CommandPacketView args);
+  void LeVendor155(CommandPacketView args);
+  void LeVendor157(CommandPacketView args);
+  void LeEnergyInfo(CommandPacketView args);
+  void LeAdvertisingFilter(CommandPacketView args);
+  void LeExtendedScanParams(CommandPacketView args);
 
   void SetTimerPeriod(std::chrono::milliseconds new_period);
   void StartTimer();
@@ -364,37 +487,24 @@
 
   void AddConnectionAction(const TaskCallback& callback, uint16_t handle);
 
-  // Creates a command complete event and sends it back to the HCI.
-  void SendCommandComplete(hci::OpCode command_opcode, const std::vector<uint8_t>& return_parameters) const;
-
-  // Sends a command complete event with no return parameters.
-  void SendCommandCompleteSuccess(hci::OpCode command_opcode) const;
-
   void SendCommandCompleteUnknownOpCodeEvent(uint16_t command_opcode) const;
 
-  // Sends a command complete event with no return parameters.
-  void SendCommandCompleteOnlyStatus(hci::OpCode command_opcode, hci::Status status) const;
-
-  void SendCommandCompleteStatusAndAddress(hci::OpCode command_opcode, hci::Status status,
-                                           const Address& address) const;
-
-  // Creates a command status event and sends it back to the HCI.
-  void SendCommandStatus(hci::Status status, hci::OpCode command_opcode) const;
-
-  // Sends a command status event with default event parameters.
-  void SendCommandStatusSuccess(hci::OpCode command_opcode) const;
-
   // Callbacks to send packets back to the HCI.
-  std::function<void(std::shared_ptr<std::vector<uint8_t>>)> send_acl_;
-  std::function<void(std::shared_ptr<std::vector<uint8_t>>)> send_event_;
+  std::function<void(std::shared_ptr<bluetooth::hci::AclPacketBuilder>)>
+      send_acl_;
+  std::function<void(std::shared_ptr<bluetooth::hci::EventPacketBuilder>)>
+      send_event_;
   std::function<void(std::shared_ptr<std::vector<uint8_t>>)> send_sco_;
+  std::function<void(std::shared_ptr<std::vector<uint8_t>>)> send_iso_;
 
   // Maintains the commands to be registered and used in the HciHandler object.
   // Keys are command opcodes and values are the callbacks to handle each
   // command.
-  std::unordered_map<uint16_t, std::function<void(packets::PacketView<true>)>> active_hci_commands_;
+  std::unordered_map<bluetooth::hci::OpCode,
+                     std::function<void(bluetooth::hci::CommandPacketView)>>
+      active_hci_commands_;
 
-  hci::LoopbackMode loopback_mode_;
+  bluetooth::hci::LoopbackMode loopback_mode_;
 
   SecurityManager security_manager_;
 
diff --git a/vendor_libs/test_vendor_lib/model/controller/link_layer_controller.cc b/vendor_libs/test_vendor_lib/model/controller/link_layer_controller.cc
index ba11436..daac8a1 100644
--- a/vendor_libs/test_vendor_lib/model/controller/link_layer_controller.cc
+++ b/vendor_libs/test_vendor_lib/model/controller/link_layer_controller.cc
@@ -14,36 +14,19 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "link_layer_controller"
-
 #include "link_layer_controller.h"
 
-#include <base/logging.h>
-
-#include "hci.h"
-#include "osi/include/log.h"
-#include "packets/hci/acl_packet_builder.h"
-#include "packets/hci/command_packet_view.h"
-#include "packets/hci/event_packet_builder.h"
-#include "packets/hci/sco_packet_builder.h"
-#include "packets/link_layer/command_builder.h"
-#include "packets/link_layer/command_view.h"
-#include "packets/link_layer/disconnect_view.h"
-#include "packets/link_layer/encrypt_connection_view.h"
-#include "packets/link_layer/inquiry_response_view.h"
-#include "packets/link_layer/inquiry_view.h"
-#include "packets/link_layer/io_capability_view.h"
-#include "packets/link_layer/le_advertisement_view.h"
-#include "packets/link_layer/page_response_view.h"
-#include "packets/link_layer/page_view.h"
-#include "packets/link_layer/response_view.h"
+#include "include/le_advertisement.h"
+#include "os/log.h"
+#include "packet/raw_builder.h"
 
 using std::vector;
 using namespace std::chrono;
-using namespace test_vendor_lib::packets;
 
 namespace test_vendor_lib {
 
+constexpr uint16_t kNumCommandPackets = 0x01;
+
 // TODO: Model Rssi?
 static uint8_t GetRssi() {
   static uint8_t rssi = 0;
@@ -54,70 +37,136 @@
   return -(rssi);
 }
 
-void LinkLayerController::SendLELinkLayerPacket(std::shared_ptr<LinkLayerPacketBuilder> packet) {
-  if (schedule_task_) {
-    schedule_task_(milliseconds(50), [this, packet]() { send_to_remote_(packet, Phy::Type::LOW_ENERGY); });
-  } else {
-    send_to_remote_(packet, Phy::Type::LOW_ENERGY);
-  }
+void LinkLayerController::SendLeLinkLayerPacket(
+    std::unique_ptr<model::packets::LinkLayerPacketBuilder> packet) {
+  std::shared_ptr<model::packets::LinkLayerPacketBuilder> shared_packet =
+      std::move(packet);
+  ScheduleTask(milliseconds(50), [this, shared_packet]() {
+    send_to_remote_(std::move(shared_packet), Phy::Type::LOW_ENERGY);
+  });
 }
 
-void LinkLayerController::SendLinkLayerPacket(std::shared_ptr<LinkLayerPacketBuilder> packet) {
-  if (schedule_task_) {
-    schedule_task_(milliseconds(50), [this, packet]() { send_to_remote_(packet, Phy::Type::BR_EDR); });
-  } else {
-    send_to_remote_(packet, Phy::Type::BR_EDR);
-  }
+void LinkLayerController::SendLinkLayerPacket(
+    std::unique_ptr<model::packets::LinkLayerPacketBuilder> packet) {
+  std::shared_ptr<model::packets::LinkLayerPacketBuilder> shared_packet =
+      std::move(packet);
+  ScheduleTask(milliseconds(50), [this, shared_packet]() {
+    send_to_remote_(std::move(shared_packet), Phy::Type::BR_EDR);
+  });
 }
 
-hci::Status LinkLayerController::SendCommandToRemoteByAddress(hci::OpCode opcode, PacketView<true> args,
-                                                              const Address& remote, bool use_public_address) {
-  std::shared_ptr<LinkLayerPacketBuilder> command;
-  Address local_address;
-  if (use_public_address) {
-    local_address = properties_.GetAddress();
-  } else {
-    local_address = properties_.GetLeAddress();
+ErrorCode LinkLayerController::SendCommandToRemoteByAddress(
+    OpCode opcode, bluetooth::packet::PacketView<true> args,
+    const Address& remote) {
+  Address local_address = properties_.GetAddress();
+
+  switch (opcode) {
+    case (OpCode::REMOTE_NAME_REQUEST):
+      // LMP features get requested with remote name requests.
+      SendLinkLayerPacket(model::packets::ReadRemoteLmpFeaturesBuilder::Create(
+          local_address, remote));
+      SendLinkLayerPacket(model::packets::RemoteNameRequestBuilder::Create(
+          local_address, remote));
+      break;
+    case (OpCode::READ_REMOTE_SUPPORTED_FEATURES):
+      SendLinkLayerPacket(
+          model::packets::ReadRemoteSupportedFeaturesBuilder::Create(
+              local_address, remote));
+      break;
+    case (OpCode::READ_REMOTE_EXTENDED_FEATURES): {
+      uint8_t page_number =
+          (args.begin() + 2).extract<uint8_t>();  // skip the handle
+      SendLinkLayerPacket(
+          model::packets::ReadRemoteExtendedFeaturesBuilder::Create(
+              local_address, remote, page_number));
+    } break;
+    case (OpCode::READ_REMOTE_VERSION_INFORMATION):
+      SendLinkLayerPacket(
+          model::packets::ReadRemoteVersionInformationBuilder::Create(
+              local_address, remote));
+      break;
+    case (OpCode::READ_CLOCK_OFFSET):
+      SendLinkLayerPacket(model::packets::ReadClockOffsetBuilder::Create(
+          local_address, remote));
+      break;
+    default:
+      LOG_INFO("Dropping unhandled command 0x%04x",
+               static_cast<uint16_t>(opcode));
+      return ErrorCode::UNKNOWN_HCI_COMMAND;
   }
-  command = LinkLayerPacketBuilder::WrapCommand(CommandBuilder::Create(static_cast<uint16_t>(opcode), args),
-                                                local_address, remote);
-  SendLinkLayerPacket(std::move(command));
-  return hci::Status::SUCCESS;
+
+  return ErrorCode::SUCCESS;
 }
 
-hci::Status LinkLayerController::SendCommandToRemoteByHandle(hci::OpCode opcode, PacketView<true> args,
-                                                             uint16_t handle) {
+ErrorCode LinkLayerController::SendCommandToRemoteByHandle(
+    OpCode opcode, bluetooth::packet::PacketView<true> args, uint16_t handle) {
   // TODO: Handle LE connections
-  bool use_public_address = true;
-  if (!classic_connections_.HasHandle(handle)) {
-    return hci::Status::UNKNOWN_CONNECTION;
+  if (!connections_.HasHandle(handle)) {
+    return ErrorCode::UNKNOWN_CONNECTION;
   }
-  return SendCommandToRemoteByAddress(opcode, args, classic_connections_.GetAddress(handle), use_public_address);
+  return SendCommandToRemoteByAddress(
+      opcode, args, connections_.GetAddress(handle).GetAddress());
 }
 
-hci::Status LinkLayerController::SendAclToRemote(AclPacketView acl_packet) {
-  // TODO: Handle LE connections
+ErrorCode LinkLayerController::SendAclToRemote(
+    bluetooth::hci::AclPacketView acl_packet) {
   uint16_t handle = acl_packet.GetHandle();
-  if (!classic_connections_.HasHandle(handle)) {
-    return hci::Status::UNKNOWN_CONNECTION;
+  if (!connections_.HasHandle(handle)) {
+    return ErrorCode::UNKNOWN_CONNECTION;
   }
 
-  std::unique_ptr<ViewForwarderBuilder> acl_builder = ViewForwarderBuilder::Create(acl_packet);
+  AddressWithType my_address = connections_.GetOwnAddress(handle);
+  AddressWithType destination = connections_.GetAddress(handle);
+  Phy::Type phy = connections_.GetPhyType(handle);
 
-  std::shared_ptr<LinkLayerPacketBuilder> acl = LinkLayerPacketBuilder::WrapAcl(
-      std::move(acl_builder), properties_.GetAddress(), classic_connections_.GetAddress(handle));
-
-  LOG_INFO(LOG_TAG, "%s(%s): handle 0x%x size %d", __func__, properties_.GetAddress().ToString().c_str(), handle,
+  LOG_INFO("%s(%s): handle 0x%x size %d", __func__,
+           properties_.GetAddress().ToString().c_str(), handle,
            static_cast<int>(acl_packet.size()));
 
-  schedule_task_(milliseconds(5), [this, handle]() {
-    send_event_(EventPacketBuilder::CreateNumberOfCompletedPacketsEvent(handle, 1)->ToVector());
+  ScheduleTask(milliseconds(5), [this, handle]() {
+    std::vector<bluetooth::hci::CompletedPackets> completed_packets;
+    bluetooth::hci::CompletedPackets cp;
+    cp.connection_handle_ = handle;
+    cp.host_num_of_completed_packets_ = kNumCommandPackets;
+    completed_packets.push_back(cp);
+    auto packet = bluetooth::hci::NumberOfCompletedPacketsBuilder::Create(
+        completed_packets);
+    send_event_(std::move(packet));
   });
-  SendLinkLayerPacket(acl);
-  return hci::Status::SUCCESS;
+
+  auto acl_payload = acl_packet.GetPayload();
+
+  std::unique_ptr<bluetooth::packet::RawBuilder> raw_builder_ptr =
+      std::make_unique<bluetooth::packet::RawBuilder>();
+  std::vector<uint8_t> payload_bytes(acl_payload.begin(), acl_payload.end());
+
+  uint16_t first_two_bytes =
+      static_cast<uint16_t>(acl_packet.GetHandle()) +
+      (static_cast<uint16_t>(acl_packet.GetPacketBoundaryFlag()) << 12) +
+      (static_cast<uint16_t>(acl_packet.GetBroadcastFlag()) << 14);
+  raw_builder_ptr->AddOctets2(first_two_bytes);
+  raw_builder_ptr->AddOctets2(static_cast<uint16_t>(payload_bytes.size()));
+  raw_builder_ptr->AddOctets(payload_bytes);
+
+  auto acl = model::packets::AclPacketBuilder::Create(
+      my_address.GetAddress(), destination.GetAddress(),
+      std::move(raw_builder_ptr));
+
+  switch (phy) {
+    case Phy::Type::BR_EDR:
+      SendLinkLayerPacket(std::move(acl));
+      break;
+    case Phy::Type::LOW_ENERGY:
+      SendLeLinkLayerPacket(std::move(acl));
+      break;
+  }
+  return ErrorCode::SUCCESS;
 }
 
-void LinkLayerController::IncomingPacket(LinkLayerPacketView incoming) {
+void LinkLayerController::IncomingPacket(
+    model::packets::LinkLayerPacketView incoming) {
+  ASSERT(incoming.IsValid());
+
   // TODO: Resolvable private addresses?
   if (incoming.GetDestinationAddress() != properties_.GetAddress() &&
       incoming.GetDestinationAddress() != properties_.GetLeAddress() &&
@@ -127,570 +176,973 @@
   }
 
   switch (incoming.GetType()) {
-    case Link::PacketType::ACL:
+    case model::packets::PacketType::ACL:
       IncomingAclPacket(incoming);
       break;
-    case Link::PacketType::COMMAND:
-      IncomingCommandPacket(incoming);
-      break;
-    case Link::PacketType::DISCONNECT:
+    case model::packets::PacketType::DISCONNECT:
       IncomingDisconnectPacket(incoming);
       break;
-    case Link::PacketType::ENCRYPT_CONNECTION:
+    case model::packets::PacketType::ENCRYPT_CONNECTION:
       IncomingEncryptConnection(incoming);
       break;
-    case Link::PacketType::ENCRYPT_CONNECTION_RESPONSE:
+    case model::packets::PacketType::ENCRYPT_CONNECTION_RESPONSE:
       IncomingEncryptConnectionResponse(incoming);
       break;
-    case Link::PacketType::INQUIRY:
+    case model::packets::PacketType::INQUIRY:
       if (inquiry_scans_enabled_) {
         IncomingInquiryPacket(incoming);
       }
       break;
-    case Link::PacketType::INQUIRY_RESPONSE:
+    case model::packets::PacketType::INQUIRY_RESPONSE:
       IncomingInquiryResponsePacket(incoming);
       break;
-    case Link::PacketType::IO_CAPABILITY_REQUEST:
+    case model::packets::PacketType::IO_CAPABILITY_REQUEST:
       IncomingIoCapabilityRequestPacket(incoming);
       break;
-    case Link::PacketType::IO_CAPABILITY_RESPONSE:
+    case model::packets::PacketType::IO_CAPABILITY_RESPONSE:
       IncomingIoCapabilityResponsePacket(incoming);
       break;
-    case Link::PacketType::IO_CAPABILITY_NEGATIVE_RESPONSE:
+    case model::packets::PacketType::IO_CAPABILITY_NEGATIVE_RESPONSE:
       IncomingIoCapabilityNegativeResponsePacket(incoming);
       break;
-    case Link::PacketType::LE_ADVERTISEMENT:
-      if (le_scan_enable_ || le_connect_) {
+    case model::packets::PacketType::LE_ADVERTISEMENT:
+      if (le_scan_enable_ != bluetooth::hci::OpCode::NONE || le_connect_) {
         IncomingLeAdvertisementPacket(incoming);
       }
       break;
-    case Link::PacketType::LE_SCAN:
+    case model::packets::PacketType::LE_CONNECT:
+      IncomingLeConnectPacket(incoming);
+      break;
+    case model::packets::PacketType::LE_CONNECT_COMPLETE:
+      IncomingLeConnectCompletePacket(incoming);
+      break;
+    case model::packets::PacketType::LE_SCAN:
       // TODO: Check Advertising flags and see if we are scannable.
       IncomingLeScanPacket(incoming);
       break;
-    case Link::PacketType::LE_SCAN_RESPONSE:
-      if (le_scan_enable_ && le_scan_type_ == 1) {
+    case model::packets::PacketType::LE_SCAN_RESPONSE:
+      if (le_scan_enable_ != bluetooth::hci::OpCode::NONE &&
+          le_scan_type_ == 1) {
         IncomingLeScanResponsePacket(incoming);
       }
       break;
-    case Link::PacketType::PAGE:
+    case model::packets::PacketType::PAGE:
       if (page_scans_enabled_) {
         IncomingPagePacket(incoming);
       }
       break;
-    case Link::PacketType::PAGE_RESPONSE:
+    case model::packets::PacketType::PAGE_RESPONSE:
       IncomingPageResponsePacket(incoming);
       break;
-    case Link::PacketType::RESPONSE:
-      IncomingResponsePacket(incoming);
+    case model::packets::PacketType::PAGE_REJECT:
+      IncomingPageRejectPacket(incoming);
+      break;
+    case (model::packets::PacketType::REMOTE_NAME_REQUEST):
+      IncomingRemoteNameRequest(incoming);
+      break;
+    case (model::packets::PacketType::REMOTE_NAME_REQUEST_RESPONSE):
+      IncomingRemoteNameRequestResponse(incoming);
+      break;
+    case (model::packets::PacketType::READ_REMOTE_SUPPORTED_FEATURES):
+      IncomingReadRemoteSupportedFeatures(incoming);
+      break;
+    case (model::packets::PacketType::READ_REMOTE_SUPPORTED_FEATURES_RESPONSE):
+      IncomingReadRemoteSupportedFeaturesResponse(incoming);
+      break;
+    case (model::packets::PacketType::READ_REMOTE_LMP_FEATURES):
+      IncomingReadRemoteLmpFeatures(incoming);
+      break;
+    case (model::packets::PacketType::READ_REMOTE_LMP_FEATURES_RESPONSE):
+      IncomingReadRemoteLmpFeaturesResponse(incoming);
+      break;
+    case (model::packets::PacketType::READ_REMOTE_EXTENDED_FEATURES):
+      IncomingReadRemoteExtendedFeatures(incoming);
+      break;
+    case (model::packets::PacketType::READ_REMOTE_EXTENDED_FEATURES_RESPONSE):
+      IncomingReadRemoteExtendedFeaturesResponse(incoming);
+      break;
+    case (model::packets::PacketType::READ_REMOTE_VERSION_INFORMATION):
+      IncomingReadRemoteVersion(incoming);
+      break;
+    case (model::packets::PacketType::READ_REMOTE_VERSION_INFORMATION_RESPONSE):
+      IncomingReadRemoteVersionResponse(incoming);
+      break;
+    case (model::packets::PacketType::READ_CLOCK_OFFSET):
+      IncomingReadClockOffset(incoming);
+      break;
+    case (model::packets::PacketType::READ_CLOCK_OFFSET_RESPONSE):
+      IncomingReadClockOffsetResponse(incoming);
       break;
     default:
-      LOG_WARN(LOG_TAG, "Dropping unhandled packet of type %d", static_cast<int32_t>(incoming.GetType()));
+      LOG_WARN("Dropping unhandled packet of type %s",
+               model::packets::PacketTypeText(incoming.GetType()).c_str());
   }
 }
 
-void LinkLayerController::IncomingAclPacket(LinkLayerPacketView incoming) {
-  LOG_INFO(LOG_TAG, "Acl Packet %s -> %s", incoming.GetSourceAddress().ToString().c_str(),
+void LinkLayerController::IncomingAclPacket(
+    model::packets::LinkLayerPacketView incoming) {
+  LOG_INFO("Acl Packet %s -> %s",
+           incoming.GetSourceAddress().ToString().c_str(),
            incoming.GetDestinationAddress().ToString().c_str());
-  AclPacketView acl_view = AclPacketView::Create(incoming.GetPayload());
-  LOG_INFO(LOG_TAG, "%s: remote handle 0x%x size %d", __func__, acl_view.GetHandle(),
-           static_cast<int>(acl_view.size()));
-  uint16_t local_handle = classic_connections_.GetHandle(incoming.GetSourceAddress());
-  LOG_INFO(LOG_TAG, "%s: local handle 0x%x", __func__, local_handle);
 
-  acl::PacketBoundaryFlagsType boundary_flags = acl_view.GetPacketBoundaryFlags();
-  acl::BroadcastFlagsType broadcast_flags = acl_view.GetBroadcastFlags();
-  std::unique_ptr<ViewForwarderBuilder> builder = ViewForwarderBuilder::Create(acl_view.GetPayload());
-  std::unique_ptr<AclPacketBuilder> local_acl =
-      AclPacketBuilder::Create(local_handle, boundary_flags, broadcast_flags, std::move(builder));
-  send_acl_(local_acl->ToVector());
+  auto acl = model::packets::AclPacketView::Create(incoming);
+  ASSERT(acl.IsValid());
+  auto payload = acl.GetPayload();
+  std::shared_ptr<std::vector<uint8_t>> payload_bytes =
+      std::make_shared<std::vector<uint8_t>>(payload.begin(), payload.end());
+
+  bluetooth::hci::PacketView<bluetooth::hci::kLittleEndian> raw_packet(
+      payload_bytes);
+  auto acl_view = bluetooth::hci::AclPacketView::Create(raw_packet);
+  ASSERT(acl_view.IsValid());
+
+  LOG_INFO("%s: remote handle 0x%x size %d", __func__, acl_view.GetHandle(),
+           static_cast<int>(acl_view.size()));
+  uint16_t local_handle =
+      connections_.GetHandleOnlyAddress(incoming.GetSourceAddress());
+  LOG_INFO("%s: local handle 0x%x", __func__, local_handle);
+
+  std::vector<uint8_t> payload_data(acl_view.GetPayload().begin(),
+                                    acl_view.GetPayload().end());
+  uint16_t acl_buffer_size = properties_.GetAclDataPacketSize();
+  int num_packets =
+      (payload_data.size() + acl_buffer_size - 1) / acl_buffer_size;
+
+  auto pb_flag_controller_to_host = acl_view.GetPacketBoundaryFlag();
+  for (int i = 0; i < num_packets; i++) {
+    size_t start_index = acl_buffer_size * i;
+    size_t end_index =
+        std::min(start_index + acl_buffer_size, payload_data.size());
+    std::vector<uint8_t> fragment(&payload_data[start_index],
+                                  &payload_data[end_index]);
+    std::unique_ptr<bluetooth::packet::RawBuilder> raw_builder_ptr =
+        std::make_unique<bluetooth::packet::RawBuilder>(fragment);
+    auto acl_packet = bluetooth::hci::AclPacketBuilder::Create(
+        local_handle, pb_flag_controller_to_host, acl_view.GetBroadcastFlag(),
+        std::move(raw_builder_ptr));
+    pb_flag_controller_to_host =
+        bluetooth::hci::PacketBoundaryFlag::CONTINUING_FRAGMENT;
+
+    send_acl_(std::move(acl_packet));
+  }
 }
 
-void LinkLayerController::IncomingCommandPacket(LinkLayerPacketView incoming) {
-  // TODO: Check the destination address to see if this packet is for me.
-  CommandView command = CommandView::GetCommand(incoming);
-  hci::OpCode opcode = static_cast<hci::OpCode>(command.GetOpcode());
-  auto args = command.GetData();
-  std::vector<uint64_t> response_data;
+void LinkLayerController::IncomingRemoteNameRequest(
+    model::packets::LinkLayerPacketView packet) {
+  auto view = model::packets::RemoteNameRequestView::Create(packet);
+  ASSERT(view.IsValid());
 
-  switch (opcode) {
-    case (hci::OpCode::REMOTE_NAME_REQUEST): {
-      std::vector<uint8_t> name = properties_.GetName();
-      LOG_INFO(LOG_TAG, "Remote Name (Local Name) %d", static_cast<int>(name.size()));
-      response_data.push_back(static_cast<uint8_t>(hci::Status::SUCCESS));
-      response_data.push_back(name.size());
-      uint64_t word = 0;
-      for (size_t i = 0; i < name.size(); i++) {
-        if (i > 0 && (i % 8 == 0)) {
-          response_data.push_back(word);
-          word = 0;
-        }
-        word |= static_cast<uint64_t>(name[i]) << (8 * (i % 8));
-      }
-      response_data.push_back(word);
-    } break;
-    case (hci::OpCode::READ_REMOTE_SUPPORTED_FEATURES):
-      LOG_INFO(LOG_TAG, "(%s) Remote Supported Features Requested by: %s %x",
-               incoming.GetDestinationAddress().ToString().c_str(), incoming.GetSourceAddress().ToString().c_str(),
-               static_cast<int>(properties_.GetSupportedFeatures()));
-      response_data.push_back(static_cast<uint8_t>(hci::Status::SUCCESS));
-      response_data.push_back(properties_.GetSupportedFeatures());
-      break;
-    case (hci::OpCode::READ_REMOTE_EXTENDED_FEATURES): {
-      uint8_t page_number = (args + 2).extract<uint8_t>();  // skip the handle
-      LOG_INFO(LOG_TAG, "(%s) Remote Extended Features %d Requested by: %s",
-               incoming.GetDestinationAddress().ToString().c_str(), page_number,
-               incoming.GetSourceAddress().ToString().c_str());
-      uint8_t max_page_number = properties_.GetExtendedFeaturesMaximumPageNumber();
-      if (page_number > max_page_number) {
-        response_data.push_back(static_cast<uint8_t>(hci::Status::INVALID_HCI_COMMAND_PARAMETERS));
-        response_data.push_back(page_number);
-        response_data.push_back(max_page_number);
-        response_data.push_back(0);
-      } else {
-        response_data.push_back(static_cast<uint8_t>(hci::Status::SUCCESS));
-        response_data.push_back(page_number);
-        response_data.push_back(max_page_number);
-        response_data.push_back(properties_.GetExtendedFeatures(page_number));
-      }
-    } break;
-    case (hci::OpCode::READ_REMOTE_VERSION_INFORMATION):
-      response_data.push_back(static_cast<uint8_t>(hci::Status::SUCCESS));
-      response_data.push_back(properties_.GetLmpPalVersion());
-      response_data.push_back(properties_.GetManufacturerName());
-      response_data.push_back(properties_.GetLmpPalSubversion());
-      break;
-    case (hci::OpCode::READ_CLOCK_OFFSET):
-      response_data.push_back(static_cast<uint8_t>(hci::Status::SUCCESS));
-      response_data.push_back(properties_.GetClockOffset());
-      break;
-    default:
-      LOG_INFO(LOG_TAG, "Dropping unhandled command 0x%04x", static_cast<uint16_t>(opcode));
-      return;
+  SendLinkLayerPacket(model::packets::RemoteNameRequestResponseBuilder::Create(
+      packet.GetDestinationAddress(), packet.GetSourceAddress(),
+      properties_.GetName()));
+}
+
+void LinkLayerController::IncomingRemoteNameRequestResponse(
+    model::packets::LinkLayerPacketView packet) {
+  auto view = model::packets::RemoteNameRequestResponseView::Create(packet);
+  ASSERT(view.IsValid());
+
+  send_event_(bluetooth::hci::RemoteNameRequestCompleteBuilder::Create(
+      ErrorCode::SUCCESS, packet.GetSourceAddress(), view.GetName()));
+}
+
+void LinkLayerController::IncomingReadRemoteLmpFeatures(
+    model::packets::LinkLayerPacketView packet) {
+  SendLinkLayerPacket(
+      model::packets::ReadRemoteLmpFeaturesResponseBuilder::Create(
+          packet.GetDestinationAddress(), packet.GetSourceAddress(),
+          properties_.GetExtendedFeatures(1)));
+}
+
+void LinkLayerController::IncomingReadRemoteLmpFeaturesResponse(
+    model::packets::LinkLayerPacketView packet) {
+  auto view = model::packets::ReadRemoteLmpFeaturesResponseView::Create(packet);
+  ASSERT(view.IsValid());
+  send_event_(
+      bluetooth::hci::RemoteHostSupportedFeaturesNotificationBuilder::Create(
+          packet.GetSourceAddress(), view.GetFeatures()));
+}
+
+void LinkLayerController::IncomingReadRemoteSupportedFeatures(
+    model::packets::LinkLayerPacketView packet) {
+  SendLinkLayerPacket(
+      model::packets::ReadRemoteSupportedFeaturesResponseBuilder::Create(
+          packet.GetDestinationAddress(), packet.GetSourceAddress(),
+          properties_.GetSupportedFeatures()));
+}
+
+void LinkLayerController::IncomingReadRemoteSupportedFeaturesResponse(
+    model::packets::LinkLayerPacketView packet) {
+  auto view =
+      model::packets::ReadRemoteSupportedFeaturesResponseView::Create(packet);
+  ASSERT(view.IsValid());
+  Address source = packet.GetSourceAddress();
+  uint16_t handle = connections_.GetHandleOnlyAddress(source);
+  if (handle == acl::kReservedHandle) {
+    LOG_INFO("Discarding response from a disconnected device %s",
+             source.ToString().c_str());
+    return;
+  }
+  send_event_(
+      bluetooth::hci::ReadRemoteSupportedFeaturesCompleteBuilder::Create(
+          ErrorCode::SUCCESS, handle, view.GetFeatures()));
+}
+
+void LinkLayerController::IncomingReadRemoteExtendedFeatures(
+    model::packets::LinkLayerPacketView packet) {
+  auto view = model::packets::ReadRemoteExtendedFeaturesView::Create(packet);
+  ASSERT(view.IsValid());
+  uint8_t page_number = view.GetPageNumber();
+  uint8_t error_code = static_cast<uint8_t>(ErrorCode::SUCCESS);
+  if (page_number > properties_.GetExtendedFeaturesMaximumPageNumber()) {
+    error_code = static_cast<uint8_t>(ErrorCode::INVALID_LMP_OR_LL_PARAMETERS);
   }
   SendLinkLayerPacket(
-      LinkLayerPacketBuilder::WrapResponse(ResponseBuilder::Create(static_cast<uint16_t>(opcode), response_data),
-                                           properties_.GetAddress(), incoming.GetSourceAddress()));
+      model::packets::ReadRemoteExtendedFeaturesResponseBuilder::Create(
+          packet.GetDestinationAddress(), packet.GetSourceAddress(), error_code,
+          page_number, properties_.GetExtendedFeaturesMaximumPageNumber(),
+          properties_.GetExtendedFeatures(view.GetPageNumber())));
 }
 
-void LinkLayerController::IncomingDisconnectPacket(LinkLayerPacketView incoming) {
-  LOG_INFO(LOG_TAG, "Disconnect Packet");
-  DisconnectView disconnect = DisconnectView::GetDisconnect(incoming);
-  Address peer = incoming.GetSourceAddress();
-  uint16_t handle = classic_connections_.GetHandle(peer);
+void LinkLayerController::IncomingReadRemoteExtendedFeaturesResponse(
+    model::packets::LinkLayerPacketView packet) {
+  auto view =
+      model::packets::ReadRemoteExtendedFeaturesResponseView::Create(packet);
+  ASSERT(view.IsValid());
+  Address source = packet.GetSourceAddress();
+  uint16_t handle = connections_.GetHandleOnlyAddress(source);
   if (handle == acl::kReservedHandle) {
-    LOG_INFO(LOG_TAG, "%s: Unknown connection @%s", __func__, peer.ToString().c_str());
+    LOG_INFO("Discarding response from a disconnected device %s",
+             source.ToString().c_str());
     return;
   }
-  CHECK(classic_connections_.Disconnect(handle)) << "GetHandle() returned invalid handle " << handle;
+  send_event_(bluetooth::hci::ReadRemoteExtendedFeaturesCompleteBuilder::Create(
+      static_cast<ErrorCode>(view.GetStatus()), handle, view.GetPageNumber(),
+      view.GetMaxPageNumber(), view.GetFeatures()));
+}
+
+void LinkLayerController::IncomingReadRemoteVersion(
+    model::packets::LinkLayerPacketView packet) {
+  SendLinkLayerPacket(
+      model::packets::ReadRemoteSupportedFeaturesResponseBuilder::Create(
+          packet.GetDestinationAddress(), packet.GetSourceAddress(),
+          properties_.GetSupportedFeatures()));
+}
+
+void LinkLayerController::IncomingReadRemoteVersionResponse(
+    model::packets::LinkLayerPacketView packet) {
+  auto view =
+      model::packets::ReadRemoteVersionInformationResponseView::Create(packet);
+  ASSERT(view.IsValid());
+  Address source = packet.GetSourceAddress();
+  uint16_t handle = connections_.GetHandleOnlyAddress(source);
+  if (handle == acl::kReservedHandle) {
+    LOG_INFO("Discarding response from a disconnected device %s",
+             source.ToString().c_str());
+    return;
+  }
+  send_event_(
+      bluetooth::hci::ReadRemoteVersionInformationCompleteBuilder::Create(
+          ErrorCode::SUCCESS, handle, view.GetLmpVersion(),
+          view.GetManufacturerName(), view.GetLmpSubversion()));
+}
+
+void LinkLayerController::IncomingReadClockOffset(
+    model::packets::LinkLayerPacketView packet) {
+  SendLinkLayerPacket(model::packets::ReadClockOffsetResponseBuilder::Create(
+      packet.GetDestinationAddress(), packet.GetSourceAddress(),
+      properties_.GetClockOffset()));
+}
+
+void LinkLayerController::IncomingReadClockOffsetResponse(
+    model::packets::LinkLayerPacketView packet) {
+  auto view = model::packets::ReadClockOffsetResponseView::Create(packet);
+  ASSERT(view.IsValid());
+  Address source = packet.GetSourceAddress();
+  uint16_t handle = connections_.GetHandleOnlyAddress(source);
+  if (handle == acl::kReservedHandle) {
+    LOG_INFO("Discarding response from a disconnected device %s",
+             source.ToString().c_str());
+    return;
+  }
+  send_event_(bluetooth::hci::ReadClockOffsetCompleteBuilder::Create(
+      ErrorCode::SUCCESS, handle, view.GetOffset()));
+}
+
+void LinkLayerController::IncomingDisconnectPacket(
+    model::packets::LinkLayerPacketView incoming) {
+  LOG_INFO("Disconnect Packet");
+  auto disconnect = model::packets::DisconnectView::Create(incoming);
+  ASSERT(disconnect.IsValid());
+
+  Address peer = incoming.GetSourceAddress();
+  uint16_t handle = connections_.GetHandleOnlyAddress(peer);
+  if (handle == acl::kReservedHandle) {
+    LOG_INFO("Discarding disconnect from a disconnected device %s",
+             peer.ToString().c_str());
+    return;
+  }
+  ASSERT_LOG(connections_.Disconnect(handle),
+             "GetHandle() returned invalid handle %hx", handle);
 
   uint8_t reason = disconnect.GetReason();
-  schedule_task_(milliseconds(20), [this, handle, reason]() { DisconnectCleanup(handle, reason); });
+  ScheduleTask(milliseconds(20),
+               [this, handle, reason]() { DisconnectCleanup(handle, reason); });
 }
 
-void LinkLayerController::IncomingEncryptConnection(LinkLayerPacketView incoming) {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+void LinkLayerController::IncomingEncryptConnection(
+    model::packets::LinkLayerPacketView incoming) {
+  LOG_INFO("%s", __func__);
+
   // TODO: Check keys
   Address peer = incoming.GetSourceAddress();
-  uint16_t handle = classic_connections_.GetHandle(peer);
+  uint16_t handle = connections_.GetHandleOnlyAddress(peer);
   if (handle == acl::kReservedHandle) {
-    LOG_INFO(LOG_TAG, "%s: Unknown connection @%s", __func__, peer.ToString().c_str());
+    LOG_INFO("%s: Unknown connection @%s", __func__, peer.ToString().c_str());
     return;
   }
-  send_event_(EventPacketBuilder::CreateEncryptionChange(hci::Status::SUCCESS, handle, 1)->ToVector());
-  SendLinkLayerPacket(LinkLayerPacketBuilder::WrapEncryptConnectionResponse(
-      EncryptConnectionBuilder::Create(security_manager_.GetKey(peer)), properties_.GetAddress(), peer));
+  send_event_(bluetooth::hci::EncryptionChangeBuilder::Create(
+      ErrorCode::SUCCESS, handle, bluetooth::hci::EncryptionEnabled::ON));
+
+  uint16_t count = security_manager_.ReadKey(peer);
+  if (count == 0) {
+    LOG_ERROR("NO KEY HERE for %s", peer.ToString().c_str());
+    return;
+  }
+  auto array = security_manager_.GetKey(peer);
+  std::vector<uint8_t> key_vec{array.begin(), array.end()};
+  auto response = model::packets::EncryptConnectionResponseBuilder::Create(
+      properties_.GetAddress(), peer, key_vec);
+  SendLinkLayerPacket(std::move(response));
 }
 
-void LinkLayerController::IncomingEncryptConnectionResponse(LinkLayerPacketView incoming) {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+void LinkLayerController::IncomingEncryptConnectionResponse(
+    model::packets::LinkLayerPacketView incoming) {
+  LOG_INFO("%s", __func__);
   // TODO: Check keys
-  uint16_t handle = classic_connections_.GetHandle(incoming.GetSourceAddress());
+  uint16_t handle =
+      connections_.GetHandleOnlyAddress(incoming.GetSourceAddress());
   if (handle == acl::kReservedHandle) {
-    LOG_INFO(LOG_TAG, "%s: Unknown connection @%s", __func__, incoming.GetSourceAddress().ToString().c_str());
+    LOG_INFO("%s: Unknown connection @%s", __func__,
+             incoming.GetSourceAddress().ToString().c_str());
     return;
   }
-  send_event_(EventPacketBuilder::CreateEncryptionChange(hci::Status::SUCCESS, handle, 1)->ToVector());
+  auto packet = bluetooth::hci::EncryptionChangeBuilder::Create(
+      ErrorCode::SUCCESS, handle, bluetooth::hci::EncryptionEnabled::ON);
+  send_event_(std::move(packet));
 }
 
-void LinkLayerController::IncomingInquiryPacket(LinkLayerPacketView incoming) {
-  InquiryView inquiry = InquiryView::GetInquiry(incoming);
-  std::unique_ptr<InquiryResponseBuilder> inquiry_response;
-  switch (inquiry.GetType()) {
-    case (Inquiry::InquiryType::STANDARD):
-      inquiry_response = InquiryResponseBuilder::CreateStandard(
-          properties_.GetPageScanRepetitionMode(), properties_.GetClassOfDevice(), properties_.GetClockOffset());
-      break;
+void LinkLayerController::IncomingInquiryPacket(
+    model::packets::LinkLayerPacketView incoming) {
+  auto inquiry = model::packets::InquiryView::Create(incoming);
+  ASSERT(inquiry.IsValid());
 
-    case (Inquiry::InquiryType::RSSI):
-      inquiry_response =
-          InquiryResponseBuilder::CreateRssi(properties_.GetPageScanRepetitionMode(), properties_.GetClassOfDevice(),
-                                             properties_.GetClockOffset(), GetRssi());
-      break;
+  Address peer = incoming.GetSourceAddress();
 
-    case (Inquiry::InquiryType::EXTENDED):
-      inquiry_response = InquiryResponseBuilder::CreateExtended(
-          properties_.GetPageScanRepetitionMode(), properties_.GetClassOfDevice(), properties_.GetClockOffset(),
-          GetRssi(), properties_.GetExtendedInquiryData());
-      break;
+  switch (inquiry.GetInquiryType()) {
+    case (model::packets::InquiryType::STANDARD): {
+      auto inquiry_response = model::packets::InquiryResponseBuilder::Create(
+          properties_.GetAddress(), peer,
+          properties_.GetPageScanRepetitionMode(),
+          properties_.GetClassOfDevice(), properties_.GetClockOffset());
+      SendLinkLayerPacket(std::move(inquiry_response));
+    } break;
+    case (model::packets::InquiryType::RSSI): {
+      auto inquiry_response =
+          model::packets::InquiryResponseWithRssiBuilder::Create(
+              properties_.GetAddress(), peer,
+              properties_.GetPageScanRepetitionMode(),
+              properties_.GetClassOfDevice(), properties_.GetClockOffset(),
+              GetRssi());
+      SendLinkLayerPacket(std::move(inquiry_response));
+    } break;
+    case (model::packets::InquiryType::EXTENDED): {
+      auto inquiry_response =
+          model::packets::ExtendedInquiryResponseBuilder::Create(
+              properties_.GetAddress(), peer,
+              properties_.GetPageScanRepetitionMode(),
+              properties_.GetClassOfDevice(), properties_.GetClockOffset(),
+              GetRssi(), properties_.GetExtendedInquiryData());
+      SendLinkLayerPacket(std::move(inquiry_response));
+
+    } break;
     default:
-      LOG_WARN(LOG_TAG, "Unhandled Incoming Inquiry of type %d", static_cast<int>(inquiry.GetType()));
+      LOG_WARN("Unhandled Incoming Inquiry of type %d",
+               static_cast<int>(inquiry.GetType()));
       return;
   }
-  SendLinkLayerPacket(LinkLayerPacketBuilder::WrapInquiryResponse(std::move(inquiry_response), properties_.GetAddress(),
-                                                                  incoming.GetSourceAddress()));
-  // TODO: Send an Inquriy Response Notification Event 7.7.74
+  // TODO: Send an Inquiry Response Notification Event 7.7.74
 }
 
-void LinkLayerController::IncomingInquiryResponsePacket(LinkLayerPacketView incoming) {
-  InquiryResponseView inquiry_response = InquiryResponseView::GetInquiryResponse(incoming);
+void LinkLayerController::IncomingInquiryResponsePacket(
+    model::packets::LinkLayerPacketView incoming) {
+  auto basic_inquiry_response =
+      model::packets::BasicInquiryResponseView::Create(incoming);
+  ASSERT(basic_inquiry_response.IsValid());
   std::vector<uint8_t> eir;
 
-  switch (inquiry_response.GetType()) {
-    case (Inquiry::InquiryType::STANDARD): {
-      LOG_WARN(LOG_TAG, "Incoming Standard Inquiry Response");
+  switch (basic_inquiry_response.GetInquiryType()) {
+    case (model::packets::InquiryType::STANDARD): {
       // TODO: Support multiple inquiries in the same packet.
-      std::unique_ptr<EventPacketBuilder> inquiry_result = EventPacketBuilder::CreateInquiryResultEvent();
-      bool result_added =
-          inquiry_result->AddInquiryResult(incoming.GetSourceAddress(), inquiry_response.GetPageScanRepetitionMode(),
-                                           inquiry_response.GetClassOfDevice(), inquiry_response.GetClockOffset());
-      CHECK(result_added);
-      send_event_(inquiry_result->ToVector());
+      auto inquiry_response =
+          model::packets::InquiryResponseView::Create(basic_inquiry_response);
+      ASSERT(inquiry_response.IsValid());
+
+      auto page_scan_repetition_mode =
+          (bluetooth::hci::PageScanRepetitionMode)
+              inquiry_response.GetPageScanRepetitionMode();
+
+      std::vector<bluetooth::hci::InquiryResult> responses;
+      responses.emplace_back();
+      responses.back().bd_addr_ = inquiry_response.GetSourceAddress();
+      responses.back().page_scan_repetition_mode_ = page_scan_repetition_mode;
+      responses.back().class_of_device_ = inquiry_response.GetClassOfDevice();
+      responses.back().clock_offset_ = inquiry_response.GetClockOffset();
+      auto packet = bluetooth::hci::InquiryResultBuilder::Create(responses);
+      send_event_(std::move(packet));
     } break;
 
-    case (Inquiry::InquiryType::RSSI):
-      LOG_WARN(LOG_TAG, "Incoming RSSI Inquiry Response");
-      send_event_(EventPacketBuilder::CreateExtendedInquiryResultEvent(
-                      incoming.GetSourceAddress(), inquiry_response.GetPageScanRepetitionMode(),
-                      inquiry_response.GetClassOfDevice(), inquiry_response.GetClockOffset(), GetRssi(), eir)
-                      ->ToVector());
-      break;
+    case (model::packets::InquiryType::RSSI): {
+      auto inquiry_response =
+          model::packets::InquiryResponseWithRssiView::Create(
+              basic_inquiry_response);
+      ASSERT(inquiry_response.IsValid());
 
-    case (Inquiry::InquiryType::EXTENDED): {
-      LOG_WARN(LOG_TAG, "Incoming Extended Inquiry Response");
-      auto eir_itr = inquiry_response.GetExtendedData();
-      size_t eir_bytes = eir_itr.NumBytesRemaining();
-      LOG_WARN(LOG_TAG, "Payload size = %d", static_cast<int>(eir_bytes));
-      for (size_t i = 0; i < eir_bytes; i++) {
-        eir.push_back(eir_itr.extract<uint8_t>());
+      auto page_scan_repetition_mode =
+          (bluetooth::hci::PageScanRepetitionMode)
+              inquiry_response.GetPageScanRepetitionMode();
+
+      std::vector<bluetooth::hci::InquiryResultWithRssi> responses;
+      responses.emplace_back();
+      responses.back().address_ = inquiry_response.GetSourceAddress();
+      responses.back().page_scan_repetition_mode_ = page_scan_repetition_mode;
+      responses.back().class_of_device_ = inquiry_response.GetClassOfDevice();
+      responses.back().clock_offset_ = inquiry_response.GetClockOffset();
+      responses.back().rssi_ = inquiry_response.GetRssi();
+      auto packet =
+          bluetooth::hci::InquiryResultWithRssiBuilder::Create(responses);
+      send_event_(std::move(packet));
+    } break;
+
+    case (model::packets::InquiryType::EXTENDED): {
+      auto inquiry_response =
+          model::packets::ExtendedInquiryResponseView::Create(
+              basic_inquiry_response);
+      ASSERT(inquiry_response.IsValid());
+
+      std::unique_ptr<bluetooth::packet::RawBuilder> raw_builder_ptr =
+          std::make_unique<bluetooth::packet::RawBuilder>();
+      raw_builder_ptr->AddOctets1(kNumCommandPackets);
+      raw_builder_ptr->AddAddress(inquiry_response.GetSourceAddress());
+      raw_builder_ptr->AddOctets1(inquiry_response.GetPageScanRepetitionMode());
+      raw_builder_ptr->AddOctets1(0x00);  // _reserved_
+      auto class_of_device = inquiry_response.GetClassOfDevice();
+      for (unsigned int i = 0; i < class_of_device.kLength; i++) {
+        raw_builder_ptr->AddOctets1(class_of_device.cod[i]);
       }
-      send_event_(EventPacketBuilder::CreateExtendedInquiryResultEvent(
-                      incoming.GetSourceAddress(), inquiry_response.GetPageScanRepetitionMode(),
-                      inquiry_response.GetClassOfDevice(), inquiry_response.GetClockOffset(), GetRssi(), eir)
-                      ->ToVector());
+      raw_builder_ptr->AddOctets2(inquiry_response.GetClockOffset());
+      raw_builder_ptr->AddOctets1(inquiry_response.GetRssi());
+      raw_builder_ptr->AddOctets(inquiry_response.GetExtendedData());
+
+      auto packet = bluetooth::hci::EventPacketBuilder::Create(
+          bluetooth::hci::EventCode::EXTENDED_INQUIRY_RESULT,
+          std::move(raw_builder_ptr));
+      send_event_(std::move(packet));
     } break;
     default:
-      LOG_WARN(LOG_TAG, "Unhandled Incoming Inquiry Response of type %d", static_cast<int>(inquiry_response.GetType()));
+      LOG_WARN("Unhandled Incoming Inquiry Response of type %d",
+               static_cast<int>(basic_inquiry_response.GetInquiryType()));
   }
 }
 
-void LinkLayerController::IncomingIoCapabilityRequestPacket(LinkLayerPacketView incoming) {
-  LOG_DEBUG(LOG_TAG, "%s", __func__);
+void LinkLayerController::IncomingIoCapabilityRequestPacket(
+    model::packets::LinkLayerPacketView incoming) {
+  LOG_DEBUG("%s", __func__);
   if (!simple_pairing_mode_enabled_) {
-    LOG_WARN(LOG_TAG, "%s: Only simple pairing mode is implemented", __func__);
+    LOG_WARN("%s: Only simple pairing mode is implemented", __func__);
     return;
   }
-  auto request = IoCapabilityView::GetIoCapability(incoming);
-  Address peer = incoming.GetSourceAddress();
 
+  auto request = model::packets::IoCapabilityRequestView::Create(incoming);
+  ASSERT(request.IsValid());
+
+  Address peer = incoming.GetSourceAddress();
   uint8_t io_capability = request.GetIoCapability();
   uint8_t oob_data_present = request.GetOobDataPresent();
   uint8_t authentication_requirements = request.GetAuthenticationRequirements();
 
-  uint16_t handle = classic_connections_.GetHandle(peer);
+  uint16_t handle = connections_.GetHandle(AddressWithType(
+      peer, bluetooth::hci::AddressType::PUBLIC_DEVICE_ADDRESS));
   if (handle == acl::kReservedHandle) {
-    LOG_INFO(LOG_TAG, "%s: Device not connected %s", __func__, peer.ToString().c_str());
+    LOG_INFO("%s: Device not connected %s", __func__, peer.ToString().c_str());
     return;
   }
 
   security_manager_.AuthenticationRequest(peer, handle);
 
-  security_manager_.SetPeerIoCapability(peer, io_capability, oob_data_present, authentication_requirements);
+  security_manager_.SetPeerIoCapability(peer, io_capability, oob_data_present,
+                                        authentication_requirements);
 
-  send_event_(EventPacketBuilder::CreateIoCapabilityResponseEvent(peer, io_capability, oob_data_present,
-                                                                  authentication_requirements)
-                  ->ToVector());
+  auto packet = bluetooth::hci::IoCapabilityResponseBuilder::Create(
+      peer, static_cast<bluetooth::hci::IoCapability>(io_capability),
+      static_cast<bluetooth::hci::OobDataPresent>(oob_data_present),
+      static_cast<bluetooth::hci::AuthenticationRequirements>(
+          authentication_requirements));
+  send_event_(std::move(packet));
 
   StartSimplePairing(peer);
 }
 
-void LinkLayerController::IncomingIoCapabilityResponsePacket(LinkLayerPacketView incoming) {
-  LOG_DEBUG(LOG_TAG, "%s", __func__);
-  auto response = IoCapabilityView::GetIoCapability(incoming);
+void LinkLayerController::IncomingIoCapabilityResponsePacket(
+    model::packets::LinkLayerPacketView incoming) {
+  LOG_DEBUG("%s", __func__);
+
+  auto response = model::packets::IoCapabilityResponseView::Create(incoming);
+  ASSERT(response.IsValid());
+
   Address peer = incoming.GetSourceAddress();
   uint8_t io_capability = response.GetIoCapability();
   uint8_t oob_data_present = response.GetOobDataPresent();
-  uint8_t authentication_requirements = response.GetAuthenticationRequirements();
+  uint8_t authentication_requirements =
+      response.GetAuthenticationRequirements();
 
-  security_manager_.SetPeerIoCapability(peer, io_capability, oob_data_present, authentication_requirements);
+  security_manager_.SetPeerIoCapability(peer, io_capability, oob_data_present,
+                                        authentication_requirements);
 
-  send_event_(EventPacketBuilder::CreateIoCapabilityResponseEvent(peer, io_capability, oob_data_present,
-                                                                  authentication_requirements)
-                  ->ToVector());
+  auto packet = bluetooth::hci::IoCapabilityResponseBuilder::Create(
+      peer, static_cast<bluetooth::hci::IoCapability>(io_capability),
+      static_cast<bluetooth::hci::OobDataPresent>(oob_data_present),
+      static_cast<bluetooth::hci::AuthenticationRequirements>(
+          authentication_requirements));
+  send_event_(std::move(packet));
 
   PairingType pairing_type = security_manager_.GetSimplePairingType();
   if (pairing_type != PairingType::INVALID) {
-    schedule_task_(milliseconds(5), [this, peer, pairing_type]() { AuthenticateRemoteStage1(peer, pairing_type); });
+    ScheduleTask(milliseconds(5), [this, peer, pairing_type]() {
+      AuthenticateRemoteStage1(peer, pairing_type);
+    });
   } else {
-    LOG_INFO(LOG_TAG, "%s: Security Manager returned INVALID", __func__);
+    LOG_INFO("%s: Security Manager returned INVALID", __func__);
   }
 }
 
-void LinkLayerController::IncomingIoCapabilityNegativeResponsePacket(LinkLayerPacketView incoming) {
-  LOG_DEBUG(LOG_TAG, "%s", __func__);
+void LinkLayerController::IncomingIoCapabilityNegativeResponsePacket(
+    model::packets::LinkLayerPacketView incoming) {
+  LOG_DEBUG("%s", __func__);
   Address peer = incoming.GetSourceAddress();
 
-  CHECK(security_manager_.GetAuthenticationAddress() == peer);
+  ASSERT(security_manager_.GetAuthenticationAddress() == peer);
 
   security_manager_.InvalidateIoCapabilities();
 }
 
-void LinkLayerController::IncomingLeAdvertisementPacket(LinkLayerPacketView incoming) {
+void LinkLayerController::IncomingLeAdvertisementPacket(
+    model::packets::LinkLayerPacketView incoming) {
   // TODO: Handle multiple advertisements per packet.
 
-  LeAdvertisementView advertisement = LeAdvertisementView::GetLeAdvertisementView(incoming);
-  LeAdvertisement::AdvertisementType adv_type = advertisement.GetAdvertisementType();
-  LeAdvertisement::AddressType addr_type = advertisement.GetAddressType();
+  Address address = incoming.GetSourceAddress();
+  auto advertisement = model::packets::LeAdvertisementView::Create(incoming);
+  ASSERT(advertisement.IsValid());
+  auto adv_type = static_cast<LeAdvertisement::AdvertisementType>(
+      advertisement.GetAdvertisementType());
+  auto address_type = advertisement.GetAddressType();
 
-  if (le_scan_enable_) {
-    vector<uint8_t> ad;
-    auto itr = advertisement.GetData();
-    size_t ad_size = itr.NumBytesRemaining();
-    for (size_t i = 0; i < ad_size; i++) {
-      ad.push_back(itr.extract<uint8_t>());
-    }
-    std::unique_ptr<EventPacketBuilder> le_adverts = EventPacketBuilder::CreateLeAdvertisingReportEvent();
+  if (le_scan_enable_ == bluetooth::hci::OpCode::LE_SET_SCAN_ENABLE) {
+    vector<uint8_t> ad = advertisement.GetData();
 
-    if (!le_adverts->AddLeAdvertisingReport(adv_type, addr_type, incoming.GetSourceAddress(), ad, GetRssi())) {
-      LOG_INFO(LOG_TAG, "Couldn't add the advertising report.");
-    } else {
-      send_event_(le_adverts->ToVector());
-    }
+    std::unique_ptr<bluetooth::packet::RawBuilder> raw_builder_ptr =
+        std::make_unique<bluetooth::packet::RawBuilder>();
+    raw_builder_ptr->AddOctets1(
+        static_cast<uint8_t>(bluetooth::hci::SubeventCode::ADVERTISING_REPORT));
+    raw_builder_ptr->AddOctets1(0x01);  // num reports
+    raw_builder_ptr->AddOctets1(static_cast<uint8_t>(adv_type));
+    raw_builder_ptr->AddOctets1(static_cast<uint8_t>(address_type));
+    raw_builder_ptr->AddAddress(address);
+    raw_builder_ptr->AddOctets1(ad.size());
+    raw_builder_ptr->AddOctets(ad);
+    raw_builder_ptr->AddOctets1(GetRssi());
+    auto packet = bluetooth::hci::EventPacketBuilder::Create(
+        bluetooth::hci::EventCode::LE_META_EVENT, std::move(raw_builder_ptr));
+    send_event_(std::move(packet));
   }
 
-#if 0
-      // Connect
-      if (le_connect_ && (adv_type == BTM_BLE_CONNECT_EVT ||
-                          adv_type == BTM_BLE_CONNECT_DIR_EVT)) {
-        LOG_INFO(LOG_TAG, "Connecting to device %d", static_cast<int>(dev));
-        if (le_peer_address_ == addr && le_peer_address_type_ == addr_type &&
-            remote_devices_[dev]->LeConnect()) {
-          uint16_t handle = LeGetHandle();
-          send_event_(EventPacketBuilder::CreateLeConnectionCompleteEvent(
-              hci::Status::SUCCESS, handle, HCI_ROLE_MASTER, addr_type, addr, 1,
-              2, 3)->ToVector());
+  if (le_scan_enable_ == bluetooth::hci::OpCode::LE_SET_EXTENDED_SCAN_ENABLE) {
+    vector<uint8_t> ad = advertisement.GetData();
 
-          // TODO: LeGetConnInterval(), LeGetConnLatency(),
-          // LeGetSupervisionTimeout()));
-          le_connect_ = false;
-
-          std::shared_ptr<Connection> new_connection =
-              std::make_shared<Connection>(remote_devices_[dev], handle);
-          /*
-          connections_.push_back(new_connection);
-
-          remote_devices_[dev]->SetConnection(new_connection);
-          */
-        }
-
-        if (LeWhiteListContainsDevice(addr, addr_type) &&
-            remote_devices_[dev]->LeConnect()) {
-          LOG_INFO(LOG_TAG, "White List Connecting to device %d",
-                   static_cast<int>(dev));
-          uint16_t handle = LeGetHandle();
-          send_event_(EventPacketBuilder::CreateLeConnectionCompleteEvent(
-              hci::Status::SUCCESS, handle, HCI_ROLE_MASTER, addr_type, addr, 1,
-              2, 3)->ToVector());
-          // TODO: LeGetConnInterval(), LeGetConnLatency(),
-          // LeGetSupervisionTimeout()));
-          le_connect_ = false;
-
-          std::shared_ptr<Connection> new_connection =
-              std::make_shared<Connection>(remote_devices_[dev], handle);
-          /*
-          connections_.push_back(new_connection);
-          remote_devices_[dev]->SetConnection(new_connection);
-          */
-        }
-      }
-#endif
+    std::unique_ptr<bluetooth::packet::RawBuilder> raw_builder_ptr =
+        std::make_unique<bluetooth::packet::RawBuilder>();
+    raw_builder_ptr->AddOctets1(static_cast<uint8_t>(
+        bluetooth::hci::SubeventCode::EXTENDED_ADVERTISING_REPORT));
+    raw_builder_ptr->AddOctets1(0x01);  // num reports
+    raw_builder_ptr->AddOctets1(static_cast<uint8_t>(adv_type));
+    raw_builder_ptr->AddOctets1(static_cast<uint8_t>(address_type));
+    raw_builder_ptr->AddAddress(address);
+    raw_builder_ptr->AddOctets1(1);     // Primary_PHY
+    raw_builder_ptr->AddOctets1(0);     // Secondary_PHY
+    raw_builder_ptr->AddOctets1(0xFF);  // Advertising_SID - not provided
+    raw_builder_ptr->AddOctets1(0x7F);  // Tx_Power - Not available
+    raw_builder_ptr->AddOctets1(GetRssi());
+    raw_builder_ptr->AddOctets1(0);  // Periodic_Advertising_Interval - None
+    raw_builder_ptr->AddOctets1(0);  // Direct_Address_Type - PUBLIC
+    raw_builder_ptr->AddAddress(Address::kEmpty);  // Direct_Address
+    raw_builder_ptr->AddOctets1(ad.size());
+    raw_builder_ptr->AddOctets(ad);
+    auto packet = bluetooth::hci::EventPacketBuilder::Create(
+        bluetooth::hci::EventCode::LE_META_EVENT, std::move(raw_builder_ptr));
+    send_event_(std::move(packet));
+  }
 
   // Active scanning
-  if (le_scan_enable_ && le_scan_type_ == 1) {
-    std::shared_ptr<LinkLayerPacketBuilder> to_send =
-        LinkLayerPacketBuilder::WrapLeScan(properties_.GetLeAddress(), incoming.GetSourceAddress());
-    SendLELinkLayerPacket(to_send);
+  if (le_scan_enable_ != bluetooth::hci::OpCode::NONE && le_scan_type_ == 1) {
+    auto to_send = model::packets::LeScanBuilder::Create(
+        properties_.GetLeAddress(), address);
+    SendLeLinkLayerPacket(std::move(to_send));
+  }
+
+  // Connect
+  if ((le_connect_ && le_peer_address_ == address &&
+       le_peer_address_type_ == static_cast<uint8_t>(address_type) &&
+       (adv_type == LeAdvertisement::AdvertisementType::ADV_IND ||
+        adv_type == LeAdvertisement::AdvertisementType::ADV_DIRECT_IND)) ||
+      (LeWhiteListContainsDevice(address,
+                                 static_cast<uint8_t>(address_type)))) {
+    if (!connections_.CreatePendingLeConnection(AddressWithType(
+            address, static_cast<bluetooth::hci::AddressType>(address_type)))) {
+      LOG_WARN(
+          "%s: CreatePendingLeConnection failed for connection to %s (type "
+          "%hhx)",
+          __func__, incoming.GetSourceAddress().ToString().c_str(),
+          address_type);
+    }
+    LOG_INFO("%s: connecting to %s (type %hhx)", __func__,
+             incoming.GetSourceAddress().ToString().c_str(), address_type);
+    le_connect_ = false;
+    le_scan_enable_ = bluetooth::hci::OpCode::NONE;
+
+    auto to_send = model::packets::LeConnectBuilder::Create(
+        properties_.GetLeAddress(), incoming.GetSourceAddress(),
+        le_connection_interval_min_, le_connection_interval_max_,
+        le_connection_latency_, le_connection_supervision_timeout_,
+        static_cast<uint8_t>(le_address_type_));
+
+    SendLeLinkLayerPacket(std::move(to_send));
   }
 }
 
-void LinkLayerController::IncomingLeScanPacket(LinkLayerPacketView incoming) {
-  LOG_INFO(LOG_TAG, "LE Scan Packet");
-  std::unique_ptr<LeAdvertisementBuilder> response = LeAdvertisementBuilder::Create(
-      static_cast<LeAdvertisement::AddressType>(properties_.GetLeAddressType()),
-      static_cast<LeAdvertisement::AdvertisementType>(properties_.GetLeAdvertisementType()),
-      properties_.GetLeScanResponse());
-  std::shared_ptr<LinkLayerPacketBuilder> to_send = LinkLayerPacketBuilder::WrapLeScanResponse(
-      std::move(response), properties_.GetLeAddress(), incoming.GetSourceAddress());
-  SendLELinkLayerPacket(to_send);
-}
-
-void LinkLayerController::IncomingLeScanResponsePacket(LinkLayerPacketView incoming) {
-  LeAdvertisementView scan_response = LeAdvertisementView::GetLeAdvertisementView(incoming);
-  vector<uint8_t> ad;
-  auto itr = scan_response.GetData();
-  size_t scan_size = itr.NumBytesRemaining();
-  for (size_t i = 0; i < scan_size; i++) {
-    ad.push_back(itr.extract<uint8_t>());
-  }
-
-  std::unique_ptr<EventPacketBuilder> le_adverts = EventPacketBuilder::CreateLeAdvertisingReportEvent();
-
-  if (!le_adverts->AddLeAdvertisingReport(scan_response.GetAdvertisementType(), scan_response.GetAddressType(),
-                                          incoming.GetSourceAddress(), ad, GetRssi())) {
-    LOG_INFO(LOG_TAG, "Couldn't add the scan response.");
-  } else {
-    LOG_INFO(LOG_TAG, "Sending scan response");
-    send_event_(le_adverts->ToVector());
-  }
-}
-
-void LinkLayerController::IncomingPagePacket(LinkLayerPacketView incoming) {
-  PageView page = PageView::GetPage(incoming);
-  LOG_INFO(LOG_TAG, "%s from %s", __func__, incoming.GetSourceAddress().ToString().c_str());
-
-  if (!classic_connections_.CreatePendingConnection(incoming.GetSourceAddress())) {
-    // Send a response to indicate that we're busy, or drop the packet?
-    LOG_WARN(LOG_TAG, "%s: Failed to create a pending connection", __func__);
-  }
-
-  send_event_(EventPacketBuilder::CreateConnectionRequestEvent(incoming.GetSourceAddress(), page.GetClassOfDevice(),
-                                                               hci::LinkType::ACL)
-                  ->ToVector());
-}
-
-void LinkLayerController::IncomingPageResponsePacket(LinkLayerPacketView incoming) {
-  LOG_INFO(LOG_TAG, "%s: %s", __func__, incoming.GetSourceAddress().ToString().c_str());
-  uint16_t handle = classic_connections_.CreateConnection(incoming.GetSourceAddress());
+void LinkLayerController::HandleLeConnection(AddressWithType address,
+                                             AddressWithType own_address,
+                                             uint8_t role,
+                                             uint16_t connection_interval,
+                                             uint16_t connection_latency,
+                                             uint16_t supervision_timeout) {
+  // TODO: Choose between LeConnectionComplete and LeEnhancedConnectionComplete
+  uint16_t handle = connections_.CreateLeConnection(address, own_address);
   if (handle == acl::kReservedHandle) {
-    LOG_WARN(LOG_TAG, "%s: No free handles", __func__);
+    LOG_WARN("%s: No pending connection for connection from %s", __func__,
+             address.ToString().c_str());
     return;
   }
-  LOG_INFO(LOG_TAG, "%s: Sending CreateConnectionComplete", __func__);
-  send_event_(EventPacketBuilder::CreateConnectionCompleteEvent(hci::Status::SUCCESS, handle,
-                                                                incoming.GetSourceAddress(), hci::LinkType::ACL, false)
-                  ->ToVector());
+  auto packet = bluetooth::hci::LeConnectionCompleteBuilder::Create(
+      ErrorCode::SUCCESS, handle, static_cast<bluetooth::hci::Role>(role),
+      address.GetAddressType(), address.GetAddress(), connection_interval,
+      connection_latency, supervision_timeout,
+      static_cast<bluetooth::hci::MasterClockAccuracy>(0x00));
+  send_event_(std::move(packet));
 }
 
-void LinkLayerController::IncomingResponsePacket(LinkLayerPacketView incoming) {
-  ResponseView response = ResponseView::GetResponse(incoming);
+void LinkLayerController::IncomingLeConnectPacket(
+    model::packets::LinkLayerPacketView incoming) {
+  auto connect = model::packets::LeConnectView::Create(incoming);
+  ASSERT(connect.IsValid());
+  uint16_t connection_interval = (connect.GetLeConnectionIntervalMax() +
+                                  connect.GetLeConnectionIntervalMin()) /
+                                 2;
+  if (!connections_.CreatePendingLeConnection(AddressWithType(
+          incoming.GetSourceAddress(), static_cast<bluetooth::hci::AddressType>(
+                                           connect.GetAddressType())))) {
+    LOG_WARN(
+        "%s: CreatePendingLeConnection failed for connection from %s (type "
+        "%hhx)",
+        __func__, incoming.GetSourceAddress().ToString().c_str(),
+        connect.GetAddressType());
+    return;
+  }
+  HandleLeConnection(
+      AddressWithType(
+          incoming.GetSourceAddress(),
+          static_cast<bluetooth::hci::AddressType>(connect.GetAddressType())),
+      AddressWithType(incoming.GetDestinationAddress(),
+                      static_cast<bluetooth::hci::AddressType>(
+                          properties_.GetLeAdvertisingOwnAddressType())),
+      static_cast<uint8_t>(bluetooth::hci::Role::SLAVE), connection_interval,
+      connect.GetLeConnectionLatency(),
+      connect.GetLeConnectionSupervisionTimeout());
 
-  // TODO: Check to see if I'm expecting this response.
+  auto to_send = model::packets::LeConnectCompleteBuilder::Create(
+      incoming.GetDestinationAddress(), incoming.GetSourceAddress(),
+      connection_interval, connect.GetLeConnectionLatency(),
+      connect.GetLeConnectionSupervisionTimeout(),
+      properties_.GetLeAdvertisingOwnAddressType());
+  SendLeLinkLayerPacket(std::move(to_send));
+}
 
-  hci::OpCode opcode = static_cast<hci::OpCode>(response.GetOpcode());
-  auto args = response.GetResponseData();
-  hci::Status status = static_cast<hci::Status>(args.extract<uint64_t>());
+void LinkLayerController::IncomingLeConnectCompletePacket(
+    model::packets::LinkLayerPacketView incoming) {
+  auto complete = model::packets::LeConnectCompleteView::Create(incoming);
+  ASSERT(complete.IsValid());
+  HandleLeConnection(
+      AddressWithType(
+          incoming.GetSourceAddress(),
+          static_cast<bluetooth::hci::AddressType>(complete.GetAddressType())),
+      AddressWithType(
+          incoming.GetDestinationAddress(),
+          static_cast<bluetooth::hci::AddressType>(le_address_type_)),
+      static_cast<uint8_t>(bluetooth::hci::Role::MASTER),
+      complete.GetLeConnectionInterval(), complete.GetLeConnectionLatency(),
+      complete.GetLeConnectionSupervisionTimeout());
+}
 
-  uint16_t handle = classic_connections_.GetHandle(incoming.GetSourceAddress());
+void LinkLayerController::IncomingLeScanPacket(
+    model::packets::LinkLayerPacketView incoming) {
 
-  switch (opcode) {
-    case (hci::OpCode::REMOTE_NAME_REQUEST): {
-      std::string remote_name = "";
-      size_t length = args.extract<uint64_t>();
-      uint64_t word = 0;
-      for (size_t b = 0; b < length; b++) {
-        size_t byte = b % 8;
-        if (byte == 0) {
-          word = args.extract<uint64_t>();
-        }
-        remote_name += static_cast<uint8_t>(word >> (byte * 8));
-      }
-      send_event_(
-          EventPacketBuilder::CreateRemoteNameRequestCompleteEvent(status, incoming.GetSourceAddress(), remote_name)
-              ->ToVector());
-    } break;
-    case (hci::OpCode::READ_REMOTE_SUPPORTED_FEATURES): {
-      send_event_(
-          EventPacketBuilder::CreateRemoteSupportedFeaturesEvent(status, handle, args.extract<uint64_t>())->ToVector());
-    } break;
-    case (hci::OpCode::READ_REMOTE_EXTENDED_FEATURES): {
-      if (status == hci::Status::SUCCESS) {
-        send_event_(EventPacketBuilder::CreateReadRemoteExtendedFeaturesEvent(
-                        status, handle, args.extract<uint64_t>(), args.extract<uint64_t>(), args.extract<uint64_t>())
-                        ->ToVector());
-      } else {
-        send_event_(EventPacketBuilder::CreateReadRemoteExtendedFeaturesEvent(status, handle, 0, 0, 0)->ToVector());
-      }
-    } break;
-    case (hci::OpCode::READ_REMOTE_VERSION_INFORMATION): {
-      send_event_(EventPacketBuilder::CreateReadRemoteVersionInformationEvent(
-                      status, handle, args.extract<uint64_t>(), args.extract<uint64_t>(), args.extract<uint64_t>())
-                      ->ToVector());
-      LOG_INFO(LOG_TAG, "Read remote version handle 0x%04x", handle);
-    } break;
-    case (hci::OpCode::READ_CLOCK_OFFSET): {
-      send_event_(EventPacketBuilder::CreateReadClockOffsetEvent(status, handle, args.extract<uint64_t>())->ToVector());
-    } break;
-    default:
-      LOG_INFO(LOG_TAG, "Unhandled response to command 0x%04x", static_cast<uint16_t>(opcode));
+  auto to_send = model::packets::LeScanResponseBuilder::Create(
+      properties_.GetLeAddress(), incoming.GetSourceAddress(),
+      static_cast<model::packets::AddressType>(properties_.GetLeAddressType()),
+      static_cast<model::packets::AdvertisementType>(
+          properties_.GetLeAdvertisementType()),
+      properties_.GetLeScanResponse());
+
+  SendLeLinkLayerPacket(std::move(to_send));
+}
+
+void LinkLayerController::IncomingLeScanResponsePacket(
+    model::packets::LinkLayerPacketView incoming) {
+  auto scan_response = model::packets::LeScanResponseView::Create(incoming);
+  ASSERT(scan_response.IsValid());
+  vector<uint8_t> ad = scan_response.GetData();
+  auto adv_type = static_cast<LeAdvertisement::AdvertisementType>(
+      scan_response.GetAdvertisementType());
+  auto address_type =
+      static_cast<LeAdvertisement::AddressType>(scan_response.GetAddressType());
+  if (le_scan_enable_ == bluetooth::hci::OpCode::LE_SET_SCAN_ENABLE) {
+    std::unique_ptr<bluetooth::packet::RawBuilder> raw_builder_ptr =
+        std::make_unique<bluetooth::packet::RawBuilder>();
+    raw_builder_ptr->AddOctets1(
+        static_cast<uint8_t>(bluetooth::hci::SubeventCode::ADVERTISING_REPORT));
+    raw_builder_ptr->AddOctets1(0x01);  // num reports
+    raw_builder_ptr->AddOctets1(static_cast<uint8_t>(adv_type));
+    raw_builder_ptr->AddOctets1(static_cast<uint8_t>(address_type));
+    raw_builder_ptr->AddAddress(incoming.GetSourceAddress());
+    raw_builder_ptr->AddOctets1(ad.size());
+    raw_builder_ptr->AddOctets(ad);
+    raw_builder_ptr->AddOctets1(GetRssi());
+    auto packet = bluetooth::hci::EventPacketBuilder::Create(
+        bluetooth::hci::EventCode::LE_META_EVENT, std::move(raw_builder_ptr));
+    send_event_(std::move(packet));
+  }
+
+  if (le_scan_enable_ == bluetooth::hci::OpCode::LE_SET_EXTENDED_SCAN_ENABLE) {
+    std::unique_ptr<bluetooth::packet::RawBuilder> raw_builder_ptr =
+        std::make_unique<bluetooth::packet::RawBuilder>();
+    raw_builder_ptr->AddOctets1(static_cast<uint8_t>(
+        bluetooth::hci::SubeventCode::EXTENDED_ADVERTISING_REPORT));
+    raw_builder_ptr->AddOctets1(0x01);  // num reports
+    raw_builder_ptr->AddOctets1(static_cast<uint8_t>(adv_type));
+    raw_builder_ptr->AddOctets1(static_cast<uint8_t>(address_type));
+    raw_builder_ptr->AddAddress(incoming.GetSourceAddress());
+    raw_builder_ptr->AddOctets1(1);     // Primary_PHY
+    raw_builder_ptr->AddOctets1(0);     // Secondary_PHY
+    raw_builder_ptr->AddOctets1(0xFF);  // Advertising_SID - not provided
+    raw_builder_ptr->AddOctets1(0x7F);  // Tx_Power - Not available
+    raw_builder_ptr->AddOctets1(GetRssi());
+    raw_builder_ptr->AddOctets1(0);  // Periodic_Advertising_Interval - None
+    raw_builder_ptr->AddOctets1(0);  // Direct_Address_Type - PUBLIC
+    raw_builder_ptr->AddAddress(Address::kEmpty);  // Direct_Address
+    raw_builder_ptr->AddOctets1(ad.size());
+    raw_builder_ptr->AddOctets(ad);
+    auto packet = bluetooth::hci::EventPacketBuilder::Create(
+        bluetooth::hci::EventCode::LE_META_EVENT, std::move(raw_builder_ptr));
+    send_event_(std::move(packet));
+  }
+}
+
+void LinkLayerController::IncomingPagePacket(
+    model::packets::LinkLayerPacketView incoming) {
+  auto page = model::packets::PageView::Create(incoming);
+  ASSERT(page.IsValid());
+  LOG_INFO("%s from %s", __func__,
+           incoming.GetSourceAddress().ToString().c_str());
+
+  if (!connections_.CreatePendingConnection(
+          incoming.GetSourceAddress(), properties_.GetAuthenticationEnable())) {
+    // Send a response to indicate that we're busy, or drop the packet?
+    LOG_WARN("%s: Failed to create a pending connection for %s", __func__,
+             incoming.GetSourceAddress().ToString().c_str());
+  }
+
+  bluetooth::hci::Address source_address;
+  bluetooth::hci::Address::FromString(page.GetSourceAddress().ToString(),
+                                      source_address);
+
+  auto packet = bluetooth::hci::ConnectionRequestBuilder::Create(
+      source_address, page.GetClassOfDevice(),
+      bluetooth::hci::ConnectionRequestLinkType::ACL);
+
+  send_event_(std::move(packet));
+}
+
+void LinkLayerController::IncomingPageRejectPacket(
+    model::packets::LinkLayerPacketView incoming) {
+  LOG_INFO("%s: %s", __func__, incoming.GetSourceAddress().ToString().c_str());
+  auto reject = model::packets::PageRejectView::Create(incoming);
+  ASSERT(reject.IsValid());
+  LOG_INFO("%s: Sending CreateConnectionComplete", __func__);
+  auto packet = bluetooth::hci::ConnectionCompleteBuilder::Create(
+      static_cast<ErrorCode>(reject.GetReason()), 0x0eff,
+      incoming.GetSourceAddress(), bluetooth::hci::LinkType::ACL,
+      bluetooth::hci::Enable::DISABLED);
+  send_event_(std::move(packet));
+}
+
+void LinkLayerController::IncomingPageResponsePacket(
+    model::packets::LinkLayerPacketView incoming) {
+  Address peer = incoming.GetSourceAddress();
+  LOG_INFO("%s: %s", __func__, peer.ToString().c_str());
+  bool awaiting_authentication = connections_.AuthenticatePendingConnection();
+  uint16_t handle =
+      connections_.CreateConnection(peer, incoming.GetDestinationAddress());
+  if (handle == acl::kReservedHandle) {
+    LOG_WARN("%s: No free handles", __func__);
+    return;
+  }
+  auto packet = bluetooth::hci::ConnectionCompleteBuilder::Create(
+      ErrorCode::SUCCESS, handle, incoming.GetSourceAddress(),
+      bluetooth::hci::LinkType::ACL, bluetooth::hci::Enable::DISABLED);
+  send_event_(std::move(packet));
+
+  if (awaiting_authentication) {
+    ScheduleTask(milliseconds(5), [this, peer, handle]() {
+      HandleAuthenticationRequest(peer, handle);
+    });
   }
 }
 
 void LinkLayerController::TimerTick() {
   if (inquiry_state_ == Inquiry::InquiryState::INQUIRY) Inquiry();
   if (inquiry_state_ == Inquiry::InquiryState::INQUIRY) PageScan();
+  LeAdvertising();
   Connections();
 }
 
+void LinkLayerController::LeAdvertising() {
+  if (!le_advertising_enable_) {
+    return;
+  }
+  steady_clock::time_point now = steady_clock::now();
+  if (duration_cast<milliseconds>(now - last_le_advertisement_) <
+      milliseconds(200)) {
+    return;
+  }
+  last_le_advertisement_ = now;
+
+  auto own_address_type = static_cast<model::packets::AddressType>(
+      properties_.GetLeAdvertisingOwnAddressType());
+  Address advertising_address = Address::kEmpty;
+  if (own_address_type == model::packets::AddressType::PUBLIC) {
+    advertising_address = properties_.GetAddress();
+  } else if (own_address_type == model::packets::AddressType::RANDOM) {
+    advertising_address = properties_.GetLeAddress();
+  }
+  ASSERT(advertising_address != Address::kEmpty);
+  auto to_send = model::packets::LeAdvertisementBuilder::Create(
+      advertising_address, Address::kEmpty, own_address_type,
+      static_cast<model::packets::AdvertisementType>(own_address_type),
+      properties_.GetLeAdvertisement());
+  SendLeLinkLayerPacket(std::move(to_send));
+}
+
 void LinkLayerController::Connections() {
   // TODO: Keep connections alive?
 }
 
 void LinkLayerController::RegisterEventChannel(
-    const std::function<void(std::shared_ptr<std::vector<uint8_t>>)>& callback) {
+    const std::function<
+        void(std::shared_ptr<bluetooth::hci::EventPacketBuilder>)>& callback) {
   send_event_ = callback;
 }
 
 void LinkLayerController::RegisterAclChannel(
-    const std::function<void(std::shared_ptr<std::vector<uint8_t>>)>& callback) {
+    const std::function<
+        void(std::shared_ptr<bluetooth::hci::AclPacketBuilder>)>& callback) {
   send_acl_ = callback;
 }
 
 void LinkLayerController::RegisterScoChannel(
-    const std::function<void(std::shared_ptr<std::vector<uint8_t>>)>& callback) {
+    const std::function<void(std::shared_ptr<std::vector<uint8_t>>)>&
+        callback) {
   send_sco_ = callback;
 }
 
+void LinkLayerController::RegisterIsoChannel(
+    const std::function<void(std::shared_ptr<std::vector<uint8_t>>)>&
+        callback) {
+  send_iso_ = callback;
+}
+
 void LinkLayerController::RegisterRemoteChannel(
-    const std::function<void(std::shared_ptr<LinkLayerPacketBuilder>, Phy::Type)>& callback) {
+    const std::function<void(
+        std::shared_ptr<model::packets::LinkLayerPacketBuilder>, Phy::Type)>&
+        callback) {
   send_to_remote_ = callback;
 }
 
 void LinkLayerController::RegisterTaskScheduler(
-    std::function<AsyncTaskId(milliseconds, const TaskCallback&)> event_scheduler) {
+    std::function<AsyncTaskId(milliseconds, const TaskCallback&)>
+        event_scheduler) {
   schedule_task_ = event_scheduler;
 }
 
+AsyncTaskId LinkLayerController::ScheduleTask(milliseconds delay_ms,
+                                              const TaskCallback& callback) {
+  if (schedule_task_) {
+    return schedule_task_(delay_ms, callback);
+  } else {
+    callback();
+    return 0;
+  }
+}
+
 void LinkLayerController::RegisterPeriodicTaskScheduler(
-    std::function<AsyncTaskId(milliseconds, milliseconds, const TaskCallback&)> periodic_event_scheduler) {
+    std::function<AsyncTaskId(milliseconds, milliseconds, const TaskCallback&)>
+        periodic_event_scheduler) {
   schedule_periodic_task_ = periodic_event_scheduler;
 }
 
-void LinkLayerController::RegisterTaskCancel(std::function<void(AsyncTaskId)> task_cancel) {
+void LinkLayerController::CancelScheduledTask(AsyncTaskId task_id) {
+  if (schedule_task_ && cancel_task_) {
+    cancel_task_(task_id);
+  }
+}
+
+void LinkLayerController::RegisterTaskCancel(
+    std::function<void(AsyncTaskId)> task_cancel) {
   cancel_task_ = task_cancel;
 }
 
-void LinkLayerController::AddControllerEvent(milliseconds delay, const TaskCallback& task) {
-  controller_events_.push_back(schedule_task_(delay, task));
+void LinkLayerController::AddControllerEvent(milliseconds delay,
+                                             const TaskCallback& task) {
+  controller_events_.push_back(ScheduleTask(delay, task));
 }
 
 void LinkLayerController::WriteSimplePairingMode(bool enabled) {
-  CHECK(enabled) << "The spec says don't disable this!";
+  ASSERT_LOG(enabled, "The spec says don't disable this!");
   simple_pairing_mode_enabled_ = enabled;
 }
 
 void LinkLayerController::StartSimplePairing(const Address& address) {
   // IO Capability Exchange (See the Diagram in the Spec)
-  send_event_(EventPacketBuilder::CreateIoCapabilityRequestEvent(address)->ToVector());
+  auto packet = bluetooth::hci::IoCapabilityRequestBuilder::Create(address);
+  send_event_(std::move(packet));
 
   // Get a Key, then authenticate
   // PublicKeyExchange(address);
@@ -698,339 +1150,508 @@
   // AuthenticateRemoteStage2(address);
 }
 
-void LinkLayerController::AuthenticateRemoteStage1(const Address& peer, PairingType pairing_type) {
-  CHECK(security_manager_.GetAuthenticationAddress() == peer);
+void LinkLayerController::AuthenticateRemoteStage1(const Address& peer,
+                                                   PairingType pairing_type) {
+  ASSERT(security_manager_.GetAuthenticationAddress() == peer);
   // TODO: Public key exchange first?
   switch (pairing_type) {
     case PairingType::AUTO_CONFIRMATION:
-      send_event_(EventPacketBuilder::CreateUserConfirmationRequestEvent(peer, 123456)->ToVector());
+      send_event_(
+          bluetooth::hci::UserConfirmationRequestBuilder::Create(peer, 123456));
       break;
     case PairingType::CONFIRM_Y_N:
-      CHECK(false) << __func__ << "Unimplemented PairingType" << static_cast<int>(pairing_type);
+      send_event_(
+          bluetooth::hci::UserConfirmationRequestBuilder::Create(peer, 123456));
       break;
     case PairingType::DISPLAY_PIN:
-      CHECK(false) << __func__ << "Unimplemented PairingType" << static_cast<int>(pairing_type);
+      send_event_(
+          bluetooth::hci::UserConfirmationRequestBuilder::Create(peer, 123456));
       break;
     case PairingType::DISPLAY_AND_CONFIRM:
-      CHECK(false) << __func__ << "Unimplemented PairingType" << static_cast<int>(pairing_type);
+      send_event_(
+          bluetooth::hci::UserConfirmationRequestBuilder::Create(peer, 123456));
       break;
     case PairingType::INPUT_PIN:
-      CHECK(false) << __func__ << "Unimplemented PairingType" << static_cast<int>(pairing_type);
-      break;
-    case PairingType::INVALID:
-      CHECK(false) << __func__ << "Unimplemented PairingType" << static_cast<int>(pairing_type);
+      send_event_(bluetooth::hci::UserPasskeyRequestBuilder::Create(peer));
       break;
     default:
-      CHECK(false) << __func__ << ": Invalid PairingType " << static_cast<int>(pairing_type);
+      LOG_ALWAYS_FATAL("Invalid PairingType %d",
+                       static_cast<int>(pairing_type));
   }
 }
 
 void LinkLayerController::AuthenticateRemoteStage2(const Address& peer) {
   uint16_t handle = security_manager_.GetAuthenticationHandle();
-  CHECK(security_manager_.GetAuthenticationAddress() == peer);
+  ASSERT(security_manager_.GetAuthenticationAddress() == peer);
   // Check key in security_manager_ ?
-  send_event_(EventPacketBuilder::CreateAuthenticationCompleteEvent(hci::Status::SUCCESS, handle)->ToVector());
+  auto packet = bluetooth::hci::AuthenticationCompleteBuilder::Create(
+      ErrorCode::SUCCESS, handle);
+  send_event_(std::move(packet));
 }
 
-hci::Status LinkLayerController::LinkKeyRequestReply(const Address& peer, PacketView<true> key) {
-  std::vector<uint8_t> key_vec(key.begin(), key.end());
-  security_manager_.WriteKey(peer, key_vec);
+ErrorCode LinkLayerController::LinkKeyRequestReply(
+    const Address& peer, const std::array<uint8_t, 16>& key) {
+  security_manager_.WriteKey(peer, key);
   security_manager_.AuthenticationRequestFinished();
 
-  schedule_task_(milliseconds(5), [this, peer]() { AuthenticateRemoteStage2(peer); });
+  ScheduleTask(milliseconds(5),
+               [this, peer]() { AuthenticateRemoteStage2(peer); });
 
-  return hci::Status::SUCCESS;
+  return ErrorCode::SUCCESS;
 }
 
-hci::Status LinkLayerController::LinkKeyRequestNegativeReply(const Address& address) {
+ErrorCode LinkLayerController::LinkKeyRequestNegativeReply(
+    const Address& address) {
   security_manager_.DeleteKey(address);
   // Simple pairing to get a key
-  uint16_t handle = classic_connections_.GetHandle(address);
+  uint16_t handle = connections_.GetHandleOnlyAddress(address);
   if (handle == acl::kReservedHandle) {
-    LOG_INFO(LOG_TAG, "%s: Device not connected %s", __func__, address.ToString().c_str());
-    return hci::Status::UNKNOWN_CONNECTION;
+    LOG_INFO("%s: Device not connected %s", __func__,
+             address.ToString().c_str());
+    return ErrorCode::UNKNOWN_CONNECTION;
   }
 
   security_manager_.AuthenticationRequest(address, handle);
 
-  schedule_task_(milliseconds(5), [this, address]() { StartSimplePairing(address); });
-  return hci::Status::SUCCESS;
+  ScheduleTask(milliseconds(5),
+               [this, address]() { StartSimplePairing(address); });
+  return ErrorCode::SUCCESS;
 }
 
-hci::Status LinkLayerController::IoCapabilityRequestReply(const Address& peer, uint8_t io_capability,
-                                                          uint8_t oob_data_present_flag,
-                                                          uint8_t authentication_requirements) {
-  security_manager_.SetLocalIoCapability(peer, io_capability, oob_data_present_flag, authentication_requirements);
+ErrorCode LinkLayerController::IoCapabilityRequestReply(
+    const Address& peer, uint8_t io_capability, uint8_t oob_data_present_flag,
+    uint8_t authentication_requirements) {
+  security_manager_.SetLocalIoCapability(
+      peer, io_capability, oob_data_present_flag, authentication_requirements);
 
   PairingType pairing_type = security_manager_.GetSimplePairingType();
+
   if (pairing_type != PairingType::INVALID) {
-    schedule_task_(milliseconds(5), [this, peer, pairing_type]() { AuthenticateRemoteStage1(peer, pairing_type); });
-    SendLinkLayerPacket(LinkLayerPacketBuilder::WrapIoCapabilityResponse(
-        IoCapabilityBuilder::Create(io_capability, oob_data_present_flag, authentication_requirements),
-        properties_.GetAddress(), peer));
+    ScheduleTask(milliseconds(5), [this, peer, pairing_type]() {
+      AuthenticateRemoteStage1(peer, pairing_type);
+    });
+    SendLinkLayerPacket(model::packets::IoCapabilityResponseBuilder::Create(
+        properties_.GetAddress(), peer, io_capability, oob_data_present_flag,
+        authentication_requirements));
   } else {
-    LOG_INFO(LOG_TAG, "%s: Requesting remote capability", __func__);
-    SendLinkLayerPacket(LinkLayerPacketBuilder::WrapIoCapabilityRequest(
-        IoCapabilityBuilder::Create(io_capability, oob_data_present_flag, authentication_requirements),
-        properties_.GetAddress(), peer));
+    LOG_INFO("%s: Requesting remote capability", __func__);
+
+    SendLinkLayerPacket(model::packets::IoCapabilityRequestBuilder::Create(
+        properties_.GetAddress(), peer, io_capability, oob_data_present_flag,
+        authentication_requirements));
   }
 
-  return hci::Status::SUCCESS;
+  return ErrorCode::SUCCESS;
 }
 
-hci::Status LinkLayerController::IoCapabilityRequestNegativeReply(const Address& peer, hci::Status reason) {
+ErrorCode LinkLayerController::IoCapabilityRequestNegativeReply(
+    const Address& peer, ErrorCode reason) {
   if (security_manager_.GetAuthenticationAddress() != peer) {
-    return hci::Status::AUTHENTICATION_FAILURE;
+    return ErrorCode::AUTHENTICATION_FAILURE;
   }
 
   security_manager_.InvalidateIoCapabilities();
 
-  SendLinkLayerPacket(LinkLayerPacketBuilder::WrapIoCapabilityNegativeResponse(
-      IoCapabilityNegativeResponseBuilder::Create(static_cast<uint8_t>(reason)), properties_.GetAddress(), peer));
+  auto packet = model::packets::IoCapabilityNegativeResponseBuilder::Create(
+      properties_.GetAddress(), peer, static_cast<uint8_t>(reason));
+  SendLinkLayerPacket(std::move(packet));
 
-  return hci::Status::SUCCESS;
+  return ErrorCode::SUCCESS;
 }
 
-hci::Status LinkLayerController::UserConfirmationRequestReply(const Address& peer) {
+ErrorCode LinkLayerController::UserConfirmationRequestReply(
+    const Address& peer) {
   if (security_manager_.GetAuthenticationAddress() != peer) {
-    return hci::Status::AUTHENTICATION_FAILURE;
+    return ErrorCode::AUTHENTICATION_FAILURE;
   }
   // TODO: Key could be calculated here.
-  std::vector<uint8_t> key_vec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
+  std::array<uint8_t, 16> key_vec{1, 2,  3,  4,  5,  6,  7,  8,
+                                  9, 10, 11, 12, 13, 14, 15, 16};
   security_manager_.WriteKey(peer, key_vec);
 
   security_manager_.AuthenticationRequestFinished();
 
-  schedule_task_(milliseconds(5), [this, peer]() { AuthenticateRemoteStage2(peer); });
-  return hci::Status::SUCCESS;
+  ScheduleTask(milliseconds(5), [this, peer, key_vec]() {
+    send_event_(bluetooth::hci::LinkKeyNotificationBuilder::Create(
+        peer, key_vec, bluetooth::hci::KeyType::AUTHENTICATED_P256));
+  });
+
+  ScheduleTask(milliseconds(5), [this, peer]() {
+    send_event_(bluetooth::hci::SimplePairingCompleteBuilder::Create(
+        ErrorCode::SUCCESS, peer));
+  });
+
+  ScheduleTask(milliseconds(15),
+               [this, peer]() { AuthenticateRemoteStage2(peer); });
+  return ErrorCode::SUCCESS;
 }
 
-hci::Status LinkLayerController::UserConfirmationRequestNegativeReply(const Address& peer) {
+ErrorCode LinkLayerController::UserConfirmationRequestNegativeReply(
+    const Address& peer) {
   if (security_manager_.GetAuthenticationAddress() != peer) {
-    return hci::Status::AUTHENTICATION_FAILURE;
+    return ErrorCode::AUTHENTICATION_FAILURE;
   }
-  return hci::Status::SUCCESS;
+  return ErrorCode::SUCCESS;
 }
 
-hci::Status LinkLayerController::UserPasskeyRequestReply(const Address& peer, uint32_t numeric_value) {
+ErrorCode LinkLayerController::UserPasskeyRequestReply(const Address& peer,
+                                                       uint32_t numeric_value) {
   if (security_manager_.GetAuthenticationAddress() != peer) {
-    return hci::Status::AUTHENTICATION_FAILURE;
+    return ErrorCode::AUTHENTICATION_FAILURE;
   }
-  LOG_INFO(LOG_TAG, "TODO:Do something with the passkey %06d", numeric_value);
-  return hci::Status::SUCCESS;
+  LOG_INFO("TODO:Do something with the passkey %06d", numeric_value);
+  return ErrorCode::SUCCESS;
 }
 
-hci::Status LinkLayerController::UserPasskeyRequestNegativeReply(const Address& peer) {
+ErrorCode LinkLayerController::UserPasskeyRequestNegativeReply(
+    const Address& peer) {
   if (security_manager_.GetAuthenticationAddress() != peer) {
-    return hci::Status::AUTHENTICATION_FAILURE;
+    return ErrorCode::AUTHENTICATION_FAILURE;
   }
-  return hci::Status::SUCCESS;
+  return ErrorCode::SUCCESS;
 }
 
-hci::Status LinkLayerController::RemoteOobDataRequestReply(const Address& peer, const std::vector<uint8_t>& c,
-                                                           const std::vector<uint8_t>& r) {
+ErrorCode LinkLayerController::RemoteOobDataRequestReply(
+    const Address& peer, const std::vector<uint8_t>& c,
+    const std::vector<uint8_t>& r) {
   if (security_manager_.GetAuthenticationAddress() != peer) {
-    return hci::Status::AUTHENTICATION_FAILURE;
+    return ErrorCode::AUTHENTICATION_FAILURE;
   }
-  LOG_INFO(LOG_TAG, "TODO:Do something with the OOB data c=%d r=%d", c[0], r[0]);
-  return hci::Status::SUCCESS;
+  LOG_INFO("TODO:Do something with the OOB data c=%d r=%d", c[0], r[0]);
+  return ErrorCode::SUCCESS;
 }
 
-hci::Status LinkLayerController::RemoteOobDataRequestNegativeReply(const Address& peer) {
+ErrorCode LinkLayerController::RemoteOobDataRequestNegativeReply(
+    const Address& peer) {
   if (security_manager_.GetAuthenticationAddress() != peer) {
-    return hci::Status::AUTHENTICATION_FAILURE;
+    return ErrorCode::AUTHENTICATION_FAILURE;
   }
-  return hci::Status::SUCCESS;
+  return ErrorCode::SUCCESS;
 }
 
-void LinkLayerController::HandleAuthenticationRequest(const Address& address, uint16_t handle) {
+void LinkLayerController::HandleAuthenticationRequest(const Address& address,
+                                                      uint16_t handle) {
   if (simple_pairing_mode_enabled_ == true) {
     security_manager_.AuthenticationRequest(address, handle);
-    send_event_(EventPacketBuilder::CreateLinkKeyRequestEvent(address)->ToVector());
+    auto packet = bluetooth::hci::LinkKeyRequestBuilder::Create(address);
+    send_event_(std::move(packet));
   } else {  // Should never happen for our phones
     // Check for a key, try to authenticate, ask for a PIN.
-    send_event_(
-        EventPacketBuilder::CreateAuthenticationCompleteEvent(hci::Status::AUTHENTICATION_FAILURE, handle)->ToVector());
+    auto packet = bluetooth::hci::AuthenticationCompleteBuilder::Create(
+        ErrorCode::AUTHENTICATION_FAILURE, handle);
+    send_event_(std::move(packet));
   }
 }
 
-hci::Status LinkLayerController::AuthenticationRequested(uint16_t handle) {
-  if (!classic_connections_.HasHandle(handle)) {
-    LOG_INFO(LOG_TAG, "Authentication Requested for unknown handle %04x", handle);
-    return hci::Status::UNKNOWN_CONNECTION;
+ErrorCode LinkLayerController::AuthenticationRequested(uint16_t handle) {
+  if (!connections_.HasHandle(handle)) {
+    LOG_INFO("Authentication Requested for unknown handle %04x", handle);
+    return ErrorCode::UNKNOWN_CONNECTION;
   }
 
-  Address remote = classic_connections_.GetAddress(handle);
+  AddressWithType remote = connections_.GetAddress(handle);
 
-  schedule_task_(milliseconds(5), [this, remote, handle]() { HandleAuthenticationRequest(remote, handle); });
+  ScheduleTask(milliseconds(5), [this, remote, handle]() {
+    HandleAuthenticationRequest(remote.GetAddress(), handle);
+  });
 
-  return hci::Status::SUCCESS;
+  return ErrorCode::SUCCESS;
 }
 
-void LinkLayerController::HandleSetConnectionEncryption(const Address& peer, uint16_t handle,
-                                                        uint8_t encryption_enable) {
+void LinkLayerController::HandleSetConnectionEncryption(
+    const Address& peer, uint16_t handle, uint8_t encryption_enable) {
   // TODO: Block ACL traffic or at least guard against it
 
-  if (classic_connections_.IsEncrypted(handle) && encryption_enable) {
-    send_event_(
-        EventPacketBuilder::CreateEncryptionChange(hci::Status::SUCCESS, handle, encryption_enable)->ToVector());
+  if (connections_.IsEncrypted(handle) && encryption_enable) {
+    auto packet = bluetooth::hci::EncryptionChangeBuilder::Create(
+        ErrorCode::SUCCESS, handle,
+        static_cast<bluetooth::hci::EncryptionEnabled>(encryption_enable));
+    send_event_(std::move(packet));
     return;
   }
 
-  SendLinkLayerPacket(LinkLayerPacketBuilder::WrapEncryptConnection(
-      EncryptConnectionBuilder::Create(security_manager_.GetKey(peer)), properties_.GetAddress(), peer));
+  uint16_t count = security_manager_.ReadKey(peer);
+  if (count == 0) {
+    LOG_ERROR("NO KEY HERE for %s", peer.ToString().c_str());
+    return;
+  }
+  auto array = security_manager_.GetKey(peer);
+  std::vector<uint8_t> key_vec{array.begin(), array.end()};
+  auto packet = model::packets::EncryptConnectionBuilder::Create(
+      properties_.GetAddress(), peer, key_vec);
+  SendLinkLayerPacket(std::move(packet));
 }
 
-hci::Status LinkLayerController::SetConnectionEncryption(uint16_t handle, uint8_t encryption_enable) {
-  if (!classic_connections_.HasHandle(handle)) {
-    LOG_INFO(LOG_TAG, "Authentication Requested for unknown handle %04x", handle);
-    return hci::Status::UNKNOWN_CONNECTION;
+ErrorCode LinkLayerController::SetConnectionEncryption(
+    uint16_t handle, uint8_t encryption_enable) {
+  if (!connections_.HasHandle(handle)) {
+    LOG_INFO("Set Connection Encryption for unknown handle %04x", handle);
+    return ErrorCode::UNKNOWN_CONNECTION;
   }
 
-  if (classic_connections_.IsEncrypted(handle) && !encryption_enable) {
-    return hci::Status::ENCRYPTION_MODE_NOT_ACCEPTABLE;
+  if (connections_.IsEncrypted(handle) && !encryption_enable) {
+    return ErrorCode::ENCRYPTION_MODE_NOT_ACCEPTABLE;
   }
-  Address remote = classic_connections_.GetAddress(handle);
+  AddressWithType remote = connections_.GetAddress(handle);
 
-  schedule_task_(milliseconds(5), [this, remote, handle, encryption_enable]() {
-    HandleSetConnectionEncryption(remote, handle, encryption_enable);
+  if (security_manager_.ReadKey(remote.GetAddress()) == 0) {
+    return ErrorCode::PIN_OR_KEY_MISSING;
+  }
+
+  ScheduleTask(milliseconds(5), [this, remote, handle, encryption_enable]() {
+    HandleSetConnectionEncryption(remote.GetAddress(), handle,
+                                  encryption_enable);
   });
-
-  return hci::Status::SUCCESS;
+  return ErrorCode::SUCCESS;
 }
 
-hci::Status LinkLayerController::AcceptConnectionRequest(const Address& addr, bool try_role_switch) {
-  if (!classic_connections_.HasPendingConnection(addr)) {
-    LOG_INFO(LOG_TAG, "%s: No pending connection", __func__);
-    return hci::Status::UNKNOWN_CONNECTION;
+ErrorCode LinkLayerController::AcceptConnectionRequest(const Address& addr,
+                                                       bool try_role_switch) {
+  if (!connections_.HasPendingConnection(addr)) {
+    LOG_INFO("%s: No pending connection for %s", __func__,
+             addr.ToString().c_str());
+    return ErrorCode::UNKNOWN_CONNECTION;
   }
 
-  LOG_INFO(LOG_TAG, "%s: Accept in 200ms", __func__);
-  schedule_task_(milliseconds(200), [this, addr, try_role_switch]() {
-    LOG_INFO(LOG_TAG, "%s: Accepted", __func__);
+  LOG_INFO("%s: Accept in 200ms", __func__);
+  ScheduleTask(milliseconds(200), [this, addr, try_role_switch]() {
+    LOG_INFO("%s: Accepted", __func__);
     MakeSlaveConnection(addr, try_role_switch);
   });
 
-  return hci::Status::SUCCESS;
+  return ErrorCode::SUCCESS;
 }
 
-void LinkLayerController::MakeSlaveConnection(const Address& addr, bool try_role_switch) {
-  std::shared_ptr<LinkLayerPacketBuilder> to_send = LinkLayerPacketBuilder::WrapPageResponse(
-      PageResponseBuilder::Create(try_role_switch), properties_.GetAddress(), addr);
-  LOG_INFO(LOG_TAG, "%s sending page response to %s", __func__, addr.ToString().c_str());
-  SendLinkLayerPacket(to_send);
+void LinkLayerController::MakeSlaveConnection(const Address& addr,
+                                              bool try_role_switch) {
+  LOG_INFO("%s sending page response to %s", __func__, addr.ToString().c_str());
+  auto to_send = model::packets::PageResponseBuilder::Create(
+      properties_.GetAddress(), addr, try_role_switch);
+  SendLinkLayerPacket(std::move(to_send));
 
-  uint16_t handle = classic_connections_.CreateConnection(addr);
+  uint16_t handle =
+      connections_.CreateConnection(addr, properties_.GetAddress());
   if (handle == acl::kReservedHandle) {
-    LOG_INFO(LOG_TAG, "%s CreateConnection failed", __func__);
+    LOG_INFO("%s CreateConnection failed", __func__);
     return;
   }
-  LOG_INFO(LOG_TAG, "%s CreateConnection returned handle 0x%x", __func__, handle);
-  send_event_(
-      EventPacketBuilder::CreateConnectionCompleteEvent(hci::Status::SUCCESS, handle, addr, hci::LinkType::ACL, false)
-          ->ToVector());
+  LOG_INFO("%s CreateConnection returned handle 0x%x", __func__, handle);
+  auto packet = bluetooth::hci::ConnectionCompleteBuilder::Create(
+      ErrorCode::SUCCESS, handle, addr, bluetooth::hci::LinkType::ACL,
+      bluetooth::hci::Enable::DISABLED);
+  send_event_(std::move(packet));
 }
 
-hci::Status LinkLayerController::RejectConnectionRequest(const Address& addr, uint8_t reason) {
-  if (!classic_connections_.HasPendingConnection(addr)) {
-    LOG_INFO(LOG_TAG, "%s: No pending connection", __func__);
-    return hci::Status::UNKNOWN_CONNECTION;
+ErrorCode LinkLayerController::RejectConnectionRequest(const Address& addr,
+                                                       uint8_t reason) {
+  if (!connections_.HasPendingConnection(addr)) {
+    LOG_INFO("%s: No pending connection for %s", __func__,
+             addr.ToString().c_str());
+    return ErrorCode::UNKNOWN_CONNECTION;
   }
 
-  LOG_INFO(LOG_TAG, "%s: Reject in 200ms", __func__);
-  schedule_task_(milliseconds(200), [this, addr, reason]() {
-    LOG_INFO(LOG_TAG, "%s: Reject", __func__);
-    RejectSlaveConnection(addr, reason);
+  ScheduleTask(milliseconds(200),
+               [this, addr, reason]() { RejectSlaveConnection(addr, reason); });
+
+  return ErrorCode::SUCCESS;
+}
+
+void LinkLayerController::RejectSlaveConnection(const Address& addr,
+                                                uint8_t reason) {
+  auto to_send = model::packets::PageRejectBuilder::Create(
+      properties_.GetAddress(), addr, reason);
+  LOG_INFO("%s sending page reject to %s (reason 0x%02hhx)", __func__,
+           addr.ToString().c_str(), reason);
+  SendLinkLayerPacket(std::move(to_send));
+
+  auto packet = bluetooth::hci::ConnectionCompleteBuilder::Create(
+      static_cast<ErrorCode>(reason), 0xeff, addr,
+      bluetooth::hci::LinkType::ACL, bluetooth::hci::Enable::DISABLED);
+  send_event_(std::move(packet));
+}
+
+ErrorCode LinkLayerController::CreateConnection(const Address& addr, uint16_t,
+                                                uint8_t, uint16_t,
+                                                uint8_t allow_role_switch) {
+  if (!connections_.CreatePendingConnection(
+          addr, properties_.GetAuthenticationEnable() == 1)) {
+    return ErrorCode::CONTROLLER_BUSY;
+  }
+  auto page = model::packets::PageBuilder::Create(
+      properties_.GetAddress(), addr, properties_.GetClassOfDevice(),
+      allow_role_switch);
+  SendLinkLayerPacket(std::move(page));
+
+  return ErrorCode::SUCCESS;
+}
+
+ErrorCode LinkLayerController::CreateConnectionCancel(const Address& addr) {
+  if (!connections_.CancelPendingConnection(addr)) {
+    return ErrorCode::UNKNOWN_CONNECTION;
+  }
+  return ErrorCode::SUCCESS;
+}
+
+ErrorCode LinkLayerController::Disconnect(uint16_t handle, uint8_t reason) {
+  if (!connections_.HasHandle(handle)) {
+    return ErrorCode::UNKNOWN_CONNECTION;
+  }
+
+  const AddressWithType remote = connections_.GetAddress(handle);
+  auto packet = model::packets::DisconnectBuilder::Create(
+      properties_.GetAddress(), remote.GetAddress(), reason);
+  SendLinkLayerPacket(std::move(packet));
+  ASSERT_LOG(connections_.Disconnect(handle), "Disconnecting %hx", handle);
+
+  ScheduleTask(milliseconds(20), [this, handle]() {
+    DisconnectCleanup(
+        handle,
+        static_cast<uint8_t>(ErrorCode::CONNECTION_TERMINATED_BY_LOCAL_HOST));
   });
 
-  return hci::Status::SUCCESS;
-}
-
-void LinkLayerController::RejectSlaveConnection(const Address& addr, uint8_t reason) {
-  CHECK(reason > 0x0f || reason < 0x0d);
-  send_event_(EventPacketBuilder::CreateConnectionCompleteEvent(static_cast<hci::Status>(reason), 0xeff, addr,
-                                                                hci::LinkType::ACL, false)
-                  ->ToVector());
-}
-
-hci::Status LinkLayerController::CreateConnection(const Address& addr, uint16_t, uint8_t, uint16_t,
-                                                  uint8_t allow_role_switch) {
-  if (!classic_connections_.CreatePendingConnection(addr)) {
-    return hci::Status::CONTROLLER_BUSY;
-  }
-
-  std::unique_ptr<PageBuilder> page = PageBuilder::Create(properties_.GetClassOfDevice(), allow_role_switch);
-  SendLinkLayerPacket(LinkLayerPacketBuilder::WrapPage(std::move(page), properties_.GetAddress(), addr));
-
-  return hci::Status::SUCCESS;
-}
-
-hci::Status LinkLayerController::CreateConnectionCancel(const Address& addr) {
-  if (!classic_connections_.CancelPendingConnection(addr)) {
-    return hci::Status::UNKNOWN_CONNECTION;
-  }
-  return hci::Status::SUCCESS;
-}
-
-hci::Status LinkLayerController::Disconnect(uint16_t handle, uint8_t reason) {
-  // TODO: Handle LE
-  if (!classic_connections_.HasHandle(handle)) {
-    return hci::Status::UNKNOWN_CONNECTION;
-  }
-
-  const Address& remote = classic_connections_.GetAddress(handle);
-  std::shared_ptr<LinkLayerPacketBuilder> to_send =
-      LinkLayerPacketBuilder::WrapDisconnect(DisconnectBuilder::Create(reason), properties_.GetAddress(), remote);
-  SendLinkLayerPacket(to_send);
-  CHECK(classic_connections_.Disconnect(handle)) << "Disconnecting " << handle;
-
-  schedule_task_(milliseconds(20), [this, handle]() {
-    DisconnectCleanup(handle, static_cast<uint8_t>(hci::Status::CONNECTION_TERMINATED_BY_LOCAL_HOST));
-  });
-
-  return hci::Status::SUCCESS;
+  return ErrorCode::SUCCESS;
 }
 
 void LinkLayerController::DisconnectCleanup(uint16_t handle, uint8_t reason) {
   // TODO: Clean up other connection state.
-  send_event_(EventPacketBuilder::CreateDisconnectionCompleteEvent(hci::Status::SUCCESS, handle, reason)->ToVector());
+  auto packet = bluetooth::hci::DisconnectionCompleteBuilder::Create(
+      ErrorCode::SUCCESS, handle, static_cast<ErrorCode>(reason));
+  send_event_(std::move(packet));
 }
 
-hci::Status LinkLayerController::ChangeConnectionPacketType(uint16_t handle, uint16_t types) {
-  if (!classic_connections_.HasHandle(handle)) {
-    return hci::Status::UNKNOWN_CONNECTION;
+ErrorCode LinkLayerController::ChangeConnectionPacketType(uint16_t handle,
+                                                          uint16_t types) {
+  if (!connections_.HasHandle(handle)) {
+    return ErrorCode::UNKNOWN_CONNECTION;
   }
-  std::unique_ptr<EventPacketBuilder> packet =
-      EventPacketBuilder::CreateConnectionPacketTypeChangedEvent(hci::Status::SUCCESS, handle, types);
-  std::shared_ptr<std::vector<uint8_t>> raw_packet = packet->ToVector();
-  if (schedule_task_) {
-    schedule_task_(milliseconds(20), [this, raw_packet]() { send_event_(raw_packet); });
-  } else {
-    send_event_(raw_packet);
+  auto packet = bluetooth::hci::ConnectionPacketTypeChangedBuilder::Create(
+      ErrorCode::SUCCESS, handle, types);
+  std::shared_ptr<bluetooth::hci::ConnectionPacketTypeChangedBuilder>
+      shared_packet = std::move(packet);
+  ScheduleTask(milliseconds(20), [this, shared_packet]() {
+    send_event_(std::move(shared_packet));
+  });
+
+  return ErrorCode::SUCCESS;
+}
+
+ErrorCode LinkLayerController::ChangeConnectionLinkKey(uint16_t handle) {
+  if (!connections_.HasHandle(handle)) {
+    return ErrorCode::UNKNOWN_CONNECTION;
   }
 
-  return hci::Status::SUCCESS;
+  // TODO: implement real logic
+  return ErrorCode::COMMAND_DISALLOWED;
 }
 
-hci::Status LinkLayerController::WriteLinkPolicySettings(uint16_t handle, uint16_t) {
-  if (!classic_connections_.HasHandle(handle)) {
-    return hci::Status::UNKNOWN_CONNECTION;
+ErrorCode LinkLayerController::MasterLinkKey(uint8_t /* key_flag */) {
+  // TODO: implement real logic
+  return ErrorCode::COMMAND_DISALLOWED;
+}
+
+ErrorCode LinkLayerController::HoldMode(uint16_t handle,
+                                        uint16_t hold_mode_max_interval,
+                                        uint16_t hold_mode_min_interval) {
+  if (!connections_.HasHandle(handle)) {
+    return ErrorCode::UNKNOWN_CONNECTION;
   }
-  return hci::Status::SUCCESS;
-}
 
-hci::Status LinkLayerController::WriteLinkSupervisionTimeout(uint16_t handle, uint16_t) {
-  if (!classic_connections_.HasHandle(handle)) {
-    return hci::Status::UNKNOWN_CONNECTION;
+  if (hold_mode_max_interval < hold_mode_min_interval) {
+    return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
   }
-  return hci::Status::SUCCESS;
+
+  // TODO: implement real logic
+  return ErrorCode::COMMAND_DISALLOWED;
 }
 
-void LinkLayerController::LeWhiteListClear() {
-  le_white_list_.clear();
+ErrorCode LinkLayerController::SniffMode(uint16_t handle,
+                                         uint16_t sniff_max_interval,
+                                         uint16_t sniff_min_interval,
+                                         uint16_t sniff_attempt,
+                                         uint16_t sniff_timeout) {
+  if (!connections_.HasHandle(handle)) {
+    return ErrorCode::UNKNOWN_CONNECTION;
+  }
+
+  if (sniff_max_interval < sniff_min_interval || sniff_attempt < 0x0001 ||
+      sniff_attempt > 0x7FFF || sniff_timeout > 0x7FFF) {
+    return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
+  }
+
+  // TODO: implement real logic
+  return ErrorCode::COMMAND_DISALLOWED;
 }
 
-void LinkLayerController::LeWhiteListAddDevice(Address addr, uint8_t addr_type) {
+ErrorCode LinkLayerController::ExitSniffMode(uint16_t handle) {
+  if (!connections_.HasHandle(handle)) {
+    return ErrorCode::UNKNOWN_CONNECTION;
+  }
+
+  // TODO: implement real logic
+  return ErrorCode::COMMAND_DISALLOWED;
+}
+
+ErrorCode LinkLayerController::QosSetup(uint16_t handle, uint8_t service_type,
+                                        uint32_t /* token_rate */,
+                                        uint32_t /* peak_bandwidth */,
+                                        uint32_t /* latency */,
+                                        uint32_t /* delay_variation */) {
+  if (!connections_.HasHandle(handle)) {
+    return ErrorCode::UNKNOWN_CONNECTION;
+  }
+
+  if (service_type > 0x02) {
+    return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
+  }
+
+  // TODO: implement real logic
+  return ErrorCode::COMMAND_DISALLOWED;
+}
+
+ErrorCode LinkLayerController::SwitchRole(Address /* bd_addr */,
+                                          uint8_t /* role */) {
+  // TODO: implement real logic
+  return ErrorCode::COMMAND_DISALLOWED;
+}
+
+ErrorCode LinkLayerController::WriteLinkPolicySettings(uint16_t handle,
+                                                       uint16_t) {
+  if (!connections_.HasHandle(handle)) {
+    return ErrorCode::UNKNOWN_CONNECTION;
+  }
+  return ErrorCode::SUCCESS;
+}
+
+ErrorCode LinkLayerController::FlowSpecification(
+    uint16_t handle, uint8_t flow_direction, uint8_t service_type,
+    uint32_t /* token_rate */, uint32_t /* token_bucket_size */,
+    uint32_t /* peak_bandwidth */, uint32_t /* access_latency */) {
+  if (!connections_.HasHandle(handle)) {
+    return ErrorCode::UNKNOWN_CONNECTION;
+  }
+
+  if (flow_direction > 0x01 || service_type > 0x02) {
+    return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
+  }
+
+  // TODO: implement real logic
+  return ErrorCode::COMMAND_DISALLOWED;
+}
+
+ErrorCode LinkLayerController::WriteLinkSupervisionTimeout(uint16_t handle,
+                                                           uint16_t) {
+  if (!connections_.HasHandle(handle)) {
+    return ErrorCode::UNKNOWN_CONNECTION;
+  }
+  return ErrorCode::SUCCESS;
+}
+
+void LinkLayerController::LeWhiteListClear() { le_white_list_.clear(); }
+
+void LinkLayerController::LeResolvingListClear() { le_resolving_list_.clear(); }
+
+void LinkLayerController::LeWhiteListAddDevice(Address addr,
+                                               uint8_t addr_type) {
   std::tuple<Address, uint8_t> new_tuple = std::make_tuple(addr, addr_type);
   for (auto dev : le_white_list_) {
     if (dev == new_tuple) {
@@ -1040,7 +1661,32 @@
   le_white_list_.emplace_back(new_tuple);
 }
 
-void LinkLayerController::LeWhiteListRemoveDevice(Address addr, uint8_t addr_type) {
+void LinkLayerController::LeResolvingListAddDevice(
+    Address addr, uint8_t addr_type, std::array<uint8_t, kIrk_size> peerIrk,
+    std::array<uint8_t, kIrk_size> localIrk) {
+  std::tuple<Address, uint8_t, std::array<uint8_t, kIrk_size>,
+             std::array<uint8_t, kIrk_size>>
+      new_tuple = std::make_tuple(addr, addr_type, peerIrk, localIrk);
+  for (size_t i = 0; i < le_white_list_.size(); i++) {
+    auto curr = le_white_list_[i];
+    if (std::get<0>(curr) == addr && std::get<1>(curr) == addr_type) {
+      le_resolving_list_[i] = new_tuple;
+      return;
+    }
+  }
+  le_resolving_list_.emplace_back(new_tuple);
+}
+
+void LinkLayerController::LeSetPrivacyMode(uint8_t address_type, Address addr,
+                                           uint8_t mode) {
+  // set mode for addr
+  LOG_INFO("address type = %d ", address_type);
+  LOG_INFO("address = %s ", addr.ToString().c_str());
+  LOG_INFO("mode = %d ", mode);
+}
+
+void LinkLayerController::LeWhiteListRemoveDevice(Address addr,
+                                                  uint8_t addr_type) {
   // TODO: Add checks to see if advertising, scanning, or a connection request
   // with the white list is ongoing.
   std::tuple<Address, uint8_t> erase_tuple = std::make_tuple(addr, addr_type);
@@ -1051,7 +1697,20 @@
   }
 }
 
-bool LinkLayerController::LeWhiteListContainsDevice(Address addr, uint8_t addr_type) {
+void LinkLayerController::LeResolvingListRemoveDevice(Address addr,
+                                                      uint8_t addr_type) {
+  // TODO: Add checks to see if advertising, scanning, or a connection request
+  // with the white list is ongoing.
+  for (size_t i = 0; i < le_white_list_.size(); i++) {
+    auto curr = le_white_list_[i];
+    if (std::get<0>(curr) == addr && std::get<1>(curr) == addr_type) {
+      le_resolving_list_.erase(le_resolving_list_.begin() + i);
+    }
+  }
+}
+
+bool LinkLayerController::LeWhiteListContainsDevice(Address addr,
+                                                    uint8_t addr_type) {
   std::tuple<Address, uint8_t> sought_tuple = std::make_tuple(addr, addr_type);
   for (size_t i = 0; i < le_white_list_.size(); i++) {
     if (le_white_list_[i] == sought_tuple) {
@@ -1061,44 +1720,60 @@
   return false;
 }
 
+bool LinkLayerController::LeResolvingListContainsDevice(Address addr,
+                                                        uint8_t addr_type) {
+  for (size_t i = 0; i < le_white_list_.size(); i++) {
+    auto curr = le_white_list_[i];
+    if (std::get<0>(curr) == addr && std::get<1>(curr) == addr_type) {
+      return true;
+    }
+  }
+  return false;
+}
+
 bool LinkLayerController::LeWhiteListFull() {
   return le_white_list_.size() >= properties_.GetLeWhiteListSize();
 }
 
+bool LinkLayerController::LeResolvingListFull() {
+  return le_resolving_list_.size() >= properties_.GetLeResolvingListSize();
+}
+
 void LinkLayerController::Reset() {
   inquiry_state_ = Inquiry::InquiryState::STANDBY;
   last_inquiry_ = steady_clock::now();
-  le_scan_enable_ = 0;
+  le_scan_enable_ = bluetooth::hci::OpCode::NONE;
+  le_advertising_enable_ = 0;
   le_connect_ = 0;
 }
 
 void LinkLayerController::PageScan() {}
 
 void LinkLayerController::StartInquiry(milliseconds timeout) {
-  schedule_task_(milliseconds(timeout), [this]() { LinkLayerController::InquiryTimeout(); });
+  ScheduleTask(milliseconds(timeout),
+               [this]() { LinkLayerController::InquiryTimeout(); });
   inquiry_state_ = Inquiry::InquiryState::INQUIRY;
-  LOG_INFO(LOG_TAG, "InquiryState = %d ", static_cast<int>(inquiry_state_));
 }
 
 void LinkLayerController::InquiryCancel() {
-  CHECK(inquiry_state_ == Inquiry::InquiryState::INQUIRY);
+  ASSERT(inquiry_state_ == Inquiry::InquiryState::INQUIRY);
   inquiry_state_ = Inquiry::InquiryState::STANDBY;
 }
 
 void LinkLayerController::InquiryTimeout() {
   if (inquiry_state_ == Inquiry::InquiryState::INQUIRY) {
     inquiry_state_ = Inquiry::InquiryState::STANDBY;
-    send_event_(EventPacketBuilder::CreateInquiryCompleteEvent(hci::Status::SUCCESS)->ToVector());
+    auto packet =
+        bluetooth::hci::InquiryCompleteBuilder::Create(ErrorCode::SUCCESS);
+    send_event_(std::move(packet));
   }
 }
 
 void LinkLayerController::SetInquiryMode(uint8_t mode) {
-  inquiry_mode_ = static_cast<Inquiry::InquiryType>(mode);
+  inquiry_mode_ = static_cast<model::packets::InquiryType>(mode);
 }
 
-void LinkLayerController::SetInquiryLAP(uint64_t lap) {
-  inquiry_lap_ = lap;
-}
+void LinkLayerController::SetInquiryLAP(uint64_t lap) { inquiry_lap_ = lap; }
 
 void LinkLayerController::SetInquiryMaxResponses(uint8_t max) {
   inquiry_max_responses_ = max;
@@ -1109,11 +1784,10 @@
   if (duration_cast<milliseconds>(now - last_inquiry_) < milliseconds(2000)) {
     return;
   }
-  LOG_INFO(LOG_TAG, "Inquiry ");
-  std::unique_ptr<InquiryBuilder> inquiry = InquiryBuilder::Create(inquiry_mode_);
-  std::shared_ptr<LinkLayerPacketBuilder> to_send =
-      LinkLayerPacketBuilder::WrapInquiry(std::move(inquiry), properties_.GetAddress());
-  SendLinkLayerPacket(to_send);
+
+  auto packet = model::packets::InquiryBuilder::Create(
+      properties_.GetAddress(), Address::kEmpty, inquiry_mode_);
+  SendLinkLayerPacket(std::move(packet));
   last_inquiry_ = now;
 }
 
@@ -1125,18 +1799,4 @@
   page_scans_enabled_ = enable;
 }
 
-/* TODO: Connection handling
-  // TODO: Handle in the link manager.
-  uint16_t handle = LeGetHandle();
-
-  std::shared_ptr<Connection> new_connection =
-      std::make_shared<Connection>(peer, handle);
-  connections_.push_back(new_connection);
-  peer->SetConnection(new_connection);
-
-  send_event_(EventPacketBuilder::CreateLeEnhancedConnectionCompleteEvent(
-      hci::Status::SUCCESS, handle, 0x00,  // role
-      le_peer_address_type_, le_peer_address_, Address::kEmpty,
-  Address::kEmpty, 0x0024, 0x0000, 0x01f4)->ToVector());
-*/
 }  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/model/controller/link_layer_controller.h b/vendor_libs/test_vendor_lib/model/controller/link_layer_controller.h
index 5cec196..738865d 100644
--- a/vendor_libs/test_vendor_lib/model/controller/link_layer_controller.h
+++ b/vendor_libs/test_vendor_lib/model/controller/link_layer_controller.h
@@ -17,77 +17,102 @@
 #pragma once
 
 #include "acl_connection_handler.h"
+#include "hci/address.h"
+#include "hci/hci_packets.h"
 #include "include/hci.h"
 #include "include/inquiry.h"
-#include "include/link.h"
 #include "include/phy.h"
 #include "model/devices/device_properties.h"
 #include "model/setup/async_manager.h"
-#include "packets/hci/acl_packet_view.h"
-#include "packets/hci/sco_packet_view.h"
-#include "packets/link_layer/link_layer_packet_builder.h"
-#include "packets/link_layer/link_layer_packet_view.h"
+#include "packets/link_layer_packets.h"
 #include "security_manager.h"
-#include "types/address.h"
 
 namespace test_vendor_lib {
 
+using ::bluetooth::hci::Address;
+using ::bluetooth::hci::ErrorCode;
+using ::bluetooth::hci::OpCode;
+
 class LinkLayerController {
  public:
+  static constexpr size_t kIrk_size = 16;
+
   LinkLayerController(const DeviceProperties& properties) : properties_(properties) {}
-  hci::Status SendCommandToRemoteByAddress(hci::OpCode opcode, packets::PacketView<true> args, const Address& remote,
-                                           bool use_public_address);
-  hci::Status SendCommandToRemoteByHandle(hci::OpCode opcode, packets::PacketView<true> args, uint16_t handle);
-  hci::Status SendScoToRemote(packets::ScoPacketView sco_packet);
-  hci::Status SendAclToRemote(packets::AclPacketView acl_packet);
+  ErrorCode SendCommandToRemoteByAddress(
+      OpCode opcode, bluetooth::packet::PacketView<true> args,
+      const Address& remote);
+  ErrorCode SendCommandToRemoteByHandle(
+      OpCode opcode, bluetooth::packet::PacketView<true> args, uint16_t handle);
+  ErrorCode SendScoToRemote(bluetooth::hci::ScoPacketView sco_packet);
+  ErrorCode SendAclToRemote(bluetooth::hci::AclPacketView acl_packet);
 
   void WriteSimplePairingMode(bool enabled);
   void StartSimplePairing(const Address& address);
   void AuthenticateRemoteStage1(const Address& address, PairingType pairing_type);
   void AuthenticateRemoteStage2(const Address& address);
-  hci::Status LinkKeyRequestReply(const Address& address, packets::PacketView<true> key);
-  hci::Status LinkKeyRequestNegativeReply(const Address& address);
-  hci::Status IoCapabilityRequestReply(const Address& peer, uint8_t io_capability, uint8_t oob_data_present_flag,
-                                       uint8_t authentication_requirements);
-  hci::Status IoCapabilityRequestNegativeReply(const Address& peer, hci::Status reason);
-  hci::Status UserConfirmationRequestReply(const Address& peer);
-  hci::Status UserConfirmationRequestNegativeReply(const Address& peer);
-  hci::Status UserPasskeyRequestReply(const Address& peer, uint32_t numeric_value);
-  hci::Status UserPasskeyRequestNegativeReply(const Address& peer);
-  hci::Status RemoteOobDataRequestReply(const Address& peer, const std::vector<uint8_t>& c,
-                                        const std::vector<uint8_t>& r);
-  hci::Status RemoteOobDataRequestNegativeReply(const Address& peer);
+  ErrorCode LinkKeyRequestReply(const Address& address,
+                                const std::array<uint8_t, 16>& key);
+  ErrorCode LinkKeyRequestNegativeReply(const Address& address);
+  ErrorCode IoCapabilityRequestReply(const Address& peer, uint8_t io_capability,
+                                     uint8_t oob_data_present_flag,
+                                     uint8_t authentication_requirements);
+  ErrorCode IoCapabilityRequestNegativeReply(const Address& peer,
+                                             ErrorCode reason);
+  ErrorCode UserConfirmationRequestReply(const Address& peer);
+  ErrorCode UserConfirmationRequestNegativeReply(const Address& peer);
+  ErrorCode UserPasskeyRequestReply(const Address& peer,
+                                    uint32_t numeric_value);
+  ErrorCode UserPasskeyRequestNegativeReply(const Address& peer);
+  ErrorCode RemoteOobDataRequestReply(const Address& peer,
+                                      const std::vector<uint8_t>& c,
+                                      const std::vector<uint8_t>& r);
+  ErrorCode RemoteOobDataRequestNegativeReply(const Address& peer);
   void HandleSetConnectionEncryption(const Address& address, uint16_t handle, uint8_t encryption_enable);
-  hci::Status SetConnectionEncryption(uint16_t handle, uint8_t encryption_enable);
+  ErrorCode SetConnectionEncryption(uint16_t handle, uint8_t encryption_enable);
   void HandleAuthenticationRequest(const Address& address, uint16_t handle);
-  hci::Status AuthenticationRequested(uint16_t handle);
+  ErrorCode AuthenticationRequested(uint16_t handle);
 
-  hci::Status AcceptConnectionRequest(const Address& addr, bool try_role_switch);
+  ErrorCode AcceptConnectionRequest(const Address& addr, bool try_role_switch);
   void MakeSlaveConnection(const Address& addr, bool try_role_switch);
-  hci::Status RejectConnectionRequest(const Address& addr, uint8_t reason);
+  ErrorCode RejectConnectionRequest(const Address& addr, uint8_t reason);
   void RejectSlaveConnection(const Address& addr, uint8_t reason);
-  hci::Status CreateConnection(const Address& addr, uint16_t packet_type, uint8_t page_scan_mode, uint16_t clock_offset,
-                               uint8_t allow_role_switch);
-  hci::Status CreateConnectionCancel(const Address& addr);
-  hci::Status Disconnect(uint16_t handle, uint8_t reason);
+  ErrorCode CreateConnection(const Address& addr, uint16_t packet_type,
+                             uint8_t page_scan_mode, uint16_t clock_offset,
+                             uint8_t allow_role_switch);
+  ErrorCode CreateConnectionCancel(const Address& addr);
+  ErrorCode Disconnect(uint16_t handle, uint8_t reason);
 
  private:
   void DisconnectCleanup(uint16_t handle, uint8_t reason);
 
  public:
-  void IncomingPacket(packets::LinkLayerPacketView incoming);
+  void IncomingPacket(model::packets::LinkLayerPacketView incoming);
 
   void TimerTick();
 
-  // Set the callbacks for sending packets to the HCI.
-  void RegisterEventChannel(const std::function<void(std::shared_ptr<std::vector<uint8_t>>)>& send_event);
+  AsyncTaskId ScheduleTask(std::chrono::milliseconds delay_ms, const TaskCallback& task);
 
-  void RegisterAclChannel(const std::function<void(std::shared_ptr<std::vector<uint8_t>>)>& send_acl);
+  void CancelScheduledTask(AsyncTaskId task);
+
+  // Set the callbacks for sending packets to the HCI.
+  void RegisterEventChannel(
+      const std::function<void(
+          std::shared_ptr<bluetooth::hci::EventPacketBuilder>)>& send_event);
+
+  void RegisterAclChannel(
+      const std::function<
+          void(std::shared_ptr<bluetooth::hci::AclPacketBuilder>)>& send_acl);
 
   void RegisterScoChannel(const std::function<void(std::shared_ptr<std::vector<uint8_t>>)>& send_sco);
 
+  void RegisterIsoChannel(
+      const std::function<void(std::shared_ptr<std::vector<uint8_t>>)>&
+          send_iso);
+
   void RegisterRemoteChannel(
-      const std::function<void(std::shared_ptr<packets::LinkLayerPacketBuilder>, Phy::Type)>& send_to_remote);
+      const std::function<void(
+          std::shared_ptr<model::packets::LinkLayerPacketBuilder>, Phy::Type)>&
+          send_to_remote);
 
   // Set the callbacks for scheduling tasks.
   void RegisterTaskScheduler(
@@ -104,14 +129,35 @@
   void PageScan();
   void Connections();
 
+  void LeAdvertising();
+
+  void HandleLeConnection(AddressWithType addr, AddressWithType own_addr,
+                          uint8_t role, uint16_t connection_interval,
+                          uint16_t connection_latency,
+                          uint16_t supervision_timeout);
+
   void LeWhiteListClear();
   void LeWhiteListAddDevice(Address addr, uint8_t addr_type);
   void LeWhiteListRemoveDevice(Address addr, uint8_t addr_type);
   bool LeWhiteListContainsDevice(Address addr, uint8_t addr_type);
   bool LeWhiteListFull();
+  void LeResolvingListClear();
+  void LeResolvingListAddDevice(Address addr, uint8_t addr_type,
+                                std::array<uint8_t, kIrk_size> peerIrk,
+                                std::array<uint8_t, kIrk_size> localIrk);
+  void LeResolvingListRemoveDevice(Address addr, uint8_t addr_type);
+  bool LeResolvingListContainsDevice(Address addr, uint8_t addr_type);
+  bool LeResolvingListFull();
+  void LeSetPrivacyMode(uint8_t address_type, Address addr, uint8_t mode);
 
-  void SetLeScanEnable(uint8_t le_scan_enable) {
-    le_scan_enable_ = le_scan_enable;
+  ErrorCode SetLeAdvertisingEnable(uint8_t le_advertising_enable) {
+    le_advertising_enable_ = le_advertising_enable;
+    // TODO: Check properties and return errors
+    return ErrorCode::SUCCESS;
+  }
+
+  void SetLeScanEnable(bluetooth::hci::OpCode enabling_opcode) {
+    le_scan_enable_ = enabling_opcode;
   }
   void SetLeScanType(uint8_t le_scan_type) {
     le_scan_type_ = le_scan_type;
@@ -131,9 +177,9 @@
   void SetLeAddressType(uint8_t le_address_type) {
     le_address_type_ = le_address_type;
   }
-  hci::Status SetLeConnect(bool le_connect) {
+  ErrorCode SetLeConnect(bool le_connect) {
     le_connect_ = le_connect;
-    return hci::Status::SUCCESS;
+    return ErrorCode::SUCCESS;
   }
   void SetLeConnectionIntervalMin(uint16_t min) {
     le_connection_interval_min_ = min;
@@ -175,37 +221,86 @@
   void SetInquiryScanEnable(bool enable);
   void SetPageScanEnable(bool enable);
 
-  hci::Status ChangeConnectionPacketType(uint16_t handle, uint16_t types);
-  hci::Status WriteLinkPolicySettings(uint16_t handle, uint16_t settings);
-  hci::Status WriteLinkSupervisionTimeout(uint16_t handle, uint16_t timeout);
+  ErrorCode ChangeConnectionPacketType(uint16_t handle, uint16_t types);
+  ErrorCode ChangeConnectionLinkKey(uint16_t handle);
+  ErrorCode MasterLinkKey(uint8_t key_flag);
+  ErrorCode HoldMode(uint16_t handle, uint16_t hold_mode_max_interval,
+                     uint16_t hold_mode_min_interval);
+  ErrorCode SniffMode(uint16_t handle, uint16_t sniff_max_interval,
+                      uint16_t sniff_min_interval, uint16_t sniff_attempt,
+                      uint16_t sniff_timeout);
+  ErrorCode ExitSniffMode(uint16_t handle);
+  ErrorCode QosSetup(uint16_t handle, uint8_t service_type, uint32_t token_rate,
+                     uint32_t peak_bandwidth, uint32_t latency,
+                     uint32_t delay_variation);
+  ErrorCode SwitchRole(Address bd_addr, uint8_t role);
+  ErrorCode WriteLinkPolicySettings(uint16_t handle, uint16_t settings);
+  ErrorCode FlowSpecification(uint16_t handle, uint8_t flow_direction,
+                              uint8_t service_type, uint32_t token_rate,
+                              uint32_t token_bucket_size,
+                              uint32_t peak_bandwidth, uint32_t access_latency);
+  ErrorCode WriteLinkSupervisionTimeout(uint16_t handle, uint16_t timeout);
 
  protected:
-  void SendLELinkLayerPacket(std::shared_ptr<packets::LinkLayerPacketBuilder> packet);
-  void SendLinkLayerPacket(std::shared_ptr<packets::LinkLayerPacketBuilder> packet);
-  void IncomingAclPacket(packets::LinkLayerPacketView packet);
-  void IncomingAclAckPacket(packets::LinkLayerPacketView packet);
-  void IncomingCommandPacket(packets::LinkLayerPacketView packet);
-  void IncomingCreateConnectionPacket(packets::LinkLayerPacketView packet);
-  void IncomingDisconnectPacket(packets::LinkLayerPacketView packet);
-  void IncomingEncryptConnection(packets::LinkLayerPacketView packet);
-  void IncomingEncryptConnectionResponse(packets::LinkLayerPacketView packet);
-  void IncomingInquiryPacket(packets::LinkLayerPacketView packet);
-  void IncomingInquiryResponsePacket(packets::LinkLayerPacketView packet);
-  void IncomingIoCapabilityRequestPacket(packets::LinkLayerPacketView packet);
-  void IncomingIoCapabilityResponsePacket(packets::LinkLayerPacketView packet);
-  void IncomingIoCapabilityNegativeResponsePacket(packets::LinkLayerPacketView packet);
-  void IncomingLeAdvertisementPacket(packets::LinkLayerPacketView packet);
-  void IncomingLeScanPacket(packets::LinkLayerPacketView packet);
-  void IncomingLeScanResponsePacket(packets::LinkLayerPacketView packet);
-  void IncomingPagePacket(packets::LinkLayerPacketView packet);
-  void IncomingPageResponsePacket(packets::LinkLayerPacketView packet);
-  void IncomingResponsePacket(packets::LinkLayerPacketView packet);
+  void SendLeLinkLayerPacket(
+      std::unique_ptr<model::packets::LinkLayerPacketBuilder> packet);
+  void SendLinkLayerPacket(
+      std::unique_ptr<model::packets::LinkLayerPacketBuilder> packet);
+  void IncomingAclPacket(model::packets::LinkLayerPacketView packet);
+  void IncomingAclAckPacket(model::packets::LinkLayerPacketView packet);
+  void IncomingCreateConnectionPacket(
+      model::packets::LinkLayerPacketView packet);
+  void IncomingDisconnectPacket(model::packets::LinkLayerPacketView packet);
+  void IncomingEncryptConnection(model::packets::LinkLayerPacketView packet);
+  void IncomingEncryptConnectionResponse(
+      model::packets::LinkLayerPacketView packet);
+  void IncomingInquiryPacket(model::packets::LinkLayerPacketView packet);
+  void IncomingInquiryResponsePacket(
+      model::packets::LinkLayerPacketView packet);
+  void IncomingIoCapabilityRequestPacket(
+      model::packets::LinkLayerPacketView packet);
+  void IncomingIoCapabilityResponsePacket(
+      model::packets::LinkLayerPacketView packet);
+  void IncomingIoCapabilityNegativeResponsePacket(
+      model::packets::LinkLayerPacketView packet);
+  void IncomingLeAdvertisementPacket(
+      model::packets::LinkLayerPacketView packet);
+  void IncomingLeConnectPacket(model::packets::LinkLayerPacketView packet);
+  void IncomingLeConnectCompletePacket(
+      model::packets::LinkLayerPacketView packet);
+  void IncomingLeScanPacket(model::packets::LinkLayerPacketView packet);
+  void IncomingLeScanResponsePacket(model::packets::LinkLayerPacketView packet);
+  void IncomingPagePacket(model::packets::LinkLayerPacketView packet);
+  void IncomingPageRejectPacket(model::packets::LinkLayerPacketView packet);
+  void IncomingPageResponsePacket(model::packets::LinkLayerPacketView packet);
+  void IncomingReadRemoteLmpFeatures(
+      model::packets::LinkLayerPacketView packet);
+  void IncomingReadRemoteLmpFeaturesResponse(
+      model::packets::LinkLayerPacketView packet);
+  void IncomingReadRemoteSupportedFeatures(
+      model::packets::LinkLayerPacketView packet);
+  void IncomingReadRemoteSupportedFeaturesResponse(
+      model::packets::LinkLayerPacketView packet);
+  void IncomingReadRemoteExtendedFeatures(
+      model::packets::LinkLayerPacketView packet);
+  void IncomingReadRemoteExtendedFeaturesResponse(
+      model::packets::LinkLayerPacketView packet);
+  void IncomingReadRemoteVersion(model::packets::LinkLayerPacketView packet);
+  void IncomingReadRemoteVersionResponse(
+      model::packets::LinkLayerPacketView packet);
+  void IncomingReadClockOffset(model::packets::LinkLayerPacketView packet);
+  void IncomingReadClockOffsetResponse(
+      model::packets::LinkLayerPacketView packet);
+  void IncomingRemoteNameRequest(model::packets::LinkLayerPacketView packet);
+  void IncomingRemoteNameRequestResponse(
+      model::packets::LinkLayerPacketView packet);
 
  private:
   const DeviceProperties& properties_;
-  AclConnectionHandler classic_connections_;
+  AclConnectionHandler connections_;
   // Add timestamps?
-  std::vector<std::shared_ptr<packets::LinkLayerPacketBuilder>> commands_awaiting_responses_;
+  std::vector<std::shared_ptr<model::packets::LinkLayerPacketBuilder>>
+      commands_awaiting_responses_;
 
   // Timing related state
   std::vector<AsyncTaskId> controller_events_;
@@ -219,19 +314,30 @@
   std::function<void(AsyncTaskId)> cancel_task_;
 
   // Callbacks to send packets back to the HCI.
-  std::function<void(std::shared_ptr<std::vector<uint8_t>>)> send_acl_;
-  std::function<void(std::shared_ptr<std::vector<uint8_t>>)> send_event_;
+  std::function<void(std::shared_ptr<bluetooth::hci::AclPacketBuilder>)>
+      send_acl_;
+  std::function<void(std::shared_ptr<bluetooth::hci::EventPacketBuilder>)>
+      send_event_;
   std::function<void(std::shared_ptr<std::vector<uint8_t>>)> send_sco_;
+  std::function<void(std::shared_ptr<std::vector<uint8_t>>)> send_iso_;
 
   // Callback to send packets to remote devices.
-  std::function<void(std::shared_ptr<packets::LinkLayerPacketBuilder>, Phy::Type phy_type)> send_to_remote_;
+  std::function<void(std::shared_ptr<model::packets::LinkLayerPacketBuilder>,
+                     Phy::Type phy_type)>
+      send_to_remote_;
 
   // LE state
   std::vector<uint8_t> le_event_mask_;
 
   std::vector<std::tuple<Address, uint8_t>> le_white_list_;
+  std::vector<std::tuple<Address, uint8_t, std::array<uint8_t, kIrk_size>,
+                         std::array<uint8_t, kIrk_size>>>
+      le_resolving_list_;
 
-  uint8_t le_scan_enable_;
+  uint8_t le_advertising_enable_{false};
+  std::chrono::steady_clock::time_point last_le_advertisement_;
+
+  bluetooth::hci::OpCode le_scan_enable_{bluetooth::hci::OpCode::NONE};
   uint8_t le_scan_type_;
   uint16_t le_scan_interval_;
   uint16_t le_scan_window_;
@@ -239,7 +345,7 @@
   uint8_t le_scan_filter_duplicates_;
   uint8_t le_address_type_;
 
-  bool le_connect_;
+  bool le_connect_{false};
   uint16_t le_connection_interval_min_;
   uint16_t le_connection_interval_max_;
   uint16_t le_connection_latency_;
@@ -255,7 +361,7 @@
 
   SecurityManager security_manager_{10};
   std::chrono::steady_clock::time_point last_inquiry_;
-  Inquiry::InquiryType inquiry_mode_;
+  model::packets::InquiryType inquiry_mode_;
   Inquiry::InquiryState inquiry_state_;
   uint64_t inquiry_lap_;
   uint8_t inquiry_max_responses_;
diff --git a/vendor_libs/test_vendor_lib/model/controller/security_manager.cc b/vendor_libs/test_vendor_lib/model/controller/security_manager.cc
index 6ab1351..38b9e64 100644
--- a/vendor_libs/test_vendor_lib/model/controller/security_manager.cc
+++ b/vendor_libs/test_vendor_lib/model/controller/security_manager.cc
@@ -14,11 +14,9 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "security_manager"
-
 #include "security_manager.h"
 
-#include "base/logging.h"
+#include "os/log.h"
 
 using std::vector;
 
@@ -46,7 +44,8 @@
   return key_store_.count(addr.ToString());
 }
 
-uint16_t SecurityManager::WriteKey(const Address& addr, const std::vector<uint8_t>& key) {
+uint16_t SecurityManager::WriteKey(const Address& addr,
+                                   const std::array<uint8_t, 16>& key) {
   if (key_store_.size() >= max_keys_) {
     return 0;
   }
@@ -54,8 +53,9 @@
   return 1;
 }
 
-const std::vector<uint8_t>& SecurityManager::GetKey(const Address& addr) const {
-  CHECK(ReadKey(addr)) << "No such key";
+const std::array<uint8_t, 16>& SecurityManager::GetKey(
+    const Address& addr) const {
+  ASSERT_LOG(ReadKey(addr), "No such key");
   return key_store_.at(addr.ToString());
 }
 
@@ -83,7 +83,7 @@
 
 void SecurityManager::SetPeerIoCapability(const Address& addr, uint8_t io_capability, uint8_t oob_present_flag,
                                           uint8_t authentication_requirements) {
-  CHECK_EQ(addr, peer_address_);
+  ASSERT(addr == peer_address_);
   peer_capabilities_valid_ = true;
   if (io_capability <= static_cast<uint8_t>(IoCapabilityType::NO_INPUT_NO_OUTPUT)) {
     peer_io_capability_ = static_cast<IoCapabilityType>(io_capability);
@@ -102,12 +102,12 @@
 
 void SecurityManager::SetLocalIoCapability(const Address& peer, uint8_t io_capability, uint8_t oob_present_flag,
                                            uint8_t authentication_requirements) {
-  CHECK_EQ(peer, peer_address_);
-  CHECK(io_capability <= static_cast<uint8_t>(IoCapabilityType::NO_INPUT_NO_OUTPUT))
-      << "io_capability = " << io_capability;
-  CHECK(oob_present_flag <= 1) << "oob_present_flag = " << oob_present_flag;
-  CHECK(authentication_requirements <= static_cast<uint8_t>(AuthenticationType::GENERAL_BONDING_MITM))
-      << "authentication_requirements = " << authentication_requirements;
+  ASSERT(peer == peer_address_);
+  ASSERT_LOG(io_capability <= static_cast<uint8_t>(IoCapabilityType::NO_INPUT_NO_OUTPUT), "io_capability = %d",
+             static_cast<int>(io_capability));
+  ASSERT_LOG(oob_present_flag <= 1, "oob_present_flag = %hhx ", oob_present_flag);
+  ASSERT_LOG(authentication_requirements <= static_cast<uint8_t>(AuthenticationType::GENERAL_BONDING_MITM),
+             "authentication_requirements = %d", static_cast<int>(authentication_requirements));
   host_io_capability_ = static_cast<IoCapabilityType>(io_capability);
   host_oob_present_flag_ = (oob_present_flag == 1);
   host_authentication_requirements_ = static_cast<AuthenticationType>(authentication_requirements);
@@ -132,7 +132,53 @@
   if (!(peer_requires_mitm || host_requires_mitm)) {
     return PairingType::AUTO_CONFIRMATION;
   }
-  return PairingType::INVALID;
+  LOG_INFO("%s: host does%s require peer does%s require MITM",
+           peer_address_.ToString().c_str(), host_requires_mitm ? "" : "n't",
+           peer_requires_mitm ? "" : "n't");
+  switch (peer_io_capability_) {
+    case IoCapabilityType::DISPLAY_ONLY:
+      switch (host_io_capability_) {
+        case IoCapabilityType::DISPLAY_ONLY:
+        case IoCapabilityType::DISPLAY_YES_NO:
+          return PairingType::AUTO_CONFIRMATION;
+        case IoCapabilityType::KEYBOARD_ONLY:
+          return PairingType::INPUT_PIN;
+        case IoCapabilityType::NO_INPUT_NO_OUTPUT:
+          return PairingType::AUTO_CONFIRMATION;
+        default:
+          return PairingType::INVALID;
+      }
+    case IoCapabilityType::DISPLAY_YES_NO:
+      switch (host_io_capability_) {
+        case IoCapabilityType::DISPLAY_ONLY:
+          return PairingType::AUTO_CONFIRMATION;
+        case IoCapabilityType::DISPLAY_YES_NO:
+          return PairingType::DISPLAY_AND_CONFIRM;
+        case IoCapabilityType::KEYBOARD_ONLY:
+          return PairingType::DISPLAY_PIN;
+        case IoCapabilityType::NO_INPUT_NO_OUTPUT:
+          return PairingType::AUTO_CONFIRMATION;
+        default:
+          return PairingType::INVALID;
+      }
+    case IoCapabilityType::KEYBOARD_ONLY:
+      switch (host_io_capability_) {
+        case IoCapabilityType::DISPLAY_ONLY:
+          return PairingType::DISPLAY_PIN;
+        case IoCapabilityType::DISPLAY_YES_NO:
+          return PairingType::DISPLAY_PIN;
+        case IoCapabilityType::KEYBOARD_ONLY:
+          return PairingType::INPUT_PIN;
+        case IoCapabilityType::NO_INPUT_NO_OUTPUT:
+          return PairingType::AUTO_CONFIRMATION;
+        default:
+          return PairingType::INVALID;
+      }
+    case IoCapabilityType::NO_INPUT_NO_OUTPUT:
+      return PairingType::AUTO_CONFIRMATION;
+    default:
+      return PairingType::INVALID;
+  }
 }
 
 }  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/model/controller/security_manager.h b/vendor_libs/test_vendor_lib/model/controller/security_manager.h
index 94284cb..e77f1d1 100644
--- a/vendor_libs/test_vendor_lib/model/controller/security_manager.h
+++ b/vendor_libs/test_vendor_lib/model/controller/security_manager.h
@@ -16,15 +16,17 @@
 
 #pragma once
 
+#include <array>
 #include <cstdint>
 #include <string>
 #include <unordered_map>
-#include <vector>
 
-#include "types/address.h"
+#include "hci/address.h"
 
 namespace test_vendor_lib {
 
+using ::bluetooth::hci::Address;
+
 enum class PairingType : uint8_t {
   AUTO_CONFIRMATION,
   CONFIRM_Y_N,
@@ -62,12 +64,10 @@
   uint16_t DeleteKey(const Address& addr);
   uint16_t ReadAllKeys() const;
   uint16_t ReadKey(const Address& addr) const;
-  uint16_t WriteKey(const Address& addr, const std::vector<uint8_t>& key);
-  uint16_t ReadCapacity() const {
-    return max_keys_;
-  };
+  uint16_t WriteKey(const Address& addr, const std::array<uint8_t, 16>& key);
+  uint16_t ReadCapacity() const { return max_keys_; };
 
-  const std::vector<uint8_t>& GetKey(const Address& addr) const;
+  const std::array<uint8_t, 16>& GetKey(const Address& addr) const;
 
   void AuthenticationRequest(const Address& addr, uint16_t handle);
   void AuthenticationRequestFinished();
@@ -87,16 +87,16 @@
 
  private:
   uint16_t max_keys_;
-  std::unordered_map<std::string, std::vector<uint8_t>> key_store_;
+  std::unordered_map<std::string, std::array<uint8_t, 16>> key_store_;
 
   bool peer_capabilities_valid_{false};
   IoCapabilityType peer_io_capability_;
-  bool peer_oob_present_flag_;
+  bool peer_oob_present_flag_{false};
   AuthenticationType peer_authentication_requirements_;
 
   bool host_capabilities_valid_{false};
   IoCapabilityType host_io_capability_;
-  bool host_oob_present_flag_;
+  bool host_oob_present_flag_{false};
   AuthenticationType host_authentication_requirements_;
 
   bool authenticating_{false};
diff --git a/vendor_libs/test_vendor_lib/model/devices/beacon.cc b/vendor_libs/test_vendor_lib/model/devices/beacon.cc
index 10ebf5c..96f6099 100644
--- a/vendor_libs/test_vendor_lib/model/devices/beacon.cc
+++ b/vendor_libs/test_vendor_lib/model/devices/beacon.cc
@@ -14,31 +14,27 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "beacon"
-
 #include "beacon.h"
 
-#include "le_advertisement.h"
 #include "model/setup/device_boutique.h"
-#include "osi/include/log.h"
 
 using std::vector;
 
 namespace test_vendor_lib {
 
-bool Beacon::registered_ = DeviceBoutique::Register(LOG_TAG, &Beacon::Create);
+bool Beacon::registered_ = DeviceBoutique::Register("beacon", &Beacon::Create);
 
 Beacon::Beacon() {
   advertising_interval_ms_ = std::chrono::milliseconds(1280);
-  properties_.SetLeAdvertisementType(BTM_BLE_NON_CONNECT_EVT);
+  properties_.SetLeAdvertisementType(0x03 /* NON_CONNECT */);
   properties_.SetLeAdvertisement({0x0F,  // Length
-                                  BTM_BLE_AD_TYPE_NAME_CMPL, 'g', 'D', 'e', 'v', 'i', 'c', 'e', '-', 'b', 'e', 'a', 'c',
+                                  0x09 /* TYPE_NAME_CMPL */, 'g', 'D', 'e', 'v', 'i', 'c', 'e', '-', 'b', 'e', 'a', 'c',
                                   'o', 'n',
                                   0x02,  // Length
-                                  BTM_BLE_AD_TYPE_FLAG, BTM_BLE_BREDR_NOT_SPT | BTM_BLE_GEN_DISC_FLAG});
+                                  0x01 /* TYPE_FLAG */, 0x4 /* BREDR_NOT_SPT */ | 0x2 /* GEN_DISC_FLAG */});
 
   properties_.SetLeScanResponse({0x05,  // Length
-                                 BTM_BLE_AD_TYPE_NAME_SHORT, 'b', 'e', 'a', 'c'});
+                                 0x08 /* TYPE_NAME_SHORT */, 'b', 'e', 'a', 'c'});
 }
 
 std::string Beacon::GetTypeString() const {
@@ -63,29 +59,35 @@
 }
 
 void Beacon::TimerTick() {
-  if (IsAdvertisementAvailable(std::chrono::milliseconds(5000))) {
-    std::unique_ptr<packets::LeAdvertisementBuilder> ad = packets::LeAdvertisementBuilder::Create(
-        LeAdvertisement::AddressType::PUBLIC,
-        static_cast<LeAdvertisement::AdvertisementType>(properties_.GetLeAdvertisementType()),
+  if (IsAdvertisementAvailable()) {
+    last_advertisement_ = std::chrono::steady_clock::now();
+    auto ad = model::packets::LeAdvertisementBuilder::Create(
+        properties_.GetLeAddress(), Address::kEmpty,
+        model::packets::AddressType::PUBLIC,
+        static_cast<model::packets::AdvertisementType>(
+            properties_.GetLeAdvertisementType()),
         properties_.GetLeAdvertisement());
-    std::shared_ptr<packets::LinkLayerPacketBuilder> to_send =
-        packets::LinkLayerPacketBuilder::WrapLeAdvertisement(std::move(ad), properties_.GetLeAddress());
-    std::vector<std::shared_ptr<PhyLayer>> le_phys = phy_layers_[Phy::Type::LOW_ENERGY];
-    for (std::shared_ptr<PhyLayer> phy : le_phys) {
+    std::shared_ptr<model::packets::LinkLayerPacketBuilder> to_send =
+        std::move(ad);
+
+    for (auto phy : phy_layers_[Phy::Type::LOW_ENERGY]) {
       phy->Send(to_send);
     }
   }
 }
 
-void Beacon::IncomingPacket(packets::LinkLayerPacketView packet) {
-  if (packet.GetDestinationAddress() == properties_.GetLeAddress() && packet.GetType() == Link::PacketType::LE_SCAN) {
-    std::unique_ptr<packets::LeAdvertisementBuilder> scan_response = packets::LeAdvertisementBuilder::Create(
-        LeAdvertisement::AddressType::PUBLIC, LeAdvertisement::AdvertisementType::SCAN_RESPONSE,
+void Beacon::IncomingPacket(model::packets::LinkLayerPacketView packet) {
+  if (packet.GetDestinationAddress() == properties_.GetLeAddress() &&
+      packet.GetType() == model::packets::PacketType::LE_SCAN) {
+    auto scan_response = model::packets::LeScanResponseBuilder::Create(
+        properties_.GetLeAddress(), packet.GetSourceAddress(),
+        model::packets::AddressType::PUBLIC,
+        model::packets::AdvertisementType::SCAN_RESPONSE,
         properties_.GetLeScanResponse());
-    std::shared_ptr<packets::LinkLayerPacketBuilder> to_send = packets::LinkLayerPacketBuilder::WrapLeScanResponse(
-        std::move(scan_response), properties_.GetLeAddress(), packet.GetSourceAddress());
-    std::vector<std::shared_ptr<PhyLayer>> le_phys = phy_layers_[Phy::Type::LOW_ENERGY];
-    for (auto phy : le_phys) {
+    std::shared_ptr<model::packets::LinkLayerPacketBuilder> to_send =
+        std::move(scan_response);
+
+    for (auto phy : phy_layers_[Phy::Type::LOW_ENERGY]) {
       phy->Send(to_send);
     }
   }
diff --git a/vendor_libs/test_vendor_lib/model/devices/beacon.h b/vendor_libs/test_vendor_lib/model/devices/beacon.h
index faac5cc..87bb228 100644
--- a/vendor_libs/test_vendor_lib/model/devices/beacon.h
+++ b/vendor_libs/test_vendor_lib/model/devices/beacon.h
@@ -20,7 +20,6 @@
 #include <vector>
 
 #include "device.h"
-#include "stack/include/btm_ble_api.h"
 
 namespace test_vendor_lib {
 
@@ -43,7 +42,8 @@
   // Set the address and advertising interval from string args.
   virtual void Initialize(const std::vector<std::string>& args) override;
 
-  virtual void IncomingPacket(packets::LinkLayerPacketView packet) override;
+  virtual void IncomingPacket(
+      model::packets::LinkLayerPacketView packet) override;
 
   virtual void TimerTick() override;
 
diff --git a/vendor_libs/test_vendor_lib/model/devices/beacon_swarm.cc b/vendor_libs/test_vendor_lib/model/devices/beacon_swarm.cc
index a065c36..c9f17f1 100644
--- a/vendor_libs/test_vendor_lib/model/devices/beacon_swarm.cc
+++ b/vendor_libs/test_vendor_lib/model/devices/beacon_swarm.cc
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "beacon_swarm"
-
 #include "beacon_swarm.h"
 
 #include "model/setup/device_boutique.h"
@@ -23,14 +21,14 @@
 using std::vector;
 
 namespace test_vendor_lib {
-bool BeaconSwarm::registered_ = DeviceBoutique::Register(LOG_TAG, &BeaconSwarm::Create);
+bool BeaconSwarm::registered_ = DeviceBoutique::Register("beacon_swarm", &BeaconSwarm::Create);
 
 BeaconSwarm::BeaconSwarm() {
   advertising_interval_ms_ = std::chrono::milliseconds(1280);
-  properties_.SetLeAdvertisementType(BTM_BLE_NON_CONNECT_EVT);
+  properties_.SetLeAdvertisementType(0x03 /* NON_CONNECT */);
   properties_.SetLeAdvertisement({
       0x15,  // Length
-      BTM_BLE_AD_TYPE_NAME_CMPL,
+      0x09 /* TYPE_NAME_CMPL */,
       'g',
       'D',
       'e',
@@ -52,12 +50,12 @@
       'r',
       'm',
       0x02,  // Length
-      BTM_BLE_AD_TYPE_FLAG,
-      BTM_BLE_BREDR_NOT_SPT | BTM_BLE_GEN_DISC_FLAG,
+      0x01 /* TYPE_FLAG */,
+      0x4 /* BREDR_NOT_SPT */ | 0x2 /* GEN_DISC_FLAG */,
   });
 
   properties_.SetLeScanResponse({0x06,  // Length
-                                 BTM_BLE_AD_TYPE_NAME_SHORT, 'c', 'b', 'e', 'a', 'c'});
+                                 0x08 /* TYPE_NAME_SHORT */, 'c', 'b', 'e', 'a', 'c'});
 }
 
 void BeaconSwarm::Initialize(const vector<std::string>& args) {
diff --git a/vendor_libs/test_vendor_lib/model/devices/broken_adv.cc b/vendor_libs/test_vendor_lib/model/devices/broken_adv.cc
index c7d6822..32c5ebb 100644
--- a/vendor_libs/test_vendor_lib/model/devices/broken_adv.cc
+++ b/vendor_libs/test_vendor_lib/model/devices/broken_adv.cc
@@ -14,54 +14,36 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "broken_adv"
-
 #include "broken_adv.h"
 
 #include "model/setup/device_boutique.h"
-#include "osi/include/log.h"
 
 using std::vector;
 
 namespace test_vendor_lib {
 
-bool BrokenAdv::registered_ = DeviceBoutique::Register(LOG_TAG, &BrokenAdv::Create);
+bool BrokenAdv::registered_ = DeviceBoutique::Register("broken_adv", &BrokenAdv::Create);
 
 BrokenAdv::BrokenAdv() {
   advertising_interval_ms_ = std::chrono::milliseconds(1280);
-  properties_.SetLeAdvertisementType(BTM_BLE_NON_CONNECT_EVT);
+  properties_.SetLeAdvertisementType(0x03 /* NON_CONNECT */);
   constant_adv_data_ = {
-      0x02,  // Length
-      BTM_BLE_AD_TYPE_FLAG,
-      BTM_BLE_BREDR_NOT_SPT | BTM_BLE_GEN_DISC_FLAG,
-      0x13,  // Length
-      BTM_BLE_AD_TYPE_NAME_CMPL,
-      'g',
-      'D',
-      'e',
-      'v',
-      'i',
-      'c',
-      'e',
-      '-',
-      'b',
-      'r',
-      'o',
-      'k',
-      'e',
-      'n',
-      '_',
-      'a',
-      'd',
-      'v',
+      0x02,       // Length
+      0x01,       // TYPE_FLAG
+      0x4 | 0x2,  // BREDR_NOT_SPT |  GEN_DISC_FLAG
+      0x13,       // Length
+      0x09,       // TYPE_NAME_CMPL
+      'g',       'D', 'e', 'v', 'i', 'c', 'e', '-', 'b', 'r', 'o', 'k', 'e', 'n', '_', 'a', 'd', 'v',
   };
   properties_.SetLeAdvertisement(constant_adv_data_);
 
   properties_.SetLeScanResponse({0x0b,  // Length
-                                 BTM_BLE_AD_TYPE_NAME_SHORT, 'b', 'r', 'o', 'k', 'e', 'n', 'n', 'e', 's', 's'});
+                                 0x08,  // TYPE_NAME_SHORT
+                                 'b', 'r', 'o', 'k', 'e', 'n', 'n', 'e', 's', 's'});
 
   properties_.SetExtendedInquiryData({0x07,  // Length
-                                      BT_EIR_COMPLETE_LOCAL_NAME_TYPE, 'B', 'R', '0', 'K', '3', 'N'});
+                                      0x09,  // TYPE_NAME_COMPLETE
+                                      'B', 'R', '0', 'K', '3', 'N'});
   properties_.SetPageScanRepetitionMode(0);
   page_scan_delay_ms_ = std::chrono::milliseconds(600);
 }
@@ -106,7 +88,7 @@
 
   switch ((randomness & 0xf000000) >> 24) {
     case (0):
-      return BTM_EIR_MANUFACTURER_SPECIFIC_TYPE;
+      return 0xff;  // TYPE_MANUFACTURER_SPECIFIC
     case (1):
       return (randomness & 0xff);
     default:
diff --git a/vendor_libs/test_vendor_lib/model/devices/car_kit.cc b/vendor_libs/test_vendor_lib/model/devices/car_kit.cc
index 5f7fa4a..65603f1 100644
--- a/vendor_libs/test_vendor_lib/model/devices/car_kit.cc
+++ b/vendor_libs/test_vendor_lib/model/devices/car_kit.cc
@@ -14,19 +14,16 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "car_kit"
-
 #include "car_kit.h"
 
-#include "osi/include/log.h"
-
 #include "model/setup/device_boutique.h"
+#include "os/log.h"
 
 using std::vector;
 
 namespace test_vendor_lib {
 
-bool CarKit::registered_ = DeviceBoutique::Register(LOG_TAG, &CarKit::Create);
+bool CarKit::registered_ = DeviceBoutique::Register("car_kit", &CarKit::Create);
 
 CarKit::CarKit() : Device(kCarKitPropertiesFile) {
   advertising_interval_ms_ = std::chrono::milliseconds(0);
@@ -34,17 +31,20 @@
   page_scan_delay_ms_ = std::chrono::milliseconds(600);
 
   // Stub in packet handling for now
-  link_layer_controller_.RegisterAclChannel([](std::shared_ptr<std::vector<uint8_t>>) {});
-  link_layer_controller_.RegisterEventChannel([](std::shared_ptr<std::vector<uint8_t>>) {});
+  link_layer_controller_.RegisterAclChannel(
+      [](std::shared_ptr<bluetooth::hci::AclPacketBuilder>) {});
+  link_layer_controller_.RegisterEventChannel(
+      [](std::shared_ptr<bluetooth::hci::EventPacketBuilder>) {});
   link_layer_controller_.RegisterScoChannel([](std::shared_ptr<std::vector<uint8_t>>) {});
   link_layer_controller_.RegisterRemoteChannel(
-      [this](std::shared_ptr<packets::LinkLayerPacketBuilder> packet, Phy::Type phy_type) {
+      [this](std::shared_ptr<model::packets::LinkLayerPacketBuilder> packet,
+             Phy::Type phy_type) {
         CarKit::SendLinkLayerPacket(packet, phy_type);
       });
 
   properties_.SetPageScanRepetitionMode(0);
   properties_.SetClassOfDevice(0x600420);
-  properties_.SetSupportedFeatures(0x8779ff9bfe8defff);
+  properties_.SetExtendedFeatures(0x8779ff9bfe8defff, 0);
   properties_.SetExtendedInquiryData({
       16,  // length
       9,   // Type: Device Name
@@ -82,7 +82,7 @@
 
   Address addr;
   if (Address::FromString(args[1], addr)) properties_.SetAddress(addr);
-  LOG_INFO(LOG_TAG, "%s SetAddress %s", ToString().c_str(), addr.ToString().c_str());
+  LOG_INFO("%s SetAddress %s", ToString().c_str(), addr.ToString().c_str());
 
   if (args.size() < 3) return;
 
@@ -93,8 +93,8 @@
   link_layer_controller_.TimerTick();
 }
 
-void CarKit::IncomingPacket(packets::LinkLayerPacketView packet) {
-  LOG_WARN(LOG_TAG, "Incoming Packet");
+void CarKit::IncomingPacket(model::packets::LinkLayerPacketView packet) {
+  LOG_WARN("Incoming Packet");
   link_layer_controller_.IncomingPacket(packet);
 }
 
diff --git a/vendor_libs/test_vendor_lib/model/devices/car_kit.h b/vendor_libs/test_vendor_lib/model/devices/car_kit.h
index 94057ad..f59a2ad 100644
--- a/vendor_libs/test_vendor_lib/model/devices/car_kit.h
+++ b/vendor_libs/test_vendor_lib/model/devices/car_kit.h
@@ -20,6 +20,7 @@
 #include <vector>
 
 #include "device.h"
+#include "hci/hci_packets.h"
 #include "model/controller/link_layer_controller.h"
 
 namespace {
@@ -45,7 +46,8 @@
     return "car_kit";
   }
 
-  virtual void IncomingPacket(packets::LinkLayerPacketView packet) override;
+  virtual void IncomingPacket(
+      model::packets::LinkLayerPacketView packet) override;
 
   virtual void TimerTick() override;
 
diff --git a/vendor_libs/test_vendor_lib/model/devices/classic.cc b/vendor_libs/test_vendor_lib/model/devices/classic.cc
index a8f8a99..e8df43a 100644
--- a/vendor_libs/test_vendor_lib/model/devices/classic.cc
+++ b/vendor_libs/test_vendor_lib/model/devices/classic.cc
@@ -14,28 +14,25 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "classic"
-
 #include "classic.h"
 #include "model/setup/device_boutique.h"
-#include "osi/include/log.h"
 
 using std::vector;
 
 namespace test_vendor_lib {
 
-bool Classic::registered_ = DeviceBoutique::Register(LOG_TAG, &Classic::Create);
+bool Classic::registered_ = DeviceBoutique::Register("classic", &Classic::Create);
 
 Classic::Classic() {
   advertising_interval_ms_ = std::chrono::milliseconds(0);
   properties_.SetClassOfDevice(0x30201);
 
-  properties_.SetExtendedInquiryData({0x10,                             // Length
-                                      BT_EIR_COMPLETE_LOCAL_NAME_TYPE,  // Type
+  properties_.SetExtendedInquiryData({0x10,  // Length
+                                      0x09,  // TYPE_NAME_CMPL
                                       'g', 'D', 'e', 'v', 'i', 'c', 'e', '-', 'c', 'l', 'a', 's', 's', 'i', 'c',
                                       '\0'});  // End of data
   properties_.SetPageScanRepetitionMode(0);
-  properties_.SetSupportedFeatures(0x87593F9bFE8FFEFF);
+  properties_.SetExtendedFeatures(0x87593F9bFE8FFEFF, 0);
 
   page_scan_delay_ms_ = std::chrono::milliseconds(600);
 }
diff --git a/vendor_libs/test_vendor_lib/model/devices/device.cc b/vendor_libs/test_vendor_lib/model/devices/device.cc
index 52a37c1..9fdcd5e 100644
--- a/vendor_libs/test_vendor_lib/model/devices/device.cc
+++ b/vendor_libs/test_vendor_lib/model/devices/device.cc
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "device"
-
 #include <vector>
 
 #include "device.h"
@@ -34,35 +32,46 @@
   phy_layers_[phy->GetType()].push_back(phy);
 }
 
-void Device::UnregisterPhyLayer(std::shared_ptr<PhyLayer> phy) {
+void Device::UnregisterPhyLayers() {
   for (auto phy_pair : phy_layers_) {
     auto phy_list = std::get<1>(phy_pair);
-    for (size_t i = 0; i < phy_list.size(); i++) {
-      if (phy == phy_list[i]) {
-        phy_list.erase(phy_list.begin() + i);
-      }
+    for (auto phy : phy_list) {
+      phy->Unregister();
     }
   }
 }
 
-bool Device::IsAdvertisementAvailable(std::chrono::milliseconds scan_time) const {
-  if (advertising_interval_ms_ == std::chrono::milliseconds(0)) return false;
-
-  std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
-
-  std::chrono::steady_clock::time_point last_interval =
-      ((now - time_stamp_) / advertising_interval_ms_) * advertising_interval_ms_ + time_stamp_;
-
-  std::chrono::steady_clock::time_point next_interval = last_interval + advertising_interval_ms_;
-
-  return ((now + scan_time) >= next_interval);
+void Device::UnregisterPhyLayer(Phy::Type phy_type, uint32_t factory_id) {
+  for (const auto phy_layer : phy_layers_[phy_type]) {
+    if (phy_layer->IsFactoryId(factory_id)) {
+      phy_layer->Unregister();
+      phy_layers_[phy_type].remove(phy_layer);
+    }
+  }
 }
 
-void Device::SendLinkLayerPacket(std::shared_ptr<packets::LinkLayerPacketBuilder> to_send, Phy::Type phy_type) {
-  auto phy_list = phy_layers_[phy_type];
-  for (auto phy : phy_list) {
+bool Device::IsAdvertisementAvailable() const {
+  return (advertising_interval_ms_ > std::chrono::milliseconds(0)) &&
+         (std::chrono::steady_clock::now() >= last_advertisement_ + advertising_interval_ms_);
+}
+
+void Device::SendLinkLayerPacket(
+    std::shared_ptr<model::packets::LinkLayerPacketBuilder> to_send,
+    Phy::Type phy_type) {
+  for (auto phy : phy_layers_[phy_type]) {
     phy->Send(to_send);
   }
 }
 
+void Device::SendLinkLayerPacket(model::packets::LinkLayerPacketView to_send,
+                                 Phy::Type phy_type) {
+  for (auto phy : phy_layers_[phy_type]) {
+    phy->Send(to_send);
+  }
+}
+
+void Device::SetAddress(Address) {
+  LOG_INFO("%s does not implement %s", GetTypeString().c_str(), __func__);
+}
+
 }  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/model/devices/device.h b/vendor_libs/test_vendor_lib/model/devices/device.h
index 18842cd..b985888 100644
--- a/vendor_libs/test_vendor_lib/model/devices/device.h
+++ b/vendor_libs/test_vendor_lib/model/devices/device.h
@@ -18,26 +18,27 @@
 
 #include <chrono>
 #include <cstdint>
+#include <list>
 #include <map>
 #include <string>
 #include <vector>
 
+#include "hci/address.h"
 #include "model/devices/device_properties.h"
 #include "model/setup/phy_layer.h"
-#include "packets/link_layer/link_layer_packet_builder.h"
-#include "packets/link_layer/link_layer_packet_view.h"
-#include "types/address.h"
 
-#include "stack/include/btm_ble_api.h"
+#include "packets/link_layer_packets.h"
 
 namespace test_vendor_lib {
 
+using ::bluetooth::hci::Address;
+
 // Represent a Bluetooth Device
 //  - Provide Get*() and Set*() functions for device attributes.
 class Device {
  public:
   Device(const std::string properties_filename = "")
-      : time_stamp_(std::chrono::steady_clock::now()), properties_(properties_filename) {}
+      : last_advertisement_(std::chrono::steady_clock::now()), properties_(properties_filename) {}
   virtual ~Device() = default;
 
   // Initialize the device based on the values of |args|.
@@ -57,30 +58,38 @@
     return false;
   }
 
+  // Set the device's Bluetooth address.
+  virtual void SetAddress(Address address);
+
   // Set the advertisement interval in milliseconds.
   void SetAdvertisementInterval(std::chrono::milliseconds ms) {
     advertising_interval_ms_ = ms;
   }
 
-  // Returns true if the host could see an advertisement in the next
-  // |scan_time| milliseconds.
-  virtual bool IsAdvertisementAvailable(std::chrono::milliseconds scan_time) const;
+  // Returns true if the host could see an advertisement about now.
+  virtual bool IsAdvertisementAvailable() const;
 
   // Let the device know that time has passed.
   virtual void TimerTick() {}
 
   void RegisterPhyLayer(std::shared_ptr<PhyLayer> phy);
 
-  void UnregisterPhyLayer(std::shared_ptr<PhyLayer> phy);
+  void UnregisterPhyLayers();
 
-  virtual void IncomingPacket(packets::LinkLayerPacketView){};
+  void UnregisterPhyLayer(Phy::Type phy_type, uint32_t factory_id);
 
-  virtual void SendLinkLayerPacket(std::shared_ptr<packets::LinkLayerPacketBuilder> packet, Phy::Type phy_type);
+  virtual void IncomingPacket(model::packets::LinkLayerPacketView){};
+
+  virtual void SendLinkLayerPacket(
+      std::shared_ptr<model::packets::LinkLayerPacketBuilder> packet,
+      Phy::Type phy_type);
+  virtual void SendLinkLayerPacket(model::packets::LinkLayerPacketView packet,
+                                   Phy::Type phy_type);
 
  protected:
-  std::map<Phy::Type, std::vector<std::shared_ptr<PhyLayer>>> phy_layers_;
+  std::map<Phy::Type, std::list<std::shared_ptr<PhyLayer>>> phy_layers_;
 
-  std::chrono::steady_clock::time_point time_stamp_;
+  std::chrono::steady_clock::time_point last_advertisement_;
 
   // The time between page scans.
   std::chrono::milliseconds page_scan_delay_ms_;
diff --git a/vendor_libs/test_vendor_lib/model/devices/device_properties.cc b/vendor_libs/test_vendor_lib/model/devices/device_properties.cc
index 164160a..8309268 100644
--- a/vendor_libs/test_vendor_lib/model/devices/device_properties.cc
+++ b/vendor_libs/test_vendor_lib/model/devices/device_properties.cc
@@ -14,19 +14,15 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "device_properties"
-
 #include "device_properties.h"
 
 #include <memory>
 
-#include <base/logging.h>
 #include "base/files/file_util.h"
 #include "base/json/json_reader.h"
 #include "base/values.h"
 
-#include "hci.h"
-#include "osi/include/log.h"
+#include "os/log.h"
 #include "osi/include/osi.h"
 
 using std::vector;
@@ -48,20 +44,32 @@
 namespace test_vendor_lib {
 
 DeviceProperties::DeviceProperties(const std::string& file_name)
-    : acl_data_packet_size_(1024), sco_data_packet_size_(255), num_acl_data_packets_(10), num_sco_data_packets_(10),
-      version_(static_cast<uint8_t>(hci::Version::V4_1)), revision_(0),
-      lmp_pal_version_(static_cast<uint8_t>(hci::Version::V4_1)), manufacturer_name_(0), lmp_pal_subversion_(0),
-      le_data_packet_length_(27), num_le_data_packets_(15), le_white_list_size_(15) {
+    : acl_data_packet_size_(1024),
+      sco_data_packet_size_(255),
+      num_acl_data_packets_(10),
+      num_sco_data_packets_(10),
+      version_(static_cast<uint8_t>(bluetooth::hci::HciVersion::V_4_1)),
+      revision_(0),
+      lmp_pal_version_(static_cast<uint8_t>(bluetooth::hci::LmpVersion::V_4_1)),
+      manufacturer_name_(0),
+      lmp_pal_subversion_(0),
+      le_data_packet_length_(27),
+      num_le_data_packets_(15),
+      le_white_list_size_(15),
+      le_resolving_list_size_(15) {
   std::string properties_raw;
 
-  CHECK(Address::FromString("BB:BB:BB:BB:BB:AD", address_));
-  CHECK(Address::FromString("BB:BB:BB:BB:AD:1E", le_address_));
+  ASSERT(Address::FromString("BB:BB:BB:BB:BB:AD", address_));
+  ASSERT(Address::FromString("BB:BB:BB:BB:AD:1E", le_address_));
   name_ = {'D', 'e', 'f', 'a', 'u', 'l', 't'};
 
   supported_codecs_ = {0};  // Only SBC is supported.
   vendor_specific_codecs_ = {};
 
-  for (int i = 0; i < 64; i++) supported_commands_.push_back(0xff);
+  for (int i = 0; i < 35; i++) supported_commands_.push_back(0xff);
+  // Mark HCI_LE_Transmitter_Test[v2] and newer commands as unsupported
+  // TODO: Implement a better mapping.
+  for (int i = 35; i < 64; i++) supported_commands_.push_back(0x00);
 
   le_supported_features_ = 0x1f;
   le_supported_states_ = 0x3ffffffffff;
@@ -70,15 +78,14 @@
   if (file_name.size() == 0) {
     return;
   }
-  LOG_INFO(LOG_TAG, "Reading controller properties from %s.", file_name.c_str());
+  LOG_INFO("Reading controller properties from %s.", file_name.c_str());
   if (!base::ReadFileToString(base::FilePath(file_name), &properties_raw)) {
-    LOG_ERROR(LOG_TAG, "Error reading controller properties from file.");
+    LOG_ERROR("Error reading controller properties from file.");
     return;
   }
 
   std::unique_ptr<base::Value> properties_value_ptr = base::JSONReader::Read(properties_raw);
-  if (properties_value_ptr.get() == nullptr)
-    LOG_INFO(LOG_TAG, "Error controller properties may consist of ill-formed JSON.");
+  if (properties_value_ptr.get() == nullptr) LOG_INFO("Error controller properties may consist of ill-formed JSON.");
 
   // Get the underlying base::Value object, which is of type
   // base::Value::TYPE_DICTIONARY, and read it into member variables.
@@ -86,7 +93,7 @@
   base::JSONValueConverter<DeviceProperties> converter;
 
   if (!converter.Convert(properties_dictionary, this))
-    LOG_INFO(LOG_TAG, "Error converting JSON properties into Properties object.");
+    LOG_INFO("Error converting JSON properties into Properties object.");
 }
 
 // static
@@ -98,6 +105,7 @@
   converter->RegisterCustomField<uint16_t>(field_name, &DeviceProperties::field, &ParseUint16t);
   REGISTER_UINT16_T("AclDataPacketSize", acl_data_packet_size_);
   REGISTER_UINT8_T("ScoDataPacketSize", sco_data_packet_size_);
+  REGISTER_UINT8_T("EncryptionKeySize", encryption_key_size_);
   REGISTER_UINT16_T("NumAclDataPackets", num_acl_data_packets_);
   REGISTER_UINT16_T("NumScoDataPackets", num_sco_data_packets_);
   REGISTER_UINT8_T("Version", version_);
diff --git a/vendor_libs/test_vendor_lib/model/devices/device_properties.h b/vendor_libs/test_vendor_lib/model/devices/device_properties.h
index 6960553..960e5d0 100644
--- a/vendor_libs/test_vendor_lib/model/devices/device_properties.h
+++ b/vendor_libs/test_vendor_lib/model/devices/device_properties.h
@@ -16,16 +16,21 @@
 
 #pragma once
 
+#include <array>
 #include <cstdint>
 #include <string>
 #include <vector>
 
 #include "base/json/json_value_converter.h"
-#include "types/address.h"
-#include "types/class_of_device.h"
+#include "hci/address.h"
+#include "hci/hci_packets.h"
+#include "os/log.h"
 
 namespace test_vendor_lib {
 
+using ::bluetooth::hci::Address;
+using ::bluetooth::hci::ClassOfDevice;
+
 class DeviceProperties {
  public:
   explicit DeviceProperties(const std::string& file_name = "");
@@ -45,8 +50,9 @@
     return extended_features_[0];
   }
 
-  void SetSupportedFeatures(uint64_t features) {
-    extended_features_[0] = features;
+  void SetExtendedFeatures(uint64_t features, uint8_t page_number) {
+    ASSERT(page_number < extended_features_.size());
+    extended_features_[page_number] = features;
   }
 
   // Specification Version 4.2, Volume 2, Part E, Section 7.4.4
@@ -55,7 +61,7 @@
   }
 
   uint64_t GetExtendedFeatures(uint8_t page_number) const {
-    CHECK(page_number < extended_features_.size());
+    ASSERT(page_number < extended_features_.size());
     return extended_features_[page_number];
   }
 
@@ -68,6 +74,8 @@
     return sco_data_packet_size_;
   }
 
+  uint8_t GetEncryptionKeySize() const { return encryption_key_size_; }
+
   uint16_t GetTotalNumAclDataPackets() const {
     return num_acl_data_packets_;
   }
@@ -138,12 +146,13 @@
   }
 
   void SetName(const std::vector<uint8_t>& name) {
-    name_ = name;
+    name_.fill(0);
+    for (size_t i = 0; i < 248 && i < name.size(); i++) {
+      name_[i] = name[i];
+    }
   }
 
-  const std::vector<uint8_t>& GetName() const {
-    return name_;
-  }
+  const std::array<uint8_t, 248>& GetName() const { return name_; }
 
   void SetExtendedInquiryData(const std::vector<uint8_t>& eid) {
     extended_inquiry_data_ = eid;
@@ -190,6 +199,47 @@
     return le_advertisement_type_;
   }
 
+  uint16_t GetLeAdvertisingIntervalMin() const {
+    return le_advertising_interval_min_;
+  }
+
+  uint16_t GetLeAdvertisingIntervalMax() const {
+    return le_advertising_interval_max_;
+  }
+
+  uint8_t GetLeAdvertisingOwnAddressType() const {
+    return le_advertising_own_address_type_;
+  }
+
+  uint8_t GetLeAdvertisingPeerAddressType() const {
+    return le_advertising_peer_address_type_;
+  }
+
+  Address GetLeAdvertisingPeerAddress() const {
+    return le_advertising_peer_address_;
+  }
+
+  uint8_t GetLeAdvertisingChannelMap() const {
+    return le_advertising_channel_map_;
+  }
+
+  uint8_t GetLeAdvertisingFilterPolicy() const {
+    return le_advertising_filter_policy_;
+  }
+
+  void SetLeAdvertisingParameters(uint16_t interval_min, uint16_t interval_max, uint8_t ad_type,
+                                  uint8_t own_address_type, uint8_t peer_address_type, Address peer_address,
+                                  uint8_t channel_map, uint8_t filter_policy) {
+    le_advertisement_type_ = ad_type;
+    le_advertising_interval_min_ = interval_min;
+    le_advertising_interval_max_ = interval_max;
+    le_advertising_own_address_type_ = own_address_type;
+    le_advertising_peer_address_type_ = peer_address_type;
+    le_advertising_peer_address_ = peer_address;
+    le_advertising_channel_map_ = channel_map;
+    le_advertising_filter_policy_ = filter_policy;
+  }
+
   void SetLeAdvertisementType(uint8_t ad_type) {
     le_advertisement_type_ = ad_type;
   }
@@ -238,6 +288,9 @@
     return le_supported_states_;
   }
 
+  // Specification Version 4.2, Volume 2, Part E, Section 7.8.41
+  uint8_t GetLeResolvingListSize() const { return le_resolving_list_size_; }
+
   // Vendor-specific commands
   const std::vector<uint8_t>& GetLeVendorCap() const {
     return le_vendor_cap_;
@@ -261,23 +314,33 @@
   std::vector<uint8_t> supported_codecs_;
   std::vector<uint32_t> vendor_specific_codecs_;
   std::vector<uint8_t> supported_commands_;
-  std::vector<uint64_t> extended_features_{{0x875b3fd8fe8ffeff, 0x07}};
+  std::vector<uint64_t> extended_features_{{0x875b3fd8fe8ffeff, 0x0f}};
   ClassOfDevice class_of_device_{{0, 0, 0}};
   std::vector<uint8_t> extended_inquiry_data_;
-  std::vector<uint8_t> name_;
+  std::array<uint8_t, 248> name_;
   Address address_;
   uint8_t page_scan_repetition_mode_;
   uint16_t clock_offset_;
+  uint8_t encryption_key_size_{10};
 
   // Low Energy
   uint16_t le_data_packet_length_;
   uint8_t num_le_data_packets_;
   uint8_t le_white_list_size_;
+  uint8_t le_resolving_list_size_;
   uint64_t le_supported_features_{0x075b3fd8fe8ffeff};
   uint64_t le_supported_states_;
   std::vector<uint8_t> le_vendor_cap_;
   Address le_address_;
   uint8_t le_address_type_;
+
+  uint16_t le_advertising_interval_min_;
+  uint16_t le_advertising_interval_max_;
+  uint8_t le_advertising_own_address_type_;
+  uint8_t le_advertising_peer_address_type_;
+  Address le_advertising_peer_address_;
+  uint8_t le_advertising_channel_map_;
+  uint8_t le_advertising_filter_policy_;
   uint8_t le_advertisement_type_;
   std::vector<uint8_t> le_advertisement_;
   std::vector<uint8_t> le_scan_response_;
diff --git a/vendor_libs/test_vendor_lib/model/devices/h4_packetizer.cc b/vendor_libs/test_vendor_lib/model/devices/h4_packetizer.cc
index 49e8727..6c69697 100644
--- a/vendor_libs/test_vendor_lib/model/devices/h4_packetizer.cc
+++ b/vendor_libs/test_vendor_lib/model/devices/h4_packetizer.cc
@@ -16,15 +16,14 @@
 
 #include "h4_packetizer.h"
 
-#define LOG_TAG "h4_packetizer"
-
 #include <dlfcn.h>
 #include <errno.h>
 #include <fcntl.h>
-#include <log/log.h>
 #include <sys/uio.h>
 #include <unistd.h>
 
+#include "os/log.h"
+
 namespace test_vendor_lib {
 namespace hci {
 constexpr size_t H4Packetizer::COMMAND_PREAMBLE_SIZE;
@@ -53,8 +52,9 @@
 }
 
 H4Packetizer::H4Packetizer(int fd, PacketReadCallback command_cb, PacketReadCallback event_cb,
-                           PacketReadCallback acl_cb, PacketReadCallback sco_cb)
-    : uart_fd_(fd), command_cb_(command_cb), event_cb_(event_cb), acl_cb_(acl_cb), sco_cb_(sco_cb) {}
+                           PacketReadCallback acl_cb, PacketReadCallback sco_cb, ClientDisconnectCallback disconnect_cb)
+    : uart_fd_(fd), command_cb_(command_cb), event_cb_(event_cb), acl_cb_(acl_cb), sco_cb_(sco_cb),
+      disconnect_cb_(disconnect_cb) {}
 
 size_t H4Packetizer::Send(uint8_t type, const uint8_t* data, size_t length) {
   struct iovec iov[] = {{&type, sizeof(type)}, {const_cast<uint8_t*>(data), length}};
@@ -64,16 +64,15 @@
   } while (-1 == ret && EAGAIN == errno);
 
   if (ret == -1) {
-    ALOGE("%s error writing to UART (%s)", __func__, strerror(errno));
+    LOG_ERROR("%s error writing to UART (%s)", __func__, strerror(errno));
   } else if (ret < static_cast<ssize_t>(length + 1)) {
-    ALOGE("%s: %d / %d bytes written - something went wrong...", __func__, static_cast<int>(ret),
-          static_cast<int>(length + 1));
+    LOG_ERROR("%s: %d / %d bytes written - something went wrong...", __func__, static_cast<int>(ret),
+              static_cast<int>(length + 1));
   }
   return ret;
 }
 
 void H4Packetizer::OnPacketReady() {
-  ALOGE("%s: before switch", __func__);
   switch (hci_packet_type_) {
     case hci::PacketType::COMMAND:
       command_cb_(packet_);
@@ -98,20 +97,22 @@
   if (hci_packet_type_ == hci::PacketType::UNKNOWN) {
     uint8_t buffer[1] = {0};
     ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer, 1));
-    if (bytes_read != 1) {
-      if (bytes_read == 0) {
-        ALOGI("%s: Nothing ready, will retry!", __func__);
+    if (bytes_read == 0) {
+      LOG_INFO("%s: remote disconnected!", __func__);
+      disconnect_cb_();
+      return;
+    } else if (bytes_read < 0) {
+      if (errno == EAGAIN) {
+        // No data, try again later.
         return;
-      } else if (bytes_read < 0) {
-        if (errno == EAGAIN) {
-          // No data, try again later.
-          return;
-        } else {
-          LOG_ALWAYS_FATAL("%s: Read packet type error: %s", __func__, strerror(errno));
-        }
+      } else if (errno == ECONNRESET) {
+        // They probably rejected our packet
+        return;
       } else {
-        LOG_ALWAYS_FATAL("%s: More bytes read than expected (%u)!", __func__, static_cast<unsigned int>(bytes_read));
+        LOG_ALWAYS_FATAL("%s: Read packet type error: %s", __func__, strerror(errno));
       }
+    } else if (bytes_read > 1) {
+      LOG_ALWAYS_FATAL("%s: More bytes read than expected (%u)!", __func__, static_cast<unsigned int>(bytes_read));
     }
     hci_packet_type_ = static_cast<hci::PacketType>(buffer[0]);
     if (hci_packet_type_ != hci::PacketType::ACL && hci_packet_type_ != hci::PacketType::SCO &&
@@ -132,7 +133,7 @@
         size_t preamble_bytes = preamble_size[static_cast<size_t>(hci_packet_type_)];
         ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd, preamble_ + bytes_read_, preamble_bytes - bytes_read_));
         if (bytes_read == 0) {
-          ALOGE("%s: Will try again to read the header!", __func__);
+          LOG_ERROR("%s: Will try again to read the header!", __func__);
           return;
         }
         if (bytes_read < 0) {
@@ -147,8 +148,6 @@
           size_t packet_length = HciGetPacketLengthForType(hci_packet_type_, preamble_);
           packet_.resize(preamble_bytes + packet_length);
           memcpy(packet_.data(), preamble_, preamble_bytes);
-          ALOGI("%s: Read a preamble of size %d length %d %0x %0x %0x", __func__, static_cast<int>(bytes_read_),
-                static_cast<int>(packet_length), preamble_[0], preamble_[1], preamble_[2]);
           bytes_remaining_ = packet_length;
           if (bytes_remaining_ == 0) {
             OnPacketReady();
@@ -167,7 +166,7 @@
         ssize_t bytes_read =
             TEMP_FAILURE_RETRY(read(fd, packet_.data() + preamble_bytes + bytes_read_, bytes_remaining_));
         if (bytes_read == 0) {
-          ALOGI("%s: Will try again to read the payload!", __func__);
+          LOG_INFO("%s: Will try again to read the payload!", __func__);
           return;
         }
         if (bytes_read < 0) {
diff --git a/vendor_libs/test_vendor_lib/model/devices/h4_packetizer.h b/vendor_libs/test_vendor_lib/model/devices/h4_packetizer.h
index f81cf77..c8b7917 100644
--- a/vendor_libs/test_vendor_lib/model/devices/h4_packetizer.h
+++ b/vendor_libs/test_vendor_lib/model/devices/h4_packetizer.h
@@ -26,11 +26,12 @@
 namespace hci {
 
 using HciPacketReadyCallback = std::function<void(void)>;
+using ClientDisconnectCallback = std::function<void()>;
 
 class H4Packetizer : public HciProtocol {
  public:
   H4Packetizer(int fd, PacketReadCallback command_cb, PacketReadCallback event_cb, PacketReadCallback acl_cb,
-               PacketReadCallback sco_cb);
+               PacketReadCallback sco_cb, ClientDisconnectCallback disconnect_cb);
 
   size_t Send(uint8_t type, const uint8_t* data, size_t length);
 
@@ -46,6 +47,8 @@
   PacketReadCallback acl_cb_;
   PacketReadCallback sco_cb_;
 
+  ClientDisconnectCallback disconnect_cb_;
+
   hci::PacketType hci_packet_type_{hci::PacketType::UNKNOWN};
 
   // 2 bytes for opcode, 1 byte for parameter length (Volume 2, Part E, 5.4.1)
diff --git a/vendor_libs/test_vendor_lib/model/devices/h4_protocol.cc b/vendor_libs/test_vendor_lib/model/devices/h4_protocol.cc
index c4704ea..7ba432d 100644
--- a/vendor_libs/test_vendor_lib/model/devices/h4_protocol.cc
+++ b/vendor_libs/test_vendor_lib/model/devices/h4_protocol.cc
@@ -16,14 +16,13 @@
 
 #include "h4_protocol.h"
 
-#define LOG_TAG "hci-h4_protocol"
-
 #include <errno.h>
 #include <fcntl.h>
-#include <log/log.h>
 #include <sys/uio.h>
 #include <unistd.h>
 
+#include "os/log.h"
+
 namespace test_vendor_lib {
 namespace hci {
 
@@ -31,7 +30,7 @@
                        PacketReadCallback sco_cb)
     : uart_fd_(fd), command_cb_(command_cb), event_cb_(event_cb), acl_cb_(acl_cb), sco_cb_(sco_cb),
       hci_packetizer_([this]() {
-        ALOGI("in lambda");
+        LOG_INFO("in lambda");
         this->OnPacketReady();
       }) {}
 
@@ -43,16 +42,16 @@
   } while (-1 == ret && EAGAIN == errno);
 
   if (ret == -1) {
-    ALOGE("%s error writing to UART (%s)", __func__, strerror(errno));
+    LOG_ERROR("%s error writing to UART (%s)", __func__, strerror(errno));
   } else if (ret < static_cast<ssize_t>(length + 1)) {
-    ALOGE("%s: %d / %d bytes written - something went wrong...", __func__, static_cast<int>(ret),
-          static_cast<int>(length + 1));
+    LOG_ERROR("%s: %d / %d bytes written - something went wrong...", __func__, static_cast<int>(ret),
+              static_cast<int>(length + 1));
   }
   return ret;
 }
 
 void H4Protocol::OnPacketReady() {
-  ALOGE("%s: before switch", __func__);
+  LOG_ERROR("%s: before switch", __func__);
   switch (hci_packet_type_) {
     case hci::PacketType::COMMAND:
       command_cb_(hci_packetizer_.GetPacket());
@@ -79,7 +78,7 @@
     ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer, 1));
     if (bytes_read != 1) {
       if (bytes_read == 0) {
-        ALOGI("%s: Nothing ready, will retry!", __func__);
+        LOG_INFO("%s: Nothing ready, will retry!", __func__);
         return;
       } else if (bytes_read < 0) {
         if (errno == EAGAIN) {
diff --git a/vendor_libs/test_vendor_lib/model/devices/hci_packetizer.cc b/vendor_libs/test_vendor_lib/model/devices/hci_packetizer.cc
index cca7780..e0c4e4a 100644
--- a/vendor_libs/test_vendor_lib/model/devices/hci_packetizer.cc
+++ b/vendor_libs/test_vendor_lib/model/devices/hci_packetizer.cc
@@ -16,14 +16,13 @@
 
 #include "hci_packetizer.h"
 
-#define LOG_TAG "hci_packetizer"
-
 #include <dlfcn.h>
 #include <errno.h>
 #include <fcntl.h>
-#include <log/log.h>
 #include <unistd.h>
 
+#include "os/log.h"
+
 namespace test_vendor_lib {
 namespace hci {
 constexpr size_t HciPacketizer::COMMAND_PREAMBLE_SIZE;
@@ -69,7 +68,7 @@
       ssize_t bytes_read = TEMP_FAILURE_RETRY(
           read(fd, preamble_ + bytes_read_, preamble_size[static_cast<uint8_t>(packet_type)] - bytes_read_));
       if (bytes_read == 0) {
-        ALOGE("%s: Will try again to read the header!", __func__);
+        LOG_ERROR("%s: Will try again to read the header!", __func__);
         return;
       }
       if (bytes_read < 0) {
@@ -77,15 +76,15 @@
         if (errno == EAGAIN) {
           return;
         }
-        LOG_ALWAYS_FATAL("%s: Read header error: %s", __func__, strerror(errno));
+        LOG_ALWAYS_FATAL("Read header error: %s", strerror(errno));
       }
       bytes_read_ += bytes_read;
       if (bytes_read_ == preamble_size[static_cast<uint8_t>(packet_type)]) {
         size_t packet_length = HciGetPacketLengthForType(packet_type, preamble_);
         packet_.resize(preamble_size[static_cast<uint8_t>(packet_type)] + packet_length);
         memcpy(packet_.data(), preamble_, preamble_size[static_cast<uint8_t>(packet_type)]);
-        ALOGI("%s: Read a preamble of size %d length %d %0x %0x %0x", __func__, static_cast<int>(bytes_read_),
-              static_cast<int>(packet_length), preamble_[0], preamble_[1], preamble_[2]);
+        LOG_INFO("%s: Read a preamble of size %d length %d %0x %0x %0x", __func__, static_cast<int>(bytes_read_),
+                 static_cast<int>(packet_length), preamble_[0], preamble_[1], preamble_[2]);
         bytes_remaining_ = packet_length;
         if (bytes_remaining_ == 0) {
           packet_ready_cb_();
@@ -103,7 +102,7 @@
       ssize_t bytes_read = TEMP_FAILURE_RETRY(
           read(fd, packet_.data() + preamble_size[static_cast<uint8_t>(packet_type)] + bytes_read_, bytes_remaining_));
       if (bytes_read == 0) {
-        ALOGI("%s: Will try again to read the payload!", __func__);
+        LOG_INFO("Will try again to read the payload!");
         return;
       }
       if (bytes_read < 0) {
@@ -111,7 +110,7 @@
         if (errno == EAGAIN) {
           return;
         }
-        LOG_ALWAYS_FATAL("%s: Read payload error: %s", __func__, strerror(errno));
+        LOG_ALWAYS_FATAL("Read payload error: %s", strerror(errno));
       }
       bytes_remaining_ -= bytes_read;
       bytes_read_ += bytes_read;
diff --git a/vendor_libs/test_vendor_lib/model/devices/hci_protocol.cc b/vendor_libs/test_vendor_lib/model/devices/hci_protocol.cc
index e01bfa5..d2ef535 100644
--- a/vendor_libs/test_vendor_lib/model/devices/hci_protocol.cc
+++ b/vendor_libs/test_vendor_lib/model/devices/hci_protocol.cc
@@ -16,13 +16,13 @@
 
 #include "hci_protocol.h"
 
-#define LOG_TAG "hci-hci_protocol"
 #include <assert.h>
 #include <errno.h>
 #include <fcntl.h>
-#include <log/log.h>
 #include <unistd.h>
 
+#include "os/log.h"
+
 namespace test_vendor_lib {
 namespace hci {
 
@@ -33,12 +33,12 @@
 
     if (ret == -1) {
       if (errno == EAGAIN) continue;
-      ALOGE("%s error writing to UART (%s)", __func__, strerror(errno));
+      LOG_ERROR("%s error writing to UART (%s)", __func__, strerror(errno));
       break;
 
     } else if (ret == 0) {
       // Nothing written :(
-      ALOGE("%s zero bytes written - something went wrong...", __func__);
+      LOG_ERROR("%s zero bytes written - something went wrong...", __func__);
       break;
     }
 
diff --git a/vendor_libs/test_vendor_lib/model/devices/hci_socket_device.cc b/vendor_libs/test_vendor_lib/model/devices/hci_socket_device.cc
index e146fcc..6865714 100644
--- a/vendor_libs/test_vendor_lib/model/devices/hci_socket_device.cc
+++ b/vendor_libs/test_vendor_lib/model/devices/hci_socket_device.cc
@@ -14,24 +14,21 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "hci_socket_device"
-
 #include "hci_socket_device.h"
 
 #include <fcntl.h>
 #include <netinet/in.h>
 #include <sys/socket.h>
 
-#include "osi/include/log.h"
-
 #include "model/setup/device_boutique.h"
+#include "os/log.h"
 
 using std::vector;
 
 namespace test_vendor_lib {
 
 HciSocketDevice::HciSocketDevice(int file_descriptor) : socket_file_descriptor_(file_descriptor) {
-  advertising_interval_ms_ = std::chrono::milliseconds(0);
+  advertising_interval_ms_ = std::chrono::milliseconds(1000);
 
   page_scan_delay_ms_ = std::chrono::milliseconds(600);
 
@@ -77,57 +74,54 @@
   h4_ = hci::H4Packetizer(
       socket_file_descriptor_,
       [this](const std::vector<uint8_t>& raw_command) {
-        LOG_INFO(LOG_TAG, "Rx Command");
         std::shared_ptr<std::vector<uint8_t>> packet_copy = std::make_shared<std::vector<uint8_t>>(raw_command);
         HandleCommand(packet_copy);
       },
-      [](const std::vector<uint8_t>&) { CHECK(false) << "Unexpected Event in HciSocketDevice!"; },
+      [](const std::vector<uint8_t>&) { LOG_ALWAYS_FATAL("Unexpected Event in HciSocketDevice!"); },
       [this](const std::vector<uint8_t>& raw_acl) {
-        LOG_INFO(LOG_TAG, "Rx ACL");
         std::shared_ptr<std::vector<uint8_t>> packet_copy = std::make_shared<std::vector<uint8_t>>(raw_acl);
         HandleAcl(packet_copy);
       },
       [this](const std::vector<uint8_t>& raw_sco) {
-        LOG_INFO(LOG_TAG, "Rx SCO");
         std::shared_ptr<std::vector<uint8_t>> packet_copy = std::make_shared<std::vector<uint8_t>>(raw_sco);
         HandleSco(packet_copy);
+      },
+      [this]() {
+        LOG_INFO("HCI socket device disconnected");
+        close_callback_();
       });
 
   RegisterEventChannel([this](std::shared_ptr<std::vector<uint8_t>> packet) {
-    LOG_INFO(LOG_TAG, "Tx Event");
     SendHci(hci::PacketType::EVENT, packet);
   });
-  RegisterAclChannel([this](std::shared_ptr<std::vector<uint8_t>> packet) {
-    LOG_INFO(LOG_TAG, "Tx ACL");
-    SendHci(hci::PacketType::ACL, packet);
-  });
-  RegisterScoChannel([this](std::shared_ptr<std::vector<uint8_t>> packet) {
-    LOG_INFO(LOG_TAG, "Tx SCO");
-    SendHci(hci::PacketType::SCO, packet);
-  });
+  RegisterAclChannel([this](std::shared_ptr<std::vector<uint8_t>> packet) { SendHci(hci::PacketType::ACL, packet); });
+  RegisterScoChannel([this](std::shared_ptr<std::vector<uint8_t>> packet) { SendHci(hci::PacketType::SCO, packet); });
 }
 
 void HciSocketDevice::TimerTick() {
-  LOG_INFO(LOG_TAG, "TimerTick fd = %d", socket_file_descriptor_);
   h4_.OnDataReady(socket_file_descriptor_);
   DualModeController::TimerTick();
 }
 
 void HciSocketDevice::SendHci(hci::PacketType packet_type, const std::shared_ptr<std::vector<uint8_t>> packet) {
   if (socket_file_descriptor_ == -1) {
-    LOG_INFO(LOG_TAG, "socket_file_descriptor == -1");
+    LOG_INFO("socket_file_descriptor == -1");
     return;
   }
   uint8_t type = static_cast<uint8_t>(packet_type);
   int bytes_written;
   bytes_written = write(socket_file_descriptor_, &type, sizeof(type));
   if (bytes_written != sizeof(type)) {
-    LOG_INFO(LOG_TAG, "bytes_written %d != sizeof(type)", bytes_written);
+    LOG_WARN("bytes_written %d != sizeof(type)", bytes_written);
   }
   bytes_written = write(socket_file_descriptor_, packet->data(), packet->size());
   if (static_cast<size_t>(bytes_written) != packet->size()) {
-    LOG_INFO(LOG_TAG, "bytes_written %d != packet->size", bytes_written);
+    LOG_WARN("bytes_written %d != packet->size", bytes_written);
   }
 }
 
+void HciSocketDevice::RegisterCloseCallback(std::function<void()> close_callback) {
+  close_callback_ = close_callback;
+}
+
 }  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/model/devices/hci_socket_device.h b/vendor_libs/test_vendor_lib/model/devices/hci_socket_device.h
index 11eddae..0a3bb3b 100644
--- a/vendor_libs/test_vendor_lib/model/devices/hci_socket_device.h
+++ b/vendor_libs/test_vendor_lib/model/devices/hci_socket_device.h
@@ -45,10 +45,18 @@
 
   void SendHci(hci::PacketType packet_type, const std::shared_ptr<std::vector<uint8_t>> packet);
 
+  void RegisterCloseCallback(std::function<void()>);
+
  private:
   int socket_file_descriptor_{-1};
-  hci::H4Packetizer h4_{socket_file_descriptor_, [](const std::vector<uint8_t>&) {}, [](const std::vector<uint8_t>&) {},
-                        [](const std::vector<uint8_t>&) {}, [](const std::vector<uint8_t>&) {}};
+  hci::H4Packetizer h4_{socket_file_descriptor_,
+                        [](const std::vector<uint8_t>&) {},
+                        [](const std::vector<uint8_t>&) {},
+                        [](const std::vector<uint8_t>&) {},
+                        [](const std::vector<uint8_t>&) {},
+                        [] {}};
+
+  std::function<void()> close_callback_;
 };
 
 }  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/model/devices/keyboard.cc b/vendor_libs/test_vendor_lib/model/devices/keyboard.cc
index 2cfc992..1244d3e 100644
--- a/vendor_libs/test_vendor_lib/model/devices/keyboard.cc
+++ b/vendor_libs/test_vendor_lib/model/devices/keyboard.cc
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "keyboard"
-
 #include "keyboard.h"
 
 #include "model/setup/device_boutique.h"
@@ -23,12 +21,12 @@
 using std::vector;
 
 namespace test_vendor_lib {
-bool Keyboard::registered_ = DeviceBoutique::Register(LOG_TAG, &Keyboard::Create);
+bool Keyboard::registered_ = DeviceBoutique::Register("keyboard", &Keyboard::Create);
 
 Keyboard::Keyboard() {
-  properties_.SetLeAdvertisementType(BTM_BLE_CONNECT_EVT);
+  properties_.SetLeAdvertisementType(0x00 /* CONNECTABLE */);
   properties_.SetLeAdvertisement({0x11,  // Length
-                                  BTM_BLE_AD_TYPE_NAME_CMPL,
+                                  0x09 /* TYPE_NAME_CMPL */,
                                   'g',
                                   'D',
                                   'e',
@@ -54,11 +52,11 @@
                                   0x12,
                                   0x18,
                                   0x02,  // Length
-                                  BTM_BLE_AD_TYPE_FLAG,
-                                  BTM_BLE_BREDR_NOT_SPT | BTM_BLE_GEN_DISC_FLAG});
+                                  0x01 /* TYPE_FLAGS */,
+                                  0x04 /* BREDR_NOT_SPT */ | 0x02 /* GEN_DISC_FLAG */});
 
   properties_.SetLeScanResponse({0x04,  // Length
-                                 BTM_BLE_AD_TYPE_NAME_SHORT, 'k', 'e', 'y'});
+                                 0x08 /* TYPE_NAME_SHORT */, 'k', 'e', 'y'});
 }
 
 std::string Keyboard::GetTypeString() const {
@@ -82,7 +80,7 @@
   }
 }
 
-void Keyboard::IncomingPacket(packets::LinkLayerPacketView packet) {
+void Keyboard::IncomingPacket(model::packets::LinkLayerPacketView packet) {
   if (!connected_) {
     Beacon::IncomingPacket(packet);
   }
diff --git a/vendor_libs/test_vendor_lib/model/devices/keyboard.h b/vendor_libs/test_vendor_lib/model/devices/keyboard.h
index 871fa52..cf00dfe 100644
--- a/vendor_libs/test_vendor_lib/model/devices/keyboard.h
+++ b/vendor_libs/test_vendor_lib/model/devices/keyboard.h
@@ -40,7 +40,8 @@
   // Initialize the device based on the values of |args|.
   virtual void Initialize(const std::vector<std::string>& args) override;
 
-  virtual void IncomingPacket(packets::LinkLayerPacketView packet) override;
+  virtual void IncomingPacket(
+      model::packets::LinkLayerPacketView packet) override;
 
   virtual void TimerTick() override;
 
diff --git a/vendor_libs/test_vendor_lib/model/devices/link_layer_socket_device.cc b/vendor_libs/test_vendor_lib/model/devices/link_layer_socket_device.cc
index 9ccfd61..ddff51b 100644
--- a/vendor_libs/test_vendor_lib/model/devices/link_layer_socket_device.cc
+++ b/vendor_libs/test_vendor_lib/model/devices/link_layer_socket_device.cc
@@ -14,17 +14,13 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "link_layer_socket_device"
-
 #include "link_layer_socket_device.h"
 
 #include <unistd.h>
 
-#include "osi/include/log.h"
-#include "packets/link_layer/link_layer_packet_builder.h"
-#include "packets/link_layer/link_layer_packet_view.h"
-#include "packets/packet_view.h"
-#include "packets/view.h"
+#include "packet/packet_view.h"
+#include "packet/raw_builder.h"
+#include "packet/view.h"
 
 using std::vector;
 
@@ -35,16 +31,18 @@
 
 void LinkLayerSocketDevice::TimerTick() {
   if (bytes_left_ == 0) {
-    received_ = std::make_shared<std::vector<uint8_t>>(Link::kSizeBytes);
-    size_t bytes_received = socket_.TryReceive(Link::kSizeBytes, received_->data());
+    auto packet_size = std::make_shared<std::vector<uint8_t>>(kSizeBytes);
+
+    size_t bytes_received = socket_.TryReceive(kSizeBytes, packet_size->data());
     if (bytes_received == 0) {
       return;
     }
-    CHECK(bytes_received == Link::kSizeBytes) << "bytes_received == " << bytes_received;
-    packets::PacketView<true> size({packets::View(received_, 0, Link::kSizeBytes)});
+    ASSERT_LOG(bytes_received == kSizeBytes, "bytes_received == %d", static_cast<int>(bytes_received));
+    bluetooth::packet::PacketView<bluetooth::packet::kLittleEndian> size(
+        {bluetooth::packet::View(packet_size, 0, kSizeBytes)});
     bytes_left_ = size.begin().extract<uint32_t>();
-    received_->resize(Link::kSizeBytes + bytes_left_);
-    offset_ = Link::kSizeBytes;
+    received_ = std::make_shared<std::vector<uint8_t>>(bytes_left_);
+    offset_ = 0;
   }
   size_t bytes_received = socket_.TryReceive(bytes_left_, received_->data() + offset_);
   if (bytes_received == 0) {
@@ -53,14 +51,28 @@
   bytes_left_ -= bytes_received;
   offset_ += bytes_received;
   if (bytes_left_ == 0) {
-    SendLinkLayerPacket(packets::LinkLayerPacketBuilder::ReWrap(received_), phy_type_);
+    bluetooth::packet::PacketView<bluetooth::packet::kLittleEndian> packet_view(
+        received_);
+    auto packet = model::packets::LinkLayerPacketView::Create(packet_view);
+    ASSERT(packet.IsValid());
+    SendLinkLayerPacket(packet, phy_type_);
     offset_ = 0;
     received_.reset();
   }
 }
 
-void LinkLayerSocketDevice::IncomingPacket(packets::LinkLayerPacketView packet) {
-  socket_.TrySend(packet);
+void LinkLayerSocketDevice::IncomingPacket(
+    model::packets::LinkLayerPacketView packet) {
+  auto size_packet = bluetooth::packet::RawBuilder();
+  size_packet.AddOctets4(packet.size());
+  std::vector<uint8_t> size_bytes;
+  bluetooth::packet::BitInserter bit_inserter(size_bytes);
+  size_packet.Serialize(bit_inserter);
+
+  if (socket_.TrySend(size_bytes) == kSizeBytes) {
+    std::vector<uint8_t> payload_bytes{packet.begin(), packet.end()};
+    socket_.TrySend(payload_bytes);
+  }
 }
 
 }  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/model/devices/link_layer_socket_device.h b/vendor_libs/test_vendor_lib/model/devices/link_layer_socket_device.h
index d3bbb2e..564233c 100644
--- a/vendor_libs/test_vendor_lib/model/devices/link_layer_socket_device.h
+++ b/vendor_libs/test_vendor_lib/model/devices/link_layer_socket_device.h
@@ -20,9 +20,8 @@
 #include <vector>
 
 #include "device.h"
-#include "include/link.h"
 #include "include/phy.h"
-#include "packets/link_layer/link_layer_packet_view.h"
+#include "packets/link_layer_packets.h"
 #include "polled_socket.h"
 
 namespace test_vendor_lib {
@@ -43,17 +42,20 @@
 
   virtual void Initialize(const std::vector<std::string>&) override {}
 
-  virtual void IncomingPacket(packets::LinkLayerPacketView packet) override;
+  virtual void IncomingPacket(
+      model::packets::LinkLayerPacketView packet) override;
 
   virtual void TimerTick() override;
 
+  static constexpr size_t kSizeBytes = sizeof(uint32_t);
+
  private:
   net::PolledSocket socket_;
   Phy::Type phy_type_;
   size_t bytes_left_{0};
   size_t offset_;
   std::shared_ptr<std::vector<uint8_t>> received_;
-  std::vector<packets::LinkLayerPacketView> packet_queue_;
+  std::vector<model::packets::LinkLayerPacketView> packet_queue_;
 };
 
 }  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/model/devices/loopback.cc b/vendor_libs/test_vendor_lib/model/devices/loopback.cc
index 4d9078e..df0c762 100644
--- a/vendor_libs/test_vendor_lib/model/devices/loopback.cc
+++ b/vendor_libs/test_vendor_lib/model/devices/loopback.cc
@@ -14,47 +14,33 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "loopback"
-
 #include "loopback.h"
 
 #include "le_advertisement.h"
 #include "model/setup/device_boutique.h"
-#include "osi/include/log.h"
+#include "os/log.h"
 
 using std::vector;
 
 namespace test_vendor_lib {
 
-bool Loopback::registered_ = DeviceBoutique::Register(LOG_TAG, &Loopback::Create);
+bool Loopback::registered_ = DeviceBoutique::Register("loopback", &Loopback::Create);
 
 Loopback::Loopback() {
   advertising_interval_ms_ = std::chrono::milliseconds(1280);
-  properties_.SetLeAdvertisementType(BTM_BLE_NON_CONNECT_EVT);
-  properties_.SetLeAdvertisement({0x11,  // Length
-                                  BTM_BLE_AD_TYPE_NAME_CMPL,
-                                  'g',
-                                  'D',
-                                  'e',
-                                  'v',
-                                  'i',
-                                  'c',
-                                  'e',
-                                  '-',
-                                  'l',
-                                  'o',
-                                  'o',
-                                  'p',
-                                  'b',
-                                  'a',
-                                  'c',
-                                  'k',
-                                  0x02,  // Length
-                                  BTM_BLE_AD_TYPE_FLAG,
-                                  BTM_BLE_BREDR_NOT_SPT | BTM_BLE_GEN_DISC_FLAG});
+  properties_.SetLeAdvertisementType(0x03);  // NON_CONNECT
+  properties_.SetLeAdvertisement({
+      0x11,  // Length
+      0x09,  // NAME_CMPL
+      'g',         'D', 'e', 'v', 'i', 'c', 'e', '-', 'l', 'o', 'o', 'p', 'b', 'a', 'c', 'k',
+      0x02,         // Length
+      0x01,         // TYPE_FLAG
+      0x04 | 0x02,  // BREDR_NOT_SPT | GEN_DISC
+  });
 
   properties_.SetLeScanResponse({0x05,  // Length
-                                 BTM_BLE_AD_TYPE_NAME_SHORT, 'l', 'o', 'o', 'p'});
+                                 0x08,  // NAME_SHORT
+                                 'l', 'o', 'o', 'p'});
 }
 
 std::string Loopback::GetTypeString() const {
@@ -80,18 +66,22 @@
 
 void Loopback::TimerTick() {}
 
-void Loopback::IncomingPacket(packets::LinkLayerPacketView packet) {
-  LOG_INFO(LOG_TAG, "Got a packet of type %d", static_cast<int>(packet.GetType()));
-  if (packet.GetDestinationAddress() == properties_.GetLeAddress() && packet.GetType() == Link::PacketType::LE_SCAN) {
-    LOG_INFO(LOG_TAG, "Got a scan");
-    std::unique_ptr<packets::LeAdvertisementBuilder> scan_response = packets::LeAdvertisementBuilder::Create(
-        LeAdvertisement::AddressType::PUBLIC, LeAdvertisement::AdvertisementType::SCAN_RESPONSE,
+void Loopback::IncomingPacket(model::packets::LinkLayerPacketView packet) {
+  LOG_INFO("Got a packet of type %d", static_cast<int>(packet.GetType()));
+  if (packet.GetDestinationAddress() == properties_.GetLeAddress() &&
+      packet.GetType() == model::packets::PacketType::LE_SCAN) {
+    LOG_INFO("Got a scan");
+
+    auto scan_response = model::packets::LeScanResponseBuilder::Create(
+        properties_.GetLeAddress(), packet.GetSourceAddress(),
+        model::packets::AddressType::PUBLIC,
+        model::packets::AdvertisementType::SCAN_RESPONSE,
         properties_.GetLeScanResponse());
-    std::shared_ptr<packets::LinkLayerPacketBuilder> to_send = packets::LinkLayerPacketBuilder::WrapLeScanResponse(
-        std::move(scan_response), properties_.GetLeAddress(), packet.GetSourceAddress());
-    std::vector<std::shared_ptr<PhyLayer>> le_phys = phy_layers_[Phy::Type::LOW_ENERGY];
-    for (auto phy : le_phys) {
-      LOG_INFO(LOG_TAG, "Sending a Scan Response on a Phy");
+    std::shared_ptr<model::packets::LinkLayerPacketBuilder> to_send =
+        std::move(scan_response);
+
+    for (auto phy : phy_layers_[Phy::Type::LOW_ENERGY]) {
+      LOG_INFO("Sending a Scan Response on a Phy");
       phy->Send(to_send);
     }
   }
diff --git a/vendor_libs/test_vendor_lib/model/devices/loopback.h b/vendor_libs/test_vendor_lib/model/devices/loopback.h
index 31dd1cd..dd5c069 100644
--- a/vendor_libs/test_vendor_lib/model/devices/loopback.h
+++ b/vendor_libs/test_vendor_lib/model/devices/loopback.h
@@ -42,7 +42,8 @@
   // Set the address and advertising interval from string args.
   virtual void Initialize(const std::vector<std::string>& args) override;
 
-  virtual void IncomingPacket(packets::LinkLayerPacketView packet) override;
+  virtual void IncomingPacket(
+      model::packets::LinkLayerPacketView packet) override;
 
   virtual void TimerTick() override;
 
diff --git a/vendor_libs/test_vendor_lib/model/devices/polled_socket.cc b/vendor_libs/test_vendor_lib/model/devices/polled_socket.cc
index 5719132..6fea766 100644
--- a/vendor_libs/test_vendor_lib/model/devices/polled_socket.cc
+++ b/vendor_libs/test_vendor_lib/model/devices/polled_socket.cc
@@ -14,11 +14,8 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "polled_socket"
-
 #include "polled_socket.h"
 
-#include <base/logging.h>
 #include <fcntl.h>
 #include <netdb.h>
 #include <netinet/in.h>
@@ -26,7 +23,7 @@
 #include <sys/types.h>
 #include <sys/uio.h>
 
-#include "osi/include/log.h"
+#include "os/log.h"
 
 namespace test_vendor_lib {
 namespace net {
@@ -48,19 +45,13 @@
   file_descriptor_ = -1;
 }
 
-size_t PolledSocket::TrySend(packets::PacketView<true> packet) {
+size_t PolledSocket::TrySend(const std::vector<uint8_t>& packet) {
   if (file_descriptor_ == -1) {
     return 0;
   }
-  // Could skip this copy if the packet is guaranteed to be contiguous.
-  std::vector<uint8_t> copy;
-  copy.reserve(packet.size());
-  for (const auto&& c : packet) {
-    copy.push_back(c);
-  }
-  int ret = write(file_descriptor_, copy.data(), copy.size());
+  int ret = write(file_descriptor_, packet.data(), packet.size());
   if (ret == -1) {
-    ALOGW("%s error %s", __func__, strerror(errno));
+    LOG_WARN("%s error %s", __func__, strerror(errno));
     return 0;
   } else {
     return static_cast<size_t>(ret);
@@ -100,7 +91,7 @@
     if (errno == EAGAIN) {
       return 0;
     } else {
-      ALOGW("%s error %s", __func__, strerror(errno));
+      LOG_WARN("%s error %s", __func__, strerror(errno));
       CleanUp();
       return 0;
     }
diff --git a/vendor_libs/test_vendor_lib/model/devices/polled_socket.h b/vendor_libs/test_vendor_lib/model/devices/polled_socket.h
index b697f56..a196fd6 100644
--- a/vendor_libs/test_vendor_lib/model/devices/polled_socket.h
+++ b/vendor_libs/test_vendor_lib/model/devices/polled_socket.h
@@ -26,8 +26,6 @@
 #include <sys/socket.h>
 #include <unistd.h>
 
-#include "packets/packet_view.h"
-
 namespace test_vendor_lib {
 namespace net {
 
@@ -41,7 +39,7 @@
   PolledSocket(PolledSocket&& p);
   virtual ~PolledSocket();
 
-  size_t TrySend(packets::PacketView<true> packet);
+  size_t TrySend(const std::vector<uint8_t>& packet);
   // size_t TrySendVector(const std::vector<const std::vector<uint8_t>&>& data);
   size_t TryReceive(size_t num_bytes, uint8_t* data);
 
diff --git a/vendor_libs/test_vendor_lib/model/devices/remote_loopback_device.cc b/vendor_libs/test_vendor_lib/model/devices/remote_loopback_device.cc
index 787e2f7..db87028 100644
--- a/vendor_libs/test_vendor_lib/model/devices/remote_loopback_device.cc
+++ b/vendor_libs/test_vendor_lib/model/devices/remote_loopback_device.cc
@@ -14,25 +14,20 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "remote_loopback_device"
-
 #include "remote_loopback_device.h"
 
 #include "model/setup/device_boutique.h"
-
-#include "osi/include/log.h"
-#include "packets/link_layer/link_layer_packet_builder.h"
-#include "packets/link_layer/link_layer_packet_view.h"
+#include "os/log.h"
 
 using std::vector;
 
 namespace test_vendor_lib {
 
-using packets::LinkLayerPacketBuilder;
-using packets::LinkLayerPacketView;
-using packets::PageResponseBuilder;
+using model::packets::LinkLayerPacketBuilder;
+using model::packets::LinkLayerPacketView;
+using model::packets::PageResponseBuilder;
 
-bool RemoteLoopbackDevice::registered_ = DeviceBoutique::Register(LOG_TAG, &RemoteLoopbackDevice::Create);
+bool RemoteLoopbackDevice::registered_ = DeviceBoutique::Register("remote_loopback", &RemoteLoopbackDevice::Create);
 
 RemoteLoopbackDevice::RemoteLoopbackDevice() {}
 
@@ -47,27 +42,23 @@
   if (Address::FromString(args[1], addr)) properties_.SetAddress(addr);
 }
 
-void RemoteLoopbackDevice::IncomingPacket(LinkLayerPacketView packet) {
+void RemoteLoopbackDevice::IncomingPacket(
+    model::packets::LinkLayerPacketView packet) {
   // TODO: Check sender?
   // TODO: Handle other packet types
   Phy::Type phy_type = Phy::Type::BR_EDR;
 
-  Link::PacketType type = packet.GetType();
+  model::packets::PacketType type = packet.GetType();
   switch (type) {
-    case Link::PacketType::PAGE:
-      SendLinkLayerPacket(LinkLayerPacketBuilder::WrapPageResponse(
-                              PageResponseBuilder::Create(true), packet.GetSourceAddress(), packet.GetSourceAddress()),
-                          Phy::Type::BR_EDR);
+    case model::packets::PacketType::PAGE:
+      SendLinkLayerPacket(
+          PageResponseBuilder::Create(packet.GetSourceAddress(),
+                                      packet.GetSourceAddress(), true),
+          Phy::Type::BR_EDR);
       break;
     default: {
-      ALOGW("Resend = %d", static_cast<int>(packet.size()));
-      std::shared_ptr<std::vector<uint8_t>> extracted_packet = std::make_shared<std::vector<uint8_t>>();
-      extracted_packet->reserve(packet.size());
-      for (const auto byte : packet) {
-        extracted_packet->push_back(byte);
-      }
-
-      SendLinkLayerPacket(LinkLayerPacketBuilder::ReWrap(extracted_packet), phy_type);
+      LOG_WARN("Resend = %d", static_cast<int>(packet.size()));
+      SendLinkLayerPacket(packet, phy_type);
     }
   }
 }
diff --git a/vendor_libs/test_vendor_lib/model/devices/remote_loopback_device.h b/vendor_libs/test_vendor_lib/model/devices/remote_loopback_device.h
index 5513677..98c9a83 100644
--- a/vendor_libs/test_vendor_lib/model/devices/remote_loopback_device.h
+++ b/vendor_libs/test_vendor_lib/model/devices/remote_loopback_device.h
@@ -20,7 +20,6 @@
 #include <vector>
 
 #include "device.h"
-#include "packets/link_layer/link_layer_packet_view.h"
 
 namespace test_vendor_lib {
 
@@ -41,7 +40,8 @@
 
   virtual void Initialize(const std::vector<std::string>& args) override;
 
-  virtual void IncomingPacket(packets::LinkLayerPacketView packet) override;
+  virtual void IncomingPacket(
+      model::packets::LinkLayerPacketView packet) override;
 
  private:
   static bool registered_;
diff --git a/vendor_libs/test_vendor_lib/model/devices/scripted_beacon.cc b/vendor_libs/test_vendor_lib/model/devices/scripted_beacon.cc
new file mode 100644
index 0000000..ffbdd8c
--- /dev/null
+++ b/vendor_libs/test_vendor_lib/model/devices/scripted_beacon.cc
@@ -0,0 +1,242 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "scripted_beacon.h"
+
+#include <fstream>
+#include <cstdint>
+#include <unistd.h>
+
+#include "model/devices/scripted_beacon_ble_payload.pb.h"
+#include "model/setup/device_boutique.h"
+#include "os/log.h"
+
+using std::vector;
+using std::chrono::steady_clock;
+using std::chrono::system_clock;
+
+namespace test_vendor_lib {
+bool ScriptedBeacon::registered_ =
+    DeviceBoutique::Register("scripted_beacon", &ScriptedBeacon::Create);
+ScriptedBeacon::ScriptedBeacon() {
+  advertising_interval_ms_ = std::chrono::milliseconds(1280);
+  properties_.SetLeAdvertisementType(0x02 /* SCANNABLE */);
+  properties_.SetLeAdvertisement({
+      0x18,  // Length
+      0x09 /* TYPE_NAME_CMPL */,
+      'g',
+      'D',
+      'e',
+      'v',
+      'i',
+      'c',
+      'e',
+      '-',
+      's',
+      'c',
+      'r',
+      'i',
+      'p',
+      't',
+      'e',
+      'd',
+      '-',
+      'b',
+      'e',
+      'a',
+      'c',
+      'o',
+      'n',
+      0x02,  // Length
+      0x01 /* TYPE_FLAG */,
+      0x4 /* BREDR_NOT_SPT */ | 0x2 /* GEN_DISC_FLAG */,
+  });
+
+  properties_.SetLeScanResponse({0x05,  // Length
+                                 0x08,  // TYPE_NAME_SHORT
+                                 'g', 'b', 'e', 'a'});
+  LOG_INFO("Scripted_beacon registered %s", registered_ ? "true" : "false");
+}
+
+bool has_time_elapsed(steady_clock::time_point time_point) {
+  return steady_clock::now() > time_point;
+}
+
+void ScriptedBeacon::Initialize(const vector<std::string>& args) {
+  if (args.size() < 2) {
+    LOG_ERROR(
+        "Initialization failed, need mac address, playback and playback events "
+        "file arguments");
+    return;
+  }
+
+  Address addr{};
+  if (Address::FromString(args[1], addr)) properties_.SetLeAddress(addr);
+
+  if (args.size() < 4) {
+    LOG_ERROR(
+        "Initialization failed, need playback and playback events file "
+        "arguments");
+  }
+  config_file_ = args[2];
+  events_file_ = args[3];
+  set_state(PlaybackEvent::INITIALIZED);
+}
+
+void ScriptedBeacon::populate_event(PlaybackEvent * event, PlaybackEvent::PlaybackEventType type) {
+  LOG_INFO("Adding event: %d", type);
+  event->set_type(type);
+  event->set_secs_since_epoch(system_clock::now().time_since_epoch().count());
+}
+
+// Adds events to events file; we won't be able to post anything to the file
+// until we set to permissive mode in tests. No events are posted until then.
+void ScriptedBeacon::set_state(PlaybackEvent::PlaybackEventType state) {
+  PlaybackEvent event;
+  current_state_ = state;
+  if (!events_ostream_.is_open()) {
+    events_ostream_.open(events_file_, std::ios::out | std::ios::binary | std::ios::trunc);
+    if (!events_ostream_.is_open()) {
+      LOG_INFO("Events file not opened yet, for event: %d", state);
+      return;
+    }
+  }
+  populate_event(&event, state);
+  event.SerializeToOstream(&events_ostream_);
+  events_ostream_.flush();
+}
+
+void ScriptedBeacon::TimerTick() {
+  switch (current_state_) {
+    case PlaybackEvent::INITIALIZED:
+      Beacon::TimerTick();
+      break;
+    case PlaybackEvent::SCANNED_ONCE:
+      next_check_time_ =
+          steady_clock::now() + steady_clock::duration(std::chrono::seconds(1));
+      set_state(PlaybackEvent::WAITING_FOR_FILE);
+      break;
+    case PlaybackEvent::WAITING_FOR_FILE:
+      if (!has_time_elapsed(next_check_time_)) {
+        return;
+      }
+      next_check_time_ =
+          steady_clock::now() + steady_clock::duration(std::chrono::seconds(1));
+      if (access(config_file_.c_str(), F_OK) == -1) {
+        return;
+      }
+      set_state(PlaybackEvent::WAITING_FOR_FILE_TO_BE_READABLE);
+      break;
+    case PlaybackEvent::WAITING_FOR_FILE_TO_BE_READABLE:
+      if (access(config_file_.c_str(), R_OK) == -1) {
+        return;
+      }
+      set_state(PlaybackEvent::PARSING_FILE);
+      break;
+    case PlaybackEvent::PARSING_FILE: {
+      if (!has_time_elapsed(next_check_time_)) {
+        return;
+      }
+      std::fstream input(config_file_, std::ios::in | std::ios::binary);
+      if (!ble_ad_list_.ParseFromIstream(&input)) {
+        LOG_ERROR("Cannot parse playback file %s", config_file_.c_str());
+        set_state(PlaybackEvent::FILE_PARSING_FAILED);
+        return;
+      } else {
+        set_state(PlaybackEvent::PLAYBACK_STARTED);
+        LOG_INFO("Starting Ble advertisement playback from file: %s",
+                 config_file_.c_str());
+        next_ad_.ad_time = steady_clock::now();
+        get_next_advertisement();
+        input.close();
+      }
+    } break;
+    case PlaybackEvent::PLAYBACK_STARTED: {
+      std::shared_ptr<model::packets::LinkLayerPacketBuilder> to_send;
+      while (has_time_elapsed(next_ad_.ad_time)) {
+        auto ad = model::packets::LeAdvertisementBuilder::Create(
+            next_ad_.address, Address::kEmpty /* Destination */,
+            model::packets::AddressType::RANDOM,
+            model::packets::AdvertisementType::ADV_NONCONN_IND, next_ad_.ad);
+        to_send = std::move(ad);
+        for (const auto& phy : phy_layers_[Phy::Type::LOW_ENERGY]) {
+          phy->Send(to_send);
+        }
+        if (packet_num_ < ble_ad_list_.advertisements().size()) {
+          get_next_advertisement();
+        } else {
+          set_state(PlaybackEvent::PLAYBACK_ENDED);
+          if (events_ostream_.is_open()) {
+            events_ostream_.close();
+          }
+          LOG_INFO(
+              "Completed Ble advertisement playback from file: %s with %d "
+              "packets",
+              config_file_.c_str(), packet_num_);
+          break;
+        }
+      }
+    } break;
+    case PlaybackEvent::FILE_PARSING_FAILED:
+    case PlaybackEvent::PLAYBACK_ENDED:
+    case PlaybackEvent::UNKNOWN:
+      return;
+  }
+}
+
+void ScriptedBeacon::IncomingPacket(
+    model::packets::LinkLayerPacketView packet) {
+  if (current_state_ == PlaybackEvent::INITIALIZED) {
+    if (packet.GetDestinationAddress() == properties_.GetLeAddress() &&
+        packet.GetType() == model::packets::PacketType::LE_SCAN) {
+      auto scan_response = model::packets::LeScanResponseBuilder::Create(
+          properties_.GetLeAddress(), packet.GetSourceAddress(),
+          static_cast<model::packets::AddressType>(
+              properties_.GetLeAddressType()),
+          model::packets::AdvertisementType::SCAN_RESPONSE,
+          properties_.GetLeScanResponse());
+      std::shared_ptr<model::packets::LinkLayerPacketBuilder> to_send =
+          std::move(scan_response);
+      set_state(PlaybackEvent::SCANNED_ONCE);
+      for (const auto& phy : phy_layers_[Phy::Type::LOW_ENERGY]) {
+        phy->Send(to_send);
+      }
+    }
+  }
+}
+
+void ScriptedBeacon::get_next_advertisement() {
+  std::string payload = ble_ad_list_.advertisements(packet_num_).payload();
+  std::string mac_address =
+      ble_ad_list_.advertisements(packet_num_).mac_address();
+  uint32_t delay_before_send_ms =
+      ble_ad_list_.advertisements(packet_num_).delay_before_send_ms();
+  next_ad_.ad.assign(payload.begin(), payload.end());
+  if (Address::IsValidAddress(mac_address)) {
+    // formatted string with colons like "12:34:56:78:9a:bc"
+    Address::FromString(mac_address, next_ad_.address);
+  } else if (mac_address.size() == Address::kLength) {
+    // six-byte binary address
+    std::vector<uint8_t> mac_vector(mac_address.cbegin(), mac_address.cend());
+    next_ad_.address.Address::FromOctets(mac_vector.data());
+  } else {
+    Address::FromString("BA:D0:AD:BA:D0:AD", next_ad_.address);
+  }
+  next_ad_.ad_time +=
+      steady_clock::duration(std::chrono::milliseconds(delay_before_send_ms));
+  packet_num_++;
+}
+}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/model/devices/scripted_beacon.h b/vendor_libs/test_vendor_lib/model/devices/scripted_beacon.h
new file mode 100644
index 0000000..05406d0
--- /dev/null
+++ b/vendor_libs/test_vendor_lib/model/devices/scripted_beacon.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <vector>
+#include <fstream>
+
+#include "model/devices/scripted_beacon_ble_payload.pb.h"
+#include "beacon.h"
+
+using android::bluetooth::test_vendor_lib::model::devices::ScriptedBeaconBleAdProto::PlaybackEvent;
+
+namespace test_vendor_lib {
+// Pretend to be a lot of beacons by advertising from a file.
+class ScriptedBeacon : public Beacon {
+ public:
+  ScriptedBeacon();
+  virtual ~ScriptedBeacon() = default;
+
+  static std::shared_ptr<Device> Create() {
+    return std::make_shared<ScriptedBeacon>();
+  }
+
+  // Return a string representation of the type of device.
+  virtual std::string GetTypeString() const override {
+    return "scripted_beacon";
+  }
+
+  virtual std::string ToString() const override {
+    return "scripted_beacon " + config_file_;
+  }
+
+  // Set the address and advertising interval from string args.
+  void Initialize(const std::vector<std::string>& args) override;
+
+  void TimerTick() override;
+
+  void IncomingPacket(model::packets::LinkLayerPacketView packet_view) override;
+
+ private:
+  static bool registered_;
+  std::string config_file_{};
+  std::string events_file_{};
+  std::ofstream events_ostream_;
+  struct Advertisement {
+    std::vector<uint8_t> ad;
+    Address address;
+    std::chrono::steady_clock::time_point ad_time;
+  };
+
+  void populate_event(PlaybackEvent * event, PlaybackEvent::PlaybackEventType type);
+
+  void get_next_advertisement();
+
+  void set_state(
+      android::bluetooth::test_vendor_lib::model::devices::
+          ScriptedBeaconBleAdProto::PlaybackEvent::PlaybackEventType type);
+
+  Advertisement next_ad_{};
+  int packet_num_{0};
+  PlaybackEvent::PlaybackEventType current_state_{PlaybackEvent::UNKNOWN};
+  std::chrono::steady_clock::time_point next_check_time_{};
+  android::bluetooth::test_vendor_lib::model::devices::ScriptedBeaconBleAdProto::BleAdvertisementList ble_ad_list_;
+};
+}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/model/devices/scripted_beacon_ble_payload.proto b/vendor_libs/test_vendor_lib/model/devices/scripted_beacon_ble_payload.proto
new file mode 100644
index 0000000..588519c
--- /dev/null
+++ b/vendor_libs/test_vendor_lib/model/devices/scripted_beacon_ble_payload.proto
@@ -0,0 +1,33 @@
+syntax = "proto2";
+
+package android.bluetooth.test_vendor_lib.model.devices.ScriptedBeaconBleAdProto;
+
+option optimize_for = LITE_RUNTIME;
+
+message BleAdvertisement {
+  optional bytes payload = 1;
+  optional bytes mac_address = 2;
+  optional uint32 delay_before_send_ms = 3;
+}
+
+message BleAdvertisementList {
+  repeated BleAdvertisement advertisements = 1;
+}
+
+message PlaybackEvent {
+  // These events should occur in order, starting from INITIALIZED
+  enum PlaybackEventType {
+    UNKNOWN = 0;
+    INITIALIZED = 1;
+    SCANNED_ONCE = 2;
+    WAITING_FOR_FILE = 3;
+    WAITING_FOR_FILE_TO_BE_READABLE = 4;
+    PARSING_FILE = 5;
+    PLAYBACK_STARTED = 6;
+    PLAYBACK_ENDED = 7;
+    // Error conditions
+    FILE_PARSING_FAILED = 8;
+  }
+  optional PlaybackEventType type = 1;
+  optional uint64 secs_since_epoch = 2;
+}
diff --git a/vendor_libs/test_vendor_lib/model/devices/server_port_factory.cc b/vendor_libs/test_vendor_lib/model/devices/server_port_factory.cc
index 72008a6..b0b2165 100644
--- a/vendor_libs/test_vendor_lib/model/devices/server_port_factory.cc
+++ b/vendor_libs/test_vendor_lib/model/devices/server_port_factory.cc
@@ -14,13 +14,9 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "server_port_factory"
-
 #include "server_port_factory.h"
 
-#include <base/logging.h>
-
-#include "osi/include/log.h"
+#include "os/log.h"
 #include "osi/include/osi.h"
 
 #include <netinet/in.h>
@@ -43,23 +39,23 @@
 
   OSI_NO_INTR(listen_fd_ = socket(AF_INET, SOCK_STREAM, 0));
   if (listen_fd_ < 0) {
-    LOG_INFO(LOG_TAG, "Error creating socket for test channel.");
+    LOG_INFO("Error creating socket for test channel.");
     return -1;
   }
 
-  LOG_INFO(LOG_TAG, "port: %d", port);
+  LOG_INFO("port: %d", port);
   listen_address.sin_family = AF_INET;
   listen_address.sin_port = htons(port);
   listen_address.sin_addr.s_addr = htonl(INADDR_ANY);
 
   if (bind(listen_fd_, reinterpret_cast<sockaddr*>(&listen_address), sockaddr_in_size) < 0) {
-    LOG_INFO(LOG_TAG, "Error binding test channel listener socket to address.");
+    LOG_INFO("Error binding test channel listener socket to address.");
     close(listen_fd_);
     return -1;
   }
 
   if (listen(listen_fd_, 1) < 0) {
-    LOG_INFO(LOG_TAG, "Error listening for test channel.");
+    LOG_INFO("Error listening for test channel.");
     close(listen_fd_);
     return -1;
   }
@@ -71,7 +67,7 @@
     return;
   }
   if (close(listen_fd_)) {
-    LOG_ERROR(LOG_TAG, "Error closing listen_fd_.");
+    LOG_ERROR("Error closing listen_fd_.");
   }
   listen_fd_ = -1;
 }
@@ -84,16 +80,16 @@
 
   OSI_NO_INTR(accept_fd = accept(listen_fd_, reinterpret_cast<sockaddr*>(&test_channel_address), &sockaddr_in_size));
   if (accept_fd < 0) {
-    LOG_INFO(LOG_TAG, "Error accepting test channel connection errno=%d (%s).", errno, strerror(errno));
+    LOG_INFO("Error accepting test channel connection errno=%d (%s).", errno, strerror(errno));
 
     if (errno != EAGAIN && errno != EWOULDBLOCK) {
-      LOG_ERROR(LOG_TAG, "Closing listen_fd_ (won't try again).");
+      LOG_ERROR("Closing listen_fd_ (won't try again).");
       close(listen_fd_);
       return -1;
     }
   }
 
-  LOG_INFO(LOG_TAG, "accept_fd = %d.", accept_fd);
+  LOG_INFO("accept_fd = %d.", accept_fd);
 
   return accept_fd;
 }
@@ -107,7 +103,7 @@
   std::string command_name(command_name_raw.begin(), command_name_raw.end());
 
   if (command_name == "CLOSE_TEST_CHANNEL" || command_name == "") {
-    LOG_INFO(LOG_TAG, "Test channel closed");
+    LOG_INFO("Test channel closed");
     unwatch();
     close(fd);
     return;
@@ -137,9 +133,9 @@
   char size_buf[4] = {static_cast<uint8_t>(size & 0xff), static_cast<uint8_t>((size >> 8) & 0xff),
                       static_cast<uint8_t>((size >> 16) & 0xff), static_cast<uint8_t>((size >> 24) & 0xff)};
   int written = write(fd, size_buf, 4);
-  CHECK(written == 4) << "What happened? written = " << written << "errno =" << errno;
+  ASSERT_LOG(written == 4, "What happened? written = %d errno = %d", written, errno);
   written = write(fd, response.c_str(), size);
-  CHECK(written == static_cast<int>(size)) << "What happened? written = " << written << "errno =" << errno;
+  ASSERT_LOG(written == static_cast<int>(size), "What happened? written = %d errno = %d", written, errno);
 }
 
 void ServerPortFactory::RegisterCommandHandler(
diff --git a/vendor_libs/test_vendor_lib/model/devices/sniffer.cc b/vendor_libs/test_vendor_lib/model/devices/sniffer.cc
index 903ecac..efdca87 100644
--- a/vendor_libs/test_vendor_lib/model/devices/sniffer.cc
+++ b/vendor_libs/test_vendor_lib/model/devices/sniffer.cc
@@ -14,19 +14,16 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "sniffer"
-
 #include "sniffer.h"
 
-#include "osi/include/log.h"
-
 #include "model/setup/device_boutique.h"
+#include "os/log.h"
 
 using std::vector;
 
 namespace test_vendor_lib {
 
-bool Sniffer::registered_ = DeviceBoutique::Register(LOG_TAG, &Sniffer::Create);
+bool Sniffer::registered_ = DeviceBoutique::Register("sniffer", &Sniffer::Create);
 
 Sniffer::Sniffer() {}
 
@@ -42,7 +39,7 @@
 
 void Sniffer::TimerTick() {}
 
-void Sniffer::IncomingPacket(packets::LinkLayerPacketView packet) {
+void Sniffer::IncomingPacket(model::packets::LinkLayerPacketView packet) {
   Address source = packet.GetSourceAddress();
   Address dest = packet.GetDestinationAddress();
   bool match_source = device_to_sniff_ == source;
@@ -50,8 +47,10 @@
   if (!match_source && !match_dest) {
     return;
   }
-  LOG_INFO(LOG_TAG, "%s %s -> %s (Type %d)", (match_source ? (match_dest ? "<->" : "<--") : "-->"),
-           source.ToString().c_str(), dest.ToString().c_str(), static_cast<int>(packet.GetType()));
+  LOG_INFO("%s %s -> %s (Type %s)",
+           (match_source ? (match_dest ? "<->" : "<--") : "-->"),
+           source.ToString().c_str(), dest.ToString().c_str(),
+           model::packets::PacketTypeText(packet.GetType()).c_str());
 }
 
 }  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/model/devices/sniffer.h b/vendor_libs/test_vendor_lib/model/devices/sniffer.h
index 3e0cfef..a5c7277 100644
--- a/vendor_libs/test_vendor_lib/model/devices/sniffer.h
+++ b/vendor_libs/test_vendor_lib/model/devices/sniffer.h
@@ -20,11 +20,13 @@
 #include <vector>
 
 #include "device.h"
-#include "packets/link_layer/link_layer_packet_view.h"
-#include "types/address.h"
+#include "hci/address.h"
+#include "packets/link_layer_packets.h"
 
 namespace test_vendor_lib {
 
+using ::bluetooth::hci::Address;
+
 class Sniffer : public Device {
  public:
   Sniffer();
@@ -42,7 +44,8 @@
     return "sniffer";
   }
 
-  virtual void IncomingPacket(packets::LinkLayerPacketView packet) override;
+  virtual void IncomingPacket(
+      model::packets::LinkLayerPacketView packet) override;
 
   virtual void TimerTick() override;
 
diff --git a/vendor_libs/test_vendor_lib/model/setup/async_manager.cc b/vendor_libs/test_vendor_lib/model/setup/async_manager.cc
index fb9b1ad..092271d 100644
--- a/vendor_libs/test_vendor_lib/model/setup/async_manager.cc
+++ b/vendor_libs/test_vendor_lib/model/setup/async_manager.cc
@@ -14,19 +14,17 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "async_manager"
-
 #include "async_manager.h"
 
-#include "osi/include/log.h"
-
 #include <algorithm>
 #include <atomic>
 #include <condition_variable>
 #include <mutex>
 #include <thread>
 #include <vector>
+
 #include "fcntl.h"
+#include "os/log.h"
 #include "sys/select.h"
 #include "unistd.h"
 
@@ -105,7 +103,7 @@
     // start the thread if not started yet
     int started = tryStartThread();
     if (started != 0) {
-      LOG_ERROR(LOG_TAG, "%s: Unable to start thread", __func__);
+      LOG_ERROR("%s: Unable to start thread", __func__);
       return started;
     }
 
@@ -134,7 +132,7 @@
     if (std::this_thread::get_id() != thread_.get_id()) {
       thread_.join();
     } else {
-      LOG_WARN(LOG_TAG, "%s: Starting thread stop from inside the reading thread itself", __func__);
+      LOG_WARN("%s: Starting thread stop from inside the reading thread itself", __func__);
     }
 
     {
@@ -158,10 +156,10 @@
     // set up the communication channel
     int pipe_fds[2];
     if (pipe2(pipe_fds, O_NONBLOCK)) {
-      LOG_ERROR(LOG_TAG,
-                "%s:Unable to establish a communication channel to the reading "
-                "thread",
-                __func__);
+      LOG_ERROR(
+          "%s:Unable to establish a communication channel to the reading "
+          "thread",
+          __func__);
       return -1;
     }
     notification_listen_fd_ = pipe_fds[0];
@@ -169,7 +167,7 @@
 
     thread_ = std::thread([this]() { ThreadRoutine(); });
     if (!thread_.joinable()) {
-      LOG_ERROR(LOG_TAG, "%s: Unable to start reading thread", __func__);
+      LOG_ERROR("%s: Unable to start reading thread", __func__);
       return -1;
     }
     return 0;
@@ -178,7 +176,7 @@
   int notifyThread() {
     char buffer = '0';
     if (TEMP_FAILURE_RETRY(write(notification_write_fd_, &buffer, 1)) < 0) {
-      LOG_ERROR(LOG_TAG, "%s: Unable to send message to reading thread", __func__);
+      LOG_ERROR("%s: Unable to send message to reading thread", __func__);
       return -1;
     }
     return 0;
@@ -239,10 +237,10 @@
       // wait until there is data available to read on some FD
       int retval = select(nfds + 1, &read_fds, NULL, NULL, NULL);
       if (retval <= 0) {  // there was some error or a timeout
-        LOG_ERROR(LOG_TAG,
-                  "%s: There was an error while waiting for data on the file "
-                  "descriptors: %s",
-                  __func__, strerror(errno));
+        LOG_ERROR(
+            "%s: There was an error while waiting for data on the file "
+            "descriptors: %s",
+            __func__, strerror(errno));
         continue;
       }
 
@@ -310,7 +308,7 @@
     if (std::this_thread::get_id() != thread_.get_id()) {
       thread_.join();
     } else {
-      LOG_WARN(LOG_TAG, "%s: Starting thread stop from inside the task thread itself", __func__);
+      LOG_WARN("%s: Starting thread stop from inside the task thread itself", __func__);
     }
     return 0;
   }
@@ -371,7 +369,7 @@
     // start thread if necessary
     int started = tryStartThread();
     if (started != 0) {
-      LOG_ERROR(LOG_TAG, "%s: Unable to start thread", __func__);
+      LOG_ERROR("%s: Unable to start thread", __func__);
       return kInvalidTaskId;
     }
     // notify the thread so that it knows of the new task
@@ -395,7 +393,7 @@
     running_ = true;
     thread_ = std::thread([this]() { ThreadRoutine(); });
     if (!thread_.joinable()) {
-      LOG_ERROR(LOG_TAG, "%s: Unable to start task thread", __func__);
+      LOG_ERROR("%s: Unable to start task thread", __func__);
       return -1;
     }
     return 0;
diff --git a/vendor_libs/test_vendor_lib/model/setup/device_boutique.cc b/vendor_libs/test_vendor_lib/model/setup/device_boutique.cc
index 18de1b1..de3b1cc 100644
--- a/vendor_libs/test_vendor_lib/model/setup/device_boutique.cc
+++ b/vendor_libs/test_vendor_lib/model/setup/device_boutique.cc
@@ -14,12 +14,9 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "device_boutique"
-
 #include "device_boutique.h"
 
-#include "base/logging.h"
-#include "osi/include/log.h"
+#include "os/log.h"
 
 using std::vector;
 
@@ -33,16 +30,16 @@
 // Register a constructor for a device type.
 bool DeviceBoutique::Register(const std::string& device_type,
                               const std::function<std::shared_ptr<Device>()> device_constructor) {
-  LOG_INFO(LOG_TAG, "Registering %s", device_type.c_str());
+  LOG_INFO("Registering %s", device_type.c_str());
   GetMap()[device_type] = device_constructor;
   return true;
 }
 
 std::shared_ptr<Device> DeviceBoutique::Create(const vector<std::string>& args) {
-  CHECK(!args.empty());
+  ASSERT(!args.empty());
 
   if (GetMap().find(args[0]) == GetMap().end()) {
-    LOG_WARN(LOG_TAG, "No constructor registered for %s", args[0].c_str());
+    LOG_WARN("No constructor registered for %s", args[0].c_str());
     return std::shared_ptr<Device>(nullptr);
   }
 
diff --git a/vendor_libs/test_vendor_lib/model/setup/phy_layer.h b/vendor_libs/test_vendor_lib/model/setup/phy_layer.h
index 84e4cba..7f0fa33 100644
--- a/vendor_libs/test_vendor_lib/model/setup/phy_layer.h
+++ b/vendor_libs/test_vendor_lib/model/setup/phy_layer.h
@@ -17,22 +17,33 @@
 #pragma once
 
 #include "include/phy.h"
-#include "packets/link_layer/link_layer_packet_builder.h"
-#include "packets/link_layer/link_layer_packet_view.h"
 
+#include "packets/link_layer_packets.h"
 namespace test_vendor_lib {
 
 class PhyLayer {
  public:
-  PhyLayer(Phy::Type phy_type, uint32_t id, const std::function<void(packets::LinkLayerPacketView)>& device_receive)
-      : phy_type_(phy_type), id_(id), transmit_to_device_(device_receive) {}
+  PhyLayer(Phy::Type phy_type, uint32_t id,
+           const std::function<void(model::packets::LinkLayerPacketView)>&
+               device_receive,
+           uint32_t device_id)
+      : phy_type_(phy_type),
+        id_(id),
+        device_id_(device_id),
+        transmit_to_device_(device_receive) {}
 
-  virtual void Send(const std::shared_ptr<packets::LinkLayerPacketBuilder> packet) = 0;
+  virtual void Send(
+      const std::shared_ptr<model::packets::LinkLayerPacketBuilder> packet) = 0;
+  virtual void Send(model::packets::LinkLayerPacketView packet) = 0;
 
-  virtual void Receive(packets::LinkLayerPacketView packet) = 0;
+  virtual void Receive(model::packets::LinkLayerPacketView packet) = 0;
 
   virtual void TimerTick() = 0;
 
+  virtual bool IsFactoryId(uint32_t factory_id) = 0;
+
+  virtual void Unregister() = 0;
+
   Phy::Type GetType() {
     return phy_type_;
   }
@@ -41,14 +52,18 @@
     return id_;
   }
 
+  uint32_t GetDeviceId() { return device_id_; }
+
   virtual ~PhyLayer() = default;
 
  private:
   Phy::Type phy_type_;
   uint32_t id_;
+  uint32_t device_id_;
 
  protected:
-  const std::function<void(packets::LinkLayerPacketView)> transmit_to_device_;
+  const std::function<void(model::packets::LinkLayerPacketView)>
+      transmit_to_device_;
 };
 
 }  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/model/setup/phy_layer_factory.cc b/vendor_libs/test_vendor_lib/model/setup/phy_layer_factory.cc
index 182416a..b963ce0 100644
--- a/vendor_libs/test_vendor_lib/model/setup/phy_layer_factory.cc
+++ b/vendor_libs/test_vendor_lib/model/setup/phy_layer_factory.cc
@@ -14,26 +14,29 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "phy_layer_factory"
-
 #include "phy_layer_factory.h"
-
-#include "base/logging.h"
-
-#include "osi/include/log.h"
+#include <sstream>
 
 namespace test_vendor_lib {
 
-PhyLayerFactory::PhyLayerFactory(Phy::Type phy_type) : phy_type_(phy_type) {}
+PhyLayerFactory::PhyLayerFactory(Phy::Type phy_type, uint32_t factory_id)
+    : phy_type_(phy_type), factory_id_(factory_id) {}
 
 Phy::Type PhyLayerFactory::GetType() {
   return phy_type_;
 }
 
+uint32_t PhyLayerFactory::GetFactoryId() {
+  return factory_id_;
+}
+
 std::shared_ptr<PhyLayer> PhyLayerFactory::GetPhyLayer(
-    const std::function<void(packets::LinkLayerPacketView)>& device_receive) {
-  std::shared_ptr<PhyLayer> new_phy =
-      std::make_shared<PhyLayerImpl>(phy_type_, next_id_++, device_receive, std::shared_ptr<PhyLayerFactory>(this));
+    const std::function<void(model::packets::LinkLayerPacketView)>&
+        device_receive,
+    uint32_t device_id) {
+  std::shared_ptr<PhyLayer> new_phy = std::make_shared<PhyLayerImpl>(
+      phy_type_, next_id_++, device_receive, device_id,
+      std::shared_ptr<PhyLayerFactory>(this));
   phy_layers_.push_back(new_phy);
   return new_phy;
 }
@@ -48,56 +51,87 @@
   }
 }
 
-void PhyLayerFactory::Send(const std::shared_ptr<packets::LinkLayerPacketBuilder> packet, uint32_t id) {
+void PhyLayerFactory::Send(
+    const std::shared_ptr<model::packets::LinkLayerPacketBuilder> packet,
+    uint32_t id) {
   // Convert from a Builder to a View
-  std::shared_ptr<std::vector<uint8_t>> serialized_packet =
-      std::shared_ptr<std::vector<uint8_t>>(new std::vector<uint8_t>());
-  std::back_insert_iterator<std::vector<uint8_t>> itr(*serialized_packet);
-  serialized_packet->reserve(packet->size());
-  packet->Serialize(itr);
-  packets::LinkLayerPacketView packet_view = packets::LinkLayerPacketView::Create(serialized_packet);
+  auto bytes = std::make_shared<std::vector<uint8_t>>();
+  bluetooth::packet::BitInserter i(*bytes);
+  bytes->reserve(packet->size());
+  packet->Serialize(i);
+  auto packet_view =
+      bluetooth::packet::PacketView<bluetooth::packet::kLittleEndian>(bytes);
+  auto link_layer_packet_view =
+      model::packets::LinkLayerPacketView::Create(packet_view);
+  ASSERT(link_layer_packet_view.IsValid());
 
-  for (const auto phy : phy_layers_) {
+  Send(link_layer_packet_view, id);
+}
+
+void PhyLayerFactory::Send(model::packets::LinkLayerPacketView packet,
+                           uint32_t id) {
+  for (const auto& phy : phy_layers_) {
     if (id != phy->GetId()) {
-      phy->Receive(packet_view);
+      phy->Receive(packet);
     }
   }
 }
 
 void PhyLayerFactory::TimerTick() {
-  for (auto phy : phy_layers_) {
+  for (auto& phy : phy_layers_) {
     phy->TimerTick();
   }
 }
 
 std::string PhyLayerFactory::ToString() const {
+  std::stringstream factory;
   switch (phy_type_) {
     case Phy::Type::LOW_ENERGY:
-      return "LOW_ENERGY";
+      factory << "LOW_ENERGY: ";
       break;
     case Phy::Type::BR_EDR:
-      return "BR_EDR";
+      factory << "BR_EDR: ";
       break;
     default:
-      return "Unknown";
+      factory << "Unknown: ";
   }
+  for (auto& phy : phy_layers_) {
+    factory << phy->GetDeviceId();
+    factory << ",";
+  }
+
+  return factory.str();
 }
 
-PhyLayerImpl::PhyLayerImpl(Phy::Type phy_type, uint32_t id,
-                           const std::function<void(packets::LinkLayerPacketView)>& device_receive,
-                           const std::shared_ptr<PhyLayerFactory>& factory)
-    : PhyLayer(phy_type, id, device_receive), factory_(factory) {}
+PhyLayerImpl::PhyLayerImpl(
+    Phy::Type phy_type, uint32_t id,
+    const std::function<void(model::packets::LinkLayerPacketView)>&
+        device_receive,
+    uint32_t device_id, const std::shared_ptr<PhyLayerFactory> factory)
+    : PhyLayer(phy_type, id, device_receive, device_id), factory_(factory) {}
 
 PhyLayerImpl::~PhyLayerImpl() {
-  factory_->UnregisterPhyLayer(GetId());
-  PhyLayer::~PhyLayer();
+  Unregister();
 }
 
-void PhyLayerImpl::Send(const std::shared_ptr<packets::LinkLayerPacketBuilder> packet) {
+void PhyLayerImpl::Send(
+    const std::shared_ptr<model::packets::LinkLayerPacketBuilder> packet) {
   factory_->Send(packet, GetId());
 }
 
-void PhyLayerImpl::Receive(packets::LinkLayerPacketView packet) {
+void PhyLayerImpl::Send(model::packets::LinkLayerPacketView packet) {
+  factory_->Send(packet, GetId());
+}
+
+void PhyLayerImpl::Unregister() {
+  factory_->UnregisterPhyLayer(GetId());
+}
+
+bool PhyLayerImpl::IsFactoryId(uint32_t id) {
+  return factory_->GetFactoryId() == id;
+}
+
+void PhyLayerImpl::Receive(model::packets::LinkLayerPacketView packet) {
   transmit_to_device_(packet);
 }
 
diff --git a/vendor_libs/test_vendor_lib/model/setup/phy_layer_factory.h b/vendor_libs/test_vendor_lib/model/setup/phy_layer_factory.h
index 018ff78..7d46733 100644
--- a/vendor_libs/test_vendor_lib/model/setup/phy_layer_factory.h
+++ b/vendor_libs/test_vendor_lib/model/setup/phy_layer_factory.h
@@ -20,8 +20,7 @@
 #include <vector>
 
 #include "include/phy.h"
-#include "packets/link_layer/link_layer_packet_builder.h"
-#include "packets/link_layer/link_layer_packet_view.h"
+#include "packets/link_layer_packets.h"
 #include "phy_layer.h"
 
 namespace test_vendor_lib {
@@ -30,13 +29,18 @@
   friend class PhyLayerImpl;
 
  public:
-  PhyLayerFactory(Phy::Type phy_type);
+  PhyLayerFactory(Phy::Type phy_type, uint32_t factory_id);
 
   virtual ~PhyLayerFactory() = default;
 
   Phy::Type GetType();
 
-  std::shared_ptr<PhyLayer> GetPhyLayer(const std::function<void(packets::LinkLayerPacketView)>& device_receive);
+  uint32_t GetFactoryId();
+
+  std::shared_ptr<PhyLayer> GetPhyLayer(
+      const std::function<void(model::packets::LinkLayerPacketView)>&
+          device_receive,
+      uint32_t device_id);
 
   void UnregisterPhyLayer(uint32_t id);
 
@@ -45,23 +49,37 @@
   virtual std::string ToString() const;
 
  protected:
-  virtual void Send(const std::shared_ptr<packets::LinkLayerPacketBuilder> packet, uint32_t id);
+  virtual void Send(
+      const std::shared_ptr<model::packets::LinkLayerPacketBuilder> packet,
+      uint32_t id);
+  virtual void Send(model::packets::LinkLayerPacketView packet, uint32_t id);
 
  private:
   Phy::Type phy_type_;
   std::vector<std::shared_ptr<PhyLayer>> phy_layers_;
   uint32_t next_id_{1};
+  const uint32_t factory_id_;
 };
 
 class PhyLayerImpl : public PhyLayer {
  public:
-  PhyLayerImpl(Phy::Type phy_type, uint32_t id, const std::function<void(packets::LinkLayerPacketView)>& device_receive,
-               const std::shared_ptr<PhyLayerFactory>& factory);
+  PhyLayerImpl(Phy::Type phy_type, uint32_t id,
+               const std::function<void(model::packets::LinkLayerPacketView)>&
+                   device_receive,
+               uint32_t device_id,
+               const std::shared_ptr<PhyLayerFactory> factory);
   virtual ~PhyLayerImpl() override;
 
-  virtual void Send(const std::shared_ptr<packets::LinkLayerPacketBuilder> packet) override;
-  virtual void Receive(packets::LinkLayerPacketView packet) override;
-  virtual void TimerTick() override;
+  virtual void Send(
+      const std::shared_ptr<model::packets::LinkLayerPacketBuilder> packet)
+      override;
+  void Send(model::packets::LinkLayerPacketView packet) override;
+  void Receive(model::packets::LinkLayerPacketView packet) override;
+  void Unregister() override;
+  bool IsFactoryId(uint32_t factory_id) override;
+  void TimerTick() override;
+
+  uint32_t device_id_;
 
  private:
   std::shared_ptr<PhyLayerFactory> factory_;
diff --git a/vendor_libs/test_vendor_lib/model/setup/test_channel_transport.cc b/vendor_libs/test_vendor_lib/model/setup/test_channel_transport.cc
index d291896..d42bc72 100644
--- a/vendor_libs/test_vendor_lib/model/setup/test_channel_transport.cc
+++ b/vendor_libs/test_vendor_lib/model/setup/test_channel_transport.cc
@@ -14,17 +14,16 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "test_channel_transport"
-
 #include "test_channel_transport.h"
 
-#include <base/logging.h>
-
-#include "osi/include/log.h"
+#include "os/log.h"
 #include "osi/include/osi.h"
 
+#include <errno.h>
+#include <fcntl.h>
 #include <netinet/in.h>
 #include <sys/socket.h>
+#include <unistd.h>
 
 using std::vector;
 
@@ -37,23 +36,28 @@
 
   OSI_NO_INTR(listen_fd_ = socket(AF_INET, SOCK_STREAM, 0));
   if (listen_fd_ < 0) {
-    LOG_INFO(LOG_TAG, "Error creating socket for test channel.");
+    LOG_INFO("Error creating socket for test channel.");
     return -1;
   }
 
-  LOG_INFO(LOG_TAG, "port: %d", port);
+  int enable = 1;
+  if (setsockopt(listen_fd_, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) {
+    LOG_ERROR("setsockopt(SO_REUSEADDR) failed: %s", strerror(errno));
+  }
+
+  LOG_INFO("port: %d", port);
   listen_address.sin_family = AF_INET;
   listen_address.sin_port = htons(port);
   listen_address.sin_addr.s_addr = htonl(INADDR_ANY);
 
   if (bind(listen_fd_, reinterpret_cast<sockaddr*>(&listen_address), sockaddr_in_size) < 0) {
-    LOG_INFO(LOG_TAG, "Error binding test channel listener socket to address.");
+    LOG_INFO("Error binding test channel listener socket to address: %s", strerror(errno));
     close(listen_fd_);
     return -1;
   }
 
   if (listen(listen_fd_, 1) < 0) {
-    LOG_INFO(LOG_TAG, "Error listening for test channel.");
+    LOG_INFO("Error listening for test channel: %s", strerror(errno));
     close(listen_fd_);
     return -1;
   }
@@ -65,7 +69,7 @@
     return;
   }
   if (close(listen_fd_)) {
-    LOG_ERROR(LOG_TAG, "Error closing listen_fd_.");
+    LOG_ERROR("Error closing listen_fd_.");
   }
   listen_fd_ = -1;
 }
@@ -76,16 +80,16 @@
   OSI_NO_INTR(accept_fd = accept(listen_fd_, NULL, NULL));
 
   if (accept_fd < 0) {
-    LOG_INFO(LOG_TAG, "Error accepting test channel connection errno=%d (%s).", errno, strerror(errno));
+    LOG_INFO("Error accepting test channel connection errno=%d (%s).", errno, strerror(errno));
 
     if (errno != EAGAIN && errno != EWOULDBLOCK) {
-      LOG_ERROR(LOG_TAG, "Closing listen_fd_ (won't try again).");
+      LOG_ERROR("Closing listen_fd_ (won't try again).");
       close(listen_fd_);
       return -1;
     }
   }
 
-  LOG_INFO(LOG_TAG, "accept_fd = %d.", accept_fd);
+  LOG_INFO("accept_fd = %d.", accept_fd);
 
   return accept_fd;
 }
@@ -94,18 +98,18 @@
   uint8_t command_name_size = 0;
   int bytes_read = read(fd, &command_name_size, 1);
   if (bytes_read != 1) {
-    LOG_INFO(LOG_TAG, "Unexpected (command_name_size) bytes_read: %d != %d", bytes_read, 1);
+    LOG_INFO("Unexpected (command_name_size) bytes_read: %d != %d", bytes_read, 1);
   }
   vector<uint8_t> command_name_raw;
   command_name_raw.resize(command_name_size);
   bytes_read = read(fd, &command_name_raw[0], command_name_size);
   if (bytes_read != command_name_size) {
-    LOG_INFO(LOG_TAG, "Unexpected (command_name) bytes_read: %d != %d", bytes_read, command_name_size);
+    LOG_INFO("Unexpected (command_name) bytes_read: %d != %d", bytes_read, command_name_size);
   }
   std::string command_name(command_name_raw.begin(), command_name_raw.end());
 
   if (command_name == "CLOSE_TEST_CHANNEL" || command_name == "") {
-    LOG_INFO(LOG_TAG, "Test channel closed");
+    LOG_INFO("Test channel closed");
     unwatch();
     close(fd);
     return;
@@ -114,20 +118,20 @@
   uint8_t num_args = 0;
   bytes_read = read(fd, &num_args, 1);
   if (bytes_read != 1) {
-    LOG_INFO(LOG_TAG, "Unexpected (num_args) bytes_read: %d != %d", bytes_read, 1);
+    LOG_INFO("Unexpected (num_args) bytes_read: %d != %d", bytes_read, 1);
   }
   vector<std::string> args;
   for (uint8_t i = 0; i < num_args; ++i) {
     uint8_t arg_size = 0;
     bytes_read = read(fd, &arg_size, 1);
     if (bytes_read != 1) {
-      LOG_INFO(LOG_TAG, "Unexpected (arg_size) bytes_read: %d != %d", bytes_read, 1);
+      LOG_INFO("Unexpected (arg_size) bytes_read: %d != %d", bytes_read, 1);
     }
     vector<uint8_t> arg;
     arg.resize(arg_size);
     bytes_read = read(fd, &arg[0], arg_size);
     if (bytes_read != arg_size) {
-      LOG_INFO(LOG_TAG, "Unexpected (arg) bytes_read: %d != %d", bytes_read, arg_size);
+      LOG_INFO("Unexpected (arg) bytes_read: %d != %d", bytes_read, arg_size);
     }
     args.push_back(std::string(arg.begin(), arg.end()));
   }
@@ -144,9 +148,9 @@
   uint8_t size_buf[4] = {static_cast<uint8_t>(size & 0xff), static_cast<uint8_t>((size >> 8) & 0xff),
                          static_cast<uint8_t>((size >> 16) & 0xff), static_cast<uint8_t>((size >> 24) & 0xff)};
   int written = write(fd, size_buf, 4);
-  CHECK(written == 4) << "What happened? written = " << written << "errno =" << errno;
+  ASSERT_LOG(written == 4, "What happened? written = %d errno = %d", written, errno);
   written = write(fd, response.c_str(), size);
-  CHECK(written == static_cast<int>(size)) << "What happened? written = " << written << "errno =" << errno;
+  ASSERT_LOG(written == static_cast<int>(size), "What happened? written = %d errno = %d", written, errno);
 }
 
 void TestChannelTransport::RegisterCommandHandler(
diff --git a/vendor_libs/test_vendor_lib/model/setup/test_command_handler.cc b/vendor_libs/test_vendor_lib/model/setup/test_command_handler.cc
index ad8689d..a2b8b2e 100644
--- a/vendor_libs/test_vendor_lib/model/setup/test_command_handler.cc
+++ b/vendor_libs/test_vendor_lib/model/setup/test_command_handler.cc
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "test_command_handler"
-
 #include "test_command_handler.h"
 #include "device_boutique.h"
 #include "phy.h"
@@ -24,12 +22,11 @@
 
 #include <stdlib.h>
 
-#include <base/logging.h>
 #include "base/files/file_util.h"
 #include "base/json/json_reader.h"
 #include "base/values.h"
 
-#include "osi/include/log.h"
+#include "os/log.h"
 #include "osi/include/osi.h"
 
 using std::vector;
@@ -47,6 +44,7 @@
   SET_HANDLER("add_device_to_phy", AddDeviceToPhy);
   SET_HANDLER("del_device_from_phy", DelDeviceFromPhy);
   SET_HANDLER("list", List);
+  SET_HANDLER("set_device_address", SetDeviceAddress);
   SET_HANDLER("set_timer_period", SetTimerPeriod);
   SET_HANDLER("start_timer", StartTimer);
   SET_HANDLER("stop_timer", StopTimer);
@@ -59,29 +57,29 @@
   AddPhy({"BR_EDR"});
 
   // Add the controller to the Phys
-  AddDeviceToPhy({"0", "0"});
-  AddDeviceToPhy({"0", "1"});
+  AddDeviceToPhy({"1", "1"});
+  AddDeviceToPhy({"1", "2"});
 
   // Add default test devices and add the devices to the phys
   // Add({"beacon", "be:ac:10:00:00:01", "1000"});
-  // AddDeviceToPhy({"1", "0"});
+  // AddDeviceToPhy({"2", "1"});
 
   // Add({"keyboard", "cc:1c:eb:0a:12:d1", "500"});
-  // AddDeviceToPhy({"2", "0"});
-
-  // Add({"classic", "c1:a5:51:c0:00:01", "22"});
   // AddDeviceToPhy({"3", "1"});
 
+  // Add({"classic", "c1:a5:51:c0:00:01", "22"});
+  // AddDeviceToPhy({"4", "2"});
+
   // Add({"car_kit", "ca:12:1c:17:00:01", "238"});
-  // AddDeviceToPhy({"4", "1"});
+  // AddDeviceToPhy({"5", "2"});
 
   // Add({"sniffer", "ca:12:1c:17:00:01"});
-  // AddDeviceToPhy({"5", "1"});
+  // AddDeviceToPhy({"6", "2"});
 
   // Add({"sniffer", "3c:5a:b4:04:05:06"});
-  // AddDeviceToPhy({"1", "1"});
+  // AddDeviceToPhy({"7", "2"});
   // Add({"remote_loopback_device", "10:0d:00:ba:c1:06"});
-  // AddDeviceToPhy({"2", "1"});
+  // AddDeviceToPhy({"8", "2"});
   List({});
 
   SetTimerPeriod({"10"});
@@ -113,11 +111,11 @@
   if (new_dev == NULL) {
     response_string_ = "TestCommandHandler 'add' " + args[0] + " failed!";
     send_response_(response_string_);
-    LOG_WARN(LOG_TAG, "%s", response_string_.c_str());
+    LOG_WARN("%s", response_string_.c_str());
     return;
   }
 
-  LOG_INFO(LOG_TAG, "Add %s", new_dev->ToString().c_str());
+  LOG_INFO("Add %s", new_dev->ToString().c_str());
   size_t dev_index = model_.Add(new_dev);
   response_string_ = std::to_string(dev_index) + std::string(":") + new_dev->ToString();
   send_response_(response_string_);
@@ -160,11 +158,9 @@
 
 void TestCommandHandler::AddPhy(const vector<std::string>& args) {
   if (args[0] == "LOW_ENERGY") {
-    std::shared_ptr<PhyLayerFactory> new_phy = std::make_shared<PhyLayerFactory>(Phy::Type::LOW_ENERGY);
-    model_.AddPhy(new_phy);
+    model_.AddPhy(Phy::Type::LOW_ENERGY);
   } else if (args[0] == "BR_EDR") {
-    std::shared_ptr<PhyLayerFactory> new_phy = std::make_shared<PhyLayerFactory>(Phy::Type::BR_EDR);
-    model_.AddPhy(new_phy);
+    model_.AddPhy(Phy::Type::BR_EDR);
   } else {
     response_string_ = "TestCommandHandler 'add_phy' with unrecognized type " + args[0];
     send_response_(response_string_);
@@ -211,15 +207,31 @@
 
 void TestCommandHandler::List(const vector<std::string>& args) {
   if (args.size() > 0) {
-    LOG_INFO(LOG_TAG, "Unused args: arg[0] = %s", args[0].c_str());
+    LOG_INFO("Unused args: arg[0] = %s", args[0].c_str());
     return;
   }
   send_response_(model_.List());
 }
 
+void TestCommandHandler::SetDeviceAddress(const vector<std::string>& args) {
+  if (args.size() != 2) {
+    response_string_ = "TestCommandHandler 'set_device_address' takes two arguments";
+    send_response_(response_string_);
+    return;
+  }
+  size_t device_id = std::stoi(args[0]);
+  Address device_address;
+  Address::FromString(args[1], device_address);
+  model_.SetDeviceAddress(device_id, device_address);
+  response_string_ = "set_device_address " + args[0];
+  response_string_ += " ";
+  response_string_ += args[1];
+  send_response_(response_string_);
+}
+
 void TestCommandHandler::SetTimerPeriod(const vector<std::string>& args) {
   if (args.size() != 1) {
-    LOG_INFO(LOG_TAG, "SetTimerPeriod takes 1 argument");
+    LOG_INFO("SetTimerPeriod takes 1 argument");
   }
   size_t period = std::stoi(args[0]);
   model_.SetTimerPeriod(std::chrono::milliseconds(period));
@@ -227,14 +239,14 @@
 
 void TestCommandHandler::StartTimer(const vector<std::string>& args) {
   if (args.size() > 0) {
-    LOG_INFO(LOG_TAG, "Unused args: arg[0] = %s", args[0].c_str());
+    LOG_INFO("Unused args: arg[0] = %s", args[0].c_str());
   }
   model_.StartTimer();
 }
 
 void TestCommandHandler::StopTimer(const vector<std::string>& args) {
   if (args.size() > 0) {
-    LOG_INFO(LOG_TAG, "Unused args: arg[0] = %s", args[0].c_str());
+    LOG_INFO("Unused args: arg[0] = %s", args[0].c_str());
   }
   model_.StopTimer();
 }
diff --git a/vendor_libs/test_vendor_lib/model/setup/test_command_handler.h b/vendor_libs/test_vendor_lib/model/setup/test_command_handler.h
index 409966a..4c62d84 100644
--- a/vendor_libs/test_vendor_lib/model/setup/test_command_handler.h
+++ b/vendor_libs/test_vendor_lib/model/setup/test_command_handler.h
@@ -69,6 +69,9 @@
   // List the devices that the test knows about
   void List(const std::vector<std::string>& args);
 
+  // Change the device's MAC address
+  void SetDeviceAddress(const std::vector<std::string>& args);
+
   // Timer management functions
   void SetTimerPeriod(const std::vector<std::string>& args);
 
diff --git a/vendor_libs/test_vendor_lib/model/setup/test_model.cc b/vendor_libs/test_vendor_lib/model/setup/test_model.cc
index 3572761..71ef77d 100644
--- a/vendor_libs/test_vendor_lib/model/setup/test_model.cc
+++ b/vendor_libs/test_vendor_lib/model/setup/test_model.cc
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "test_model"
-
 #include "test_model.h"
 
 // TODO: Remove when registration works
@@ -25,18 +23,20 @@
 #include "model/devices/classic.h"
 #include "model/devices/keyboard.h"
 #include "model/devices/remote_loopback_device.h"
+#include "model/devices/scripted_beacon.h"
 #include "model/devices/sniffer.h"
 
 #include <memory>
 
 #include <stdlib.h>
+#include <iomanip>
+#include <iostream>
 
-#include <base/logging.h>
 #include "base/files/file_util.h"
 #include "base/json/json_reader.h"
 #include "base/values.h"
 
-#include "osi/include/log.h"
+#include "os/log.h"
 #include "osi/include/osi.h"
 
 #include "device_boutique.h"
@@ -64,6 +64,7 @@
   example_devices_.push_back(std::make_shared<CarKit>());
   example_devices_.push_back(std::make_shared<Classic>());
   example_devices_.push_back(std::make_shared<Sniffer>());
+  example_devices_.push_back(std::make_shared<ScriptedBeacon>());
   example_devices_.push_back(std::make_shared<RemoteLoopbackDevice>());
 }
 
@@ -78,73 +79,87 @@
 }
 
 void TestModel::StartTimer() {
-  LOG_INFO(LOG_TAG, "StartTimer()");
+  LOG_INFO("StartTimer()");
   timer_tick_task_ =
       schedule_periodic_task_(std::chrono::milliseconds(0), timer_period_, [this]() { TestModel::TimerTick(); });
 }
 
 void TestModel::StopTimer() {
-  LOG_INFO(LOG_TAG, "StopTimer()");
+  LOG_INFO("StopTimer()");
   cancel_task_(timer_tick_task_);
   timer_tick_task_ = kInvalidTaskId;
 }
 
 size_t TestModel::Add(std::shared_ptr<Device> new_dev) {
-  devices_.push_back(new_dev);
-  return devices_.size() - 1;
+  devices_counter_++;
+  devices_[devices_counter_] = new_dev;
+  return devices_counter_;
 }
 
 void TestModel::Del(size_t dev_index) {
-  if (dev_index >= devices_.size()) {
-    LOG_WARN(LOG_TAG, "del: index out of range!");
+  auto device = devices_.find(dev_index);
+  if (device == devices_.end()) {
+    LOG_WARN("Del: can't find device!");
     return;
   }
-  devices_.erase(devices_.begin() + dev_index);
+  devices_.erase(dev_index);
 }
 
-size_t TestModel::AddPhy(std::shared_ptr<PhyLayerFactory> new_phy) {
-  phys_.push_back(new_phy);
-  return phys_.size() - 1;
+size_t TestModel::AddPhy(Phy::Type phy_type) {
+  phys_counter_++;
+  std::shared_ptr<PhyLayerFactory> new_phy = std::make_shared<PhyLayerFactory>(phy_type, phys_counter_);
+  phys_[phys_counter_] = new_phy;
+  return phys_counter_;
 }
 
 void TestModel::DelPhy(size_t phy_index) {
-  if (phy_index >= phys_.size()) {
-    LOG_WARN(LOG_TAG, "del_phy: index %d out of range: ", static_cast<int>(phy_index));
+  auto phy = phys_.find(phy_index);
+  if (phy == phys_.end()) {
+    LOG_WARN("DelPhy: can't find device!");
     return;
   }
+  phys_.erase(phy_index);
 }
 
 void TestModel::AddDeviceToPhy(size_t dev_index, size_t phy_index) {
-  if (dev_index >= devices_.size()) {
-    LOG_WARN(LOG_TAG, "add_device_to_phy: device out of range: ");
+  auto device = devices_.find(dev_index);
+  if (device == devices_.end()) {
+    LOG_WARN("%s: can't find device!", __func__);
     return;
   }
-  if (phy_index >= phys_.size()) {
-    LOG_WARN(LOG_TAG, "add_device_to_phy: phy out of range: ");
+  auto phy = phys_.find(phy_index);
+  if (phy == phys_.end()) {
+    LOG_WARN("%s: can't find phy!", __func__);
     return;
   }
-  std::shared_ptr<Device> dev = devices_[dev_index];
-  dev->RegisterPhyLayer(
-      phys_[phy_index]->GetPhyLayer([dev](packets::LinkLayerPacketView packet) { dev->IncomingPacket(packet); }));
+  auto dev = device->second;
+  dev->RegisterPhyLayer(phy->second->GetPhyLayer(
+      [dev](model::packets::LinkLayerPacketView packet) {
+        dev->IncomingPacket(packet);
+      },
+      device->first));
 }
 
 void TestModel::DelDeviceFromPhy(size_t dev_index, size_t phy_index) {
-  if (dev_index >= devices_.size()) {
-    LOG_WARN(LOG_TAG, "del_device_from_phy: device out of range: ");
+  auto device = devices_.find(dev_index);
+  if (device == devices_.end()) {
+    LOG_WARN("%s: can't find device!", __func__);
     return;
   }
-  if (phy_index >= phys_.size()) {
-    LOG_WARN(LOG_TAG, "del_device_from_phy: phy out of range: ");
+  auto phy = phys_.find(phy_index);
+  if (phy == phys_.end()) {
+    LOG_WARN("%s: can't find phy!", __func__);
     return;
   }
+  device->second->UnregisterPhyLayer(phy->second->GetType(), phy->second->GetFactoryId());
 }
 
 void TestModel::AddLinkLayerConnection(int socket_fd, Phy::Type phy_type) {
   std::shared_ptr<Device> dev = LinkLayerSocketDevice::Create(socket_fd, phy_type);
   int index = Add(dev);
-  for (size_t phy_index = 0; phy_index < phys_.size(); phy_index++) {
-    if (phy_type == phys_[phy_index]->GetType()) {
-      AddDeviceToPhy(index, phy_index);
+  for (auto& phy : phys_) {
+    if (phy_type == phy.second->GetType()) {
+      AddDeviceToPhy(index, phy.first);
     }
   }
 }
@@ -163,40 +178,64 @@
 }
 
 void TestModel::IncomingHciConnection(int socket_fd) {
-  std::shared_ptr<HciSocketDevice> dev = HciSocketDevice::Create(socket_fd);
-  // TODO: Auto-increment addresses?
-  static int hci_devs = 0;
-  int index = Add(std::static_pointer_cast<Device>(dev));
-  std::string addr = "da:4c:10:de:17:0";  // Da HCI dev
-  CHECK(hci_devs < 10) << "Why do you need more than 9?";
-  addr += '0' + hci_devs++;
+  auto dev = HciSocketDevice::Create(socket_fd);
+  size_t index = Add(std::static_pointer_cast<Device>(dev));
+  std::string addr = "da:4c:10:de:17:";  // Da HCI dev
+  std::stringstream stream;
+  stream << std::setfill('0') << std::setw(2) << std::hex << (index % 256);
+  addr += stream.str();
+
   dev->Initialize({"IgnoredTypeName", addr});
-  // TODO: Add device to all phys?  For now, just the first two.
-  for (size_t phy = 0; phy < 2 && phy < phys_.size(); phy++) {
-    AddDeviceToPhy(index, phy);
+  LOG_INFO("initialized %s", addr.c_str());
+  for (auto& phy : phys_) {
+    AddDeviceToPhy(index, phy.first);
   }
   dev->RegisterTaskScheduler(schedule_task_);
   dev->RegisterTaskCancel(cancel_task_);
+  dev->RegisterCloseCallback([this, socket_fd, index] { OnHciConnectionClosed(socket_fd, index); });
+}
+
+void TestModel::OnHciConnectionClosed(int socket_fd, size_t index) {
+  auto device = devices_.find(index);
+  if (device == devices_.end()) {
+    LOG_WARN("OnHciConnectionClosed: can't find device!");
+    return;
+  }
+  int close_result = close(socket_fd);
+  ASSERT_LOG(close_result == 0, "can't close: %s", strerror(errno));
+  device->second->UnregisterPhyLayers();
+  devices_.erase(index);
+}
+
+void TestModel::SetDeviceAddress(size_t index, Address address) {
+  auto device = devices_.find(index);
+  if (device == devices_.end()) {
+    LOG_WARN("SetDeviceAddress can't find device!");
+    return;
+  }
+  device->second->SetAddress(address);
 }
 
 const std::string& TestModel::List() {
   list_string_ = "";
   list_string_ += " Devices: \r\n";
-  for (size_t dev = 0; dev < devices_.size(); dev++) {
-    list_string_ += "  " + std::to_string(dev) + ":";
-    list_string_ += devices_[dev]->ToString() + " \r\n";
+  for (auto& dev : devices_) {
+    list_string_ += "  " + std::to_string(dev.first) + ":";
+    list_string_ += dev.second->ToString() + " \r\n";
   }
   list_string_ += " Phys: \r\n";
-  for (size_t phy = 0; phy < phys_.size(); phy++) {
-    list_string_ += "  " + std::to_string(phy) + ":";
-    list_string_ += phys_[phy]->ToString() + " \r\n";
+  for (auto& phy : phys_) {
+    list_string_ += "  " + std::to_string(phy.first) + ":";
+    list_string_ += phy.second->ToString() + " \r\n";
   }
   return list_string_;
 }
 
 void TestModel::TimerTick() {
-  for (size_t dev = 0; dev < devices_.size(); dev++) {
-    devices_[dev]->TimerTick();
+  for (auto dev = devices_.begin(); dev != devices_.end();) {
+    auto tmp = dev;
+    dev++;
+    tmp->second->TimerTick();
   }
 }
 
diff --git a/vendor_libs/test_vendor_lib/model/setup/test_model.h b/vendor_libs/test_vendor_lib/model/setup/test_model.h
index c799f3f..d651f50 100644
--- a/vendor_libs/test_vendor_lib/model/setup/test_model.h
+++ b/vendor_libs/test_vendor_lib/model/setup/test_model.h
@@ -47,7 +47,7 @@
   void Del(size_t device_index);
 
   // Add phy, return its index
-  size_t AddPhy(std::shared_ptr<PhyLayerFactory> phy);
+  size_t AddPhy(Phy::Type phy_type);
 
   // Remove phy by index
   void DelPhy(size_t phy_index);
@@ -63,9 +63,15 @@
   void IncomingLinkLayerConnection(int socket_fd);
   void IncomingHciConnection(int socket_fd);
 
+  // Handle closed remote connections
+  void OnHciConnectionClosed(int socket_fd, size_t index);
+
   // Connect to a remote device
   void AddRemote(const std::string& server, int port, Phy::Type phy_type);
 
+  // Set the device's Bluetooth address
+  void SetDeviceAddress(size_t device_index, Address device_address);
+
   // Let devices know about the passage of time
   void TimerTick();
   void StartTimer();
@@ -79,8 +85,10 @@
   void Reset();
 
  private:
-  std::vector<std::shared_ptr<PhyLayerFactory>> phys_;
-  std::vector<std::shared_ptr<Device>> devices_;
+  std::map<size_t, std::shared_ptr<PhyLayerFactory>> phys_;
+  size_t phys_counter_ = 0;
+  std::map<size_t, std::shared_ptr<Device>> devices_;
+  size_t devices_counter_ = 0;
   std::string list_string_;
 
   // Callbacks to schedule tasks.
diff --git a/vendor_libs/test_vendor_lib/packets/Android.bp b/vendor_libs/test_vendor_lib/packets/Android.bp
deleted file mode 100644
index 8a73a64..0000000
--- a/vendor_libs/test_vendor_lib/packets/Android.bp
+++ /dev/null
@@ -1,83 +0,0 @@
-// packet library for libbt-rootcanal
-// ========================================================
-cc_library_static {
-    name: "libbt-rootcanal-packets",
-    defaults: [
-        "libchrome_support_defaults",
-        "clang_file_coverage",
-    ],
-    host_supported: true,
-    proprietary: true,
-    srcs: [
-        "iterator.cc",
-        "counted_builder.cc",
-        "packet_view.cc",
-        "raw_builder.cc",
-        "view.cc",
-        "hci/acl_packet_builder.cc",
-        "hci/acl_packet_view.cc",
-        "hci/command_packet_builder.cc",
-        "hci/command_packet_view.cc",
-        "hci/event_packet_builder.cc",
-        "hci/event_payload_builder.cc",
-        "hci/hci_packet_builder.cc",
-        "hci/le_meta_event_builder.cc",
-        "hci/sco_packet_builder.cc",
-        "hci/sco_packet_view.cc",
-        "link_layer/link_layer_packet_builder.cc",
-        "link_layer/link_layer_packet_view.cc",
-    ],
-    cflags: [
-        "-fvisibility=hidden",
-    ],
-    local_include_dirs: [
-        ".",
-    ],
-    export_include_dirs: ["."],
-    include_dirs: [
-        "system/bt/vendor_libs/test_vendor_lib/include",
-        "system/bt/vendor_libs/test_vendor_lib/",
-        "system/bt/",
-    ],
-    shared_libs: [
-        "libbase",
-        "liblog",
-    ],
-}
-
-// Unit tests for the host
-// ========================================================
-cc_test_host {
-    name: "rootcanal-packets_test_host",
-    defaults: [
-        "libchrome_support_defaults",
-        "clang_file_coverage",
-        "clang_coverage_bin",
-    ],
-    srcs: [
-        "test/link_layer_packet_builder_test.cc",
-        "test/packet_builder_test.cc",
-        "test/packet_view_test.cc",
-        "hci/test/acl_builder_test.cc",
-        "hci/test/event_builder_test.cc",
-    ],
-    header_libs: [
-        "libbluetooth_headers",
-    ],
-    local_include_dirs: [
-        ".",
-    ],
-    include_dirs: [
-        "system/bt",
-        "system/bt/hci/include",
-        "system/bt/vendor_libs/test_vendor_lib",
-        "system/bt/vendor_libs/test_vendor_lib/include",
-    ],
-    shared_libs: [
-        "liblog",
-    ],
-    static_libs: [
-        "libbt-rootcanal-types",
-        "libbt-rootcanal-packets",
-    ],
-}
diff --git a/vendor_libs/test_vendor_lib/packets/base_packet_builder.h b/vendor_libs/test_vendor_lib/packets/base_packet_builder.h
deleted file mode 100644
index 92ae3a9..0000000
--- a/vendor_libs/test_vendor_lib/packets/base_packet_builder.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cstdint>
-#include <forward_list>
-#include <iterator>
-#include <memory>
-#include <vector>
-
-namespace test_vendor_lib {
-namespace packets {
-
-// A little-endian PacketBuilder might contain a big-endian PacketBuilder,
-// so BasePacketBuilder provides a common base class.
-class BasePacketBuilder {
- public:
-  virtual ~BasePacketBuilder() = default;
-
-  virtual size_t size() const = 0;
-
-  // Write to the vector with the given iterator.
-  virtual void Serialize(std::back_insert_iterator<std::vector<uint8_t>> it) const = 0;
-
- protected:
-  BasePacketBuilder() = default;
-};
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/counted_builder.cc b/vendor_libs/test_vendor_lib/packets/counted_builder.cc
deleted file mode 100644
index 10d3ceb..0000000
--- a/vendor_libs/test_vendor_lib/packets/counted_builder.cc
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "counted_builder.h"
-
-using std::vector;
-
-namespace test_vendor_lib {
-namespace packets {
-
-size_t CountedBuilder::size() const {
-  size_t payload_size = sizeof(uint8_t);
-  for (size_t i = 0; i < sub_builders_.size(); i++) {
-    payload_size += sub_builders_[i]->size();
-  }
-  return payload_size;
-}
-
-void CountedBuilder::Serialize(std::back_insert_iterator<std::vector<uint8_t>> it) const {
-  insert(static_cast<uint8_t>(sub_builders_.size()), it);
-  for (size_t i = 0; i < sub_builders_.size(); i++) {
-    sub_builders_[i]->Serialize(it);
-  }
-}
-
-void CountedBuilder::Add(std::unique_ptr<BasePacketBuilder> builder) {
-  sub_builders_.push_back(std::move(builder));
-}
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/counted_builder.h b/vendor_libs/test_vendor_lib/packets/counted_builder.h
deleted file mode 100644
index fed88c5..0000000
--- a/vendor_libs/test_vendor_lib/packets/counted_builder.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cstdint>
-#include <forward_list>
-#include <memory>
-#include <vector>
-
-#include "packets/base_packet_builder.h"
-#include "packets/packet_builder.h"
-#include "packets/raw_builder.h"
-
-namespace test_vendor_lib {
-namespace packets {
-
-class CountedBuilder : public RawBuilder {
- public:
-  CountedBuilder() = default;
-  virtual ~CountedBuilder() = default;
-
-  virtual size_t size() const override;
-
-  virtual void Serialize(std::back_insert_iterator<std::vector<uint8_t>> it) const override;
-
-  void Add(std::unique_ptr<BasePacketBuilder> builder);
-
- private:
-  std::vector<std::unique_ptr<BasePacketBuilder>> sub_builders_;
-};
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/hci/acl_packet_builder.cc b/vendor_libs/test_vendor_lib/packets/hci/acl_packet_builder.cc
deleted file mode 100644
index 00b0ba8..0000000
--- a/vendor_libs/test_vendor_lib/packets/hci/acl_packet_builder.cc
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "packets/hci/acl_packet_builder.h"
-
-#include <base/logging.h>
-
-using std::vector;
-using test_vendor_lib::acl::BroadcastFlagsType;
-using test_vendor_lib::acl::PacketBoundaryFlagsType;
-
-namespace test_vendor_lib {
-namespace packets {
-
-AclPacketBuilder::AclPacketBuilder(uint16_t handle, PacketBoundaryFlagsType packet_boundary_flags,
-                                   BroadcastFlagsType broadcast_flags, std::unique_ptr<BasePacketBuilder> payload)
-    : handle_(handle), packet_boundary_flags_(packet_boundary_flags), broadcast_flags_(broadcast_flags),
-      payload_(std::move(payload)) {}
-
-std::unique_ptr<AclPacketBuilder> AclPacketBuilder::Create(uint16_t handle,
-                                                           PacketBoundaryFlagsType packet_boundary_flags,
-                                                           BroadcastFlagsType broadcast_flags,
-                                                           std::unique_ptr<BasePacketBuilder> payload) {
-  return std::unique_ptr<AclPacketBuilder>(
-      new AclPacketBuilder(handle, packet_boundary_flags, broadcast_flags, std::move(payload)));
-}
-
-size_t AclPacketBuilder::size() const {
-  return 2 * sizeof(uint16_t) + payload_->size();
-}
-
-void AclPacketBuilder::Serialize(std::back_insert_iterator<std::vector<uint8_t>> it) const {
-  insert(static_cast<uint16_t>((handle_ & 0xfff) | (static_cast<uint16_t>(packet_boundary_flags_) << 12) |
-                               (static_cast<uint16_t>(broadcast_flags_) << 14)),
-         it);
-  uint16_t payload_size = payload_->size();
-
-  CHECK(static_cast<size_t>(payload_size) == payload_->size())
-      << "Payload too large for an ACL packet: " << payload_->size();
-  insert(payload_size, it);
-  payload_->Serialize(it);
-}
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/hci/acl_packet_builder.h b/vendor_libs/test_vendor_lib/packets/hci/acl_packet_builder.h
deleted file mode 100644
index cd2a607..0000000
--- a/vendor_libs/test_vendor_lib/packets/hci/acl_packet_builder.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <base/logging.h>
-#include <cstdint>
-#include <memory>
-#include <vector>
-
-#include "include/acl.h"
-#include "packets/hci/hci_packet_builder.h"
-#include "packets/packet_builder.h"
-
-namespace test_vendor_lib {
-namespace packets {
-
-// ACL data packets are specified in the Bluetooth Core Specification Version
-// 4.2, Volume 2, Part E, Section 5.4.2
-class AclPacketBuilder : public HciPacketBuilder {
- public:
-  virtual ~AclPacketBuilder() override = default;
-
-  static std::unique_ptr<AclPacketBuilder> Create(uint16_t handle, acl::PacketBoundaryFlagsType packet_boundary_flags,
-                                                  acl::BroadcastFlagsType broadcast_flags,
-                                                  std::unique_ptr<BasePacketBuilder> payload);
-
-  virtual size_t size() const override;
-  virtual void Serialize(std::back_insert_iterator<std::vector<uint8_t>> it) const;
-
- private:
-  AclPacketBuilder(uint16_t handle, acl::PacketBoundaryFlagsType packet_boundary_flags,
-                   acl::BroadcastFlagsType broadcast_flags, std::unique_ptr<BasePacketBuilder> payload);
-  AclPacketBuilder() = delete;
-  uint16_t handle_;
-  acl::PacketBoundaryFlagsType packet_boundary_flags_;
-  acl::BroadcastFlagsType broadcast_flags_;
-  std::unique_ptr<BasePacketBuilder> payload_;
-};
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/hci/acl_packet_view.cc b/vendor_libs/test_vendor_lib/packets/hci/acl_packet_view.cc
deleted file mode 100644
index bfec835..0000000
--- a/vendor_libs/test_vendor_lib/packets/hci/acl_packet_view.cc
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "packets/hci/acl_packet_view.h"
-
-#include <base/logging.h>
-
-using std::vector;
-using test_vendor_lib::acl::BroadcastFlagsType;
-using test_vendor_lib::acl::PacketBoundaryFlagsType;
-
-namespace test_vendor_lib {
-namespace packets {
-
-AclPacketView::AclPacketView(std::shared_ptr<std::vector<uint8_t>> packet) : PacketView<true>(packet) {}
-
-AclPacketView::AclPacketView(PacketView<true> packet_view) : PacketView<true>(packet_view) {}
-
-AclPacketView AclPacketView::Create(std::shared_ptr<std::vector<uint8_t>> packet) {
-  return AclPacketView(packet);
-}
-
-AclPacketView AclPacketView::Create(PacketView<true> packet_view) {
-  return AclPacketView(packet_view);
-}
-
-uint16_t AclPacketView::GetHandle() const {
-  return begin().extract<uint16_t>() & 0xfff;
-}
-
-PacketBoundaryFlagsType AclPacketView::GetPacketBoundaryFlags() const {
-  return static_cast<PacketBoundaryFlagsType>(((begin() + 1).extract<uint8_t>() & 0x30) >> 4);
-}
-
-BroadcastFlagsType AclPacketView::GetBroadcastFlags() const {
-  return static_cast<BroadcastFlagsType>(((begin() + 1).extract<uint8_t>() & 0xc0) >> 6);
-}
-
-PacketView<true> AclPacketView::GetPayload() const {
-  uint16_t payload_size = (begin() + sizeof(uint16_t)).extract<uint16_t>();
-  CHECK(static_cast<uint16_t>(size() - 2 * sizeof(uint16_t)) == payload_size)
-      << "Malformed ACL packet payload_size " << payload_size << " + 4 != " << size();
-  return SubViewLittleEndian(2 * sizeof(uint16_t), size());
-}
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/hci/acl_packet_view.h b/vendor_libs/test_vendor_lib/packets/hci/acl_packet_view.h
deleted file mode 100644
index 1e69cda..0000000
--- a/vendor_libs/test_vendor_lib/packets/hci/acl_packet_view.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cstdint>
-#include <memory>
-#include <vector>
-
-#include "include/acl.h"
-#include "packets/packet_view.h"
-
-namespace test_vendor_lib {
-namespace packets {
-
-// ACL data packets are specified in the Bluetooth Core Specification Version
-// 4.2, Volume 2, Part E, Section 5.4.2
-class AclPacketView : public PacketView<true> {
- public:
-  virtual ~AclPacketView() override = default;
-
-  static AclPacketView Create(std::shared_ptr<std::vector<uint8_t>> packet);
-  static AclPacketView Create(PacketView<true> packet_view);
-
-  uint16_t GetHandle() const;
-  acl::PacketBoundaryFlagsType GetPacketBoundaryFlags() const;
-  acl::BroadcastFlagsType GetBroadcastFlags() const;
-  PacketView<true> GetPayload() const;
-
- private:
-  AclPacketView(std::shared_ptr<std::vector<uint8_t>> packet);
-  AclPacketView(PacketView<true> packet_view);
-  AclPacketView() = delete;
-};
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/hci/command_packet_builder.cc b/vendor_libs/test_vendor_lib/packets/hci/command_packet_builder.cc
deleted file mode 100644
index 7074309..0000000
--- a/vendor_libs/test_vendor_lib/packets/hci/command_packet_builder.cc
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "packets/hci/command_packet_builder.h"
-
-#include <base/logging.h>
-
-using std::vector;
-using test_vendor_lib::hci::OpCode;
-
-namespace test_vendor_lib {
-namespace packets {
-
-CommandPacketBuilder::CommandPacketBuilder(OpCode opcode, std::unique_ptr<BasePacketBuilder> payload)
-    : opcode_(opcode), payload_(std::move(payload)) {}
-
-size_t CommandPacketBuilder::size() const {
-  return sizeof(uint16_t) + sizeof(uint8_t) + payload_->size();
-}
-
-void CommandPacketBuilder::Serialize(std::back_insert_iterator<std::vector<uint8_t>> it) const {
-  insert(static_cast<uint16_t>(opcode_), it);
-  uint8_t payload_size = static_cast<uint8_t>(payload_->size());
-
-  CHECK(static_cast<size_t>(payload_size) == payload_->size())
-      << "Payload too large for a command packet: " << payload_->size();
-  insert(payload_size, it);
-  payload_->Serialize(it);
-}
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/hci/command_packet_builder.h b/vendor_libs/test_vendor_lib/packets/hci/command_packet_builder.h
deleted file mode 100644
index d8e86a0..0000000
--- a/vendor_libs/test_vendor_lib/packets/hci/command_packet_builder.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <base/logging.h>
-#include <cstdint>
-#include <memory>
-#include <vector>
-
-#include "include/hci.h"
-#include "packets/hci/hci_packet_builder.h"
-#include "packets/packet_builder.h"
-
-namespace test_vendor_lib {
-namespace packets {
-
-// ACL data packets are specified in the Bluetooth Core Specification Version
-// 4.2, Volume 2, Part E, Section 5.4.2
-class CommandPacketBuilder : public HciPacketBuilder {
- public:
-  virtual ~CommandPacketBuilder() override = default;
-
-  static std::unique_ptr<CommandPacketBuilder> Create(hci::OpCode opcode, std::unique_ptr<BasePacketBuilder> payload);
-
-  virtual size_t size() const override;
-
-  virtual void Serialize(std::back_insert_iterator<std::vector<uint8_t>> it) const;
-
- private:
-  CommandPacketBuilder(hci::OpCode opcode, std::unique_ptr<BasePacketBuilder> payload);
-  CommandPacketBuilder() = delete;
-  hci::OpCode opcode_;
-  std::unique_ptr<BasePacketBuilder> payload_;
-};
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/hci/command_packet_view.cc b/vendor_libs/test_vendor_lib/packets/hci/command_packet_view.cc
deleted file mode 100644
index f0876c1..0000000
--- a/vendor_libs/test_vendor_lib/packets/hci/command_packet_view.cc
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "packets/hci/command_packet_view.h"
-
-#include <base/logging.h>
-
-using std::vector;
-
-namespace test_vendor_lib {
-namespace packets {
-
-CommandPacketView::CommandPacketView(std::shared_ptr<std::vector<uint8_t>> packet) : PacketView<true>(packet) {}
-
-CommandPacketView CommandPacketView::Create(std::shared_ptr<std::vector<uint8_t>> packet) {
-  return CommandPacketView(packet);
-}
-
-uint16_t CommandPacketView::GetOpcode() const {
-  return begin().extract<uint16_t>();
-}
-
-PacketView<true> CommandPacketView::GetPayload() const {
-  uint8_t payload_size = (begin() + sizeof(uint16_t)).extract<uint8_t>();
-  CHECK(static_cast<uint8_t>(size() - sizeof(uint16_t) - sizeof(uint8_t)) == payload_size)
-      << "Malformed Command packet payload_size " << payload_size << " + 2 != " << size();
-  return SubViewLittleEndian(sizeof(uint16_t) + sizeof(uint8_t), size());
-}
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/hci/command_packet_view.h b/vendor_libs/test_vendor_lib/packets/hci/command_packet_view.h
deleted file mode 100644
index 6355c1e..0000000
--- a/vendor_libs/test_vendor_lib/packets/hci/command_packet_view.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cstdint>
-#include <memory>
-#include <vector>
-
-#include "packets/packet_view.h"
-
-namespace test_vendor_lib {
-namespace packets {
-
-// Command packets are specified in the Bluetooth Core Specification Version
-// 4.2, Volume 2, Part E, Section 5.4.1
-class CommandPacketView : public PacketView<true> {
- public:
-  virtual ~CommandPacketView() override = default;
-
-  static CommandPacketView Create(std::shared_ptr<std::vector<uint8_t>> packet);
-
-  uint16_t GetOpcode() const;
-
-  PacketView<true> GetPayload() const;
-
- private:
-  CommandPacketView(std::shared_ptr<std::vector<uint8_t>> packet);
-  CommandPacketView() = delete;
-};
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/hci/event_packet_builder.cc b/vendor_libs/test_vendor_lib/packets/hci/event_packet_builder.cc
deleted file mode 100644
index 010decd..0000000
--- a/vendor_libs/test_vendor_lib/packets/hci/event_packet_builder.cc
+++ /dev/null
@@ -1,810 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "packets/hci/event_packet_builder.h"
-
-#include <base/logging.h>
-#include "hci.h"
-#include "packets/hci/le_meta_event_builder.h"
-
-using std::vector;
-using test_vendor_lib::hci::EventCode;
-using test_vendor_lib::hci::OpCode;
-using test_vendor_lib::hci::Status;
-
-namespace test_vendor_lib {
-namespace packets {
-
-EventPacketBuilder::EventPacketBuilder(EventCode event_code)
-    : event_code_(event_code), payload_(std::make_unique<RawBuilder>()) {}
-
-EventPacketBuilder::EventPacketBuilder(EventCode event_code, std::unique_ptr<RawBuilder> payload)
-    : event_code_(event_code), payload_(std::move(payload)) {}
-
-// Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.7.1
-std::unique_ptr<EventPacketBuilder> EventPacketBuilder::CreateInquiryCompleteEvent(hci::Status status) {
-  std::unique_ptr<EventPacketBuilder> evt_ptr =
-      std::unique_ptr<EventPacketBuilder>(new EventPacketBuilder(EventCode::INQUIRY_COMPLETE));
-  CHECK(evt_ptr->AddPayloadOctets1(static_cast<uint8_t>(status)));
-
-  return evt_ptr;
-}
-
-// Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.7.14
-std::unique_ptr<EventPacketBuilder> EventPacketBuilder::CreateCommandCompleteEvent(
-    hci::OpCode command_opcode, const vector<uint8_t>& event_return_parameters) {
-  std::unique_ptr<EventPacketBuilder> evt_ptr =
-      std::unique_ptr<EventPacketBuilder>(new EventPacketBuilder(EventCode::COMMAND_COMPLETE));
-
-  CHECK(evt_ptr->AddPayloadOctets1(1));  // num_hci_command_packets
-  CHECK(evt_ptr->AddPayloadOctets2(static_cast<uint16_t>(command_opcode)));
-  CHECK(evt_ptr->AddPayloadOctets(event_return_parameters));
-
-  return evt_ptr;
-}
-
-std::unique_ptr<EventPacketBuilder> EventPacketBuilder::CreateCommandCompleteOnlyStatusEvent(hci::OpCode command_opcode,
-                                                                                             hci::Status status) {
-  std::unique_ptr<EventPacketBuilder> evt_ptr =
-      std::unique_ptr<EventPacketBuilder>(new EventPacketBuilder(EventCode::COMMAND_COMPLETE));
-
-  CHECK(evt_ptr->AddPayloadOctets1(1));  // num_hci_command_packets
-  CHECK(evt_ptr->AddPayloadOctets2(static_cast<uint16_t>(command_opcode)));
-  CHECK(evt_ptr->AddPayloadOctets1(static_cast<uint8_t>(status)));
-
-  return evt_ptr;
-}
-
-std::unique_ptr<EventPacketBuilder> EventPacketBuilder::CreateCommandCompleteStatusAndAddressEvent(
-    hci::OpCode command_opcode, hci::Status status, const Address& address) {
-  std::unique_ptr<EventPacketBuilder> evt_ptr =
-      std::unique_ptr<EventPacketBuilder>(new EventPacketBuilder(EventCode::COMMAND_COMPLETE));
-
-  CHECK(evt_ptr->AddPayloadOctets1(1));  // num_hci_command_packets
-  CHECK(evt_ptr->AddPayloadOctets2(static_cast<uint16_t>(command_opcode)));
-  CHECK(evt_ptr->AddPayloadOctets1(static_cast<uint8_t>(status)));
-  CHECK(evt_ptr->AddPayloadAddress(address));
-
-  return evt_ptr;
-}
-
-std::unique_ptr<EventPacketBuilder> EventPacketBuilder::CreateCommandCompleteUnknownOpCodeEvent(
-    uint16_t command_opcode) {
-  std::unique_ptr<EventPacketBuilder> evt_ptr =
-      std::unique_ptr<EventPacketBuilder>(new EventPacketBuilder(EventCode::COMMAND_COMPLETE));
-
-  CHECK(evt_ptr->AddPayloadOctets1(1));  // num_hci_command_packets
-  CHECK(evt_ptr->AddPayloadOctets2(static_cast<uint16_t>(command_opcode)));
-  CHECK(evt_ptr->AddPayloadOctets1(static_cast<uint8_t>(Status::UNKNOWN_COMMAND)));
-
-  return evt_ptr;
-}
-
-// Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.7.15
-std::unique_ptr<EventPacketBuilder> EventPacketBuilder::CreateCommandStatusEvent(hci::Status status,
-                                                                                 hci::OpCode command_opcode) {
-  std::unique_ptr<EventPacketBuilder> evt_ptr =
-      std::unique_ptr<EventPacketBuilder>(new EventPacketBuilder(EventCode::COMMAND_STATUS));
-
-  CHECK(evt_ptr->AddPayloadOctets1(static_cast<uint8_t>(status)));
-  CHECK(evt_ptr->AddPayloadOctets1(1));  // num_hci_command_packets
-  CHECK(evt_ptr->AddPayloadOctets2(static_cast<uint16_t>(command_opcode)));
-
-  return evt_ptr;
-}
-
-// Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.7.19
-std::unique_ptr<EventPacketBuilder> EventPacketBuilder::CreateNumberOfCompletedPacketsEvent(
-    uint16_t handle, uint16_t num_completed_packets) {
-  std::unique_ptr<RawBuilder> payload = std::make_unique<CountedBuilder>();
-  std::unique_ptr<EventPacketBuilder> evt_ptr = std::unique_ptr<EventPacketBuilder>(
-      new EventPacketBuilder(EventCode::NUMBER_OF_COMPLETED_PACKETS, std::move(payload)));
-
-  evt_ptr->AddCompletedPackets(handle, num_completed_packets);
-
-  return evt_ptr;
-}
-
-void EventPacketBuilder::AddCompletedPackets(uint16_t handle, uint16_t num_completed_packets) {
-  CHECK(event_code_ == EventCode::NUMBER_OF_COMPLETED_PACKETS);
-
-  std::unique_ptr<RawBuilder> handle_pair = std::make_unique<RawBuilder>();
-  CHECK(handle_pair->AddOctets2(handle));
-  CHECK(handle_pair->AddOctets2(num_completed_packets));
-  AddBuilder(std::move(handle_pair));
-}
-
-// Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.3.10
-std::unique_ptr<EventPacketBuilder> EventPacketBuilder::CreateCommandCompleteDeleteStoredLinkKey(
-    hci::Status status, uint16_t num_keys_deleted) {
-  std::unique_ptr<EventPacketBuilder> evt_ptr =
-      EventPacketBuilder::CreateCommandCompleteOnlyStatusEvent(OpCode::DELETE_STORED_LINK_KEY, status);
-
-  CHECK(evt_ptr->AddPayloadOctets2(num_keys_deleted));
-
-  return evt_ptr;
-}
-
-// Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.3.12
-std::unique_ptr<EventPacketBuilder> EventPacketBuilder::CreateCommandCompleteReadLocalName(
-    hci::Status status, const std::vector<uint8_t>& local_name) {
-  std::unique_ptr<EventPacketBuilder> evt_ptr =
-      EventPacketBuilder::CreateCommandCompleteOnlyStatusEvent(OpCode::READ_LOCAL_NAME, status);
-
-  size_t len = local_name.size();
-  if (len > 247) {
-    len = 247;
-  }
-  CHECK(evt_ptr->AddPayloadOctets(len, local_name));
-  CHECK(evt_ptr->AddPayloadOctets1(0));  // Null terminated
-  for (size_t i = 0; i < 248 - len - 1; i++) CHECK(evt_ptr->AddPayloadOctets1(0xFF));
-
-  return evt_ptr;
-}
-
-// Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.3.23
-std::unique_ptr<EventPacketBuilder> EventPacketBuilder::CreateCommandCompleteReadAuthenticationEnable(
-    hci::Status status, uint8_t enable) {
-  std::unique_ptr<EventPacketBuilder> evt_ptr =
-      EventPacketBuilder::CreateCommandCompleteOnlyStatusEvent(OpCode::READ_LOCAL_NAME, status);
-  CHECK(evt_ptr->AddPayloadOctets1(enable));
-  return evt_ptr;
-}
-
-// Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.4.1
-std::unique_ptr<EventPacketBuilder> EventPacketBuilder::CreateCommandCompleteReadLocalVersionInformation(
-    hci::Status status, uint8_t hci_version, uint16_t hci_revision, uint8_t lmp_pal_version, uint16_t manufacturer_name,
-    uint16_t lmp_pal_subversion) {
-  std::unique_ptr<EventPacketBuilder> evt_ptr =
-      EventPacketBuilder::CreateCommandCompleteOnlyStatusEvent(OpCode::READ_LOCAL_VERSION_INFORMATION, status);
-
-  CHECK(evt_ptr->AddPayloadOctets1(hci_version));
-  CHECK(evt_ptr->AddPayloadOctets2(hci_revision));
-  CHECK(evt_ptr->AddPayloadOctets1(lmp_pal_version));
-  CHECK(evt_ptr->AddPayloadOctets2(manufacturer_name));
-  CHECK(evt_ptr->AddPayloadOctets2(lmp_pal_subversion));
-
-  return evt_ptr;
-}
-
-std::unique_ptr<EventPacketBuilder> EventPacketBuilder::CreateReadRemoteVersionInformationEvent(
-    hci::Status status, uint16_t connection_handle, uint8_t lmp_pal_version, uint16_t manufacturer_name,
-    uint16_t lmp_pal_subversion) {
-  std::unique_ptr<EventPacketBuilder> evt_ptr =
-      std::unique_ptr<EventPacketBuilder>(new EventPacketBuilder(EventCode::READ_REMOTE_VERSION_INFORMATION_COMPLETE));
-
-  CHECK(evt_ptr->AddPayloadOctets1(static_cast<uint8_t>(status)));
-  CHECK(evt_ptr->AddPayloadOctets2(connection_handle));
-  CHECK(evt_ptr->AddPayloadOctets1(lmp_pal_version));
-  CHECK(evt_ptr->AddPayloadOctets2(manufacturer_name));
-  CHECK(evt_ptr->AddPayloadOctets2(lmp_pal_subversion));
-
-  return evt_ptr;
-}
-// Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.4.2
-std::unique_ptr<EventPacketBuilder> EventPacketBuilder::CreateCommandCompleteReadLocalSupportedCommands(
-    hci::Status status, const vector<uint8_t>& supported_commands) {
-  std::unique_ptr<EventPacketBuilder> evt_ptr =
-      EventPacketBuilder::CreateCommandCompleteOnlyStatusEvent(OpCode::READ_LOCAL_SUPPORTED_COMMANDS, status);
-
-  CHECK(evt_ptr->AddPayloadOctets(64, supported_commands));
-
-  return evt_ptr;
-}
-
-// Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.4.4
-std::unique_ptr<EventPacketBuilder> EventPacketBuilder::CreateCommandCompleteReadLocalExtendedFeatures(
-    hci::Status status, uint8_t page_number, uint8_t maximum_page_number, uint64_t extended_lmp_features) {
-  std::unique_ptr<EventPacketBuilder> evt_ptr =
-      EventPacketBuilder::CreateCommandCompleteOnlyStatusEvent(OpCode::READ_LOCAL_EXTENDED_FEATURES, status);
-
-  CHECK(evt_ptr->AddPayloadOctets1(page_number));
-  CHECK(evt_ptr->AddPayloadOctets1(maximum_page_number));
-  CHECK(evt_ptr->AddPayloadOctets8(extended_lmp_features));
-
-  return evt_ptr;
-}
-
-std::unique_ptr<EventPacketBuilder> EventPacketBuilder::CreateReadRemoteExtendedFeaturesEvent(
-    hci::Status status, uint16_t handle, uint8_t page_number, uint8_t maximum_page_number,
-    uint64_t extended_lmp_features) {
-  std::unique_ptr<EventPacketBuilder> evt_ptr =
-      std::unique_ptr<EventPacketBuilder>(new EventPacketBuilder(EventCode::READ_REMOTE_EXTENDED_FEATURES_COMPLETE));
-
-  CHECK(evt_ptr->AddPayloadOctets1(static_cast<uint8_t>(status)));
-  CHECK(evt_ptr->AddPayloadOctets2(handle));
-  CHECK(evt_ptr->AddPayloadOctets1(page_number));
-  CHECK(evt_ptr->AddPayloadOctets1(maximum_page_number));
-  CHECK(evt_ptr->AddPayloadOctets8(extended_lmp_features));
-
-  return evt_ptr;
-}
-
-// Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.4.5
-std::unique_ptr<EventPacketBuilder> EventPacketBuilder::CreateCommandCompleteReadBufferSize(
-    hci::Status status, uint16_t hc_acl_data_packet_length, uint8_t hc_synchronous_data_packet_length,
-    uint16_t hc_total_num_acl_data_packets, uint16_t hc_total_synchronous_data_packets) {
-  std::unique_ptr<EventPacketBuilder> evt_ptr =
-      EventPacketBuilder::CreateCommandCompleteOnlyStatusEvent(OpCode::READ_BUFFER_SIZE, status);
-
-  CHECK(evt_ptr->AddPayloadOctets2(hc_acl_data_packet_length));
-  CHECK(evt_ptr->AddPayloadOctets1(hc_synchronous_data_packet_length));
-  CHECK(evt_ptr->AddPayloadOctets2(hc_total_num_acl_data_packets));
-  CHECK(evt_ptr->AddPayloadOctets2(hc_total_synchronous_data_packets));
-
-  return evt_ptr;
-}
-
-// Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.4.6
-std::unique_ptr<EventPacketBuilder> EventPacketBuilder::CreateCommandCompleteReadBdAddr(hci::Status status,
-                                                                                        const Address& address) {
-  std::unique_ptr<EventPacketBuilder> evt_ptr =
-      EventPacketBuilder::CreateCommandCompleteOnlyStatusEvent(OpCode::READ_BD_ADDR, status);
-
-  CHECK(evt_ptr->AddPayloadAddress(address));
-
-  return evt_ptr;
-}
-
-// Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.4.8
-std::unique_ptr<EventPacketBuilder> EventPacketBuilder::CreateCommandCompleteReadLocalSupportedCodecs(
-    hci::Status status, const vector<uint8_t>& supported_codecs, const vector<uint32_t>& vendor_specific_codecs) {
-  std::unique_ptr<EventPacketBuilder> evt_ptr =
-      EventPacketBuilder::CreateCommandCompleteOnlyStatusEvent(OpCode::READ_LOCAL_SUPPORTED_CODECS, status);
-
-  CHECK(evt_ptr->AddPayloadOctets1(supported_codecs.size()));
-  CHECK(evt_ptr->AddPayloadOctets(supported_codecs));
-  CHECK(evt_ptr->AddPayloadOctets1(vendor_specific_codecs.size()));
-  for (size_t i = 0; i < vendor_specific_codecs.size(); i++)
-    CHECK(evt_ptr->AddPayloadOctets4(vendor_specific_codecs[i]));
-
-  return evt_ptr;
-}
-
-// Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.6.1
-std::unique_ptr<EventPacketBuilder> EventPacketBuilder::CreateCommandCompleteReadLoopbackMode(hci::Status status,
-                                                                                              hci::LoopbackMode mode) {
-  std::unique_ptr<EventPacketBuilder> evt_ptr =
-      EventPacketBuilder::CreateCommandCompleteOnlyStatusEvent(OpCode::READ_LOOPBACK_MODE, status);
-  CHECK(evt_ptr->AddPayloadOctets1(static_cast<uint8_t>(mode)));
-
-  return evt_ptr;
-}
-
-// Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.7.2
-std::unique_ptr<EventPacketBuilder> EventPacketBuilder::CreateInquiryResultEvent() {
-  std::unique_ptr<RawBuilder> payload = std::unique_ptr<RawBuilder>(new CountedBuilder());
-  std::unique_ptr<EventPacketBuilder> evt_ptr(new EventPacketBuilder(EventCode::INQUIRY_RESULT, std::move(payload)));
-
-  return evt_ptr;
-}
-
-bool EventPacketBuilder::AddInquiryResult(const Address& address, uint8_t page_scan_repetition_mode,
-                                          ClassOfDevice class_of_device, uint16_t clock_offset) {
-  CHECK(event_code_ == EventCode::INQUIRY_RESULT);
-
-  if (!CanAddPayloadOctets(14)) return false;
-
-  std::unique_ptr<RawBuilder> result = std::make_unique<RawBuilder>();
-
-  CHECK(result->AddAddress(address));
-  CHECK(result->AddOctets1(page_scan_repetition_mode));
-  CHECK(result->AddOctets2(0));  // Reserved
-  CHECK(result->AddOctets1(class_of_device.cod[0]));
-  CHECK(result->AddOctets1(class_of_device.cod[1]));
-  CHECK(result->AddOctets1(class_of_device.cod[2]));
-  CHECK(!(clock_offset & 0x8000));
-  CHECK(result->AddOctets2(clock_offset));
-  AddBuilder(std::move(result));
-  return true;
-}
-
-// Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.7.3
-std::unique_ptr<EventPacketBuilder> EventPacketBuilder::CreateConnectionCompleteEvent(
-    hci::Status status, uint16_t handle, const Address& address, hci::LinkType link_type, bool encryption_enabled) {
-  std::unique_ptr<EventPacketBuilder> evt_ptr =
-      std::unique_ptr<EventPacketBuilder>(new EventPacketBuilder(EventCode::CONNECTION_COMPLETE));
-
-  CHECK(evt_ptr->AddPayloadOctets1(static_cast<uint8_t>(status)));
-  CHECK((handle & 0xf000) == 0);  // Handles are 12-bit values.
-  CHECK(evt_ptr->AddPayloadOctets2(handle));
-  CHECK(evt_ptr->AddPayloadAddress(address));
-  CHECK(evt_ptr->AddPayloadOctets1(static_cast<uint8_t>(link_type)));
-  CHECK(evt_ptr->AddPayloadOctets1(encryption_enabled ? 1 : 0));
-
-  return evt_ptr;
-}
-
-// Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.7.4
-std::unique_ptr<EventPacketBuilder> EventPacketBuilder::CreateConnectionRequestEvent(const Address& address,
-                                                                                     ClassOfDevice class_of_device,
-                                                                                     hci::LinkType link_type) {
-  std::unique_ptr<EventPacketBuilder> evt_ptr =
-      std::unique_ptr<EventPacketBuilder>(new EventPacketBuilder(EventCode::CONNECTION_REQUEST));
-
-  CHECK(evt_ptr->AddPayloadAddress(address));
-  CHECK(evt_ptr->AddPayloadOctets1(class_of_device.cod[0]));
-  CHECK(evt_ptr->AddPayloadOctets1(class_of_device.cod[1]));
-  CHECK(evt_ptr->AddPayloadOctets1(class_of_device.cod[2]));
-  CHECK(evt_ptr->AddPayloadOctets1(static_cast<uint8_t>(link_type)));
-
-  return evt_ptr;
-}
-
-// Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.7.5
-std::unique_ptr<EventPacketBuilder> EventPacketBuilder::CreateDisconnectionCompleteEvent(hci::Status status,
-                                                                                         uint16_t handle,
-                                                                                         uint8_t reason) {
-  std::unique_ptr<EventPacketBuilder> evt_ptr =
-      std::unique_ptr<EventPacketBuilder>(new EventPacketBuilder(EventCode::DISCONNECTION_COMPLETE));
-
-  CHECK(evt_ptr->AddPayloadOctets1(static_cast<uint8_t>(status)));
-  CHECK((handle & 0xf000) == 0);  // Handles are 12-bit values.
-  CHECK(evt_ptr->AddPayloadOctets2(handle));
-  CHECK(evt_ptr->AddPayloadOctets1(reason));
-
-  return evt_ptr;
-}
-
-// Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.7.6
-std::unique_ptr<EventPacketBuilder> EventPacketBuilder::CreateAuthenticationCompleteEvent(hci::Status status,
-                                                                                          uint16_t handle) {
-  std::unique_ptr<EventPacketBuilder> evt_ptr =
-      std::unique_ptr<EventPacketBuilder>(new EventPacketBuilder(EventCode::AUTHENTICATION_COMPLETE));
-  CHECK(evt_ptr->AddPayloadOctets1(static_cast<uint8_t>(status)));
-  CHECK((handle & 0xf000) == 0);  // Handles are 12-bit values.
-  CHECK(evt_ptr->AddPayloadOctets2(handle));
-  return evt_ptr;
-}
-
-// Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.7.7
-std::unique_ptr<EventPacketBuilder> EventPacketBuilder::CreateRemoteNameRequestCompleteEvent(
-    hci::Status status, const Address& address, const std::string& remote_name) {
-  std::unique_ptr<EventPacketBuilder> evt_ptr =
-      std::unique_ptr<EventPacketBuilder>(new EventPacketBuilder(EventCode::REMOTE_NAME_REQUEST_COMPLETE));
-
-  CHECK(evt_ptr->AddPayloadOctets1(static_cast<uint8_t>(status)));
-  CHECK(evt_ptr->AddPayloadAddress(address));
-  for (size_t i = 0; i < remote_name.length(); i++) CHECK(evt_ptr->AddPayloadOctets1(remote_name[i]));
-  CHECK(evt_ptr->AddPayloadOctets1(0));  // Null terminated
-  for (size_t i = 0; i < 248 - remote_name.length() - 1; i++) CHECK(evt_ptr->AddPayloadOctets1(0xFF));
-
-  return evt_ptr;
-}
-
-// Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.7.23
-std::unique_ptr<EventPacketBuilder> EventPacketBuilder::CreateLinkKeyRequestEvent(const Address& remote) {
-  std::unique_ptr<EventPacketBuilder> evt_ptr =
-      std::unique_ptr<EventPacketBuilder>(new EventPacketBuilder(EventCode::LINK_KEY_REQUEST));
-  CHECK(evt_ptr->AddPayloadAddress(remote));
-  return evt_ptr;
-}
-
-// Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.7.24
-std::unique_ptr<EventPacketBuilder> EventPacketBuilder::CreateLinkKeyNotificationEvent(const Address& remote,
-                                                                                       const std::vector<uint8_t>& key,
-                                                                                       uint8_t key_type) {
-  std::unique_ptr<EventPacketBuilder> evt_ptr =
-      std::unique_ptr<EventPacketBuilder>(new EventPacketBuilder(EventCode::LINK_KEY_NOTIFICATION));
-  CHECK(evt_ptr->AddPayloadAddress(remote));
-  CHECK(key.size() == 16);
-  CHECK(evt_ptr->AddPayloadOctets(key));
-  CHECK(evt_ptr->AddPayloadOctets1(key_type));
-  return evt_ptr;
-}
-
-// Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.7.25
-std::unique_ptr<EventPacketBuilder> EventPacketBuilder::CreateLoopbackCommandEvent(hci::OpCode opcode,
-                                                                                   PacketView<true> payload) {
-  std::unique_ptr<EventPacketBuilder> evt_ptr =
-      std::unique_ptr<EventPacketBuilder>(new EventPacketBuilder(EventCode::LOOPBACK_COMMAND));
-  CHECK(evt_ptr->AddPayloadOctets2(static_cast<uint16_t>(opcode)));
-  for (const auto& payload_byte : payload)  // Fill the packet.
-    evt_ptr->AddPayloadOctets1(payload_byte);
-  return evt_ptr;
-}
-
-// Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.7.28
-std::unique_ptr<EventPacketBuilder> EventPacketBuilder::CreateReadClockOffsetEvent(hci::Status status, uint16_t handle,
-                                                                                   uint16_t offset) {
-  std::unique_ptr<EventPacketBuilder> evt_ptr =
-      std::unique_ptr<EventPacketBuilder>(new EventPacketBuilder(EventCode::READ_CLOCK_OFFSET_COMPLETE));
-  CHECK(evt_ptr->AddPayloadOctets1(static_cast<uint8_t>(status)));
-  CHECK(evt_ptr->AddPayloadOctets2(handle));
-  CHECK(evt_ptr->AddPayloadOctets2(offset));
-  return evt_ptr;
-}
-
-// Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.7.29
-std::unique_ptr<EventPacketBuilder> EventPacketBuilder::CreateConnectionPacketTypeChangedEvent(hci::Status status,
-                                                                                               uint16_t handle,
-                                                                                               uint16_t packet_type) {
-  std::unique_ptr<EventPacketBuilder> evt_ptr =
-      std::unique_ptr<EventPacketBuilder>(new EventPacketBuilder(EventCode::CONNECTION_PACKET_TYPE_CHANGE));
-  CHECK(evt_ptr->AddPayloadOctets1(static_cast<uint8_t>(status)));
-  CHECK(evt_ptr->AddPayloadOctets2(handle));
-  CHECK(evt_ptr->AddPayloadOctets2(packet_type));
-  return evt_ptr;
-}
-
-// Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.7.37
-std::unique_ptr<EventPacketBuilder> EventPacketBuilder::CreateSniffSubratingEvent(const hci::Status status,
-                                                                                  uint16_t handle) {
-  std::unique_ptr<EventPacketBuilder> evt_ptr =
-      EventPacketBuilder::CreateCommandCompleteOnlyStatusEvent(OpCode::SNIFF_SUBRATING, status);
-
-  CHECK(evt_ptr->AddPayloadOctets2(handle));
-  return evt_ptr;
-}
-
-// Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.7.38
-std::unique_ptr<EventPacketBuilder> EventPacketBuilder::CreateExtendedInquiryResultEvent(
-    const Address& address, uint8_t page_scan_repetition_mode, ClassOfDevice class_of_device, uint16_t clock_offset,
-    uint8_t rssi, const vector<uint8_t>& extended_inquiry_response) {
-  std::unique_ptr<EventPacketBuilder> evt_ptr =
-      std::unique_ptr<EventPacketBuilder>(new EventPacketBuilder(EventCode::EXTENDED_INQUIRY_RESULT));
-
-  CHECK(evt_ptr->AddPayloadOctets1(1));  // Always contains a single response
-
-  CHECK(evt_ptr->AddPayloadAddress(address));
-  CHECK(evt_ptr->AddPayloadOctets1(page_scan_repetition_mode));
-  CHECK(evt_ptr->AddPayloadOctets1(0));  // Reserved
-  CHECK(evt_ptr->AddPayloadOctets1(class_of_device.cod[0]));
-  CHECK(evt_ptr->AddPayloadOctets1(class_of_device.cod[1]));
-  CHECK(evt_ptr->AddPayloadOctets1(class_of_device.cod[2]));
-  CHECK(!(clock_offset & 0x8000));
-  CHECK(evt_ptr->AddPayloadOctets2(clock_offset));
-  CHECK(evt_ptr->AddPayloadOctets1(rssi));
-  CHECK(evt_ptr->AddPayloadOctets(extended_inquiry_response));
-  evt_ptr->AddPayloadOctets1(0x00);  // End marker
-  while (evt_ptr->AddPayloadOctets1(0x00))
-    ;  // Fill packet
-  return evt_ptr;
-}
-
-// Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.7.40
-std::unique_ptr<EventPacketBuilder> EventPacketBuilder::CreateIoCapabilityRequestEvent(const Address& peer) {
-  std::unique_ptr<EventPacketBuilder> evt_ptr =
-      std::unique_ptr<EventPacketBuilder>(new EventPacketBuilder(EventCode::IO_CAPABILITY_REQUEST));
-
-  CHECK(evt_ptr->AddPayloadAddress(peer));
-  return evt_ptr;
-}  // namespace packets
-
-// Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.7.41
-std::unique_ptr<EventPacketBuilder> EventPacketBuilder::CreateIoCapabilityResponseEvent(
-    const Address& peer, uint8_t io_capability, bool oob_data_present, uint8_t authentication_requirements) {
-  std::unique_ptr<EventPacketBuilder> evt_ptr =
-      std::unique_ptr<EventPacketBuilder>(new EventPacketBuilder(EventCode::IO_CAPABILITY_RESPONSE));
-
-  CHECK(evt_ptr->AddPayloadAddress(peer));
-  CHECK(evt_ptr->AddPayloadOctets1(io_capability));
-  CHECK(evt_ptr->AddPayloadOctets1(oob_data_present));
-  CHECK(evt_ptr->AddPayloadOctets1(authentication_requirements));
-  return evt_ptr;
-}  // namespace test_vendor_lib
-
-// Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.7.42
-std::unique_ptr<EventPacketBuilder> EventPacketBuilder::CreateUserConfirmationRequestEvent(const Address& peer,
-                                                                                           uint32_t numeric_value) {
-  std::unique_ptr<EventPacketBuilder> evt_ptr =
-      std::unique_ptr<EventPacketBuilder>(new EventPacketBuilder(EventCode::USER_CONFIRMATION_REQUEST));
-
-  CHECK(evt_ptr->AddPayloadAddress(peer));
-  CHECK(evt_ptr->AddPayloadOctets4(numeric_value));
-  return evt_ptr;
-}
-
-// Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.7.43
-std::unique_ptr<EventPacketBuilder> EventPacketBuilder::CreateUserPasskeyRequestEvent(const Address& peer) {
-  std::unique_ptr<EventPacketBuilder> evt_ptr =
-      std::unique_ptr<EventPacketBuilder>(new EventPacketBuilder(EventCode::USER_PASSKEY_REQUEST));
-
-  CHECK(evt_ptr->AddPayloadAddress(peer));
-  return evt_ptr;
-}
-
-// Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.7.44
-std::unique_ptr<EventPacketBuilder> EventPacketBuilder::CreateRemoteOobDataRequestEvent(const Address& peer) {
-  std::unique_ptr<EventPacketBuilder> evt_ptr =
-      std::unique_ptr<EventPacketBuilder>(new EventPacketBuilder(EventCode::REMOTE_OOB_DATA_REQUEST));
-
-  CHECK(evt_ptr->AddPayloadAddress(peer));
-  return evt_ptr;
-}
-
-// Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.7.45
-std::unique_ptr<EventPacketBuilder> EventPacketBuilder::CreateSimplePairingCompleteEvent(hci::Status status,
-                                                                                         const Address& peer) {
-  std::unique_ptr<EventPacketBuilder> evt_ptr =
-      std::unique_ptr<EventPacketBuilder>(new EventPacketBuilder(EventCode::SIMPLE_PAIRING_COMPLETE));
-
-  CHECK(evt_ptr->AddPayloadOctets1(static_cast<uint8_t>(status)));
-  CHECK(evt_ptr->AddPayloadAddress(peer));
-  return evt_ptr;
-}
-
-// Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.7.48
-std::unique_ptr<EventPacketBuilder> EventPacketBuilder::CreateUserPasskeyNotificationEvent(const Address& peer,
-                                                                                           uint32_t passkey) {
-  std::unique_ptr<EventPacketBuilder> evt_ptr =
-      std::unique_ptr<EventPacketBuilder>(new EventPacketBuilder(EventCode::USER_PASSKEY_NOTIFICATION));
-
-  CHECK(evt_ptr->AddPayloadAddress(peer));
-  CHECK(evt_ptr->AddPayloadOctets4(passkey));
-  return evt_ptr;
-}
-
-// Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.7.49
-std::unique_ptr<EventPacketBuilder> EventPacketBuilder::CreateKeypressNotificationEvent(const Address& peer,
-                                                                                        uint8_t notification_type) {
-  std::unique_ptr<EventPacketBuilder> evt_ptr =
-      std::unique_ptr<EventPacketBuilder>(new EventPacketBuilder(EventCode::KEYPRESS_NOTIFICATION));
-
-  CHECK(evt_ptr->AddPayloadAddress(peer));
-  CHECK(evt_ptr->AddPayloadOctets1(notification_type));
-  return evt_ptr;
-}
-
-// Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.7.65.1
-std::unique_ptr<EventPacketBuilder> EventPacketBuilder::CreateLeConnectionCompleteEvent(
-    hci::Status status, uint16_t handle, uint8_t role, uint8_t peer_address_type, const Address& peer,
-    uint16_t interval, uint16_t latency, uint16_t supervision_timeout) {
-  std::unique_ptr<RawBuilder> meta_evt = LeMetaEventBuilder::CreateLeConnectionCompleteEvent(
-      status, handle, role, peer_address_type, peer, interval, latency, supervision_timeout);
-
-  return std::unique_ptr<EventPacketBuilder>(new EventPacketBuilder(EventCode::LE_META_EVENT, std::move(meta_evt)));
-}
-
-std::unique_ptr<EventPacketBuilder> EventPacketBuilder::CreateLeEnhancedConnectionCompleteEvent(
-    hci::Status status, uint16_t handle, uint8_t role, uint8_t peer_address_type, const Address& peer,
-    const Address& local_private_address, const Address& peer_private_address, uint16_t interval, uint16_t latency,
-    uint16_t supervision_timeout) {
-  std::unique_ptr<RawBuilder> meta_evt = LeMetaEventBuilder::CreateLeEnhancedConnectionCompleteEvent(
-      status, handle, role, peer_address_type, peer, local_private_address, peer_private_address, interval, latency,
-      supervision_timeout);
-
-  return std::unique_ptr<EventPacketBuilder>(new EventPacketBuilder(EventCode::LE_META_EVENT, std::move(meta_evt)));
-}
-
-std::unique_ptr<EventPacketBuilder> EventPacketBuilder::CreateLeConnectionUpdateCompleteEvent(
-    hci::Status status, uint16_t handle, uint16_t interval, uint16_t latency, uint16_t supervision_timeout) {
-  std::unique_ptr<RawBuilder> meta_evt =
-      LeMetaEventBuilder::CreateLeConnectionUpdateCompleteEvent(status, handle, interval, latency, supervision_timeout);
-
-  return std::unique_ptr<EventPacketBuilder>(new EventPacketBuilder(EventCode::LE_META_EVENT, std::move(meta_evt)));
-}
-
-// Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.7.65.2
-std::unique_ptr<EventPacketBuilder> EventPacketBuilder::CreateLeAdvertisingReportEvent() {
-  std::unique_ptr<RawBuilder> meta_evt = LeMetaEventBuilder::CreateLeAdvertisingReportEvent();
-
-  return std::unique_ptr<EventPacketBuilder>(new EventPacketBuilder(EventCode::LE_META_EVENT, std::move(meta_evt)));
-}
-
-bool EventPacketBuilder::AddLeAdvertisingReport(LeAdvertisement::AdvertisementType event_type,
-                                                LeAdvertisement::AddressType addr_type, const Address& addr,
-                                                const vector<uint8_t>& data, uint8_t rssi) {
-  CHECK(event_code_ == EventCode::LE_META_EVENT);
-
-  // Upcast the payload to add the next report.
-  LeMetaEventBuilder* meta_ptr = static_cast<LeMetaEventBuilder*>(payload_.get());
-  return meta_ptr->AddLeAdvertisingReport(event_type, addr_type, addr, data, rssi);
-}
-
-// Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.7.65.4
-std::unique_ptr<EventPacketBuilder> EventPacketBuilder::CreateLeRemoteUsedFeaturesEvent(hci::Status status,
-                                                                                        uint16_t handle,
-                                                                                        uint64_t features) {
-  std::unique_ptr<RawBuilder> meta_evt = LeMetaEventBuilder::CreateLeRemoteUsedFeaturesEvent(status, handle, features);
-  return std::unique_ptr<EventPacketBuilder>(new EventPacketBuilder(EventCode::LE_META_EVENT, std::move(meta_evt)));
-}
-
-std::unique_ptr<EventPacketBuilder> EventPacketBuilder::CreateRemoteSupportedFeaturesEvent(hci::Status status,
-                                                                                           uint16_t handle,
-                                                                                           uint64_t features) {
-  std::unique_ptr<EventPacketBuilder> evt_ptr =
-      std::unique_ptr<EventPacketBuilder>(new EventPacketBuilder(EventCode::READ_REMOTE_SUPPORTED_FEATURES_COMPLETE));
-
-  CHECK(evt_ptr->AddPayloadOctets1(static_cast<uint8_t>(status)));
-  CHECK(evt_ptr->AddPayloadOctets2(handle));
-  CHECK(evt_ptr->AddPayloadOctets8(features));
-
-  return evt_ptr;
-}
-
-std::unique_ptr<EventPacketBuilder> EventPacketBuilder::CreateCommandCompleteLinkKeyRequestReply(hci::Status status,
-                                                                                                 Address address) {
-  std::unique_ptr<EventPacketBuilder> evt_ptr =
-      EventPacketBuilder::CreateCommandCompleteOnlyStatusEvent(OpCode::LINK_KEY_REQUEST_REPLY, status);
-
-  CHECK(evt_ptr->AddPayloadAddress(address));
-
-  return evt_ptr;
-}
-
-std::unique_ptr<EventPacketBuilder> EventPacketBuilder::CreateCommandCompleteLinkKeyRequestNegativeReply(
-    hci::Status status, Address address) {
-  std::unique_ptr<EventPacketBuilder> evt_ptr =
-      EventPacketBuilder::CreateCommandCompleteOnlyStatusEvent(OpCode::LINK_KEY_REQUEST_NEGATIVE_REPLY, status);
-
-  CHECK(evt_ptr->AddPayloadAddress(address));
-
-  return evt_ptr;
-}
-
-std::unique_ptr<EventPacketBuilder> EventPacketBuilder::CreateCommandCompleteWriteLinkPolicySettings(hci::Status status,
-                                                                                                     uint16_t handle) {
-  std::unique_ptr<EventPacketBuilder> evt_ptr =
-      EventPacketBuilder::CreateCommandCompleteOnlyStatusEvent(OpCode::WRITE_LINK_POLICY_SETTINGS, status);
-
-  CHECK(evt_ptr->AddPayloadOctets2(handle));
-
-  return evt_ptr;
-}
-
-std::unique_ptr<EventPacketBuilder> EventPacketBuilder::CreateCommandCompleteWriteLinkSupervisionTimeout(
-    hci::Status status, uint16_t handle) {
-  std::unique_ptr<EventPacketBuilder> evt_ptr =
-      EventPacketBuilder::CreateCommandCompleteOnlyStatusEvent(OpCode::WRITE_LINK_SUPERVISION_TIMEOUT, status);
-
-  CHECK(evt_ptr->AddPayloadOctets2(handle));
-
-  return evt_ptr;
-}
-
-// Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.8.2
-std::unique_ptr<EventPacketBuilder> EventPacketBuilder::CreateCommandCompleteLeReadBufferSize(
-    hci::Status status, uint16_t hc_le_data_packet_length, uint8_t hc_total_num_le_data_packets) {
-  std::unique_ptr<EventPacketBuilder> evt_ptr =
-      EventPacketBuilder::CreateCommandCompleteOnlyStatusEvent(OpCode::LE_READ_BUFFER_SIZE, status);
-
-  CHECK(evt_ptr->AddPayloadOctets2(hc_le_data_packet_length));
-  CHECK(evt_ptr->AddPayloadOctets1(hc_total_num_le_data_packets));
-
-  return evt_ptr;
-}
-
-// Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.8.3
-std::unique_ptr<EventPacketBuilder> EventPacketBuilder::CreateCommandCompleteLeReadLocalSupportedFeatures(
-    hci::Status status, uint64_t le_features) {
-  std::unique_ptr<EventPacketBuilder> evt_ptr =
-      EventPacketBuilder::CreateCommandCompleteOnlyStatusEvent(OpCode::LE_READ_LOCAL_SUPPORTED_FEATURES, status);
-
-  CHECK(evt_ptr->AddPayloadOctets8(le_features));
-
-  return evt_ptr;
-}
-
-// Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.8.14
-std::unique_ptr<EventPacketBuilder> EventPacketBuilder::CreateCommandCompleteLeReadWhiteListSize(
-    hci::Status status, uint8_t white_list_size) {
-  std::unique_ptr<EventPacketBuilder> evt_ptr =
-      EventPacketBuilder::CreateCommandCompleteOnlyStatusEvent(OpCode::LE_READ_WHITE_LIST_SIZE, status);
-
-  CHECK(evt_ptr->AddPayloadOctets8(white_list_size));
-
-  return evt_ptr;
-}
-
-// Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.8.23
-std::unique_ptr<EventPacketBuilder> EventPacketBuilder::CreateCommandCompleteLeRand(hci::Status status,
-                                                                                    uint64_t random_val) {
-  std::unique_ptr<EventPacketBuilder> evt_ptr =
-      EventPacketBuilder::CreateCommandCompleteOnlyStatusEvent(OpCode::LE_RAND, status);
-
-  CHECK(evt_ptr->AddPayloadOctets8(random_val));
-
-  return evt_ptr;
-}
-
-// Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.8.27
-std::unique_ptr<EventPacketBuilder> EventPacketBuilder::CreateCommandCompleteLeReadSupportedStates(hci::Status status,
-                                                                                                   uint64_t le_states) {
-  std::unique_ptr<EventPacketBuilder> evt_ptr =
-      EventPacketBuilder::CreateCommandCompleteOnlyStatusEvent(OpCode::LE_READ_SUPPORTED_STATES, status);
-
-  CHECK(evt_ptr->AddPayloadOctets8(le_states));
-
-  return evt_ptr;
-}
-
-// Vendor-specific commands
-
-std::unique_ptr<EventPacketBuilder> EventPacketBuilder::CreateCommandCompleteLeGetVendorCapabilities(
-    hci::Status status, const vector<uint8_t>& vendor_cap) {
-  std::unique_ptr<EventPacketBuilder> evt_ptr =
-      EventPacketBuilder::CreateCommandCompleteOnlyStatusEvent(OpCode::LE_GET_VENDOR_CAPABILITIES, status);
-
-  CHECK(evt_ptr->AddPayloadOctets(vendor_cap));
-
-  return evt_ptr;
-}
-
-std::unique_ptr<EventPacketBuilder> EventPacketBuilder::CreateEncryptionChange(hci::Status status, uint16_t handle,
-                                                                               uint8_t encryption_enable) {
-  std::unique_ptr<EventPacketBuilder> evt_ptr =
-      std::unique_ptr<EventPacketBuilder>(new EventPacketBuilder(EventCode::ENCRYPTION_CHANGE));
-
-  CHECK(evt_ptr->AddPayloadOctets1(static_cast<uint8_t>(status)));
-  CHECK(evt_ptr->AddPayloadOctets2(handle));
-  CHECK(evt_ptr->AddPayloadOctets1(encryption_enable));
-
-  return evt_ptr;
-}
-
-size_t EventPacketBuilder::size() const {
-  size_t header_size = 2;  // Event code and payload size
-  return header_size + payload_->size();
-}
-
-void EventPacketBuilder::Serialize(std::back_insert_iterator<std::vector<uint8_t>> it) const {
-  insert(static_cast<uint8_t>(event_code_), it);
-  uint8_t payload_size = size() - 2;  // Event code and payload size
-  CHECK(size() - 2 == static_cast<size_t>(payload_size)) << "Payload too large for an event: " << size();
-  insert(payload_size, it);
-  payload_->Serialize(it);
-}
-
-bool EventPacketBuilder::CanAddPayloadOctets(size_t octets) {
-  return payload_->CanAddOctets(octets);
-}
-
-bool EventPacketBuilder::AddPayloadOctets(size_t octets, const std::vector<uint8_t>& bytes) {
-  return payload_->AddOctets(octets, bytes);
-}
-
-bool EventPacketBuilder::AddPayloadOctets(const std::vector<uint8_t>& bytes) {
-  return payload_->AddOctets(bytes);
-}
-
-bool EventPacketBuilder::AddPayloadOctets1(uint8_t value) {
-  return payload_->AddOctets1(value);
-}
-
-bool EventPacketBuilder::AddPayloadOctets2(uint16_t value) {
-  return payload_->AddOctets2(value);
-}
-
-bool EventPacketBuilder::AddPayloadOctets3(uint32_t value) {
-  return payload_->AddOctets3(value);
-}
-
-bool EventPacketBuilder::AddPayloadOctets4(uint32_t value) {
-  return payload_->AddOctets4(value);
-}
-
-bool EventPacketBuilder::AddPayloadOctets6(uint64_t value) {
-  return payload_->AddOctets6(value);
-}
-
-bool EventPacketBuilder::AddPayloadOctets8(uint64_t value) {
-  return payload_->AddOctets8(value);
-}
-
-bool EventPacketBuilder::AddPayloadAddress(Address address) {
-  return payload_->AddAddress(address);
-}
-
-bool EventPacketBuilder::AddBuilder(std::unique_ptr<BasePacketBuilder> builder) {
-  // Upcast the payload to add the next builder.
-  CountedBuilder* temp_ptr = static_cast<CountedBuilder*>(payload_.get());
-  temp_ptr->Add(std::move(builder));
-  return true;
-}
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/hci/event_packet_builder.h b/vendor_libs/test_vendor_lib/packets/hci/event_packet_builder.h
deleted file mode 100644
index 495e47a..0000000
--- a/vendor_libs/test_vendor_lib/packets/hci/event_packet_builder.h
+++ /dev/null
@@ -1,328 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <base/logging.h>
-#include <cstdint>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "include/hci.h"
-#include "include/le_advertisement.h"
-#include "packets/counted_builder.h"
-#include "packets/hci/hci_packet_builder.h"
-#include "packets/packet_builder.h"
-#include "packets/packet_view.h"
-#include "packets/raw_builder.h"
-#include "types/address.h"
-
-namespace test_vendor_lib {
-namespace packets {
-
-// Event Packets are specified in the Bluetooth Core Specification Version 4.2,
-// Volume 2, Part E, Section 5.4.4 (page 477). Event Packets begin with a 2
-// octet header formatted as follows:
-// - Event Code: 1 octet
-// - Parameter Total Length: 1 octet
-class EventPacketBuilder : public HciPacketBuilder {
- public:
-  virtual ~EventPacketBuilder() override = default;
-
-  // Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.7.1
-  static std::unique_ptr<EventPacketBuilder> CreateInquiryCompleteEvent(hci::Status status);
-
-  // Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.7.14
-  // This should only be used for testing to send non-standard packets
-  // Most code should use the more specific functions that follow
-  static std::unique_ptr<EventPacketBuilder> CreateCommandCompleteEvent(
-      hci::OpCode command_opcode, const std::vector<uint8_t>& event_return_parameters);
-
-  static std::unique_ptr<EventPacketBuilder> CreateCommandCompleteOnlyStatusEvent(hci::OpCode command_opcode,
-                                                                                  hci::Status status);
-
-  static std::unique_ptr<EventPacketBuilder> CreateCommandCompleteStatusAndAddressEvent(hci::OpCode command_opcode,
-                                                                                        hci::Status status,
-                                                                                        const Address& address);
-
-  static std::unique_ptr<EventPacketBuilder> CreateCommandCompleteUnknownOpCodeEvent(uint16_t command_opcode);
-
-  // Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.7.15
-  static std::unique_ptr<EventPacketBuilder> CreateCommandStatusEvent(hci::Status status, hci::OpCode command_opcode);
-
-  // Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.7.19
-  static std::unique_ptr<EventPacketBuilder> CreateNumberOfCompletedPacketsEvent(uint16_t handle,
-                                                                                 uint16_t num_completed_packets);
-
-  void AddCompletedPackets(uint16_t handle, uint16_t num_completed_packets);
-
-  // Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.1.10
-  static std::unique_ptr<EventPacketBuilder> CreateCommandCompleteLinkKeyRequestReply(hci::Status status,
-                                                                                      Address address);
-
-  // Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.1.11
-  static std::unique_ptr<EventPacketBuilder> CreateCommandCompleteLinkKeyRequestNegativeReply(hci::Status status,
-                                                                                              Address address);
-
-  // Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.2.10
-  static std::unique_ptr<EventPacketBuilder> CreateCommandCompleteWriteLinkPolicySettings(hci::Status status,
-                                                                                          uint16_t handle);
-
-  // Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.3.10
-  static std::unique_ptr<EventPacketBuilder> CreateCommandCompleteDeleteStoredLinkKey(hci::Status status,
-                                                                                      uint16_t num_keys_deleted);
-
-  // Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.3.12
-  static std::unique_ptr<EventPacketBuilder> CreateCommandCompleteReadLocalName(hci::Status status,
-                                                                                const std::vector<uint8_t>& local_name);
-
-  // Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.3.23
-  static std::unique_ptr<EventPacketBuilder> CreateCommandCompleteReadAuthenticationEnable(hci::Status status,
-                                                                                           uint8_t enable);
-
-  // Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.3.42
-  static std::unique_ptr<EventPacketBuilder> CreateCommandCompleteWriteLinkSupervisionTimeout(hci::Status status,
-                                                                                              uint16_t handle);
-
-  // Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.4.1
-  static std::unique_ptr<EventPacketBuilder> CreateCommandCompleteReadLocalVersionInformation(
-      hci::Status status, uint8_t hci_version, uint16_t hci_revision, uint8_t lmp_pal_version,
-      uint16_t manufacturer_name, uint16_t lmp_pal_subversion);
-
-  // Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.4.2
-  static std::unique_ptr<EventPacketBuilder> CreateCommandCompleteReadLocalSupportedCommands(
-      hci::Status status, const std::vector<uint8_t>& supported_commands);
-
-  // Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.4.4
-  static std::unique_ptr<EventPacketBuilder> CreateCommandCompleteReadLocalExtendedFeatures(
-      hci::Status status, uint8_t page_number, uint8_t maximum_page_number, uint64_t extended_lmp_features);
-
-  // Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.4.5
-  static std::unique_ptr<EventPacketBuilder> CreateCommandCompleteReadBufferSize(
-      hci::Status status, uint16_t hc_acl_data_packet_length, uint8_t hc_synchronous_data_packet_length,
-      uint16_t hc_total_num_acl_data_packets, uint16_t hc_total_synchronous_data_packets);
-
-  // Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.4.6
-  static std::unique_ptr<EventPacketBuilder> CreateCommandCompleteReadBdAddr(hci::Status status,
-                                                                             const Address& bt_address);
-
-  // Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.4.8
-  static std::unique_ptr<EventPacketBuilder> CreateCommandCompleteReadLocalSupportedCodecs(
-      hci::Status status, const std::vector<uint8_t>& supported_codecs,
-      const std::vector<uint32_t>& vendor_specific_codecs);
-
-  // Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.6.1
-  static std::unique_ptr<EventPacketBuilder> CreateCommandCompleteReadLoopbackMode(hci::Status status,
-                                                                                   hci::LoopbackMode mode);
-
-  // Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.7.2
-  static std::unique_ptr<EventPacketBuilder> CreateInquiryResultEvent();
-
-  // Returns true if the result can be added to the event packet.
-  bool AddInquiryResult(const Address& bt_address, uint8_t page_scan_repetition_mode, ClassOfDevice class_of_device,
-                        uint16_t clock_offset);
-
-  // Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.7.3
-  static std::unique_ptr<EventPacketBuilder> CreateConnectionCompleteEvent(hci::Status status, uint16_t handle,
-                                                                           const Address& address,
-                                                                           hci::LinkType link_type,
-                                                                           bool encryption_enabled);
-
-  // Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.7.4
-  static std::unique_ptr<EventPacketBuilder> CreateConnectionRequestEvent(const Address& address,
-                                                                          ClassOfDevice class_of_device,
-                                                                          hci::LinkType link_type);
-
-  // Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.7.5
-  static std::unique_ptr<EventPacketBuilder> CreateDisconnectionCompleteEvent(hci::Status status, uint16_t handle,
-                                                                              uint8_t reason);
-
-  // Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.7.6
-  static std::unique_ptr<EventPacketBuilder> CreateAuthenticationCompleteEvent(hci::Status status, uint16_t handle);
-
-  // Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.7.7
-  static std::unique_ptr<EventPacketBuilder> CreateRemoteNameRequestCompleteEvent(hci::Status status,
-                                                                                  const Address& bt_address,
-                                                                                  const std::string& name);
-
-  // Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.7.11
-  static std::unique_ptr<EventPacketBuilder> CreateRemoteSupportedFeaturesEvent(hci::Status status, uint16_t handle,
-                                                                                uint64_t features);
-
-  // Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.7.12
-  static std::unique_ptr<EventPacketBuilder> CreateReadRemoteVersionInformationEvent(hci::Status status,
-                                                                                     uint16_t connection_handle,
-                                                                                     uint8_t lmp_pal_version,
-                                                                                     uint16_t manufacturer_name,
-                                                                                     uint16_t lmp_pal_subversion);
-
-  // Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.7.23
-  static std::unique_ptr<EventPacketBuilder> CreateLinkKeyRequestEvent(const Address& remote);
-
-  // Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.7.24
-  static std::unique_ptr<EventPacketBuilder> CreateLinkKeyNotificationEvent(const Address& remote,
-                                                                            const std::vector<uint8_t>& key,
-                                                                            uint8_t key_type);
-
-  // Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.7.25
-  static std::unique_ptr<EventPacketBuilder> CreateLoopbackCommandEvent(hci::OpCode opcode, PacketView<true> payload);
-
-  // Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.7.28
-  static std::unique_ptr<EventPacketBuilder> CreateReadClockOffsetEvent(hci::Status status, uint16_t handle,
-                                                                        uint16_t packet_type);
-
-  // Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.7.29
-  static std::unique_ptr<EventPacketBuilder> CreateConnectionPacketTypeChangedEvent(hci::Status status, uint16_t handle,
-                                                                                    uint16_t offset);
-
-  // Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.7.34
-  static std::unique_ptr<EventPacketBuilder> CreateReadRemoteExtendedFeaturesEvent(hci::Status status, uint16_t handle,
-                                                                                   uint8_t page_number,
-                                                                                   uint8_t maximum_page_number,
-                                                                                   uint64_t extended_lmp_features);
-
-  // Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.7.37
-  static std::unique_ptr<EventPacketBuilder> CreateSniffSubratingEvent(hci::Status status, uint16_t handle);
-
-  // Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.7.38
-  static std::unique_ptr<EventPacketBuilder> CreateExtendedInquiryResultEvent(
-      const Address& bt_address, uint8_t page_scan_repetition_mode, ClassOfDevice class_of_device,
-      uint16_t clock_offset, uint8_t rssi, const std::vector<uint8_t>& extended_inquiry_response);
-
-  // Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.7.40
-  static std::unique_ptr<EventPacketBuilder> CreateIoCapabilityRequestEvent(const Address& peer);
-
-  // Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.7.41
-  static std::unique_ptr<EventPacketBuilder> CreateIoCapabilityResponseEvent(const Address& peer, uint8_t io_capability,
-                                                                             bool oob_data_present,
-                                                                             uint8_t authentication_requirements);
-
-  // Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.7.42
-  static std::unique_ptr<EventPacketBuilder> CreateUserConfirmationRequestEvent(const Address& peer,
-                                                                                uint32_t numeric_value);
-
-  // Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.7.43
-  static std::unique_ptr<EventPacketBuilder> CreateUserPasskeyRequestEvent(const Address& peer);
-
-  // Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.7.44
-  static std::unique_ptr<EventPacketBuilder> CreateRemoteOobDataRequestEvent(const Address& peer);
-
-  // Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.7.45
-  static std::unique_ptr<EventPacketBuilder> CreateSimplePairingCompleteEvent(hci::Status status, const Address& peer);
-
-  // Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.7.48
-  static std::unique_ptr<EventPacketBuilder> CreateUserPasskeyNotificationEvent(const Address& peer, uint32_t passkey);
-
-  // Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.7.49
-  static std::unique_ptr<EventPacketBuilder> CreateKeypressNotificationEvent(const Address& peer,
-                                                                             uint8_t notification_type);
-
-  // Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section
-  // 7.7.65.1
-  static std::unique_ptr<EventPacketBuilder> CreateLeConnectionCompleteEvent(hci::Status status, uint16_t handle,
-                                                                             uint8_t role, uint8_t peer_address_type,
-                                                                             const Address& peer, uint16_t interval,
-                                                                             uint16_t latency,
-                                                                             uint16_t supervision_timeout);
-
-  // Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section
-  // 7.7.65.2
-  static std::unique_ptr<EventPacketBuilder> CreateLeAdvertisingReportEvent();
-
-  // Returns true if the report can be added to the event packet.
-  bool AddLeAdvertisingReport(LeAdvertisement::AdvertisementType event_type, LeAdvertisement::AddressType addr_type,
-                              const Address& addr, const std::vector<uint8_t>& data, uint8_t rssi);
-
-  // Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section
-  // 7.7.65.3
-  static std::unique_ptr<EventPacketBuilder> CreateLeConnectionUpdateCompleteEvent(hci::Status status, uint16_t handle,
-                                                                                   uint16_t interval, uint16_t latency,
-                                                                                   uint16_t supervision_timeout);
-
-  // Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section
-  // 7.7.65.4
-  static std::unique_ptr<EventPacketBuilder> CreateLeRemoteUsedFeaturesEvent(hci::Status status, uint16_t handle,
-                                                                             uint64_t features);
-
-  // Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section
-  // 7.7.65.10
-  static std::unique_ptr<EventPacketBuilder> CreateLeEnhancedConnectionCompleteEvent(
-      hci::Status status, uint16_t handle, uint8_t role, uint8_t peer_address_type, const Address& peer,
-      const Address& local_private_address, const Address& peer_private_address, uint16_t interval, uint16_t latency,
-      uint16_t supervision_timeout);
-
-  // Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.8.2
-  static std::unique_ptr<EventPacketBuilder> CreateCommandCompleteLeReadBufferSize(
-      hci::Status status, uint16_t hc_le_data_packet_length, uint8_t hc_total_num_le_data_packets);
-
-  // Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.8.3
-  static std::unique_ptr<EventPacketBuilder> CreateCommandCompleteLeReadLocalSupportedFeatures(hci::Status status,
-                                                                                               uint64_t le_features);
-
-  // Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.8.14
-  static std::unique_ptr<EventPacketBuilder> CreateCommandCompleteLeReadWhiteListSize(hci::Status status,
-                                                                                      uint8_t white_list_size);
-
-  // Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.8.23
-  static std::unique_ptr<EventPacketBuilder> CreateCommandCompleteLeRand(hci::Status status, uint64_t random_val);
-
-  // Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.8.27
-  static std::unique_ptr<EventPacketBuilder> CreateCommandCompleteLeReadSupportedStates(hci::Status status,
-                                                                                        uint64_t le_states);
-
-  /*
-  static std::unique_ptr<EventPacketBuilder>
-      CreateLeStartEncryption(hci::Status status, uint8_t encryption_enable);
-*/
-  static std::unique_ptr<EventPacketBuilder> CreateEncryptionChange(hci::Status status, uint16_t handle,
-                                                                    uint8_t encryption_enable);
-
-  // Vendor-specific commands
-
-  static std::unique_ptr<EventPacketBuilder> CreateCommandCompleteLeGetVendorCapabilities(
-      hci::Status status, const std::vector<uint8_t>& vendor_cap);
-
-  virtual size_t size() const override;
-
-  virtual void Serialize(std::back_insert_iterator<std::vector<uint8_t>> it) const override;
-
-  bool CanAddPayloadOctets(size_t octets);
-
-  bool AddPayloadOctets(size_t octets, const std::vector<uint8_t>& bytes);
-
-  bool AddPayloadOctets(const std::vector<uint8_t>& bytes);
-
-  bool AddPayloadOctets1(uint8_t value);
-  bool AddPayloadOctets2(uint16_t value);
-  bool AddPayloadOctets3(uint32_t value);
-  bool AddPayloadOctets4(uint32_t value);
-  bool AddPayloadOctets6(uint64_t value);
-  bool AddPayloadOctets8(uint64_t value);
-
-  bool AddPayloadAddress(Address address);
-
-  bool AddBuilder(std::unique_ptr<BasePacketBuilder> builder);
-
- private:
-  explicit EventPacketBuilder(hci::EventCode event_code);
-  explicit EventPacketBuilder(hci::EventCode event_code, std::unique_ptr<RawBuilder> payload);
-  hci::EventCode event_code_;
-  std::unique_ptr<RawBuilder> payload_;
-};
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/hci/event_payload_builder.cc b/vendor_libs/test_vendor_lib/packets/hci/event_payload_builder.cc
deleted file mode 100644
index dd866a2..0000000
--- a/vendor_libs/test_vendor_lib/packets/hci/event_payload_builder.cc
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "event_payload_builder.h"
-
-#include <base/logging.h>
-#include <algorithm>
-
-using std::vector;
-
-namespace test_vendor_lib {
-namespace packets {
-
-EventPayloadBuilder::EventPayloadBuilder(size_t max_bytes) : max_bytes_(max_bytes) {}
-
-bool EventPayloadBuilder::AddPayloadOctets(size_t octets, const vector<uint8_t>& bytes) {
-  if (payload_.size() + octets > max_bytes_) return false;
-
-  if (octets != bytes.size()) return false;
-
-  payload_.insert(payload_.end(), bytes.begin(), bytes.end());
-
-  return true;
-}
-
-bool EventPayloadBuilder::AddPayloadOctets(const vector<uint8_t>& bytes) {
-  return AddPayloadOctets(bytes.size(), bytes);
-}
-
-bool EventPayloadBuilder::AddPayloadOctets(size_t octets, uint64_t value) {
-  vector<uint8_t> val_vector;
-
-  uint64_t v = value;
-
-  if (octets > sizeof(uint64_t)) return false;
-
-  for (size_t i = 0; i < octets; i++) {
-    val_vector.push_back(v & 0xff);
-    v = v >> 8;
-  }
-
-  if (v != 0) return false;
-
-  return AddPayloadOctets(octets, val_vector);
-}
-
-bool EventPayloadBuilder::AddPayloadAddress(const Address& address) {
-  if (payload_.size() + Address::kLength > max_bytes_) return false;
-
-  for (size_t i = 0; i < Address::kLength; i++) {
-    payload_.push_back(address.address[i]);
-  }
-  return true;
-}
-
-bool EventPayloadBuilder::AddPayloadOctets1(uint8_t value) {
-  return AddPayloadOctets(1, value);
-}
-
-bool EventPayloadBuilder::AddPayloadOctets2(uint16_t value) {
-  return AddPayloadOctets(2, value);
-}
-
-bool EventPayloadBuilder::AddPayloadOctets3(uint32_t value) {
-  return AddPayloadOctets(3, value);
-}
-
-bool EventPayloadBuilder::AddPayloadOctets4(uint32_t value) {
-  return AddPayloadOctets(4, value);
-}
-
-bool EventPayloadBuilder::AddPayloadOctets6(uint64_t value) {
-  return AddPayloadOctets(6, value);
-}
-
-bool EventPayloadBuilder::AddPayloadOctets8(uint64_t value) {
-  return AddPayloadOctets(8, value);
-}
-
-bool EventPayloadBuilder::CanAddPayloadOctets(size_t num_bytes) const {
-  return payload_.size() + num_bytes <= max_bytes_;
-}
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/hci/event_payload_builder.h b/vendor_libs/test_vendor_lib/packets/hci/event_payload_builder.h
deleted file mode 100644
index b656edf..0000000
--- a/vendor_libs/test_vendor_lib/packets/hci/event_payload_builder.h
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cstdint>
-#include <vector>
-
-#include "packets/packet_builder.h"
-#include "types/address.h"
-
-namespace test_vendor_lib {
-namespace packets {
-
-class EventPayloadBuilder : public PacketBuilder<true> {
- public:
-  EventPayloadBuilder() = default;
-  EventPayloadBuilder(size_t max_bytes);
-  virtual ~EventPayloadBuilder() = default;
-
-  virtual size_t size() const override;
-
-  virtual void Serialize(std::back_insert_iterator<std::vector<uint8_t>> it) const;
-
-  // Add |octets| bytes to the payload.  Return true if:
-  // - the size of |bytes| is equal to |octets| and
-  // - the new size of the payload is still < |max_bytes_|
-  bool AddPayloadOctets(size_t octets, const std::vector<uint8_t>& bytes);
-
-  bool AddPayloadOctets(const std::vector<uint8_t>& bytes);
-
-  bool AddPayloadOctets1(uint8_t value);
-  bool AddPayloadOctets2(uint16_t value);
-  bool AddPayloadOctets3(uint32_t value);
-  bool AddPayloadOctets4(uint32_t value);
-  bool AddPayloadOctets6(uint64_t value);
-  bool AddPayloadOctets8(uint64_t value);
-
- private:
-  // Add |octets| bytes to the payload.  Return true if:
-  // - the value of |value| fits in |octets| bytes and
-  // - the new size of the payload is still < |max_bytes_|
-  bool AddPayloadOctets(size_t octets, uint64_t value);
-
-  // Add |address| to the payload.  Return true if:
-  // - the new size of the payload is still < |max_bytes_|
-  bool AddPayloadAddress(const Address& address);
-
-  // Return true if |num_bytes| can be added to the payload.
-  bool CanAddPayloadOctets(size_t num_bytes) const;
-
-  size_t max_bytes_{255};
-
-  // Underlying containers for storing the actual packet
-  std::vector<uint8_t> payload_;
-};
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/hci/hci_packet_builder.cc b/vendor_libs/test_vendor_lib/packets/hci/hci_packet_builder.cc
deleted file mode 100644
index 1d5689b..0000000
--- a/vendor_libs/test_vendor_lib/packets/hci/hci_packet_builder.cc
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "packets/hci/hci_packet_builder.h"
-
-using std::vector;
-
-namespace test_vendor_lib {
-namespace packets {
-
-std::shared_ptr<std::vector<uint8_t>> HciPacketBuilder::ToVector() {
-  std::shared_ptr<std::vector<uint8_t>> to_return = std::make_shared<std::vector<uint8_t>>();
-  std::back_insert_iterator<std::vector<uint8_t>> it(*to_return);
-  Serialize(it);
-  return to_return;
-}
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/hci/hci_packet_builder.h b/vendor_libs/test_vendor_lib/packets/hci/hci_packet_builder.h
deleted file mode 100644
index d508696..0000000
--- a/vendor_libs/test_vendor_lib/packets/hci/hci_packet_builder.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cstdint>
-
-#include "packets/packet_builder.h"
-
-namespace test_vendor_lib {
-namespace packets {
-
-// Base packet for HCI packets specified in the Bluetooth Core Specification
-// Version 4.2, Volume 2, Part E, Section 5.4
-class HciPacketBuilder : public PacketBuilder<true> {
- public:
-  virtual ~HciPacketBuilder() override = default;
-
-  std::shared_ptr<std::vector<uint8_t>> ToVector();
-
- protected:
-  HciPacketBuilder() = default;
-};
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/hci/le_meta_event_builder.cc b/vendor_libs/test_vendor_lib/packets/hci/le_meta_event_builder.cc
deleted file mode 100644
index f0599a7..0000000
--- a/vendor_libs/test_vendor_lib/packets/hci/le_meta_event_builder.cc
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "packets/hci/le_meta_event_builder.h"
-
-#include <base/logging.h>
-
-using std::vector;
-using test_vendor_lib::hci::LeSubEventCode;
-using test_vendor_lib::hci::Status;
-
-namespace test_vendor_lib {
-namespace packets {
-
-LeMetaEventBuilder::LeMetaEventBuilder(LeSubEventCode sub_event_code)
-    : sub_event_code_(sub_event_code), payload_(std::make_unique<RawBuilder>()) {}
-
-LeMetaEventBuilder::LeMetaEventBuilder(LeSubEventCode sub_event_code, std::unique_ptr<RawBuilder> payload)
-    : sub_event_code_(sub_event_code), payload_(std::move(payload)) {}
-
-// Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.7.65.1
-std::unique_ptr<LeMetaEventBuilder> LeMetaEventBuilder::CreateLeConnectionCompleteEvent(
-    Status status, uint16_t handle, uint8_t role, uint8_t peer_address_type, const Address& peer, uint16_t interval,
-    uint16_t latency, uint16_t supervision_timeout) {
-  std::unique_ptr<LeMetaEventBuilder> evt_ptr =
-      std::unique_ptr<LeMetaEventBuilder>(new LeMetaEventBuilder(LeSubEventCode::CONNECTION_COMPLETE));
-
-  CHECK(evt_ptr->AddOctets1(static_cast<uint8_t>(status)));
-  CHECK(evt_ptr->AddOctets2(handle));
-  CHECK(evt_ptr->AddOctets1(role));
-  CHECK(evt_ptr->AddOctets1(peer_address_type));
-  CHECK(evt_ptr->AddAddress(peer));
-  CHECK(evt_ptr->AddOctets2(interval));
-  CHECK(evt_ptr->AddOctets2(latency));
-  CHECK(evt_ptr->AddOctets2(supervision_timeout));
-  CHECK(evt_ptr->AddOctets1(0x00));  // Master Clock Accuracy (unused for master)
-
-  return evt_ptr;
-}
-
-std::unique_ptr<LeMetaEventBuilder> LeMetaEventBuilder::CreateLeEnhancedConnectionCompleteEvent(
-    Status status, uint16_t handle, uint8_t role, uint8_t peer_address_type, const Address& peer,
-    const Address& local_private_address, const Address& peer_private_address, uint16_t interval, uint16_t latency,
-    uint16_t supervision_timeout) {
-  std::unique_ptr<LeMetaEventBuilder> evt_ptr =
-      std::unique_ptr<LeMetaEventBuilder>(new LeMetaEventBuilder(LeSubEventCode::ENHANCED_CONNECTION_COMPLETE));
-
-  CHECK(evt_ptr->AddOctets1(static_cast<uint8_t>(status)));
-  CHECK(evt_ptr->AddOctets2(handle));
-  CHECK(evt_ptr->AddOctets1(role));
-  CHECK(evt_ptr->AddOctets1(peer_address_type));
-  CHECK(evt_ptr->AddAddress(peer));
-  CHECK(evt_ptr->AddAddress(local_private_address));
-  CHECK(evt_ptr->AddAddress(peer_private_address));
-  CHECK(evt_ptr->AddOctets2(interval));
-  CHECK(evt_ptr->AddOctets2(latency));
-  CHECK(evt_ptr->AddOctets2(supervision_timeout));
-  CHECK(evt_ptr->AddOctets1(0x00));  // Master Clock Accuracy (unused for master)
-
-  return evt_ptr;
-}
-
-std::unique_ptr<LeMetaEventBuilder> LeMetaEventBuilder::CreateLeConnectionUpdateCompleteEvent(
-    Status status, uint16_t handle, uint16_t interval, uint16_t latency, uint16_t supervision_timeout) {
-  std::unique_ptr<LeMetaEventBuilder> evt_ptr =
-      std::unique_ptr<LeMetaEventBuilder>(new LeMetaEventBuilder(LeSubEventCode::CONNECTION_UPDATE_COMPLETE));
-
-  CHECK(evt_ptr->AddOctets1(static_cast<uint8_t>(status)));
-  CHECK(evt_ptr->AddOctets2(handle));
-  CHECK(evt_ptr->AddOctets2(interval));
-  CHECK(evt_ptr->AddOctets2(latency));
-  CHECK(evt_ptr->AddOctets2(supervision_timeout));
-
-  return evt_ptr;
-}
-
-// Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.7.65.2
-std::unique_ptr<LeMetaEventBuilder> LeMetaEventBuilder::CreateLeAdvertisingReportEvent() {
-  std::unique_ptr<LeMetaEventBuilder> evt_ptr = std::unique_ptr<LeMetaEventBuilder>(
-      new LeMetaEventBuilder(LeSubEventCode::ADVERTISING_REPORT, std::unique_ptr<RawBuilder>(new CountedBuilder())));
-
-  return evt_ptr;
-}
-
-bool LeMetaEventBuilder::AddLeAdvertisingReport(LeAdvertisement::AdvertisementType event_type,
-                                                LeAdvertisement::AddressType addr_type, const Address& addr,
-                                                const vector<uint8_t>& data, uint8_t rssi) {
-  if (!CanAddOctets(10 + data.size())) return false;
-
-  CHECK(sub_event_code_ == LeSubEventCode::ADVERTISING_REPORT);
-
-  std::unique_ptr<RawBuilder> ad = std::make_unique<RawBuilder>();
-
-  CHECK(ad->AddOctets1(static_cast<uint8_t>(event_type)));
-  CHECK(ad->AddOctets1(static_cast<uint8_t>(addr_type)));
-  CHECK(ad->AddAddress(addr));
-  CHECK(ad->AddOctets1(data.size()));
-  CHECK(ad->AddOctets(data));
-  CHECK(ad->AddOctets1(rssi));
-  AddBuilder(std::move(ad));
-  return true;
-}
-
-// Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section 7.7.65.4
-std::unique_ptr<LeMetaEventBuilder> LeMetaEventBuilder::CreateLeRemoteUsedFeaturesEvent(Status status, uint16_t handle,
-                                                                                        uint64_t features) {
-  std::unique_ptr<LeMetaEventBuilder> evt_ptr =
-      std::unique_ptr<LeMetaEventBuilder>(new LeMetaEventBuilder(LeSubEventCode::READ_REMOTE_FEATURES_COMPLETE));
-
-  CHECK(evt_ptr->AddOctets1(static_cast<uint8_t>(status)));
-  CHECK(evt_ptr->AddOctets2(handle));
-  CHECK(evt_ptr->AddOctets8(features));
-
-  return evt_ptr;
-}
-
-size_t LeMetaEventBuilder::size() const {
-  return 1 + payload_->size();  // Add the sub_event_code
-}
-
-void LeMetaEventBuilder::Serialize(std::back_insert_iterator<std::vector<uint8_t>> it) const {
-  insert(static_cast<uint8_t>(sub_event_code_), it);
-  uint8_t payload_size = size() - sizeof(uint8_t);
-  CHECK(size() - sizeof(uint8_t) == static_cast<size_t>(payload_size)) << "Payload too large for an event: " << size();
-  payload_->Serialize(it);
-}
-
-bool LeMetaEventBuilder::AddBuilder(std::unique_ptr<BasePacketBuilder> builder) {
-  // Upcast the payload to add the next builder.
-  CountedBuilder* temp_ptr = static_cast<CountedBuilder*>(payload_.get());
-  temp_ptr->Add(std::move(builder));
-  return true;
-}
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/hci/le_meta_event_builder.h b/vendor_libs/test_vendor_lib/packets/hci/le_meta_event_builder.h
deleted file mode 100644
index b1d713d..0000000
--- a/vendor_libs/test_vendor_lib/packets/hci/le_meta_event_builder.h
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <base/logging.h>
-#include <cstdint>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "include/hci.h"
-#include "include/le_advertisement.h"
-#include "packets/counted_builder.h"
-#include "packets/hci/hci_packet_builder.h"
-#include "packets/packet_builder.h"
-#include "packets/packet_view.h"
-#include "packets/raw_builder.h"
-#include "types/address.h"
-
-namespace test_vendor_lib {
-namespace packets {
-
-// LE Meta Event Packets are specified in the Bluetooth Core Specification
-// Version 4.2, Volume 2, Part E, Section 7.7.65. The first byte is the
-// Subevent_Code.
-class LeMetaEventBuilder : public RawBuilder {
- public:
-  virtual ~LeMetaEventBuilder() override = default;
-
-  // Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section
-  // 7.7.65.1
-  static std::unique_ptr<LeMetaEventBuilder> CreateLeConnectionCompleteEvent(hci::Status status, uint16_t handle,
-                                                                             uint8_t role, uint8_t peer_address_type,
-                                                                             const Address& peer, uint16_t interval,
-                                                                             uint16_t latency,
-                                                                             uint16_t supervision_timeout);
-
-  // Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section
-  // 7.7.65.2
-  static std::unique_ptr<LeMetaEventBuilder> CreateLeAdvertisingReportEvent();
-
-  // Returns true if the report can be added to the event packet.
-  bool AddLeAdvertisingReport(LeAdvertisement::AdvertisementType event_type, LeAdvertisement::AddressType addr_type,
-                              const Address& addr, const std::vector<uint8_t>& data, uint8_t rssi);
-
-  // Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section
-  // 7.7.65.3
-  static std::unique_ptr<LeMetaEventBuilder> CreateLeConnectionUpdateCompleteEvent(hci::Status status, uint16_t handle,
-                                                                                   uint16_t interval, uint16_t latency,
-                                                                                   uint16_t supervision_timeout);
-
-  // Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section
-  // 7.7.65.4
-  static std::unique_ptr<LeMetaEventBuilder> CreateLeRemoteUsedFeaturesEvent(hci::Status status, uint16_t handle,
-                                                                             uint64_t features);
-
-  // Bluetooth Core Specification Version 4.2, Volume 2, Part E, Section
-  // 7.7.65.10
-  static std::unique_ptr<LeMetaEventBuilder> CreateLeEnhancedConnectionCompleteEvent(
-      hci::Status status, uint16_t handle, uint8_t role, uint8_t peer_address_type, const Address& peer,
-      const Address& local_private_address, const Address& peer_private_address, uint16_t interval, uint16_t latency,
-      uint16_t supervision_timeout);
-
-  virtual size_t size() const override;
-
-  virtual void Serialize(std::back_insert_iterator<std::vector<uint8_t>> it) const override;
-
-  bool AddBuilder(std::unique_ptr<BasePacketBuilder> builder);
-
- private:
-  explicit LeMetaEventBuilder(hci::LeSubEventCode sub_event_code);
-  explicit LeMetaEventBuilder(hci::LeSubEventCode sub_event_code, std::unique_ptr<RawBuilder> payload);
-  hci::LeSubEventCode sub_event_code_;
-  std::unique_ptr<RawBuilder> payload_;
-};
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/hci/sco_packet_builder.cc b/vendor_libs/test_vendor_lib/packets/hci/sco_packet_builder.cc
deleted file mode 100644
index 57203ed..0000000
--- a/vendor_libs/test_vendor_lib/packets/hci/sco_packet_builder.cc
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "packets/hci/sco_packet_builder.h"
-
-#include <base/logging.h>
-
-using std::vector;
-using test_vendor_lib::sco::PacketStatusFlagsType;
-
-namespace test_vendor_lib {
-namespace packets {
-
-ScoPacketBuilder::ScoPacketBuilder(uint16_t handle, PacketStatusFlagsType packet_status_flags,
-                                   std::unique_ptr<BasePacketBuilder> payload)
-    : handle_(handle), packet_status_flags_(packet_status_flags), payload_(std::move(payload)) {}
-
-size_t ScoPacketBuilder::size() const {
-  return 2 * sizeof(uint16_t) + payload_->size();
-}
-
-void ScoPacketBuilder::Serialize(std::back_insert_iterator<std::vector<uint8_t>> it) const {
-  insert(static_cast<uint16_t>((handle_ & 0xfff) | (static_cast<uint16_t>(packet_status_flags_) << 12)), it);
-  uint8_t payload_size = payload_->size();
-
-  CHECK(static_cast<size_t>(payload_size) == payload_->size())
-      << "Payload too large for a SCO packet: " << payload_->size();
-  insert(payload_size, it);
-  payload_->Serialize(it);
-}
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/hci/sco_packet_builder.h b/vendor_libs/test_vendor_lib/packets/hci/sco_packet_builder.h
deleted file mode 100644
index a2d8561..0000000
--- a/vendor_libs/test_vendor_lib/packets/hci/sco_packet_builder.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <base/logging.h>
-#include <cstdint>
-#include <memory>
-#include <vector>
-
-#include "include/sco.h"
-#include "packets/hci/hci_packet_builder.h"
-#include "packets/packet_builder.h"
-
-namespace test_vendor_lib {
-namespace packets {
-
-// SCO data packets are specified in the Bluetooth Core Specification Version
-// 4.2, Volume 2, Part E, Section 5.4.3
-class ScoPacketBuilder : public HciPacketBuilder {
- public:
-  virtual ~ScoPacketBuilder() override = default;
-
-  static std::unique_ptr<ScoPacketBuilder> Create(uint16_t handle, sco::PacketStatusFlagsType packet_status_flags,
-                                                  std::unique_ptr<BasePacketBuilder> payload);
-
-  virtual size_t size() const override;
-
-  virtual void Serialize(std::back_insert_iterator<std::vector<uint8_t>> it) const override;
-
- private:
-  ScoPacketBuilder(uint16_t handle, sco::PacketStatusFlagsType packet_status_flags,
-                   std::unique_ptr<BasePacketBuilder> payload);
-  ScoPacketBuilder() = delete;
-  uint16_t handle_;
-  sco::PacketStatusFlagsType packet_status_flags_;
-  std::unique_ptr<BasePacketBuilder> payload_;
-};
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/hci/sco_packet_view.cc b/vendor_libs/test_vendor_lib/packets/hci/sco_packet_view.cc
deleted file mode 100644
index 9516ef4..0000000
--- a/vendor_libs/test_vendor_lib/packets/hci/sco_packet_view.cc
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "packets/hci/sco_packet_view.h"
-
-#include <base/logging.h>
-
-using test_vendor_lib::sco::PacketStatusFlagsType;
-
-namespace test_vendor_lib {
-namespace packets {
-
-ScoPacketView::ScoPacketView(std::shared_ptr<std::vector<uint8_t>> packet) : PacketView<true>(packet) {}
-
-ScoPacketView ScoPacketView::Create(std::shared_ptr<std::vector<uint8_t>> packet) {
-  return ScoPacketView(packet);
-}
-
-uint16_t ScoPacketView::GetHandle() const {
-  return begin().extract<uint16_t>() & 0xfff;
-}
-
-PacketStatusFlagsType ScoPacketView::GetPacketStatusFlags() const {
-  return static_cast<PacketStatusFlagsType>(((begin() + 1).extract<uint8_t>() & 0x30) >> 4);
-}
-
-PacketView<true> ScoPacketView::GetPayload() const {
-  uint8_t payload_size = (begin() + sizeof(uint16_t)).extract<uint8_t>();
-  CHECK(static_cast<uint8_t>(size() - sizeof(uint16_t) - sizeof(uint8_t)) == payload_size)
-      << "Malformed SCO packet payload_size " << payload_size << " + 4 != " << size();
-  return SubViewLittleEndian(sizeof(uint16_t) + sizeof(uint8_t), size());
-}
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/hci/sco_packet_view.h b/vendor_libs/test_vendor_lib/packets/hci/sco_packet_view.h
deleted file mode 100644
index 26b9489..0000000
--- a/vendor_libs/test_vendor_lib/packets/hci/sco_packet_view.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <base/logging.h>
-#include <cstdint>
-#include <memory>
-#include <vector>
-
-#include "include/sco.h"
-#include "packets/packet_view.h"
-
-namespace test_vendor_lib {
-namespace packets {
-
-// SCO data packets are specified in the Bluetooth Core Specification Version
-// 4.2, Volume 2, Part E, Section 5.4.3
-class ScoPacketView : public PacketView<true> {
- public:
-  virtual ~ScoPacketView() override = default;
-
-  static ScoPacketView Create(std::shared_ptr<std::vector<uint8_t>> packet);
-
-  uint16_t GetHandle() const;
-  sco::PacketStatusFlagsType GetPacketStatusFlags() const;
-  PacketView<true> GetPayload() const;
-
- private:
-  ScoPacketView(std::shared_ptr<std::vector<uint8_t>> packet);
-  ScoPacketView() = delete;
-};
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/hci/test/acl_builder_test.cc b/vendor_libs/test_vendor_lib/packets/hci/test/acl_builder_test.cc
deleted file mode 100644
index 61f8415..0000000
--- a/vendor_libs/test_vendor_lib/packets/hci/test/acl_builder_test.cc
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "packets/hci/acl_packet_builder.h"
-#include "packets/hci/acl_packet_view.h"
-#include "packets/raw_builder.h"
-
-#include <gtest/gtest.h>
-#include <forward_list>
-#include <memory>
-
-#include "types/address.h"
-
-using std::vector;
-using test_vendor_lib::acl::BroadcastFlagsType;
-using test_vendor_lib::acl::PacketBoundaryFlagsType;
-
-namespace {
-vector<uint8_t> count = {
-    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
-    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
-};
-
-vector<uint8_t> information_request = {
-    0xfe, 0x2e, 0x0a, 0x00, 0x06, 0x00, 0x01, 0x00, 0x0a, 0x02, 0x02, 0x00, 0x02, 0x00,
-};
-
-}  // namespace
-
-namespace test_vendor_lib {
-namespace packets {
-
-class AclBuilderTest : public ::testing::Test {
- public:
-  AclBuilderTest() = default;
-  ~AclBuilderTest() = default;
-};
-
-TEST(AclBuilderTest, buildAclCountTest) {
-  uint16_t handle = 0x0102;
-  PacketBoundaryFlagsType packet_boundary_flags = PacketBoundaryFlagsType::FIRST_AUTOMATICALLY_FLUSHABLE;
-  BroadcastFlagsType broadcast_flags = BroadcastFlagsType::ACTIVE_SLAVE_BROADCAST;
-
-  std::unique_ptr<RawBuilder> count_payload = std::make_unique<RawBuilder>();
-  count_payload->AddOctets(count);
-  ASSERT_EQ(count.size(), count_payload->size());
-
-  std::unique_ptr<AclPacketBuilder> count_packet =
-      AclPacketBuilder::Create(handle, packet_boundary_flags, broadcast_flags, std::move(count_payload));
-
-  ASSERT_EQ(count.size() + 4, count_packet->size());
-
-  std::shared_ptr<std::vector<uint8_t>> count_packet_bytes = count_packet->ToVector();
-  AclPacketView count_packet_view = AclPacketView::Create(count_packet_bytes);
-
-  ASSERT_EQ(handle, count_packet_view.GetHandle());
-  ASSERT_EQ(packet_boundary_flags, count_packet_view.GetPacketBoundaryFlags());
-  ASSERT_EQ(broadcast_flags, count_packet_view.GetBroadcastFlags());
-  PacketView<true> count_view = count_packet_view.GetPayload();
-
-  ASSERT_EQ(count_view.size(), count.size());
-  for (size_t i = 0; i < count_view.size(); i++) {
-    ASSERT_EQ(count_view[i], count[i]);
-  }
-}
-
-TEST(AclBuilderTest, buildInformationRequest) {
-  uint16_t handle = 0x0efe;
-  PacketBoundaryFlagsType packet_boundary_flags = PacketBoundaryFlagsType::FIRST_AUTOMATICALLY_FLUSHABLE;
-  BroadcastFlagsType broadcast_flags = BroadcastFlagsType::POINT_TO_POINT;
-
-  std::vector<uint8_t> payload_bytes(information_request.begin() + 4, information_request.end());
-  std::unique_ptr<RawBuilder> payload = std::make_unique<RawBuilder>();
-  payload->AddOctets(payload_bytes);
-  ASSERT_EQ(payload_bytes.size(), payload->size());
-
-  std::unique_ptr<AclPacketBuilder> packet =
-      AclPacketBuilder::Create(handle, packet_boundary_flags, broadcast_flags, std::move(payload));
-
-  ASSERT_EQ(information_request.size(), packet->size());
-
-  std::shared_ptr<std::vector<uint8_t>> packet_bytes = packet->ToVector();
-  AclPacketView packet_view = AclPacketView::Create(packet_bytes);
-
-  ASSERT_EQ(packet_bytes->size(), information_request.size());
-  for (size_t i = 0; i < packet_bytes->size(); i++) {
-    ASSERT_EQ((*packet_bytes)[i], information_request[i]);
-  }
-
-  ASSERT_EQ(handle, packet_view.GetHandle());
-  ASSERT_EQ(packet_boundary_flags, packet_view.GetPacketBoundaryFlags());
-  ASSERT_EQ(broadcast_flags, packet_view.GetBroadcastFlags());
-  PacketView<true> payload_view = packet_view.GetPayload();
-
-  ASSERT_EQ(payload_view.size(), payload_bytes.size());
-  for (size_t i = 0; i < payload_view.size(); i++) {
-    ASSERT_EQ(payload_view[i], payload_bytes[i]);
-  }
-
-  ASSERT_EQ(packet_view.size(), information_request.size());
-  for (size_t i = 0; i < packet_view.size(); i++) {
-    ASSERT_EQ(packet_view[i], information_request[i]);
-  }
-}
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/hci/test/event_builder_test.cc b/vendor_libs/test_vendor_lib/packets/hci/test/event_builder_test.cc
deleted file mode 100644
index e63c866..0000000
--- a/vendor_libs/test_vendor_lib/packets/hci/test/event_builder_test.cc
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "packets/hci/event_packet_builder.h"
-
-#include <gtest/gtest.h>
-#include <forward_list>
-#include <memory>
-
-#include "types/address.h"
-
-using std::vector;
-
-namespace {
-vector<uint8_t> count = {
-    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
-    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
-};
-
-}  // namespace
-
-namespace test_vendor_lib {
-namespace packets {
-
-class EventBuilderTest : public ::testing::Test {
- public:
-  EventBuilderTest() = default;
-  ~EventBuilderTest() = default;
-};
-
-TEST(EventBuilderTest, buildLeAdvertisementSmallTest) {
-  LeAdvertisement::AdvertisementType adv_type = LeAdvertisement::AdvertisementType::ADV_SCAN_IND;
-  LeAdvertisement::AddressType addr_type = LeAdvertisement::AddressType::RANDOM;
-  std::unique_ptr<EventPacketBuilder> le_adv = EventPacketBuilder::CreateLeAdvertisingReportEvent();
-  Address addr({1, 2, 3, 4, 5, 6});
-  uint8_t rssi = -93;
-
-  std::vector<uint8_t> payload({0x23});
-  le_adv->AddLeAdvertisingReport(adv_type, addr_type, addr, {payload}, rssi);
-
-  uint8_t payload_size = payload.size();
-  uint8_t event_size = payload_size + sizeof(Address) + sizeof(rssi) + 5;
-  std::vector<uint8_t> expected({
-      0x3e,  // HCI LE Event
-      event_size,
-      0x02,          // LE Advertising subevent code
-      0x01,          // Number of responses
-      0x02,          // Event type is scannable undirected
-      0x01,          // Address type is random
-      0x01,          // Address
-      0x02,          // Address
-      0x03,          // Address
-      0x04,          // Address
-      0x05,          // Address
-      0x06,          // Address
-      payload_size,  // Length of the data
-  });
-
-  expected.push_back(payload[0]);
-  expected.push_back(rssi);
-
-  ASSERT_EQ(expected.size(), le_adv->size());
-  ASSERT_EQ(expected, *le_adv->ToVector());
-}
-
-TEST(EventBuilderTest, buildLeAdvertisementTest) {
-  LeAdvertisement::AdvertisementType adv_type = LeAdvertisement::AdvertisementType::ADV_SCAN_IND;
-  LeAdvertisement::AddressType addr_type = LeAdvertisement::AddressType::RANDOM;
-  std::unique_ptr<EventPacketBuilder> le_adv = EventPacketBuilder::CreateLeAdvertisingReportEvent();
-  Address addr({1, 2, 3, 4, 5, 6});
-  uint8_t rssi = -93;
-
-  le_adv->AddLeAdvertisingReport(adv_type, addr_type, addr, count, rssi);
-
-  uint8_t count_size = static_cast<uint8_t>(count.size());
-  uint8_t event_size = count_size + sizeof(Address) + sizeof(rssi) + 5;
-  std::vector<uint8_t> expected({
-      0x3e,  // HCI LE Event
-      event_size,
-      0x02,        // LE Advertising subevent code
-      0x01,        // Number of responses
-      0x02,        // Event type is scannable undirected
-      0x01,        // Address type is random
-      0x01,        // Address
-      0x02,        // Address
-      0x03,        // Address
-      0x04,        // Address
-      0x05,        // Address
-      0x06,        // Address
-      count_size,  // Length of the data
-  });
-
-  for (size_t i = 0; i < count.size(); i++) {
-    expected.push_back(count[i]);
-  }
-  expected.push_back(rssi);
-
-  std::shared_ptr<std::vector<uint8_t>> raw_adv = le_adv->ToVector();
-  ASSERT_EQ(expected, *raw_adv);
-}
-
-TEST(EventBuilderTest, buildNumberOfCompletedPackets) {
-  uint16_t handle = 0x0102;
-  uint16_t num_packets = 0x0304;
-
-  std::unique_ptr<EventPacketBuilder> event =
-      EventPacketBuilder::CreateNumberOfCompletedPacketsEvent(handle, num_packets);
-
-  uint8_t number_of_handles = 1;
-  uint8_t event_size = sizeof(uint8_t) + number_of_handles * 2 * sizeof(uint16_t);
-  std::vector<uint8_t> expected({
-      0x13,                           // HCI Number Of Completed Packets Event code
-      event_size, number_of_handles,  //
-      0x02, 0x01,                     // handle
-      0x04, 0x03,                     // count
-  });
-
-  std::shared_ptr<std::vector<uint8_t>> raw_event = event->ToVector();
-  ASSERT_EQ(expected, *raw_event);
-}
-
-TEST(EventBuilderTest, buildNumberOfCompletedPacketsMultiple) {
-  uint16_t handle = 0x0102;
-  uint16_t num_packets = 0x0304;
-  uint16_t handle2 = 0x0506;
-  uint16_t num_packets2 = 0x0708;
-
-  std::unique_ptr<EventPacketBuilder> event =
-      EventPacketBuilder::CreateNumberOfCompletedPacketsEvent(handle, num_packets);
-  event->AddCompletedPackets(handle2, num_packets2);
-
-  uint8_t number_of_handles = 2;
-  uint8_t event_size = sizeof(uint8_t) + number_of_handles * 2 * sizeof(uint16_t);
-  std::vector<uint8_t> expected({
-      0x13,                           // HCI Number Of Completed Packets Event code
-      event_size, number_of_handles,  //
-      0x02, 0x01,                     // handle
-      0x04, 0x03,                     // count
-      0x06, 0x05,                     // handle
-      0x08, 0x07,                     // count
-  });
-
-  std::shared_ptr<std::vector<uint8_t>> raw_event = event->ToVector();
-  ASSERT_EQ(expected, *raw_event);
-}
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/iterator.cc b/vendor_libs/test_vendor_lib/packets/iterator.cc
deleted file mode 100644
index 65173ee..0000000
--- a/vendor_libs/test_vendor_lib/packets/iterator.cc
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "iterator.h"
-
-#include <base/logging.h>
-
-namespace test_vendor_lib {
-namespace packets {
-
-template <bool little_endian>
-Iterator<little_endian>::Iterator(std::forward_list<View> data, size_t offset) {
-  data_ = data;
-  index_ = offset;
-  length_ = 0;
-  for (auto& view : data) {
-    length_ += view.size();
-  }
-}
-
-template <bool little_endian>
-Iterator<little_endian> Iterator<little_endian>::operator+(int offset) {
-  auto itr(*this);
-
-  return itr += offset;
-}
-
-template <bool little_endian>
-Iterator<little_endian>& Iterator<little_endian>::operator+=(int offset) {
-  index_ += offset;
-  return *this;
-}
-
-template <bool little_endian>
-Iterator<little_endian> Iterator<little_endian>::operator++(int) {
-  auto itr(*this);
-  index_++;
-  return itr;
-}
-
-template <bool little_endian>
-Iterator<little_endian>& Iterator<little_endian>::operator++() {
-  index_++;
-  return *this;
-}
-
-template <bool little_endian>
-Iterator<little_endian> Iterator<little_endian>::operator-(int offset) {
-  auto itr(*this);
-
-  return itr -= offset;
-}
-
-template <bool little_endian>
-int Iterator<little_endian>::operator-(Iterator<little_endian>& itr) {
-  return index_ - itr.index_;
-}
-
-template <bool little_endian>
-Iterator<little_endian>& Iterator<little_endian>::operator-=(int offset) {
-  index_ -= offset;
-
-  return *this;
-}
-
-template <bool little_endian>
-Iterator<little_endian> Iterator<little_endian>::operator--(int) {
-  auto itr(*this);
-  if (index_ != 0) index_--;
-
-  return itr;
-}
-
-template <bool little_endian>
-Iterator<little_endian>& Iterator<little_endian>::operator--() {
-  if (index_ != 0) index_--;
-
-  return *this;
-}
-
-template <bool little_endian>
-Iterator<little_endian>& Iterator<little_endian>::operator=(const Iterator<little_endian>& itr) {
-  data_ = itr.data_;
-  index_ = itr.index_;
-
-  return *this;
-}
-
-template <bool little_endian>
-bool Iterator<little_endian>::operator==(const Iterator<little_endian>& itr) const {
-  return index_ == itr.index_;
-}
-
-template <bool little_endian>
-bool Iterator<little_endian>::operator!=(const Iterator<little_endian>& itr) const {
-  return !(*this == itr);
-}
-
-template <bool little_endian>
-bool Iterator<little_endian>::operator<(const Iterator<little_endian>& itr) const {
-  return index_ < itr.index_;
-}
-
-template <bool little_endian>
-bool Iterator<little_endian>::operator>(const Iterator<little_endian>& itr) const {
-  return index_ > itr.index_;
-}
-
-template <bool little_endian>
-bool Iterator<little_endian>::operator<=(const Iterator<little_endian>& itr) const {
-  return index_ <= itr.index_;
-}
-
-template <bool little_endian>
-bool Iterator<little_endian>::operator>=(const Iterator<little_endian>& itr) const {
-  return index_ >= itr.index_;
-}
-
-template <bool little_endian>
-uint8_t Iterator<little_endian>::operator*() const {
-  CHECK(index_ < length_) << "Index " << index_ << " out of bounds: " << length_;
-  size_t index = index_;
-
-  for (auto view : data_) {
-    if (index < view.size()) {
-      return view[index];
-    }
-    index -= view.size();
-  }
-  CHECK(false) << "Out of fragments searching for Index " << index_;
-  return 0;
-}
-
-template <bool little_endian>
-size_t Iterator<little_endian>::NumBytesRemaining() const {
-  if (length_ > index_) {
-    return length_ - index_;
-  } else {
-    return 0;
-  }
-}
-
-// Explicit instantiations for both types of Iterators.
-template class Iterator<true>;
-template class Iterator<false>;
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/iterator.h b/vendor_libs/test_vendor_lib/packets/iterator.h
deleted file mode 100644
index b84ce64..0000000
--- a/vendor_libs/test_vendor_lib/packets/iterator.h
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cstdint>
-#include <forward_list>
-
-#include "types/address.h"
-#include "view.h"
-
-namespace test_vendor_lib {
-namespace packets {
-
-// Templated Iterator for endianness
-template <bool little_endian>
-class Iterator : public std::iterator<std::random_access_iterator_tag, uint8_t> {
- public:
-  Iterator(std::forward_list<View> data, size_t offset);
-  Iterator(const Iterator& itr) = default;
-  virtual ~Iterator() = default;
-
-  // All addition and subtraction operators are unbounded.
-  Iterator operator+(int offset);
-  Iterator& operator+=(int offset);
-  Iterator operator++(int);
-  Iterator& operator++();
-
-  Iterator operator-(int offset);
-  int operator-(Iterator& itr);
-  Iterator& operator-=(int offset);
-  Iterator operator--(int);
-  Iterator& operator--();
-
-  Iterator& operator=(const Iterator& itr);
-
-  bool operator!=(const Iterator& itr) const;
-  bool operator==(const Iterator& itr) const;
-
-  bool operator<(const Iterator& itr) const;
-  bool operator>(const Iterator& itr) const;
-
-  bool operator<=(const Iterator& itr) const;
-  bool operator>=(const Iterator& itr) const;
-
-  uint8_t operator*() const;
-  uint8_t operator->() const;
-
-  size_t NumBytesRemaining() const;
-
-  // Get the next sizeof(FixedWidthPODType) bytes and return the filled type
-  template <typename FixedWidthPODType>
-  FixedWidthPODType extract() {
-    static_assert(std::is_pod<FixedWidthPODType>::value, "Iterator::extract requires an fixed type.");
-    FixedWidthPODType extracted_value;
-    uint8_t* value_ptr = (uint8_t*)&extracted_value;
-
-    for (size_t i = 0; i < sizeof(FixedWidthPODType); i++) {
-      size_t index = (little_endian ? i : sizeof(FixedWidthPODType) - i - 1);
-      value_ptr[index] = *((*this)++);
-    }
-    return extracted_value;
-  }
-
- private:
-  std::forward_list<View> data_;
-  size_t index_;
-  size_t length_;
-};
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/link_layer/command_builder.h b/vendor_libs/test_vendor_lib/packets/link_layer/command_builder.h
deleted file mode 100644
index 15a4ab8..0000000
--- a/vendor_libs/test_vendor_lib/packets/link_layer/command_builder.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cstdint>
-#include <memory>
-
-#include "base/logging.h"
-
-#include "packets/packet_builder.h"
-#include "packets/packet_view.h"
-
-namespace test_vendor_lib {
-namespace packets {
-
-class CommandBuilder : public PacketBuilder<true> {
- public:
-  virtual ~CommandBuilder() = default;
-
-  static std::unique_ptr<CommandBuilder> Create(uint16_t opcode, PacketView<true> args) {
-    return std::unique_ptr<CommandBuilder>(new CommandBuilder(opcode, args));
-  }
-
-  virtual size_t size() const override {
-    return sizeof(opcode_) + args_.size();
-  }
-
- protected:
-  virtual void Serialize(std::back_insert_iterator<std::vector<uint8_t>> it) const override {
-    insert(opcode_, it);
-    for (const auto&& byte : args_) {
-      insert(byte, it);
-    }
-  }
-
- private:
-  explicit CommandBuilder(uint16_t opcode, PacketView<true> args) : opcode_(opcode), args_(args) {}
-  uint16_t opcode_;
-  PacketView<true> args_;
-};
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/link_layer/command_view.h b/vendor_libs/test_vendor_lib/packets/link_layer/command_view.h
deleted file mode 100644
index 0aa5683..0000000
--- a/vendor_libs/test_vendor_lib/packets/link_layer/command_view.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cstdint>
-
-#include <log/log.h>
-
-#include "packets/link_layer/link_layer_packet_view.h"
-#include "packets/packet_view.h"
-
-namespace test_vendor_lib {
-namespace packets {
-
-class CommandView : public PacketView<true> {
- public:
-  CommandView(const CommandView&) = default;
-  virtual ~CommandView() = default;
-
-  static CommandView GetCommand(const LinkLayerPacketView& view) {
-    CHECK(view.GetType() == Link::PacketType::COMMAND);
-    return CommandView(view.GetPayload());
-  }
-
-  uint16_t GetOpcode() {
-    return begin().extract<uint16_t>();
-  }
-
-  Iterator<true> GetData() {
-    return begin() + sizeof(uint16_t);
-  }
-
- private:
-  CommandView() = delete;
-  CommandView(const PacketView<true>& view) : PacketView(view) {}
-};
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/link_layer/disconnect_builder.h b/vendor_libs/test_vendor_lib/packets/link_layer/disconnect_builder.h
deleted file mode 100644
index 3c5a9f4..0000000
--- a/vendor_libs/test_vendor_lib/packets/link_layer/disconnect_builder.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cstdint>
-#include <memory>
-
-#include "base/logging.h"
-
-#include "packets/packet_builder.h"
-
-namespace test_vendor_lib {
-namespace packets {
-
-class DisconnectBuilder : public PacketBuilder<true> {
- public:
-  virtual ~DisconnectBuilder() = default;
-
-  static std::unique_ptr<DisconnectBuilder> Create(uint8_t reason) {
-    return std::unique_ptr<DisconnectBuilder>(new DisconnectBuilder(reason));
-  }
-
-  virtual size_t size() const override {
-    return sizeof(reason_);
-  }
-
- protected:
-  virtual void Serialize(std::back_insert_iterator<std::vector<uint8_t>> it) const override {
-    *it++ = reason_;
-  }
-
- private:
-  explicit DisconnectBuilder(uint8_t reason) : reason_(reason) {}
-  uint8_t reason_;
-};
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/link_layer/disconnect_view.h b/vendor_libs/test_vendor_lib/packets/link_layer/disconnect_view.h
deleted file mode 100644
index cdfcdc5..0000000
--- a/vendor_libs/test_vendor_lib/packets/link_layer/disconnect_view.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cstdint>
-
-#include "packets/link_layer/link_layer_packet_view.h"
-#include "packets/packet_view.h"
-
-namespace test_vendor_lib {
-namespace packets {
-
-class DisconnectView : public PacketView<true> {
- public:
-  DisconnectView(const DisconnectView&) = default;
-  virtual ~DisconnectView() = default;
-
-  static DisconnectView GetDisconnect(const LinkLayerPacketView& view) {
-    CHECK(view.GetType() == Link::PacketType::DISCONNECT);
-    return DisconnectView(view.GetPayload());
-  }
-
-  uint8_t GetReason() {
-    return at(0);
-  }
-
- private:
-  DisconnectView() = delete;
-  DisconnectView(const PacketView<true>& view) : PacketView(view) {}
-};
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/link_layer/encrypt_connection_builder.h b/vendor_libs/test_vendor_lib/packets/link_layer/encrypt_connection_builder.h
deleted file mode 100644
index 329ecdb..0000000
--- a/vendor_libs/test_vendor_lib/packets/link_layer/encrypt_connection_builder.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cstdint>
-#include <memory>
-
-#include "base/logging.h"
-
-#include "packets/packet_builder.h"
-
-namespace test_vendor_lib {
-namespace packets {
-
-class EncryptConnectionBuilder : public PacketBuilder<true> {
- public:
-  virtual ~EncryptConnectionBuilder() = default;
-
-  static std::unique_ptr<EncryptConnectionBuilder> Create(const std::vector<uint8_t>& key) {
-    return std::unique_ptr<EncryptConnectionBuilder>(new EncryptConnectionBuilder(key));
-  }
-
-  virtual size_t size() const override {
-    return key_.size();
-  }
-
- protected:
-  virtual void Serialize(std::back_insert_iterator<std::vector<uint8_t>> it) const override {
-    insert_vector(key_, it);
-  }
-
- private:
-  explicit EncryptConnectionBuilder(const std::vector<uint8_t>& key) : key_(key.begin(), key.begin() + 16) {}
-  std::vector<uint8_t> key_;
-};
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/link_layer/encrypt_connection_view.h b/vendor_libs/test_vendor_lib/packets/link_layer/encrypt_connection_view.h
deleted file mode 100644
index 665fe98..0000000
--- a/vendor_libs/test_vendor_lib/packets/link_layer/encrypt_connection_view.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cstdint>
-
-#include "packets/link_layer/link_layer_packet_view.h"
-#include "packets/packet_view.h"
-
-namespace test_vendor_lib {
-namespace packets {
-
-class EncryptConnectionView : public PacketView<true> {
- public:
-  EncryptConnectionView(const EncryptConnectionView&) = default;
-  virtual ~EncryptConnectionView() = default;
-
-  static EncryptConnectionView GetEncryptConnection(const LinkLayerPacketView& view) {
-    CHECK(view.GetType() == Link::PacketType::ENCRYPT_CONNECTION ||
-          view.GetType() == Link::PacketType::ENCRYPT_CONNECTION_RESPONSE);
-    return EncryptConnectionView(view.GetPayload());
-  }
-
-  Iterator<true> GetKey() {
-    return begin();
-  }
-
- private:
-  EncryptConnectionView() = delete;
-  EncryptConnectionView(const PacketView<true>& view) : PacketView(view) {}
-};
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/link_layer/inquiry_builder.h b/vendor_libs/test_vendor_lib/packets/link_layer/inquiry_builder.h
deleted file mode 100644
index 6703316..0000000
--- a/vendor_libs/test_vendor_lib/packets/link_layer/inquiry_builder.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cstdint>
-#include <memory>
-
-#include "base/logging.h"
-
-#include "inquiry.h"
-#include "packets/packet_builder.h"
-
-namespace test_vendor_lib {
-namespace packets {
-
-class InquiryBuilder : public PacketBuilder<true> {
- public:
-  virtual ~InquiryBuilder() = default;
-
-  static std::unique_ptr<InquiryBuilder> Create(Inquiry::InquiryType inquiry_type) {
-    return std::unique_ptr<InquiryBuilder>(new InquiryBuilder(inquiry_type));
-  }
-
-  virtual size_t size() const override {
-    return sizeof(uint8_t);
-  }
-
- protected:
-  virtual void Serialize(std::back_insert_iterator<std::vector<uint8_t>> it) const override {
-    insert(static_cast<uint8_t>(inquiry_type_), it);
-  }
-
- private:
-  explicit InquiryBuilder(Inquiry::InquiryType inquiry_type) : inquiry_type_(inquiry_type) {}
-  Inquiry::InquiryType inquiry_type_;
-};
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/link_layer/inquiry_response_builder.h b/vendor_libs/test_vendor_lib/packets/link_layer/inquiry_response_builder.h
deleted file mode 100644
index 694fe71..0000000
--- a/vendor_libs/test_vendor_lib/packets/link_layer/inquiry_response_builder.h
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cstdint>
-#include <memory>
-
-#include "base/logging.h"
-
-#include "include/inquiry.h"
-#include "packets/packet_builder.h"
-#include "types/class_of_device.h"
-
-namespace test_vendor_lib {
-namespace packets {
-
-class InquiryResponseBuilder : public PacketBuilder<true> {
- public:
-  virtual ~InquiryResponseBuilder() = default;
-
-  static std::unique_ptr<InquiryResponseBuilder> CreateStandard(uint8_t page_scan_repetition_mode,
-                                                                const ClassOfDevice& class_of_device,
-                                                                uint16_t clock_offset) {
-    return std::unique_ptr<InquiryResponseBuilder>(new InquiryResponseBuilder(
-        Inquiry::InquiryType::STANDARD, page_scan_repetition_mode, class_of_device, clock_offset));
-  }
-  static std::unique_ptr<InquiryResponseBuilder> CreateRssi(uint8_t page_scan_repetition_mode,
-                                                            const ClassOfDevice& class_of_device, uint16_t clock_offset,
-                                                            uint8_t rssi) {
-    return std::unique_ptr<InquiryResponseBuilder>(new InquiryResponseBuilder(
-        Inquiry::InquiryType::RSSI, page_scan_repetition_mode, class_of_device, clock_offset, rssi));
-  }
-  static std::unique_ptr<InquiryResponseBuilder> CreateExtended(uint8_t page_scan_repetition_mode,
-                                                                const ClassOfDevice& class_of_device,
-                                                                uint16_t clock_offset, uint8_t rssi,
-                                                                const std::vector<uint8_t>& extended_data) {
-    return std::unique_ptr<InquiryResponseBuilder>(new InquiryResponseBuilder(
-        Inquiry::InquiryType::EXTENDED, page_scan_repetition_mode, class_of_device, clock_offset, rssi, extended_data));
-  }
-
-  virtual size_t size() const override {
-    size_t inquiry_size =
-        sizeof(inquiry_type_) + sizeof(page_scan_repetition_mode_) + sizeof(class_of_device_) + sizeof(clock_offset_);
-    if (inquiry_type_ == Inquiry::InquiryType::STANDARD) {
-      return inquiry_size;
-    }
-    inquiry_size += sizeof(rssi_);
-    if (inquiry_type_ == Inquiry::InquiryType::RSSI) {
-      return inquiry_size;
-    }
-
-    return inquiry_size + extended_data_.size();
-  }
-
- protected:
-  virtual void Serialize(std::back_insert_iterator<std::vector<uint8_t>> it) const override {
-    insert(static_cast<uint8_t>(inquiry_type_), it);
-    insert(page_scan_repetition_mode_, it);
-    insert_class_of_device(class_of_device_, it);
-    insert(clock_offset_, it);
-    if (inquiry_type_ == Inquiry::InquiryType::STANDARD) {
-      return;
-    }
-    insert(rssi_, it);
-    if (inquiry_type_ == Inquiry::InquiryType::RSSI) {
-      return;
-    }
-    insert_vector(extended_data_, it);
-  }
-
- private:
-  Inquiry::InquiryType inquiry_type_;
-  uint8_t page_scan_repetition_mode_;
-  ClassOfDevice class_of_device_;
-  uint16_t clock_offset_;
-  uint8_t rssi_{0xff};
-  std::vector<uint8_t> extended_data_;
-  explicit InquiryResponseBuilder(Inquiry::InquiryType inquiry_type, uint8_t page_scan_repetition_mode,
-                                  const ClassOfDevice& class_of_device, uint16_t clock_offset)
-      : inquiry_type_(inquiry_type), page_scan_repetition_mode_(page_scan_repetition_mode),
-        class_of_device_(class_of_device), clock_offset_(clock_offset) {}
-  explicit InquiryResponseBuilder(Inquiry::InquiryType inquiry_type, uint8_t page_scan_repetition_mode,
-                                  const ClassOfDevice& class_of_device, uint16_t clock_offset, uint8_t rssi)
-      : inquiry_type_(inquiry_type), page_scan_repetition_mode_(page_scan_repetition_mode),
-        class_of_device_(class_of_device), clock_offset_(clock_offset), rssi_(rssi) {}
-  explicit InquiryResponseBuilder(Inquiry::InquiryType inquiry_type, uint8_t page_scan_repetition_mode,
-                                  const ClassOfDevice& class_of_device, uint16_t clock_offset, uint8_t rssi,
-                                  const std::vector<uint8_t>& extended_data)
-      : inquiry_type_(inquiry_type), page_scan_repetition_mode_(page_scan_repetition_mode),
-        class_of_device_(class_of_device), clock_offset_(clock_offset), rssi_(rssi), extended_data_(extended_data) {}
-};
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/link_layer/inquiry_response_view.h b/vendor_libs/test_vendor_lib/packets/link_layer/inquiry_response_view.h
deleted file mode 100644
index aac0585..0000000
--- a/vendor_libs/test_vendor_lib/packets/link_layer/inquiry_response_view.h
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cstdint>
-
-#include "include/inquiry.h"
-#include "packets/link_layer/link_layer_packet_view.h"
-#include "packets/packet_view.h"
-
-namespace test_vendor_lib {
-namespace packets {
-
-class InquiryResponseView : public PacketView<true> {
- public:
-  InquiryResponseView(const InquiryResponseView&) = default;
-  virtual ~InquiryResponseView() = default;
-
-  static InquiryResponseView GetInquiryResponse(const LinkLayerPacketView& view) {
-    CHECK(view.GetType() == Link::PacketType::INQUIRY_RESPONSE);
-    return InquiryResponseView(view.GetPayload());
-  }
-
-  Inquiry::InquiryType GetType() {
-    return static_cast<Inquiry::InquiryType>(at(0));
-  }
-
-  uint8_t GetPageScanRepetitionMode() {
-    return at(1);
-  }
-
-  ClassOfDevice GetClassOfDevice() {
-    size_t offset = 2 * sizeof(uint8_t);
-    return (begin() + offset).extract<ClassOfDevice>();
-  }
-
-  uint16_t GetClockOffset() {
-    size_t offset = 2 * sizeof(uint8_t) + 3;
-    return (begin() + offset).extract<uint16_t>();
-  }
-
-  uint8_t GetRssi() {
-    size_t offset = 2 * sizeof(uint8_t) + 3 + sizeof(uint16_t);
-    return at(offset);
-  }
-
-  Iterator<true> GetExtendedData() {
-    size_t offset = 2 * sizeof(uint8_t) + 3 + sizeof(uint16_t) + sizeof(uint8_t);
-    return begin() + offset;
-  }
-
- private:
-  InquiryResponseView() = delete;
-  InquiryResponseView(const PacketView<true>& view) : PacketView(view) {}
-};
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/link_layer/inquiry_view.h b/vendor_libs/test_vendor_lib/packets/link_layer/inquiry_view.h
deleted file mode 100644
index eee861c..0000000
--- a/vendor_libs/test_vendor_lib/packets/link_layer/inquiry_view.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cstdint>
-
-#include "inquiry.h"
-#include "packets/link_layer/link_layer_packet_view.h"
-#include "packets/packet_view.h"
-
-namespace test_vendor_lib {
-namespace packets {
-
-class InquiryView : public PacketView<true> {
- public:
-  InquiryView(const InquiryView&) = default;
-  virtual ~InquiryView() = default;
-
-  static InquiryView GetInquiry(const LinkLayerPacketView& view) {
-    CHECK(view.GetType() == Link::PacketType::INQUIRY);
-    return InquiryView(view.GetPayload());
-  }
-
-  Inquiry::InquiryType GetType() {
-    return static_cast<Inquiry::InquiryType>(at(0));
-  }
-
- private:
-  InquiryView() = delete;
-  InquiryView(const PacketView<true>& view) : PacketView(view) {}
-};
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/link_layer/io_capability_builder.h b/vendor_libs/test_vendor_lib/packets/link_layer/io_capability_builder.h
deleted file mode 100644
index 79efb50..0000000
--- a/vendor_libs/test_vendor_lib/packets/link_layer/io_capability_builder.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cstdint>
-#include <memory>
-
-#include "base/logging.h"
-
-#include "packets/packet_builder.h"
-
-namespace test_vendor_lib {
-namespace packets {
-
-class IoCapabilityBuilder : public PacketBuilder<true> {
- public:
-  virtual ~IoCapabilityBuilder() = default;
-
-  static std::unique_ptr<IoCapabilityBuilder> Create(uint8_t io_capability, uint8_t oob_data_present,
-                                                     uint8_t authentication_requirements) {
-    return std::unique_ptr<IoCapabilityBuilder>(
-        new IoCapabilityBuilder(io_capability, oob_data_present, authentication_requirements));
-  }
-
-  virtual size_t size() const override {
-    return sizeof(io_capability_) + sizeof(oob_data_present_) + sizeof(authentication_requirements_);
-  }
-
- protected:
-  virtual void Serialize(std::back_insert_iterator<std::vector<uint8_t>> it) const override {
-    insert(io_capability_, it);
-    insert(oob_data_present_, it);
-    insert(authentication_requirements_, it);
-  }
-
- private:
-  explicit IoCapabilityBuilder(uint8_t io_capability, uint8_t oob_data_present, uint8_t authentication_requirements)
-      : io_capability_(io_capability), oob_data_present_(oob_data_present),
-        authentication_requirements_(authentication_requirements) {}
-  uint8_t io_capability_;
-  uint8_t oob_data_present_;
-  uint8_t authentication_requirements_;
-};
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/link_layer/io_capability_negative_response_builder.h b/vendor_libs/test_vendor_lib/packets/link_layer/io_capability_negative_response_builder.h
deleted file mode 100644
index c9e7601..0000000
--- a/vendor_libs/test_vendor_lib/packets/link_layer/io_capability_negative_response_builder.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cstdint>
-#include <memory>
-
-#include "base/logging.h"
-
-#include "packets/packet_builder.h"
-
-namespace test_vendor_lib {
-namespace packets {
-
-class IoCapabilityNegativeResponseBuilder : public PacketBuilder<true> {
- public:
-  virtual ~IoCapabilityNegativeResponseBuilder() = default;
-
-  static std::unique_ptr<IoCapabilityNegativeResponseBuilder> Create(uint8_t reason) {
-    return std::unique_ptr<IoCapabilityNegativeResponseBuilder>(new IoCapabilityNegativeResponseBuilder(reason));
-  }
-
-  virtual size_t size() const override {
-    return sizeof(reason_);
-  }
-
- protected:
-  virtual void Serialize(std::back_insert_iterator<std::vector<uint8_t>> it) const override {
-    insert(reason_, it);
-  }
-
- private:
-  explicit IoCapabilityNegativeResponseBuilder(uint8_t reason) : reason_(reason) {}
-  uint8_t reason_;
-};
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/link_layer/io_capability_negative_response_view.h b/vendor_libs/test_vendor_lib/packets/link_layer/io_capability_negative_response_view.h
deleted file mode 100644
index 27c888f..0000000
--- a/vendor_libs/test_vendor_lib/packets/link_layer/io_capability_negative_response_view.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cstdint>
-
-#include "packets/link_layer/link_layer_packet_view.h"
-#include "packets/packet_view.h"
-
-namespace test_vendor_lib {
-namespace packets {
-
-class IoCapabilityNegativeResponseView : public PacketView<true> {
- public:
-  IoCapabilityNegativeResponseView(const IoCapabilityNegativeResponseView&) = default;
-  virtual ~IoCapabilityNegativeResponseView() = default;
-
-  static IoCapabilityNegativeResponseView GetIoCapabilityNegativeResponse(const LinkLayerPacketView& view) {
-    CHECK(view.GetType() == Link::PacketType::IO_CAPABILITY_NEGATIVE_RESPONSE);
-    return IoCapabilityNegativeResponseView(view.GetPayload());
-  }
-
-  uint8_t GetReason() {
-    return at(0);
-  }
-
- private:
-  IoCapabilityNegativeResponseView() = delete;
-  IoCapabilityNegativeResponseView(const PacketView<true>& view) : PacketView(view) {}
-};
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/link_layer/io_capability_view.h b/vendor_libs/test_vendor_lib/packets/link_layer/io_capability_view.h
deleted file mode 100644
index 66c7564..0000000
--- a/vendor_libs/test_vendor_lib/packets/link_layer/io_capability_view.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cstdint>
-
-#include "packets/link_layer/link_layer_packet_view.h"
-#include "packets/packet_view.h"
-
-namespace test_vendor_lib {
-namespace packets {
-
-class IoCapabilityView : public PacketView<true> {
- public:
-  IoCapabilityView(const IoCapabilityView&) = default;
-  virtual ~IoCapabilityView() = default;
-
-  static IoCapabilityView GetIoCapability(const LinkLayerPacketView& view) {
-    CHECK(view.GetType() == Link::PacketType::IO_CAPABILITY_RESPONSE ||
-          view.GetType() == Link::PacketType::IO_CAPABILITY_REQUEST);
-    return IoCapabilityView(view.GetPayload());
-  }
-
-  uint8_t GetIoCapability() {
-    return at(0);
-  }
-  uint8_t GetOobDataPresent() {
-    return at(1);
-  }
-  uint8_t GetAuthenticationRequirements() {
-    return at(2);
-  }
-
- private:
-  IoCapabilityView() = delete;
-  IoCapabilityView(const PacketView<true>& view) : PacketView(view) {}
-};
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/link_layer/le_advertisement_builder.h b/vendor_libs/test_vendor_lib/packets/link_layer/le_advertisement_builder.h
deleted file mode 100644
index 18b3167..0000000
--- a/vendor_libs/test_vendor_lib/packets/link_layer/le_advertisement_builder.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cstdint>
-#include <memory>
-
-#include "base/logging.h"
-
-#include "include/le_advertisement.h"
-#include "packets/packet_builder.h"
-
-namespace test_vendor_lib {
-namespace packets {
-
-class LeAdvertisementBuilder : public PacketBuilder<true>, public LeAdvertisement {
- public:
-  virtual ~LeAdvertisementBuilder() = default;
-
-  static std::unique_ptr<LeAdvertisementBuilder> Create(AddressType address_type, AdvertisementType advertisement_type,
-                                                        const std::vector<uint8_t>& advertisement) {
-    return std::unique_ptr<LeAdvertisementBuilder>(
-        new LeAdvertisementBuilder(address_type, advertisement_type, advertisement));
-  }
-
-  virtual size_t size() const override {
-    return sizeof(address_type_) + sizeof(advertisement_type_) + advertisement_.size();
-  }
-
- protected:
-  virtual void Serialize(std::back_insert_iterator<std::vector<uint8_t>> it) const override {
-    insert(static_cast<uint8_t>(address_type_), it);
-    insert(static_cast<uint8_t>(advertisement_type_), it);
-    insert_vector(advertisement_, it);
-  }
-
- private:
-  LeAdvertisementBuilder() = delete;
-  explicit LeAdvertisementBuilder(AddressType address_type, AdvertisementType advertisement_type,
-                                  const std::vector<uint8_t>& advertisement)
-      : address_type_(address_type), advertisement_type_(advertisement_type), advertisement_(advertisement) {}
-  AddressType address_type_;
-  AdvertisementType advertisement_type_;
-  std::vector<uint8_t> advertisement_;
-};
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/link_layer/le_advertisement_view.h b/vendor_libs/test_vendor_lib/packets/link_layer/le_advertisement_view.h
deleted file mode 100644
index 80e0367..0000000
--- a/vendor_libs/test_vendor_lib/packets/link_layer/le_advertisement_view.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cstdint>
-
-#include <log/log.h>
-
-#include "include/link.h"
-#include "packets/link_layer/link_layer_packet_view.h"
-#include "packets/packet_view.h"
-
-namespace test_vendor_lib {
-namespace packets {
-
-class LeAdvertisementView : public PacketView<true>, public LeAdvertisement {
- public:
-  LeAdvertisementView(const LeAdvertisementView&) = default;
-  virtual ~LeAdvertisementView() = default;
-
-  static LeAdvertisementView GetLeAdvertisementView(const LinkLayerPacketView& view) {
-    CHECK(view.GetType() == Link::PacketType::LE_ADVERTISEMENT || view.GetType() == Link::PacketType::LE_SCAN_RESPONSE);
-    return LeAdvertisementView(view.GetPayload());
-  }
-
-  AddressType GetAddressType() {
-    return static_cast<AddressType>(at(0));
-  }
-  AdvertisementType GetAdvertisementType() {
-    return static_cast<AdvertisementType>(at(1));
-  }
-  Iterator<true> GetData() {
-    return begin() + 2 * sizeof(uint8_t);
-  }
-
- private:
-  LeAdvertisementView() = delete;
-  LeAdvertisementView(const PacketView<true>& view) : PacketView(view) {}
-};
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/link_layer/link_layer_packet_builder.cc b/vendor_libs/test_vendor_lib/packets/link_layer/link_layer_packet_builder.cc
deleted file mode 100644
index 426244f..0000000
--- a/vendor_libs/test_vendor_lib/packets/link_layer/link_layer_packet_builder.cc
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "link_layer_packet_builder.h"
-#include "link_layer_packet_view.h"
-
-#include "base/logging.h"
-
-using std::vector;
-
-namespace test_vendor_lib {
-
-namespace packets {
-
-LinkLayerPacketBuilder::LinkLayerPacketBuilder(Link::PacketType type, const Address& source, const Address& dest)
-    : type_(type), source_addr_(source), dest_addr_(dest) {}
-
-LinkLayerPacketBuilder::LinkLayerPacketBuilder(Link::PacketType type, std::unique_ptr<PacketBuilder> packet,
-                                               const Address& source)
-    : type_(type), source_addr_(source), dest_addr_(Address::kEmpty), builder_(std::move(packet)) {}
-
-LinkLayerPacketBuilder::LinkLayerPacketBuilder(Link::PacketType type, std::unique_ptr<PacketBuilder> packet,
-                                               const Address& source, const Address& dest)
-    : type_(type), source_addr_(source), dest_addr_(dest), builder_(std::move(packet)) {}
-
-std::shared_ptr<LinkLayerPacketBuilder> LinkLayerPacketBuilder::WrapAcl(std::unique_ptr<ViewForwarderBuilder> acl,
-                                                                        const Address& source, const Address& dest) {
-  return std::shared_ptr<LinkLayerPacketBuilder>(
-      new LinkLayerPacketBuilder(Link::PacketType::ACL, std::move(acl), source, dest));
-}
-
-std::shared_ptr<LinkLayerPacketBuilder> LinkLayerPacketBuilder::WrapCommand(std::unique_ptr<CommandBuilder> command,
-                                                                            const Address& source,
-                                                                            const Address& dest) {
-  return std::shared_ptr<LinkLayerPacketBuilder>(
-      new LinkLayerPacketBuilder(Link::PacketType::COMMAND, std::move(command), source, dest));
-}
-
-std::shared_ptr<LinkLayerPacketBuilder> LinkLayerPacketBuilder::WrapDisconnect(
-    std::unique_ptr<DisconnectBuilder> disconnect, const Address& source, const Address& dest) {
-  return std::shared_ptr<LinkLayerPacketBuilder>(
-      new LinkLayerPacketBuilder(Link::PacketType::DISCONNECT, std::move(disconnect), source, dest));
-}
-
-std::shared_ptr<LinkLayerPacketBuilder> LinkLayerPacketBuilder::WrapEncryptConnection(
-    std::unique_ptr<EncryptConnectionBuilder> encrypt_connection, const Address& source, const Address& dest) {
-  return std::shared_ptr<LinkLayerPacketBuilder>(
-      new LinkLayerPacketBuilder(Link::PacketType::ENCRYPT_CONNECTION, std::move(encrypt_connection), source, dest));
-}
-
-std::shared_ptr<LinkLayerPacketBuilder> LinkLayerPacketBuilder::WrapEncryptConnectionResponse(
-    std::unique_ptr<EncryptConnectionBuilder> encrypt_connection, const Address& source, const Address& dest) {
-  return std::shared_ptr<LinkLayerPacketBuilder>(new LinkLayerPacketBuilder(
-      Link::PacketType::ENCRYPT_CONNECTION_RESPONSE, std::move(encrypt_connection), source, dest));
-}
-
-std::shared_ptr<LinkLayerPacketBuilder> LinkLayerPacketBuilder::WrapInquiry(std::unique_ptr<InquiryBuilder> inquiry,
-                                                                            const Address& source) {
-  return std::shared_ptr<LinkLayerPacketBuilder>(
-      new LinkLayerPacketBuilder(Link::PacketType::INQUIRY, std::move(inquiry), source));
-}
-
-std::shared_ptr<LinkLayerPacketBuilder> LinkLayerPacketBuilder::WrapInquiryResponse(
-    std::unique_ptr<InquiryResponseBuilder> inquiry_response, const Address& source, const Address& dest) {
-  return std::shared_ptr<LinkLayerPacketBuilder>(
-      new LinkLayerPacketBuilder(Link::PacketType::INQUIRY_RESPONSE, std::move(inquiry_response), source, dest));
-}
-
-std::shared_ptr<LinkLayerPacketBuilder> LinkLayerPacketBuilder::WrapIoCapabilityRequest(
-    std::unique_ptr<IoCapabilityBuilder> io_capability, const Address& source, const Address& dest) {
-  return std::shared_ptr<LinkLayerPacketBuilder>(
-      new LinkLayerPacketBuilder(Link::PacketType::IO_CAPABILITY_REQUEST, std::move(io_capability), source, dest));
-}
-
-std::shared_ptr<LinkLayerPacketBuilder> LinkLayerPacketBuilder::WrapIoCapabilityResponse(
-    std::unique_ptr<IoCapabilityBuilder> io_capability, const Address& source, const Address& dest) {
-  return std::shared_ptr<LinkLayerPacketBuilder>(
-      new LinkLayerPacketBuilder(Link::PacketType::IO_CAPABILITY_RESPONSE, std::move(io_capability), source, dest));
-}
-
-std::shared_ptr<LinkLayerPacketBuilder> LinkLayerPacketBuilder::WrapIoCapabilityNegativeResponse(
-    std::unique_ptr<IoCapabilityNegativeResponseBuilder> io_capability_negative_response, const Address& source,
-    const Address& dest) {
-  return std::shared_ptr<LinkLayerPacketBuilder>(new LinkLayerPacketBuilder(
-      Link::PacketType::IO_CAPABILITY_NEGATIVE_RESPONSE, std::move(io_capability_negative_response), source, dest));
-}
-
-std::shared_ptr<LinkLayerPacketBuilder> LinkLayerPacketBuilder::WrapLeAdvertisement(
-    std::unique_ptr<LeAdvertisementBuilder> advertisement, const Address& source) {
-  return std::shared_ptr<LinkLayerPacketBuilder>(
-      new LinkLayerPacketBuilder(Link::PacketType::LE_ADVERTISEMENT, std::move(advertisement), source));
-}
-
-std::shared_ptr<LinkLayerPacketBuilder> LinkLayerPacketBuilder::WrapLeScan(const Address& source, const Address& dest) {
-  return std::shared_ptr<LinkLayerPacketBuilder>(new LinkLayerPacketBuilder(Link::PacketType::LE_SCAN, source, dest));
-}
-
-std::shared_ptr<LinkLayerPacketBuilder> LinkLayerPacketBuilder::WrapLeScanResponse(
-    std::unique_ptr<LeAdvertisementBuilder> scan_response, const Address& source, const Address& dest) {
-  return std::shared_ptr<LinkLayerPacketBuilder>(
-      new LinkLayerPacketBuilder(Link::PacketType::LE_SCAN_RESPONSE, std ::move(scan_response), source, dest));
-}
-
-std::shared_ptr<LinkLayerPacketBuilder> LinkLayerPacketBuilder::WrapPage(std::unique_ptr<PageBuilder> page,
-                                                                         const Address& source, const Address& dest) {
-  return std::shared_ptr<LinkLayerPacketBuilder>(
-      new LinkLayerPacketBuilder(Link::PacketType::PAGE, std::move(page), source, dest));
-}
-
-std::shared_ptr<LinkLayerPacketBuilder> LinkLayerPacketBuilder::WrapPageResponse(
-    std::unique_ptr<PageResponseBuilder> page_response, const Address& source, const Address& dest) {
-  return std::shared_ptr<LinkLayerPacketBuilder>(
-      new LinkLayerPacketBuilder(Link::PacketType::PAGE_RESPONSE, std::move(page_response), source, dest));
-}
-
-std::shared_ptr<LinkLayerPacketBuilder> LinkLayerPacketBuilder::WrapResponse(std::unique_ptr<ResponseBuilder> response,
-                                                                             const Address& source,
-                                                                             const Address& dest) {
-  return std::shared_ptr<LinkLayerPacketBuilder>(
-      new LinkLayerPacketBuilder(Link::PacketType::RESPONSE, std::move(response), source, dest));
-}
-
-std::shared_ptr<LinkLayerPacketBuilder> LinkLayerPacketBuilder::WrapSco(std::unique_ptr<ViewForwarderBuilder> sco,
-                                                                        const Address& source, const Address& dest) {
-  return std::shared_ptr<LinkLayerPacketBuilder>(
-      new LinkLayerPacketBuilder(Link::PacketType::SCO, std::move(sco), source, dest));
-}
-
-std::shared_ptr<LinkLayerPacketBuilder> LinkLayerPacketBuilder::ReWrap(
-    const std::shared_ptr<std::vector<uint8_t>> raw_packet) {
-  LinkLayerPacketView received = LinkLayerPacketView::Create(raw_packet);
-  Link::PacketType packet_type = received.GetType();
-  Address source = received.GetSourceAddress();
-  Address dest = received.GetDestinationAddress();
-  PacketView<true> payload = received.GetPayload();
-  std::unique_ptr<PacketBuilder> builder = ViewForwarderBuilder::Create(payload);
-  return std::shared_ptr<LinkLayerPacketBuilder>(
-      new LinkLayerPacketBuilder(packet_type, std::move(builder), source, dest));
-}
-
-size_t LinkLayerPacketBuilder::size() const {
-  size_t builder_size = (builder_ ? builder_->size() : 0);
-  return Link::kTypeBytes + Link::kSizeBytes + 2 * Address::kLength + builder_size;
-}
-
-void LinkLayerPacketBuilder::Serialize(std::back_insert_iterator<std::vector<uint8_t>> it) const {
-  insert(static_cast<uint32_t>(size() - Link::kSizeBytes), it);
-  insert(static_cast<uint8_t>(type_), it);
-  insert_address(source_addr_.address, it);
-  insert_address(dest_addr_.address, it);
-  if (builder_) {
-    builder_->Serialize(it);
-  }
-}
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/link_layer/link_layer_packet_builder.h b/vendor_libs/test_vendor_lib/packets/link_layer/link_layer_packet_builder.h
deleted file mode 100644
index a9d7741..0000000
--- a/vendor_libs/test_vendor_lib/packets/link_layer/link_layer_packet_builder.h
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cstdint>
-#include <string>
-#include <vector>
-
-#include "link.h"
-#include "packets/link_layer/command_builder.h"
-#include "packets/link_layer/disconnect_builder.h"
-#include "packets/link_layer/encrypt_connection_builder.h"
-#include "packets/link_layer/inquiry_builder.h"
-#include "packets/link_layer/inquiry_response_builder.h"
-#include "packets/link_layer/io_capability_builder.h"
-#include "packets/link_layer/io_capability_negative_response_builder.h"
-#include "packets/link_layer/le_advertisement_builder.h"
-#include "packets/link_layer/page_builder.h"
-#include "packets/link_layer/page_response_builder.h"
-#include "packets/link_layer/response_builder.h"
-#include "packets/link_layer/view_forwarder_builder.h"
-#include "packets/packet_builder.h"
-#include "types/address.h"
-
-namespace test_vendor_lib {
-namespace packets {
-
-// Link-layer packets are an abstraction of LMP PDUs.
-class LinkLayerPacketBuilder : PacketBuilder<true> {
- public:
-  virtual ~LinkLayerPacketBuilder() = default;
-
-  static std::shared_ptr<LinkLayerPacketBuilder> WrapAcl(std::unique_ptr<ViewForwarderBuilder> acl,
-                                                         const Address& source, const Address& dest);
-  static std::shared_ptr<LinkLayerPacketBuilder> WrapCommand(std::unique_ptr<CommandBuilder> command,
-                                                             const Address& source, const Address& dest);
-  static std::shared_ptr<LinkLayerPacketBuilder> WrapDisconnect(std::unique_ptr<DisconnectBuilder> disconnect,
-                                                                const Address& source, const Address& dest);
-  static std::shared_ptr<LinkLayerPacketBuilder> WrapEncryptConnection(
-      std::unique_ptr<EncryptConnectionBuilder> encrypt_connection, const Address& source, const Address& dest);
-  static std::shared_ptr<LinkLayerPacketBuilder> WrapEncryptConnectionResponse(
-      std::unique_ptr<EncryptConnectionBuilder> encrypt_connection, const Address& source, const Address& dest);
-  static std::shared_ptr<LinkLayerPacketBuilder> WrapInquiry(std::unique_ptr<InquiryBuilder> inquiry,
-                                                             const Address& source);
-  static std::shared_ptr<LinkLayerPacketBuilder> WrapInquiryResponse(
-      std::unique_ptr<InquiryResponseBuilder> inquiry_response, const Address& source, const Address& dest);
-  static std::shared_ptr<LinkLayerPacketBuilder> WrapIoCapabilityRequest(
-      std::unique_ptr<IoCapabilityBuilder> io_capability, const Address& source, const Address& dest);
-  static std::shared_ptr<LinkLayerPacketBuilder> WrapIoCapabilityResponse(
-      std::unique_ptr<IoCapabilityBuilder> io_capability, const Address& source, const Address& dest);
-  static std::shared_ptr<LinkLayerPacketBuilder> WrapIoCapabilityNegativeResponse(
-      std::unique_ptr<IoCapabilityNegativeResponseBuilder> io_capability_negative_response, const Address& source,
-      const Address& dest);
-  static std::shared_ptr<LinkLayerPacketBuilder> WrapLeAdvertisement(
-      std::unique_ptr<LeAdvertisementBuilder> advertisement, const Address& source);
-  static std::shared_ptr<LinkLayerPacketBuilder> WrapLeScan(const Address& source, const Address& dest);
-  static std::shared_ptr<LinkLayerPacketBuilder> WrapLeScanResponse(
-      std::unique_ptr<LeAdvertisementBuilder> scan_response, const Address& source, const Address& dest);
-  static std::shared_ptr<LinkLayerPacketBuilder> WrapPage(std::unique_ptr<PageBuilder> page, const Address& source,
-                                                          const Address& dest);
-  static std::shared_ptr<LinkLayerPacketBuilder> WrapPageResponse(std::unique_ptr<PageResponseBuilder> page_response,
-                                                                  const Address& source, const Address& dest);
-  static std::shared_ptr<LinkLayerPacketBuilder> WrapResponse(const std::unique_ptr<ResponseBuilder> response,
-                                                              const Address& source, const Address& dest);
-  static std::shared_ptr<LinkLayerPacketBuilder> WrapSco(std::unique_ptr<ViewForwarderBuilder> sco,
-                                                         const Address& source, const Address& dest);
-  static std::shared_ptr<LinkLayerPacketBuilder> ReWrap(const std::shared_ptr<std::vector<uint8_t>> raw_packet);
-
-  virtual void Serialize(std::back_insert_iterator<std::vector<uint8_t>> it) const override;
-
-  virtual size_t size() const override;
-
- private:
-  LinkLayerPacketBuilder(const LinkLayerPacketBuilder&) = delete;
-  LinkLayerPacketBuilder() = delete;
-  LinkLayerPacketBuilder(Link::PacketType type, const Address& source, const Address& dest);
-  LinkLayerPacketBuilder(Link::PacketType type, std::unique_ptr<PacketBuilder> builder, const Address& source,
-                         const Address& dest);
-  LinkLayerPacketBuilder(Link::PacketType type, std::unique_ptr<PacketBuilder> builder, const Address& source);
-  Link::PacketType type_;
-  Address source_addr_;
-  Address dest_addr_;
-  std::unique_ptr<PacketBuilder> builder_;
-};
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/link_layer/link_layer_packet_view.cc b/vendor_libs/test_vendor_lib/packets/link_layer/link_layer_packet_view.cc
deleted file mode 100644
index 04518ca..0000000
--- a/vendor_libs/test_vendor_lib/packets/link_layer/link_layer_packet_view.cc
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "link_layer_packet_view.h"
-#include "base/logging.h"
-
-namespace test_vendor_lib {
-constexpr size_t Link::kSizeBytes;
-constexpr size_t Link::kTypeBytes;
-
-namespace packets {
-LinkLayerPacketView::LinkLayerPacketView(std::shared_ptr<std::vector<uint8_t>> raw) : PacketView<true>(raw) {}
-
-LinkLayerPacketView LinkLayerPacketView::Create(std::shared_ptr<std::vector<uint8_t>> raw) {
-  CHECK(raw->size() >= Link::kSizeBytes + Link::kTypeBytes + 2 * Address::kLength);
-  return LinkLayerPacketView(raw);
-}
-
-Link::PacketType LinkLayerPacketView::GetType() const {
-  return static_cast<Link::PacketType>(at(Link::kSizeBytes));
-}
-
-Address LinkLayerPacketView::GetSourceAddress() const {
-  size_t offset = Link::kSizeBytes + Link::kTypeBytes;
-  return (begin() + offset).extract<Address>();
-}
-
-Address LinkLayerPacketView::GetDestinationAddress() const {
-  size_t offset = Link::kSizeBytes + Link::kTypeBytes + Address::kLength;
-  return (begin() + offset).extract<Address>();
-}
-
-PacketView<true> LinkLayerPacketView::GetPayload() const {
-  return SubViewLittleEndian(Link::kSizeBytes + Link::kTypeBytes + 2 * Address::kLength, size());
-}
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/link_layer/link_layer_packet_view.h b/vendor_libs/test_vendor_lib/packets/link_layer/link_layer_packet_view.h
deleted file mode 100644
index bdbfaa7..0000000
--- a/vendor_libs/test_vendor_lib/packets/link_layer/link_layer_packet_view.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cstdint>
-#include <string>
-#include <vector>
-
-#include "include/link.h"
-#include "packets/packet_view.h"
-#include "types/address.h"
-
-namespace test_vendor_lib {
-namespace packets {
-
-// Link-layer packets are an abstraction of LMP PDUs.
-class LinkLayerPacketView : public PacketView<true> {
- public:
-  LinkLayerPacketView(const LinkLayerPacketView&) = default;
-  virtual ~LinkLayerPacketView() = default;
-
-  static LinkLayerPacketView Create(std::shared_ptr<std::vector<uint8_t>> raw);
-
-  Link::PacketType GetType() const;
-  Address GetSourceAddress() const;
-  Address GetDestinationAddress() const;
-  PacketView<true> GetPayload() const;
-
- private:
-  LinkLayerPacketView() = delete;
-  LinkLayerPacketView(std::shared_ptr<std::vector<uint8_t>> raw);
-};
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/link_layer/page_builder.h b/vendor_libs/test_vendor_lib/packets/link_layer/page_builder.h
deleted file mode 100644
index 0e18cb3..0000000
--- a/vendor_libs/test_vendor_lib/packets/link_layer/page_builder.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cstdint>
-#include <memory>
-
-#include <base/logging.h>
-
-#include "packets/packet_builder.h"
-#include "types/class_of_device.h"
-
-namespace test_vendor_lib {
-namespace packets {
-
-class PageBuilder : public PacketBuilder<true> {
- public:
-  virtual ~PageBuilder() = default;
-
-  static std::unique_ptr<PageBuilder> Create(const ClassOfDevice& class_of_device, uint8_t allow_role_switch) {
-    return std::unique_ptr<PageBuilder>(new PageBuilder(class_of_device, allow_role_switch));
-  }
-
-  virtual size_t size() const override {
-    return sizeof(class_of_device_) + sizeof(allow_role_switch_);
-  }
-
- protected:
-  virtual void Serialize(std::back_insert_iterator<std::vector<uint8_t>> it) const override {
-    insert_class_of_device(class_of_device_, it);
-    insert(allow_role_switch_, it);
-  }
-
- private:
-  explicit PageBuilder(const ClassOfDevice& class_of_device, uint8_t allow_role_switch)
-      : class_of_device_(class_of_device), allow_role_switch_(allow_role_switch) {}
-  ClassOfDevice class_of_device_;
-  uint8_t allow_role_switch_;
-};
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/link_layer/page_response_builder.h b/vendor_libs/test_vendor_lib/packets/link_layer/page_response_builder.h
deleted file mode 100644
index 78f9a80..0000000
--- a/vendor_libs/test_vendor_lib/packets/link_layer/page_response_builder.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cstdint>
-#include <memory>
-
-#include "base/logging.h"
-
-#include "packets/packet_builder.h"
-
-namespace test_vendor_lib {
-namespace packets {
-
-class PageResponseBuilder : public PacketBuilder<true> {
- public:
-  virtual ~PageResponseBuilder() = default;
-
-  static std::unique_ptr<PageResponseBuilder> Create(uint8_t try_role_switch) {
-    return std::unique_ptr<PageResponseBuilder>(new PageResponseBuilder(try_role_switch));
-  }
-
-  virtual size_t size() const override {
-    return sizeof(try_role_switch_);
-  }
-
- protected:
-  virtual void Serialize(std::back_insert_iterator<std::vector<uint8_t>> it) const override {
-    insert(try_role_switch_, it);
-  }
-
- private:
-  explicit PageResponseBuilder(uint8_t try_role_switch) : try_role_switch_(try_role_switch) {}
-  uint8_t try_role_switch_;
-};
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/link_layer/page_response_view.h b/vendor_libs/test_vendor_lib/packets/link_layer/page_response_view.h
deleted file mode 100644
index 20f0b68..0000000
--- a/vendor_libs/test_vendor_lib/packets/link_layer/page_response_view.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cstdint>
-
-#include "packets/link_layer/link_layer_packet_view.h"
-#include "packets/packet_view.h"
-
-namespace test_vendor_lib {
-namespace packets {
-
-class PageResponseView : public PacketView<true> {
- public:
-  PageResponseView(const PageResponseView&) = default;
-  virtual ~PageResponseView() = default;
-
-  static PageResponseView GetPageResponse(const LinkLayerPacketView& view) {
-    CHECK(view.GetType() == Link::PacketType::PAGE_RESPONSE);
-    return PageResponseView(view.GetPayload());
-  }
-
-  uint8_t GetTryRoleSwitch() {
-    return at(0);
-  }
-
- private:
-  PageResponseView() = delete;
-  PageResponseView(const PacketView<true>& view) : PacketView(view) {}
-};
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/link_layer/page_view.h b/vendor_libs/test_vendor_lib/packets/link_layer/page_view.h
deleted file mode 100644
index a26446d..0000000
--- a/vendor_libs/test_vendor_lib/packets/link_layer/page_view.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cstdint>
-
-#include "packets/link_layer/link_layer_packet_view.h"
-#include "packets/packet_view.h"
-
-namespace test_vendor_lib {
-namespace packets {
-
-class PageView : public PacketView<true> {
- public:
-  PageView(const PageView&) = default;
-  virtual ~PageView() = default;
-
-  static PageView GetPage(const LinkLayerPacketView& view) {
-    CHECK(view.GetType() == Link::PacketType::PAGE);
-    return PageView(view.GetPayload());
-  }
-
-  ClassOfDevice GetClassOfDevice() {
-    return begin().extract<ClassOfDevice>();
-  }
-
-  uint8_t GetAllowRoleSwitch() {
-    return at(3);
-  }
-
- private:
-  PageView() = delete;
-  PageView(const PacketView<true>& view) : PacketView(view) {}
-};
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/link_layer/response_builder.h b/vendor_libs/test_vendor_lib/packets/link_layer/response_builder.h
deleted file mode 100644
index 18f765d..0000000
--- a/vendor_libs/test_vendor_lib/packets/link_layer/response_builder.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cstdint>
-#include <memory>
-#include <vector>
-
-#include "packets/packet_builder.h"
-
-namespace test_vendor_lib {
-namespace packets {
-
-class ResponseBuilder : public PacketBuilder<true> {
- public:
-  virtual ~ResponseBuilder() = default;
-
-  static std::unique_ptr<ResponseBuilder> Create(uint16_t opcode, const std::vector<uint64_t>& data) {
-    return std::unique_ptr<ResponseBuilder>(new ResponseBuilder(opcode, data));
-  }
-
-  virtual size_t size() const override {
-    return sizeof(opcode_) + data_.size() * sizeof(uint64_t);
-  }
-
- protected:
-  virtual void Serialize(std::back_insert_iterator<std::vector<uint8_t>> it) const override {
-    insert(opcode_, it);
-    insert_vector(data_, it);
-  }
-
- private:
-  explicit ResponseBuilder(uint16_t opcode, const std::vector<uint64_t> data) : opcode_(opcode), data_(data) {}
-  uint16_t opcode_;
-  std::vector<uint64_t> data_;
-};
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/link_layer/response_view.h b/vendor_libs/test_vendor_lib/packets/link_layer/response_view.h
deleted file mode 100644
index f1ff7c9..0000000
--- a/vendor_libs/test_vendor_lib/packets/link_layer/response_view.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cstdint>
-
-#include <log/log.h>
-
-#include "packets/link_layer/link_layer_packet_view.h"
-#include "packets/packet_view.h"
-
-namespace test_vendor_lib {
-namespace packets {
-
-class ResponseView : public PacketView<true> {
- public:
-  ResponseView(const ResponseView&) = default;
-  virtual ~ResponseView() = default;
-
-  static ResponseView GetResponse(const LinkLayerPacketView& view) {
-    CHECK(view.GetType() == Link::PacketType::RESPONSE);
-    return ResponseView(view.GetPayload());
-  }
-
-  uint16_t GetOpcode() {
-    return begin().extract<uint16_t>();
-  }
-
-  Iterator<true> GetResponseData() {
-    return begin() + sizeof(uint16_t);
-  }
-
- private:
-  ResponseView() = delete;
-  ResponseView(const PacketView<true>& view) : PacketView(view) {}
-};
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/link_layer/view_forwarder_builder.h b/vendor_libs/test_vendor_lib/packets/link_layer/view_forwarder_builder.h
deleted file mode 100644
index da0d827..0000000
--- a/vendor_libs/test_vendor_lib/packets/link_layer/view_forwarder_builder.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cstdint>
-#include <memory>
-
-#include "base/logging.h"
-
-#include "packets/packet_builder.h"
-#include "packets/packet_view.h"
-
-namespace test_vendor_lib {
-namespace packets {
-
-class ViewForwarderBuilder : public PacketBuilder<true> {
- public:
-  virtual ~ViewForwarderBuilder() = default;
-
-  static std::unique_ptr<ViewForwarderBuilder> Create(PacketView<true> view) {
-    return std::unique_ptr<ViewForwarderBuilder>(new ViewForwarderBuilder(view));
-  }
-
-  virtual size_t size() const override {
-    return view_.size();
-  }
-
-  virtual void Serialize(std::back_insert_iterator<std::vector<uint8_t>> it) const override {
-    for (size_t i = 0; i < view_.size(); i++) {
-      insert(view_[i], it);
-    }
-  }
-
- private:
-  explicit ViewForwarderBuilder(PacketView<true> view) : view_(view) {}
-  PacketView<true> view_;
-};
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/link_layer_packets.pdl b/vendor_libs/test_vendor_lib/packets/link_layer_packets.pdl
new file mode 100644
index 0000000..2d601cd
--- /dev/null
+++ b/vendor_libs/test_vendor_lib/packets/link_layer_packets.pdl
@@ -0,0 +1,236 @@
+little_endian_packets
+
+custom_field Address : 48 "hci/"
+custom_field ClassOfDevice : 24 "hci/"
+
+enum PacketType : 8 {
+    UNKNOWN = 0x00,
+    ACL = 0x01,
+    DISCONNECT = 0x02,
+    ENCRYPT_CONNECTION = 0x03,
+    ENCRYPT_CONNECTION_RESPONSE = 0x04,
+    EVENT = 0x05,
+    INQUIRY = 0x06,
+    INQUIRY_RESPONSE = 0x07,
+    IO_CAPABILITY_REQUEST = 0x08,
+    IO_CAPABILITY_RESPONSE = 0x09,
+    IO_CAPABILITY_NEGATIVE_RESPONSE = 0x0A,
+    LE_ADVERTISEMENT = 0x0B,
+    LE_CONNECT = 0x0C,
+    LE_CONNECT_COMPLETE = 0x0D,
+    LE_SCAN = 0x0E,
+    LE_SCAN_RESPONSE = 0x0F,
+    PAGE = 0x10,
+    PAGE_RESPONSE = 0x11,
+    PAGE_REJECT = 0x12,
+    READ_CLOCK_OFFSET = 0x13,
+    READ_CLOCK_OFFSET_RESPONSE = 0x14,
+    READ_REMOTE_SUPPORTED_FEATURES = 0x15,
+    READ_REMOTE_SUPPORTED_FEATURES_RESPONSE = 0x16,
+    READ_REMOTE_LMP_FEATURES = 0x17,
+    READ_REMOTE_LMP_FEATURES_RESPONSE = 0x18,
+    READ_REMOTE_EXTENDED_FEATURES = 0x19,
+    READ_REMOTE_EXTENDED_FEATURES_RESPONSE = 0x1A,
+    READ_REMOTE_VERSION_INFORMATION = 0x1B,
+    READ_REMOTE_VERSION_INFORMATION_RESPONSE = 0x1C,
+    REMOTE_NAME_REQUEST = 0x1D,
+    REMOTE_NAME_REQUEST_RESPONSE = 0x1E,
+    SCO = 0x1F,
+    COMMAND = 0x23, // Remove
+    RESPONSE = 0x24, // Remove
+}
+
+packet LinkLayerPacket {
+  type : PacketType,
+  source_address : Address,
+  destination_address : Address,
+  _body_,
+}
+
+packet Command : LinkLayerPacket (type = COMMAND) {
+  _payload_,
+}
+
+packet Response : LinkLayerPacket (type = RESPONSE) {
+  opcode : 16,
+  _payload_,
+}
+
+packet AclPacket : LinkLayerPacket (type = ACL) {
+  _payload_,
+}
+
+packet Disconnect : LinkLayerPacket (type = DISCONNECT) {
+  reason : 8,
+}
+
+packet EncryptConnection : LinkLayerPacket (type = ENCRYPT_CONNECTION) {
+  key : 8[],
+}
+
+packet EncryptConnectionResponse : LinkLayerPacket (type = ENCRYPT_CONNECTION_RESPONSE) {
+  key : 8[],
+}
+
+enum InquiryState : 8 {
+  STANDBY = 0x00,
+  INQUIRY = 0x01,
+}
+
+enum InquiryType : 8 {
+  STANDARD = 0x00,
+  RSSI = 0x01,
+  EXTENDED = 0x02,
+}
+
+packet Inquiry : LinkLayerPacket (type = INQUIRY) {
+  inquiry_type : InquiryType,
+}
+
+packet BasicInquiryResponse : LinkLayerPacket(type = INQUIRY_RESPONSE) {
+  inquiry_type : InquiryType,
+  page_scan_repetition_mode : 8,
+  class_of_device : ClassOfDevice,
+  clock_offset : 15,
+  _reserved_ : 1,
+  _body_,
+}
+
+packet InquiryResponse : BasicInquiryResponse (inquiry_type = STANDARD) {
+}
+
+packet InquiryResponseWithRssi : BasicInquiryResponse (inquiry_type = RSSI)  {
+  rssi: 8,
+}
+
+packet ExtendedInquiryResponse : BasicInquiryResponse (inquiry_type = EXTENDED)  {
+  rssi: 8,
+  extended_data : 8[],
+}
+
+packet IoCapabilityRequest : LinkLayerPacket (type = IO_CAPABILITY_REQUEST) {
+  io_capability : 8,
+  oob_data_present : 8,
+  authentication_requirements : 8,
+}
+
+packet IoCapabilityResponse : LinkLayerPacket (type = IO_CAPABILITY_RESPONSE) {
+  io_capability : 8,
+  oob_data_present : 8,
+  authentication_requirements : 8,
+}
+
+packet IoCapabilityNegativeResponse : LinkLayerPacket (type = IO_CAPABILITY_NEGATIVE_RESPONSE) {
+  reason : 8,
+}
+
+enum AddressType : 8 {
+  PUBLIC = 0,
+  RANDOM = 1,
+  PUBLIC_IDENTITY = 2,
+  RANDOM_IDENTITY = 3,
+}
+
+enum AdvertisementType : 8 {
+    ADV_IND = 0,          // Connectable and scannable
+    ADV_DIRECT_IND = 1,   // Connectable directed
+    ADV_SCAN_IND = 2,     // Scannable undirected
+    ADV_NONCONN_IND = 3,  // Non connectable undirected
+    SCAN_RESPONSE = 4,
+}
+
+packet LeAdvertisement : LinkLayerPacket (type = LE_ADVERTISEMENT) {
+  address_type : AddressType,
+  advertisement_type : AdvertisementType,
+  data : 8[],
+}
+
+packet LeConnect : LinkLayerPacket (type = LE_CONNECT) {
+  le_connection_interval_min : 16,
+  le_connection_interval_max : 16,
+  le_connection_latency : 16,
+  le_connection_supervision_timeout : 16,
+  address_type : 8,
+}
+
+packet LeConnectComplete : LinkLayerPacket (type = LE_CONNECT_COMPLETE) {
+  le_connection_interval : 16,
+  le_connection_latency : 16,
+  le_connection_supervision_timeout : 16,
+  address_type : 8,
+}
+
+packet LeScan : LinkLayerPacket (type = LE_SCAN) {
+}
+
+packet LeScanResponse : LinkLayerPacket (type = LE_SCAN_RESPONSE) {
+  address_type : AddressType,
+  advertisement_type : AdvertisementType,
+  data : 8[],
+}
+
+packet Page : LinkLayerPacket (type = PAGE) {
+  class_of_device : ClassOfDevice,
+  allow_role_switch : 8,
+}
+
+packet PageResponse : LinkLayerPacket (type = PAGE_RESPONSE) {
+  try_role_switch : 8,
+}
+
+packet PageReject : LinkLayerPacket (type = PAGE_REJECT) {
+  reason : 8,
+}
+
+packet ReadClockOffset : LinkLayerPacket (type = READ_CLOCK_OFFSET) {
+}
+
+packet ReadClockOffsetResponse : LinkLayerPacket (type = READ_CLOCK_OFFSET_RESPONSE) {
+  offset : 16,
+}
+
+packet ReadRemoteSupportedFeatures : LinkLayerPacket (type = READ_REMOTE_SUPPORTED_FEATURES) {
+}
+
+packet ReadRemoteSupportedFeaturesResponse : LinkLayerPacket (type = READ_REMOTE_SUPPORTED_FEATURES_RESPONSE) {
+  features : 64,
+}
+
+packet ReadRemoteLmpFeatures : LinkLayerPacket (type = READ_REMOTE_LMP_FEATURES) {
+}
+
+packet ReadRemoteLmpFeaturesResponse : LinkLayerPacket (type = READ_REMOTE_LMP_FEATURES_RESPONSE) {
+  features : 64,
+}
+
+packet ReadRemoteExtendedFeatures : LinkLayerPacket (type = READ_REMOTE_EXTENDED_FEATURES) {
+  page_number : 8,
+}
+
+packet ReadRemoteExtendedFeaturesResponse : LinkLayerPacket (type = READ_REMOTE_EXTENDED_FEATURES_RESPONSE) {
+  status : 8,
+  page_number : 8,
+  max_page_number : 8,
+  features : 64,
+}
+
+packet ReadRemoteVersionInformation : LinkLayerPacket (type = READ_REMOTE_VERSION_INFORMATION) {
+}
+
+packet ReadRemoteVersionInformationResponse : LinkLayerPacket (type = READ_REMOTE_VERSION_INFORMATION_RESPONSE) {
+  lmp_version : 8,
+  lmp_subversion : 8,
+  manufacturer_name : 16,
+}
+
+packet RemoteNameRequest : LinkLayerPacket (type = REMOTE_NAME_REQUEST) {
+}
+
+packet RemoteNameRequestResponse : LinkLayerPacket (type = REMOTE_NAME_REQUEST_RESPONSE) {
+  name : 8[248],
+}
+
+packet ScoPacket : LinkLayerPacket (type = SCO) {
+  _payload_,
+}
+
diff --git a/vendor_libs/test_vendor_lib/packets/packet_builder.h b/vendor_libs/test_vendor_lib/packets/packet_builder.h
deleted file mode 100644
index 947b02a..0000000
--- a/vendor_libs/test_vendor_lib/packets/packet_builder.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cstdint>
-#include <forward_list>
-#include <iterator>
-#include <memory>
-#include <vector>
-
-#include "base_packet_builder.h"
-#include "types/address.h"
-#include "types/class_of_device.h"
-
-namespace test_vendor_lib {
-namespace packets {
-
-// Abstract base class that is subclassed to build specifc packets.
-// The template parameter little_endian controls the generation of insert().
-template <bool little_endian>
-class PacketBuilder : public BasePacketBuilder {
- public:
-  PacketBuilder() = default;
-  virtual ~PacketBuilder() = default;
-
-  // Classes which need fragmentation should define a function like this:
-  // std::forward_list<DerivedBuilder>& Fragment(size_t max_size);
-
- protected:
-  // Write sizeof(FixedWidthIntegerType) bytes using the iterator
-  template <typename FixedWidthIntegerType,
-            typename std::enable_if<std::is_integral<FixedWidthIntegerType>::value, int>::type = 0>
-  void insert(FixedWidthIntegerType value, std::back_insert_iterator<std::vector<uint8_t>> it) const {
-    for (size_t i = 0; i < sizeof(FixedWidthIntegerType); i++) {
-      if (little_endian == true) {
-        *it = static_cast<uint8_t>(value >> (i * 8));
-      } else {
-        *it = static_cast<uint8_t>(value >> ((sizeof(FixedWidthIntegerType) - i - 1) * 8));
-      }
-      it++;
-    }
-  }
-
-  // Specialized insert that allows inserting enums without casting
-  template <typename Enum, typename std::enable_if<std::is_enum_v<Enum>, int>::type = 0>
-  inline void insert(Enum value, std::back_insert_iterator<std::vector<uint8_t>> it) const {
-    using enum_type = typename std::underlying_type_t<Enum>;
-    static_assert(std::is_unsigned_v<enum_type>, "Enum type is signed. Did you forget to specify the enum size?");
-    insert<enum_type>(static_cast<enum_type>(value), it);
-  }
-
-  // Write a vector of FixedWidthIntegerType using the iterator
-  template <typename FixedWidthIntegerType>
-  void insert_vector(const std::vector<FixedWidthIntegerType>& vec,
-                     std::back_insert_iterator<std::vector<uint8_t>> it) const {
-    static_assert(std::is_integral<FixedWidthIntegerType>::value,
-                  "PacketBuilder::insert requires an integral type vector.");
-    for (const auto& element : vec) {
-      insert(element, it);
-    }
-  }
-
-  void insert_address(const Address& addr, std::back_insert_iterator<std::vector<uint8_t>> it) const {
-    for (const auto& element : addr.address) {
-      insert(element, it);
-    }
-  }
-
-  void insert_class_of_device(const ClassOfDevice& cod, std::back_insert_iterator<std::vector<uint8_t>> it) const {
-    for (const auto& element : cod.cod) {
-      insert(element, it);
-    }
-  }
-};
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/packet_view.cc b/vendor_libs/test_vendor_lib/packets/packet_view.cc
deleted file mode 100644
index 7ffe71a..0000000
--- a/vendor_libs/test_vendor_lib/packets/packet_view.cc
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "packet_view.h"
-
-#include <algorithm>
-
-#include <base/logging.h>
-
-namespace test_vendor_lib {
-namespace packets {
-
-template <bool little_endian>
-PacketView<little_endian>::PacketView(const std::forward_list<class View> fragments)
-    : fragments_(fragments), length_(0) {
-  for (auto fragment : fragments_) {
-    length_ += fragment.size();
-  }
-}
-
-template <bool little_endian>
-PacketView<little_endian>::PacketView(std::shared_ptr<std::vector<uint8_t>> packet)
-    : fragments_({View(packet, 0, packet->size())}), length_(packet->size()) {}
-
-template <bool little_endian>
-Iterator<little_endian> PacketView<little_endian>::begin() const {
-  return Iterator<little_endian>(this->fragments_, 0);
-}
-
-template <bool little_endian>
-Iterator<little_endian> PacketView<little_endian>::end() const {
-  return Iterator<little_endian>(this->fragments_, size());
-}
-
-template <bool little_endian>
-uint8_t PacketView<little_endian>::operator[](size_t index) const {
-  return at(index);
-}
-
-template <bool little_endian>
-uint8_t PacketView<little_endian>::at(size_t index) const {
-  CHECK(index < length_) << "Index " << index << " out of bounds";
-  for (const auto& fragment : fragments_) {
-    if (index < fragment.size()) {
-      return fragment[index];
-    }
-    index -= fragment.size();
-  }
-  CHECK(false) << "Out of fragments searching for Index " << index;
-  return 0;
-}
-
-template <bool little_endian>
-size_t PacketView<little_endian>::size() const {
-  return length_;
-}
-
-template <bool little_endian>
-std::forward_list<View> PacketView<little_endian>::SubViewList(size_t begin, size_t end) const {
-  CHECK(begin <= end) << "Begin " << begin << " is past end";
-  CHECK(end <= length_) << "End " << end << " is too large";
-  std::forward_list<View> view_list;
-  std::forward_list<View>::iterator it = view_list.before_begin();
-  size_t length = end - begin;
-  for (const auto& fragment : fragments_) {
-    if (begin >= fragment.size()) {
-      begin -= fragment.size();
-    } else {
-      View view(fragment, begin, begin + std::min(length, fragment.size() - begin));
-      length -= view.size();
-      it = view_list.insert_after(it, view);
-      begin = 0;
-    }
-  }
-  return view_list;
-}
-
-template <bool little_endian>
-PacketView<true> PacketView<little_endian>::SubViewLittleEndian(size_t begin, size_t end) const {
-  return PacketView<true>(SubViewList(begin, end));
-}
-
-template <bool little_endian>
-PacketView<false> PacketView<little_endian>::SubViewBigEndian(size_t begin, size_t end) const {
-  return PacketView<false>(SubViewList(begin, end));
-}
-
-// Explicit instantiations for both types of PacketViews.
-template class PacketView<true>;
-template class PacketView<false>;
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/packet_view.h b/vendor_libs/test_vendor_lib/packets/packet_view.h
deleted file mode 100644
index 16255fa..0000000
--- a/vendor_libs/test_vendor_lib/packets/packet_view.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cstdint>
-#include <forward_list>
-
-#include "iterator.h"
-#include "view.h"
-
-namespace test_vendor_lib {
-namespace packets {
-
-// Abstract base class that is subclassed to provide type-specifc accessors.
-// Holds a shared pointer to the underlying data.
-// The template parameter little_endian controls the generation of extract().
-template <bool little_endian>
-class PacketView {
- public:
-  PacketView(const std::forward_list<class View> fragments);
-  PacketView(const PacketView& PacketView) = default;
-  virtual ~PacketView() = default;
-
-  virtual Iterator<little_endian> begin() const;
-
-  virtual Iterator<little_endian> end() const;
-
-  uint8_t operator[](size_t i) const;
-
-  uint8_t at(size_t index) const;
-
-  size_t size() const;
-
-  PacketView<true> SubViewLittleEndian(size_t begin, size_t end) const;
-
-  PacketView<false> SubViewBigEndian(size_t begin, size_t end) const;
-
- protected:
-  PacketView(std::shared_ptr<std::vector<uint8_t>> packet);
-
- private:
-  std::forward_list<View> fragments_;
-  size_t length_;
-  PacketView<little_endian>() = delete;
-  std::forward_list<View> SubViewList(size_t begin, size_t end) const;
-};
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/raw_builder.cc b/vendor_libs/test_vendor_lib/packets/raw_builder.cc
deleted file mode 100644
index 794fbb7..0000000
--- a/vendor_libs/test_vendor_lib/packets/raw_builder.cc
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "raw_builder.h"
-
-#include <base/logging.h>
-#include <algorithm>
-
-using std::vector;
-
-namespace test_vendor_lib {
-namespace packets {
-
-RawBuilder::RawBuilder(size_t max_bytes) : max_bytes_(max_bytes) {}
-
-bool RawBuilder::AddOctets(size_t octets, const vector<uint8_t>& bytes) {
-  if (payload_.size() + octets > max_bytes_) return false;
-
-  if (octets != bytes.size()) return false;
-
-  payload_.insert(payload_.end(), bytes.begin(), bytes.end());
-
-  return true;
-}
-
-bool RawBuilder::AddOctets(const vector<uint8_t>& bytes) {
-  return AddOctets(bytes.size(), bytes);
-}
-
-bool RawBuilder::AddOctets(size_t octets, uint64_t value) {
-  vector<uint8_t> val_vector;
-
-  uint64_t v = value;
-
-  if (octets > sizeof(uint64_t)) return false;
-
-  for (size_t i = 0; i < octets; i++) {
-    val_vector.push_back(v & 0xff);
-    v = v >> 8;
-  }
-
-  if (v != 0) return false;
-
-  return AddOctets(octets, val_vector);
-}
-
-bool RawBuilder::AddAddress(const Address& address) {
-  if (payload_.size() + Address::kLength > max_bytes_) return false;
-
-  for (size_t i = 0; i < Address::kLength; i++) {
-    payload_.push_back(address.address[i]);
-  }
-  return true;
-}
-
-bool RawBuilder::AddOctets1(uint8_t value) {
-  return AddOctets(1, value);
-}
-
-bool RawBuilder::AddOctets2(uint16_t value) {
-  return AddOctets(2, value);
-}
-
-bool RawBuilder::AddOctets3(uint32_t value) {
-  return AddOctets(3, value);
-}
-
-bool RawBuilder::AddOctets4(uint32_t value) {
-  return AddOctets(4, value);
-}
-
-bool RawBuilder::AddOctets6(uint64_t value) {
-  return AddOctets(6, value);
-}
-
-bool RawBuilder::AddOctets8(uint64_t value) {
-  return AddOctets(8, value);
-}
-
-bool RawBuilder::CanAddOctets(size_t num_bytes) const {
-  return payload_.size() + num_bytes <= max_bytes_;
-}
-
-void RawBuilder::Serialize(std::back_insert_iterator<std::vector<uint8_t>> it) const {
-  for (const auto& val : payload_) {
-    insert(val, it);
-  }
-}
-
-size_t RawBuilder::size() const {
-  return payload_.size();
-}
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/raw_builder.h b/vendor_libs/test_vendor_lib/packets/raw_builder.h
deleted file mode 100644
index cbc7d6f..0000000
--- a/vendor_libs/test_vendor_lib/packets/raw_builder.h
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cstdint>
-#include <vector>
-
-#include "packets/packet_builder.h"
-#include "types/address.h"
-
-namespace test_vendor_lib {
-namespace packets {
-
-class RawBuilder : public PacketBuilder<true> {
- public:
-  RawBuilder() = default;
-  RawBuilder(size_t max_bytes);
-  virtual ~RawBuilder() = default;
-
-  virtual size_t size() const override;
-
-  virtual void Serialize(std::back_insert_iterator<std::vector<uint8_t>> it) const;
-
-  // Add |address| to the payload.  Return true if:
-  // - the new size of the payload is still <= |max_bytes_|
-  bool AddAddress(const Address& address);
-
-  // Return true if |num_bytes| can be added to the payload.
-  bool CanAddOctets(size_t num_bytes) const;
-
-  // Add |octets| bytes to the payload.  Return true if:
-  // - the size of |bytes| is equal to |octets| and
-  // - the new size of the payload is still <= |max_bytes_|
-  bool AddOctets(size_t octets, const std::vector<uint8_t>& bytes);
-
-  bool AddOctets(const std::vector<uint8_t>& bytes);
-
-  bool AddOctets1(uint8_t value);
-  bool AddOctets2(uint16_t value);
-  bool AddOctets3(uint32_t value);
-  bool AddOctets4(uint32_t value);
-  bool AddOctets6(uint64_t value);
-  bool AddOctets8(uint64_t value);
-
- private:
-  // Add |octets| bytes to the payload.  Return true if:
-  // - the value of |value| fits in |octets| bytes and
-  // - the new size of the payload is still <= |max_bytes_|
-  bool AddOctets(size_t octets, uint64_t value);
-
-  size_t max_bytes_{255};
-
-  // Underlying containers for storing the actual packet
-  std::vector<uint8_t> payload_;
-};
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/test/counted_builder_test.cc b/vendor_libs/test_vendor_lib/packets/test/counted_builder_test.cc
deleted file mode 100644
index 8d4d53a..0000000
--- a/vendor_libs/test_vendor_lib/packets/test/counted_builder_test.cc
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "packets/counted_builder.h"
-
-#include <gtest/gtest.h>
-#include <forward_list>
-#include <memory>
-
-#include "types/address.h"
-
-using std::vector;
-
-namespace {
-vector<uint8_t> count = {
-    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
-    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
-};
-
-}  // namespace
-
-namespace test_vendor_lib {
-namespace packets {
-
-class CountedBuilderTest : public ::testing::Test {
- public:
-  CountedBuilderTest() = default;
-  ~CountedBuilderTest() = default;
-};
-
-TEST(CountedBuilderTest, buildCountTest) {
-  std::unique_ptr<CountedBuilder> count_builder = std::make_unique<CountedBuilder>();
-  ASSERT_EQ(1u, count_builder->size());
-  std::unique_ptr<RawBuilder> raw1 = std::make_unique<RawBuilder>();
-  std::unique_ptr<RawBuilder> raw2 = std::make_unique<RawBuilder>();
-  std::unique_ptr<RawBuilder> raw3 = std::make_unique<RawBuilder>();
-  std::unique_ptr<RawBuilder> raw4 = std::make_unique<RawBuilder>();
-  std::unique_ptr<RawBuilder> raw5 = std::make_unique<RawBuilder>();
-  std::unique_ptr<RawBuilder> raw6 = std::make_unique<RawBuilder>();
-  std::unique_ptr<RawBuilder> raw7 = std::make_unique<RawBuilder>();
-  raw1->AddOctets8(0x0706050403020100);
-  raw2->AddOctets4(0x0b0a0908);
-  raw3->AddOctets2(0x0d0c);
-  raw4->AddOctets1(0x0e);
-  raw5->AddOctets1(0x0f);
-  raw6->AddAddress(Address({0x10, 0x11, 0x12, 0x13, 0x14, 0x15}));
-  std::vector<uint8_t> count_subset(count.begin() + 0x16, count.end());
-  raw7->AddOctets(count_subset);
-
-  count_builder->Add(std::move(raw1));
-  count_builder->Add(std::move(raw2));
-  count_builder->Add(std::move(raw3));
-  count_builder->Add(std::move(raw4));
-  count_builder->Add(std::move(raw5));
-  count_builder->Add(std::move(raw6));
-  count_builder->Add(std::move(raw7));
-
-  ASSERT_EQ(count.size(), count_builder->size() - 1);
-
-  std::vector<uint8_t> packet;
-  std::back_insert_iterator<std::vector<uint8_t>> it(packet);
-
-  count_builder->Serialize(it);
-  ASSERT_EQ(7u, packet[0]);
-  std::vector<uint8_t> payload_packet(packet.begin() + 1, packet.end());
-
-  ASSERT_EQ(count, payload_packet);
-}
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/test/link_layer_packet_builder_test.cc b/vendor_libs/test_vendor_lib/packets/test/link_layer_packet_builder_test.cc
deleted file mode 100644
index cc6c460..0000000
--- a/vendor_libs/test_vendor_lib/packets/test/link_layer_packet_builder_test.cc
+++ /dev/null
@@ -1,514 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "packets/link_layer/link_layer_packet_builder.h"
-
-#include <gtest/gtest.h>
-#include <forward_list>
-#include <memory>
-
-#include "link.h"
-#include "packets/link_layer/command_view.h"
-#include "packets/link_layer/disconnect_view.h"
-#include "packets/link_layer/encrypt_connection_view.h"
-#include "packets/link_layer/inquiry_response_view.h"
-#include "packets/link_layer/inquiry_view.h"
-#include "packets/link_layer/io_capability_negative_response_view.h"
-#include "packets/link_layer/io_capability_view.h"
-#include "packets/link_layer/le_advertisement_view.h"
-#include "packets/link_layer/page_response_view.h"
-#include "packets/link_layer/page_view.h"
-#include "packets/link_layer/response_view.h"
-
-#include "base/logging.h"
-
-using std::vector;
-
-namespace {
-vector<uint8_t> count = {
-    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
-    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
-};
-
-}  // namespace
-
-namespace test_vendor_lib {
-namespace packets {
-
-class LinkLayerPacketBuilderTest : public ::testing::Test {
- public:
-  LinkLayerPacketBuilderTest() = default;
-  ~LinkLayerPacketBuilderTest() = default;
-
-  Address source_{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}};
-  Address dest_{{0x11, 0x12, 0x13, 0x14, 0x15, 0x16}};
-};
-
-TEST_F(LinkLayerPacketBuilderTest, constructorTest) {
-  uint8_t reason = 0xf2;
-  auto disconnect = DisconnectBuilder::Create(reason);
-  ASSERT_EQ(disconnect->size(), sizeof(reason));
-  auto wrapped_disconnect = LinkLayerPacketBuilder::WrapDisconnect(std::move(disconnect), source_, dest_);
-
-  size_t wrapped_size = sizeof(uint8_t) + sizeof(uint32_t) + 2 * sizeof(Address) + sizeof(reason);
-  ASSERT_EQ(wrapped_disconnect->size(), wrapped_size);
-  std::vector<uint8_t> wrapped_vect;
-  std::back_insert_iterator<std::vector<uint8_t>> it(wrapped_vect);
-  wrapped_disconnect->Serialize(it);
-  ASSERT_EQ(wrapped_size, wrapped_vect.size());
-
-  std::vector<uint8_t> hand_wrapped_vect;
-  // Add the size
-  hand_wrapped_vect.push_back(sizeof(uint8_t) + 2 * sizeof(Address) + sizeof(reason));
-  hand_wrapped_vect.push_back(0);
-  hand_wrapped_vect.push_back(0);
-  hand_wrapped_vect.push_back(0);
-
-  hand_wrapped_vect.push_back(static_cast<uint8_t>(Link::PacketType::DISCONNECT));
-
-  for (auto byte : source_.address) {
-    hand_wrapped_vect.push_back(byte);
-  }
-  for (auto byte : dest_.address) {
-    hand_wrapped_vect.push_back(byte);
-  }
-  hand_wrapped_vect.push_back(reason);
-  ASSERT_EQ(wrapped_vect, hand_wrapped_vect);
-}
-
-TEST_F(LinkLayerPacketBuilderTest, disconnectTest) {
-  uint8_t reason = 0x32;
-  auto disconnect_builder = DisconnectBuilder::Create(reason);
-  auto wrapped_disconnect = LinkLayerPacketBuilder::WrapDisconnect(std::move(disconnect_builder), source_, dest_);
-  std::shared_ptr<std::vector<uint8_t>> packet_ptr = std::make_shared<std::vector<uint8_t>>();
-  std::back_insert_iterator<std::vector<uint8_t>> it(*packet_ptr);
-  wrapped_disconnect->Serialize(it);
-
-  LinkLayerPacketView view = LinkLayerPacketView::Create(packet_ptr);
-  ASSERT_EQ(view.size(), wrapped_disconnect->size());
-  ASSERT_EQ(view.GetType(), Link::PacketType::DISCONNECT);
-  ASSERT_EQ(source_, view.GetSourceAddress());
-  ASSERT_EQ(dest_, view.GetDestinationAddress());
-  DisconnectView disconnect = DisconnectView::GetDisconnect(view);
-  ASSERT_EQ(disconnect.GetReason(), reason);
-}
-
-TEST_F(LinkLayerPacketBuilderTest, encryptConnectionTest) {
-  std::vector<uint8_t> key = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
-  auto encrypt_connection_builder = EncryptConnectionBuilder::Create(key);
-  auto wrapped_encrypt_connection =
-      LinkLayerPacketBuilder::WrapEncryptConnection(std::move(encrypt_connection_builder), source_, dest_);
-  std::shared_ptr<std::vector<uint8_t>> packet_ptr = std::make_shared<std::vector<uint8_t>>();
-  std::back_insert_iterator<std::vector<uint8_t>> it(*packet_ptr);
-  wrapped_encrypt_connection->Serialize(it);
-
-  LinkLayerPacketView view = LinkLayerPacketView::Create(packet_ptr);
-  ASSERT_EQ(view.size(), wrapped_encrypt_connection->size());
-  ASSERT_EQ(view.GetType(), Link::PacketType::ENCRYPT_CONNECTION);
-  ASSERT_EQ(source_, view.GetSourceAddress());
-  ASSERT_EQ(dest_, view.GetDestinationAddress());
-  EncryptConnectionView encrypt_connection = EncryptConnectionView::GetEncryptConnection(view);
-  auto key_itr = encrypt_connection.GetKey();
-  ASSERT_EQ(key_itr.NumBytesRemaining(), key.size());
-  for (size_t i = 0; i < key.size(); i++) {
-    ASSERT_EQ(key[i], key_itr.extract<uint8_t>());
-  }
-}
-
-TEST_F(LinkLayerPacketBuilderTest, encryptConnectionResponseTest) {
-  std::vector<uint8_t> key = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
-  auto encrypt_connection_builder = EncryptConnectionBuilder::Create(key);
-  auto wrapped_encrypt_connection_response =
-      LinkLayerPacketBuilder::WrapEncryptConnectionResponse(std::move(encrypt_connection_builder), source_, dest_);
-  std::shared_ptr<std::vector<uint8_t>> packet_ptr = std::make_shared<std::vector<uint8_t>>();
-  std::back_insert_iterator<std::vector<uint8_t>> it(*packet_ptr);
-  wrapped_encrypt_connection_response->Serialize(it);
-
-  LinkLayerPacketView view = LinkLayerPacketView::Create(packet_ptr);
-  ASSERT_EQ(view.size(), wrapped_encrypt_connection_response->size());
-  ASSERT_EQ(view.GetType(), Link::PacketType::ENCRYPT_CONNECTION_RESPONSE);
-  ASSERT_EQ(source_, view.GetSourceAddress());
-  ASSERT_EQ(dest_, view.GetDestinationAddress());
-  EncryptConnectionView encrypt_connection = EncryptConnectionView::GetEncryptConnection(view);
-  auto key_itr = encrypt_connection.GetKey();
-  ASSERT_EQ(key_itr.NumBytesRemaining(), key.size());
-  for (size_t i = 0; i < key.size(); i++) {
-    ASSERT_EQ(key[i], key_itr.extract<uint8_t>());
-  }
-}
-
-TEST_F(LinkLayerPacketBuilderTest, inquiryTest) {
-  Inquiry::InquiryType inquiry_type = Inquiry::InquiryType::RSSI;
-  auto inquiry_builder = InquiryBuilder::Create(inquiry_type);
-  auto wrapped_inquiry = LinkLayerPacketBuilder::WrapInquiry(std::move(inquiry_builder), source_);
-  std::shared_ptr<std::vector<uint8_t>> packet_ptr = std::make_shared<std::vector<uint8_t>>();
-  std::back_insert_iterator<std::vector<uint8_t>> it(*packet_ptr);
-  wrapped_inquiry->Serialize(it);
-
-  LinkLayerPacketView view = LinkLayerPacketView::Create(packet_ptr);
-  ASSERT_EQ(view.size(), wrapped_inquiry->size());
-  ASSERT_EQ(view.GetType(), Link::PacketType::INQUIRY);
-  ASSERT_EQ(source_, view.GetSourceAddress());
-  ASSERT_EQ(Address::kEmpty, view.GetDestinationAddress());
-  ASSERT_EQ(InquiryView::GetInquiry(view).GetType(), inquiry_type);
-}
-
-TEST_F(LinkLayerPacketBuilderTest, standardInquiryResponseTest) {
-  uint8_t mode = 23;
-  ClassOfDevice class_of_device{{0x11, 0x22, 0x33}};
-  uint16_t offset = 0x3456;
-  auto inquiry_response_builder = InquiryResponseBuilder::CreateStandard(mode, class_of_device, offset);
-  auto wrapped_inquiry =
-      LinkLayerPacketBuilder::WrapInquiryResponse(std::move(inquiry_response_builder), source_, dest_);
-  std::shared_ptr<std::vector<uint8_t>> packet_ptr = std::make_shared<std::vector<uint8_t>>();
-  std::back_insert_iterator<std::vector<uint8_t>> it(*packet_ptr);
-  wrapped_inquiry->Serialize(it);
-  ASSERT_EQ(packet_ptr->size(), 24u);
-
-  LinkLayerPacketView view = LinkLayerPacketView::Create(packet_ptr);
-  ASSERT_EQ(view.size(), wrapped_inquiry->size());
-  ASSERT_EQ(view.GetType(), Link::PacketType::INQUIRY_RESPONSE);
-  ASSERT_EQ(source_, view.GetSourceAddress());
-  ASSERT_EQ(dest_, view.GetDestinationAddress());
-  ASSERT_EQ(view.GetPayload().size(), 7u);
-  InquiryResponseView inquiry_response = InquiryResponseView::GetInquiryResponse(view);
-  ASSERT_EQ(inquiry_response.GetClockOffset(), offset);
-  ASSERT_EQ(inquiry_response.GetType(), Inquiry::InquiryType::STANDARD);
-  ASSERT_EQ(inquiry_response.GetPageScanRepetitionMode(), mode);
-  ASSERT_EQ(inquiry_response.GetClassOfDevice(), class_of_device);
-  ASSERT_EQ(inquiry_response.GetClockOffset(), offset);
-}
-
-TEST_F(LinkLayerPacketBuilderTest, rssiInquiryResponseTest) {
-  uint8_t mode = 23;
-  ClassOfDevice class_of_device{{0x11, 0x22, 0x33}};
-  uint16_t offset = 0x3456;
-  uint8_t rssi = 0x78;
-  auto inquiry_response_builder = InquiryResponseBuilder::CreateRssi(mode, class_of_device, offset, rssi);
-  auto wrapped_inquiry =
-      LinkLayerPacketBuilder::WrapInquiryResponse(std::move(inquiry_response_builder), source_, dest_);
-  std::shared_ptr<std::vector<uint8_t>> packet_ptr = std::make_shared<std::vector<uint8_t>>();
-  std::back_insert_iterator<std::vector<uint8_t>> it(*packet_ptr);
-  wrapped_inquiry->Serialize(it);
-
-  LinkLayerPacketView view = LinkLayerPacketView::Create(packet_ptr);
-  ASSERT_EQ(view.size(), wrapped_inquiry->size());
-  ASSERT_EQ(view.GetType(), Link::PacketType::INQUIRY_RESPONSE);
-  ASSERT_EQ(source_, view.GetSourceAddress());
-  ASSERT_EQ(dest_, view.GetDestinationAddress());
-  InquiryResponseView inquiry_response = InquiryResponseView::GetInquiryResponse(view);
-  ASSERT_EQ(inquiry_response.GetClockOffset(), offset);
-  ASSERT_EQ(inquiry_response.GetType(), Inquiry::InquiryType::RSSI);
-  ASSERT_EQ(inquiry_response.GetPageScanRepetitionMode(), mode);
-  ASSERT_EQ(inquiry_response.GetClassOfDevice(), class_of_device);
-  ASSERT_EQ(inquiry_response.GetClockOffset(), offset);
-  ASSERT_EQ(inquiry_response.GetRssi(), rssi);
-}
-
-TEST_F(LinkLayerPacketBuilderTest, extendedInquiryResponseTest) {
-  uint8_t mode = 23;
-  ClassOfDevice class_of_device{{0x11, 0x22, 0x33}};
-  uint16_t offset = 0x3456;
-  uint8_t rssi = 0x78;
-  auto inquiry_response_builder = InquiryResponseBuilder::CreateExtended(mode, class_of_device, offset, rssi, count);
-  auto wrapped_inquiry =
-      LinkLayerPacketBuilder::WrapInquiryResponse(std::move(inquiry_response_builder), source_, dest_);
-  std::shared_ptr<std::vector<uint8_t>> packet_ptr = std::make_shared<std::vector<uint8_t>>();
-  std::back_insert_iterator<std::vector<uint8_t>> it(*packet_ptr);
-  wrapped_inquiry->Serialize(it);
-
-  LinkLayerPacketView view = LinkLayerPacketView::Create(packet_ptr);
-  ASSERT_EQ(view.size(), wrapped_inquiry->size());
-  ASSERT_EQ(view.GetType(), Link::PacketType::INQUIRY_RESPONSE);
-  ASSERT_EQ(source_, view.GetSourceAddress());
-  ASSERT_EQ(dest_, view.GetDestinationAddress());
-  InquiryResponseView inquiry_response = InquiryResponseView::GetInquiryResponse(view);
-  ASSERT_EQ(inquiry_response.GetClockOffset(), offset);
-  ASSERT_EQ(inquiry_response.GetType(), Inquiry::InquiryType::EXTENDED);
-  ASSERT_EQ(inquiry_response.GetPageScanRepetitionMode(), mode);
-  ASSERT_EQ(inquiry_response.GetClassOfDevice(), class_of_device);
-  ASSERT_EQ(inquiry_response.GetClockOffset(), offset);
-  ASSERT_EQ(inquiry_response.GetRssi(), rssi);
-  auto ext_it = inquiry_response.GetExtendedData();
-  ASSERT_EQ(ext_it.NumBytesRemaining(), count.size());
-  for (size_t i = 0; i < count.size(); i++) {
-    ASSERT_EQ(count[i], *(ext_it++));
-  }
-}
-
-TEST_F(LinkLayerPacketBuilderTest, ioCapabilityRequestTest) {
-  uint8_t io_cap = 0x2;
-  uint8_t oob_data_present = 0x1;
-  uint8_t authentication_requirements = 0x5;
-  auto io_capability_builder = IoCapabilityBuilder::Create(io_cap, oob_data_present, authentication_requirements);
-  auto wrapped_io_capability_request =
-      LinkLayerPacketBuilder::WrapIoCapabilityRequest(std::move(io_capability_builder), source_, dest_);
-  std::shared_ptr<std::vector<uint8_t>> packet_ptr = std::make_shared<std::vector<uint8_t>>();
-  std::back_insert_iterator<std::vector<uint8_t>> it(*packet_ptr);
-  wrapped_io_capability_request->Serialize(it);
-
-  LinkLayerPacketView view = LinkLayerPacketView::Create(packet_ptr);
-  ASSERT_EQ(view.size(), wrapped_io_capability_request->size());
-  ASSERT_EQ(view.GetType(), Link::PacketType::IO_CAPABILITY_REQUEST);
-  ASSERT_EQ(source_, view.GetSourceAddress());
-  ASSERT_EQ(dest_, view.GetDestinationAddress());
-  IoCapabilityView io_capability = IoCapabilityView::GetIoCapability(view);
-  ASSERT_EQ(io_capability.GetIoCapability(), io_cap);
-  ASSERT_EQ(io_capability.GetOobDataPresent(), oob_data_present);
-  ASSERT_EQ(io_capability.GetAuthenticationRequirements(), authentication_requirements);
-}
-
-TEST_F(LinkLayerPacketBuilderTest, ioCapabilityResponseTest) {
-  uint8_t io_cap = 0x2;
-  uint8_t oob_data_present = 0x1;
-  uint8_t authentication_requirements = 0x5;
-  auto io_capability_builder = IoCapabilityBuilder::Create(io_cap, oob_data_present, authentication_requirements);
-  auto wrapped_io_capability_response =
-      LinkLayerPacketBuilder::WrapIoCapabilityResponse(std::move(io_capability_builder), source_, dest_);
-  std::shared_ptr<std::vector<uint8_t>> packet_ptr = std::make_shared<std::vector<uint8_t>>();
-  std::back_insert_iterator<std::vector<uint8_t>> it(*packet_ptr);
-  wrapped_io_capability_response->Serialize(it);
-
-  LinkLayerPacketView view = LinkLayerPacketView::Create(packet_ptr);
-  ASSERT_EQ(view.size(), wrapped_io_capability_response->size());
-  ASSERT_EQ(view.GetType(), Link::PacketType::IO_CAPABILITY_RESPONSE);
-  ASSERT_EQ(source_, view.GetSourceAddress());
-  ASSERT_EQ(dest_, view.GetDestinationAddress());
-  IoCapabilityView io_capability = IoCapabilityView::GetIoCapability(view);
-  ASSERT_EQ(io_capability.GetIoCapability(), io_cap);
-  ASSERT_EQ(io_capability.GetOobDataPresent(), oob_data_present);
-  ASSERT_EQ(io_capability.GetAuthenticationRequirements(), authentication_requirements);
-}
-
-TEST_F(LinkLayerPacketBuilderTest, ioCapabilityNegativeResponseTest) {
-  uint8_t reason = 23;
-  auto io_capability_negative_response_builder = IoCapabilityNegativeResponseBuilder::Create(reason);
-  auto wrapped_io_capability_negative_response = LinkLayerPacketBuilder::WrapIoCapabilityNegativeResponse(
-      std::move(io_capability_negative_response_builder), source_, dest_);
-  std::shared_ptr<std::vector<uint8_t>> packet_ptr = std::make_shared<std::vector<uint8_t>>();
-  std::back_insert_iterator<std::vector<uint8_t>> it(*packet_ptr);
-  wrapped_io_capability_negative_response->Serialize(it);
-
-  LinkLayerPacketView view = LinkLayerPacketView::Create(packet_ptr);
-  ASSERT_EQ(view.size(), wrapped_io_capability_negative_response->size());
-  ASSERT_EQ(view.GetType(), Link::PacketType::IO_CAPABILITY_NEGATIVE_RESPONSE);
-  ASSERT_EQ(source_, view.GetSourceAddress());
-  ASSERT_EQ(dest_, view.GetDestinationAddress());
-  IoCapabilityNegativeResponseView io_capability_negative_response =
-      IoCapabilityNegativeResponseView::GetIoCapabilityNegativeResponse(view);
-  ASSERT_EQ(io_capability_negative_response.GetReason(), reason);
-}
-
-TEST_F(LinkLayerPacketBuilderTest, pageTest) {
-  uint8_t allow_role_switch = 1;
-  ClassOfDevice class_of_device{{0x11, 0x22, 0x33}};
-  auto page_builder = PageBuilder::Create(class_of_device, allow_role_switch);
-  auto wrapped_page = LinkLayerPacketBuilder::WrapPage(std::move(page_builder), source_, dest_);
-  std::shared_ptr<std::vector<uint8_t>> packet_ptr = std::make_shared<std::vector<uint8_t>>();
-  std::back_insert_iterator<std::vector<uint8_t>> it(*packet_ptr);
-  wrapped_page->Serialize(it);
-
-  LinkLayerPacketView view = LinkLayerPacketView::Create(packet_ptr);
-  ASSERT_EQ(view.size(), wrapped_page->size());
-  ASSERT_EQ(view.GetType(), Link::PacketType::PAGE);
-  ASSERT_EQ(source_, view.GetSourceAddress());
-  ASSERT_EQ(dest_, view.GetDestinationAddress());
-  PageView page = PageView::GetPage(view);
-  ASSERT_EQ(page.GetAllowRoleSwitch(), allow_role_switch);
-  ASSERT_EQ(page.GetClassOfDevice(), class_of_device);
-}
-
-TEST_F(LinkLayerPacketBuilderTest, pageResponseTest) {
-  uint8_t try_role_switch = 2;
-  auto page_response_builder = PageResponseBuilder::Create(try_role_switch);
-  auto wrapped_page_response =
-      LinkLayerPacketBuilder::WrapPageResponse(std::move(page_response_builder), source_, dest_);
-  std::shared_ptr<std::vector<uint8_t>> packet_ptr = std::make_shared<std::vector<uint8_t>>();
-  std::back_insert_iterator<std::vector<uint8_t>> it(*packet_ptr);
-  wrapped_page_response->Serialize(it);
-
-  LinkLayerPacketView view = LinkLayerPacketView::Create(packet_ptr);
-  ASSERT_EQ(view.size(), wrapped_page_response->size());
-  ASSERT_EQ(view.GetType(), Link::PacketType::PAGE_RESPONSE);
-  ASSERT_EQ(source_, view.GetSourceAddress());
-  ASSERT_EQ(dest_, view.GetDestinationAddress());
-  PageResponseView page_response = PageResponseView::GetPageResponse(view);
-  ASSERT_EQ(page_response.GetTryRoleSwitch(), try_role_switch);
-}
-
-TEST_F(LinkLayerPacketBuilderTest, responseTest) {
-  uint16_t opcode = 0x1234;
-  std::vector<uint64_t> data{
-      0x7060504030201000, 0x7161514131211101, 0x7262524232221202, 0x7363534333231303,
-      0x7464544434241404, 0x7565554535251505, 0x7666564636261606, 0x7767574737271707,
-      0x7868584838281808, 0x7969594939291909, 0x7a6a5a4a3a2a1a0a, 0x7b6b5b4b3b2b1b0b,
-      0x7c6c5c4c3c2c1c0c, 0x7d6d5d4d3d2d1d0d, 0x7e6e5e4e3e2e1e0e, 0x7f6f5f4f3f2f1f0f,
-  };
-  auto response_builder = ResponseBuilder::Create(opcode, data);
-  auto wrapped_response = LinkLayerPacketBuilder::WrapResponse(std::move(response_builder), source_, dest_);
-  std::shared_ptr<std::vector<uint8_t>> packet_ptr = std::make_shared<std::vector<uint8_t>>();
-  std::back_insert_iterator<std::vector<uint8_t>> it(*packet_ptr);
-  wrapped_response->Serialize(it);
-
-  LinkLayerPacketView view = LinkLayerPacketView::Create(packet_ptr);
-  ASSERT_EQ(view.size(), wrapped_response->size());
-  ASSERT_EQ(view.GetType(), Link::PacketType::RESPONSE);
-  ASSERT_EQ(source_, view.GetSourceAddress());
-  ASSERT_EQ(dest_, view.GetDestinationAddress());
-  ResponseView response = ResponseView::GetResponse(view);
-  ASSERT_EQ(opcode, response.GetOpcode());
-  auto data_it = response.GetResponseData();
-  ASSERT_EQ(data.size(), data_it.NumBytesRemaining() / sizeof(uint64_t));
-  ASSERT_EQ(0u, data_it.NumBytesRemaining() % sizeof(uint64_t));
-  for (size_t i = 0; i < data.size(); i++) {
-    ASSERT_EQ(data[i], data_it.extract<uint64_t>());
-  }
-}
-
-TEST_F(LinkLayerPacketBuilderTest, wrapAclTest) {
-  std::shared_ptr<std::vector<uint8_t>> count_shared = std::make_shared<std::vector<uint8_t>>(count);
-  View count_view(count_shared, 0, count_shared->size());
-  PacketView<true> count_packet_view({count_view});
-  auto builder = ViewForwarderBuilder::Create(count_packet_view);
-  auto wrapped_acl = LinkLayerPacketBuilder::WrapAcl(std::move(builder), source_, dest_);
-  std::shared_ptr<std::vector<uint8_t>> packet_ptr = std::make_shared<std::vector<uint8_t>>();
-  std::back_insert_iterator<std::vector<uint8_t>> it(*packet_ptr);
-  wrapped_acl->Serialize(it);
-
-  LinkLayerPacketView view = LinkLayerPacketView::Create(packet_ptr);
-  ASSERT_EQ(view.size(), wrapped_acl->size());
-  ASSERT_EQ(view.GetType(), Link::PacketType::ACL);
-  ASSERT_EQ(source_, view.GetSourceAddress());
-  ASSERT_EQ(dest_, view.GetDestinationAddress());
-  auto acl_view = view.GetPayload();
-  ASSERT_EQ(acl_view.size(), count_view.size());
-  for (size_t i = 0; i < count_view.size(); i++) {
-    ASSERT_EQ(acl_view[i], count_view[i]);
-  }
-}
-
-TEST_F(LinkLayerPacketBuilderTest, wrapCommandTest) {
-  uint16_t opcode = 0x0102;
-  std::shared_ptr<std::vector<uint8_t>> count_shared = std::make_shared<std::vector<uint8_t>>(count);
-  View count_view(count_shared, 0, count_shared->size());
-  PacketView<true> args({count_view});
-  auto builder = CommandBuilder::Create(opcode, args);
-  auto wrapped_command = LinkLayerPacketBuilder::WrapCommand(std::move(builder), source_, dest_);
-  std::shared_ptr<std::vector<uint8_t>> packet_ptr = std::make_shared<std::vector<uint8_t>>();
-  std::back_insert_iterator<std::vector<uint8_t>> it(*packet_ptr);
-  wrapped_command->Serialize(it);
-
-  LinkLayerPacketView view = LinkLayerPacketView::Create(packet_ptr);
-  ASSERT_EQ(view.size(), wrapped_command->size());
-  ASSERT_EQ(view.GetType(), Link::PacketType::COMMAND);
-  ASSERT_EQ(source_, view.GetSourceAddress());
-  ASSERT_EQ(dest_, view.GetDestinationAddress());
-  auto command_view = CommandView::GetCommand(view);
-  ASSERT_EQ(opcode, command_view.GetOpcode());
-  auto args_itr = command_view.GetData();
-  ASSERT_EQ(args_itr.NumBytesRemaining(), count.size());
-  for (size_t i = 0; i < count.size(); i++) {
-    ASSERT_EQ(*args_itr++, count[i]);
-  }
-}
-
-TEST_F(LinkLayerPacketBuilderTest, wrapLeAdvertisementTest) {
-  LeAdvertisement::AddressType address_type = LeAdvertisement::AddressType::RANDOM;
-  LeAdvertisement::AdvertisementType advertisement_type = LeAdvertisement::AdvertisementType::ADV_NONCONN_IND;
-  auto builder = LeAdvertisementBuilder::Create(address_type, advertisement_type, count);
-  auto wrapped_le_advertisement = LinkLayerPacketBuilder::WrapLeAdvertisement(std::move(builder), source_);
-  std::shared_ptr<std::vector<uint8_t>> packet_ptr = std::make_shared<std::vector<uint8_t>>();
-  std::back_insert_iterator<std::vector<uint8_t>> it(*packet_ptr);
-  wrapped_le_advertisement->Serialize(it);
-
-  LinkLayerPacketView view = LinkLayerPacketView::Create(packet_ptr);
-  ASSERT_EQ(view.size(), wrapped_le_advertisement->size());
-  ASSERT_EQ(view.GetType(), Link::PacketType::LE_ADVERTISEMENT);
-  ASSERT_EQ(source_, view.GetSourceAddress());
-  ASSERT_EQ(Address::kEmpty, view.GetDestinationAddress());
-  LeAdvertisementView le_advertisement_view = LeAdvertisementView::GetLeAdvertisementView(view);
-  ASSERT_EQ(address_type, le_advertisement_view.GetAddressType());
-  ASSERT_EQ(advertisement_type, le_advertisement_view.GetAdvertisementType());
-  auto le_advertisement_itr = le_advertisement_view.GetData();
-  ASSERT_EQ(le_advertisement_itr.NumBytesRemaining(), count.size());
-  for (size_t i = 0; i < count.size(); i++) {
-    ASSERT_EQ(*(le_advertisement_itr++), count[i]);
-  }
-}
-
-TEST_F(LinkLayerPacketBuilderTest, wrapLeScanTest) {
-  auto le_scan = LinkLayerPacketBuilder::WrapLeScan(source_, dest_);
-  std::shared_ptr<std::vector<uint8_t>> packet_ptr = std::make_shared<std::vector<uint8_t>>();
-  std::back_insert_iterator<std::vector<uint8_t>> it(*packet_ptr);
-  le_scan->Serialize(it);
-
-  LinkLayerPacketView view = LinkLayerPacketView::Create(packet_ptr);
-  ASSERT_EQ(view.size(), le_scan->size());
-  ASSERT_EQ(view.GetType(), Link::PacketType::LE_SCAN);
-  ASSERT_EQ(source_, view.GetSourceAddress());
-  ASSERT_EQ(dest_, view.GetDestinationAddress());
-  auto le_scan_view = view.GetPayload();
-  ASSERT_EQ(0u, le_scan_view.size());
-}
-
-TEST_F(LinkLayerPacketBuilderTest, wrapLeScanResponseTest) {
-  LeAdvertisement::AddressType address_type = LeAdvertisement::AddressType::PUBLIC_IDENTITY;
-  LeAdvertisement::AdvertisementType advertisement_type = LeAdvertisement::AdvertisementType::SCAN_RESPONSE;
-  auto builder = LeAdvertisementBuilder::Create(address_type, advertisement_type, count);
-  auto wrapped_scan_response = LinkLayerPacketBuilder::WrapLeScanResponse(std::move(builder), source_, dest_);
-  std::shared_ptr<std::vector<uint8_t>> packet_ptr = std::make_shared<std::vector<uint8_t>>();
-  std::back_insert_iterator<std::vector<uint8_t>> it(*packet_ptr);
-  wrapped_scan_response->Serialize(it);
-
-  LinkLayerPacketView view = LinkLayerPacketView::Create(packet_ptr);
-  ASSERT_EQ(view.size(), wrapped_scan_response->size());
-  ASSERT_EQ(view.GetType(), Link::PacketType::LE_SCAN_RESPONSE);
-  ASSERT_EQ(source_, view.GetSourceAddress());
-  ASSERT_EQ(dest_, view.GetDestinationAddress());
-  LeAdvertisementView le_advertisement_view = LeAdvertisementView::GetLeAdvertisementView(view);
-  ASSERT_EQ(address_type, le_advertisement_view.GetAddressType());
-  ASSERT_EQ(advertisement_type, le_advertisement_view.GetAdvertisementType());
-  auto scan_response_itr = le_advertisement_view.GetData();
-  ASSERT_EQ(scan_response_itr.NumBytesRemaining(), count.size());
-  for (size_t i = 0; i < count.size(); i++) {
-    ASSERT_EQ((*scan_response_itr++), count[i]);
-  }
-}
-
-TEST_F(LinkLayerPacketBuilderTest, wrapScoTest) {
-  std::shared_ptr<std::vector<uint8_t>> count_shared = std::make_shared<std::vector<uint8_t>>(count);
-  View count_view(count_shared, 0, count_shared->size());
-  PacketView<true> count_packet_view({count_view});
-  auto builder = ViewForwarderBuilder::Create(count_packet_view);
-  auto wrapped_sco = LinkLayerPacketBuilder::WrapSco(std::move(builder), source_, dest_);
-  std::shared_ptr<std::vector<uint8_t>> packet_ptr = std::make_shared<std::vector<uint8_t>>();
-  std::back_insert_iterator<std::vector<uint8_t>> it(*packet_ptr);
-  wrapped_sco->Serialize(it);
-
-  LinkLayerPacketView view = LinkLayerPacketView::Create(packet_ptr);
-  ASSERT_EQ(view.size(), wrapped_sco->size());
-  ASSERT_EQ(view.GetType(), Link::PacketType::SCO);
-  ASSERT_EQ(source_, view.GetSourceAddress());
-  ASSERT_EQ(dest_, view.GetDestinationAddress());
-  auto sco_view = view.GetPayload();
-  ASSERT_EQ(sco_view.size(), count.size());
-  for (size_t i = 0; i < count.size(); i++) {
-    ASSERT_EQ(sco_view[i], count[i]);
-  }
-}
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/test/packet_builder_test.cc b/vendor_libs/test_vendor_lib/packets/test/packet_builder_test.cc
deleted file mode 100644
index 60e5d95..0000000
--- a/vendor_libs/test_vendor_lib/packets/test/packet_builder_test.cc
+++ /dev/null
@@ -1,244 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "packets/packet_builder.h"
-
-#include <gtest/gtest.h>
-#include <forward_list>
-#include <memory>
-
-using std::vector;
-
-namespace {
-vector<uint8_t> count_all = {
-    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
-    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
-};
-
-vector<uint8_t> count_1 = {
-    0x00,
-    0x01,
-    0x02,
-};
-
-vector<uint8_t> count_2 = {
-    0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
-};
-
-vector<uint8_t> count_3 = {
-    0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
-};
-}  // namespace
-
-namespace test_vendor_lib {
-namespace packets {
-
-template <bool little_endian>
-class EndianBuilder : public PacketBuilder<little_endian> {
- public:
-  EndianBuilder(uint8_t byte, uint16_t two_bytes, uint32_t four_bytes, uint64_t eight_bytes)
-      : byte_(byte), two_bytes_(two_bytes), four_bytes_(four_bytes), eight_bytes_(eight_bytes) {}
-  ~EndianBuilder() = default;
-
-  virtual size_t size() const override {
-    return sizeof(signature_) + sizeof(byte_) + sizeof(two_bytes_) + sizeof(four_bytes_) + sizeof(eight_bytes_);
-  }
-
-  virtual const std::unique_ptr<std::vector<uint8_t>> FinalPacket() {
-    std::unique_ptr<std::vector<uint8_t>> packet = std::make_unique<std::vector<uint8_t>>();
-    packet->reserve(size());
-    std::back_insert_iterator<std::vector<uint8_t>> it(*packet);
-    Serialize(it);
-    return packet;
-  }
-
-  virtual void Serialize(std::back_insert_iterator<std::vector<uint8_t>> it) const override {
-    PacketBuilder<little_endian>::insert(signature_, it);
-    PacketBuilder<little_endian>::insert(byte_, it);
-    PacketBuilder<little_endian>::insert(two_bytes_, it);
-    PacketBuilder<little_endian>::insert(four_bytes_, it);
-    PacketBuilder<little_endian>::insert(eight_bytes_, it);
-  }
-
- private:
-  uint32_t signature_{(little_endian ? 0x03020100 : 0x00010203)};
-  uint8_t byte_;
-  uint16_t two_bytes_;
-  uint32_t four_bytes_;
-  uint64_t eight_bytes_;
-};
-
-class PacketBuilderEndianTest : public ::testing::Test {
- public:
-  PacketBuilderEndianTest() = default;
-  ~PacketBuilderEndianTest() = default;
-};
-
-TEST(PacketBuilderEndianTest, insertTest) {
-  EndianBuilder<true> little(0x04, 0x0605, 0x0a090807, 0x1211100f0e0d0c0b);
-  EndianBuilder<false> big(0x04, 0x0506, 0x0708090a, 0x0b0c0d0e0f101112);
-  ASSERT_EQ(*big.FinalPacket(), *little.FinalPacket());
-}
-
-template <typename T>
-class VectorBuilder : public PacketBuilder<true> {
- public:
-  VectorBuilder(std::vector<uint64_t> vect) {
-    for (uint64_t element : vect) {
-      vect.push_back(static_cast<T>(element));
-    }
-  }
-  ~VectorBuilder() = default;
-
-  virtual size_t size() const override {
-    return vect_.size() * sizeof(T);
-  }
-
-  virtual const std::unique_ptr<std::vector<uint8_t>> FinalPacket() {
-    std::unique_ptr<std::vector<uint8_t>> packet = std::make_unique<std::vector<uint8_t>>();
-    packet->reserve(size());
-    std::back_insert_iterator<std::vector<uint8_t>> it(*packet);
-    Serialize(it);
-    return packet;
-  }
-
-  virtual void Serialize(std::back_insert_iterator<std::vector<uint8_t>> it) const override {
-    PacketBuilder<true>::insert_vector(vect_, it);
-  }
-
- private:
-  std::vector<T> vect_;
-};
-
-template <typename T>
-class InsertElementsBuilder : public PacketBuilder<true> {
- public:
-  InsertElementsBuilder(std::vector<uint64_t> vect) {
-    for (uint64_t element : vect) {
-      vect.push_back(static_cast<T>(element));
-    }
-  }
-  virtual ~InsertElementsBuilder() = default;
-
-  virtual size_t size() const override {
-    return vect_.size() * sizeof(T);
-  }
-
-  virtual const std::unique_ptr<std::vector<uint8_t>> FinalPacket() {
-    std::unique_ptr<std::vector<uint8_t>> packet = std::make_unique<std::vector<uint8_t>>();
-    packet->reserve(size());
-    std::back_insert_iterator<std::vector<uint8_t>> it(*packet);
-    Serialize(it);
-    return packet;
-  }
-
-  virtual void Serialize(std::back_insert_iterator<std::vector<uint8_t>> it) const override {
-    for (T elem : vect_) {
-      PacketBuilder<true>::insert(elem, it);
-    }
-  }
-
- private:
-  std::vector<T> vect_;
-};
-
-std::vector<uint64_t> vector_data{
-    0x7060504030201000, 0x7161514131211101, 0x7262524232221202, 0x7363534333231303, 0x7464544434241404,
-    0x7565554535251505, 0x7666564636261606, 0x7767574737271707, 0x7868584838281808,
-};
-
-template <typename T>
-class VectorBuilderTest : public ::testing::Test {
- public:
-  VectorBuilderTest() = default;
-  ~VectorBuilderTest() = default;
-
-  void SetUp() {
-    packet_1_ = std::shared_ptr<VectorBuilder<T>>(new VectorBuilder<T>(vector_data));
-    packet_2_ = std::shared_ptr<InsertElementsBuilder<T>>(new InsertElementsBuilder<T>(vector_data));
-  }
-
-  void TearDown() {
-    packet_1_.reset();
-    packet_2_.reset();
-  }
-
-  std::shared_ptr<VectorBuilder<T>> packet_1_;
-  std::shared_ptr<InsertElementsBuilder<T>> packet_2_;
-};
-
-using VectorBaseTypes = ::testing::Types<uint8_t, uint16_t, uint32_t, uint64_t, int8_t, int16_t, int32_t, int64_t>;
-TYPED_TEST_CASE(VectorBuilderTest, VectorBaseTypes);
-
-TYPED_TEST(VectorBuilderTest, insertVectorTest) {
-  ASSERT_EQ(*(this->packet_1_->FinalPacket()), *(this->packet_2_->FinalPacket()));
-}
-
-class NestedBuilder : public PacketBuilder<true> {
- public:
-  ~NestedBuilder() = default;
-
-  virtual size_t size() const override {
-    size_t payload_size = (payload_ ? payload_->size() : 0);
-    return 1 + payload_size;
-  }
-
-  static std::unique_ptr<NestedBuilder> Create(uint8_t level) {
-    return std::unique_ptr<NestedBuilder>(new NestedBuilder(level));
-  }
-
-  static std::unique_ptr<NestedBuilder> CreateNested(std::unique_ptr<BasePacketBuilder> payload, uint8_t level) {
-    return std::unique_ptr<NestedBuilder>(new NestedBuilder(std::move(payload), level));
-  }
-
-  virtual const std::unique_ptr<std::vector<uint8_t>> FinalPacket() {
-    std::unique_ptr<std::vector<uint8_t>> packet = std::make_unique<std::vector<uint8_t>>();
-    packet->reserve(size());
-    std::back_insert_iterator<std::vector<uint8_t>> it(*packet);
-    Serialize(it);
-    return packet;
-  }
-
-  virtual void Serialize(std::back_insert_iterator<std::vector<uint8_t>> it) const override {
-    PacketBuilder<true>::insert(level_, it);
-    if (payload_) {
-      payload_->Serialize(it);
-    }
-  }
-
- private:
-  std::unique_ptr<BasePacketBuilder> payload_;
-  uint8_t level_;
-
-  NestedBuilder(std::unique_ptr<BasePacketBuilder> inner, uint8_t level) : payload_(std::move(inner)), level_(level) {}
-  NestedBuilder(uint8_t level) : level_(level) {}
-};
-
-class BuilderBuilderTest : public ::testing::Test {};
-
-TEST(BuilderBuilderTest, nestingTest) {
-  std::unique_ptr<BasePacketBuilder> innermost = NestedBuilder::Create(0);
-  std::unique_ptr<BasePacketBuilder> number_1 = NestedBuilder::CreateNested(std::move(innermost), 1);
-  std::unique_ptr<BasePacketBuilder> number_2 = NestedBuilder::CreateNested(std::move(number_1), 2);
-  std::unique_ptr<BasePacketBuilder> number_3 = NestedBuilder::CreateNested(std::move(number_2), 3);
-  std::unique_ptr<BasePacketBuilder> number_4 = NestedBuilder::CreateNested(std::move(number_3), 4);
-  std::unique_ptr<NestedBuilder> number_5 = NestedBuilder::CreateNested(std::move(number_4), 5);
-
-  std::vector<uint8_t> count_down{5, 4, 3, 2, 1, 0};
-  ASSERT_EQ(*number_5->FinalPacket(), count_down);
-}
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/test/packet_view_test.cc b/vendor_libs/test_vendor_lib/packets/test/packet_view_test.cc
deleted file mode 100644
index 65cd305..0000000
--- a/vendor_libs/test_vendor_lib/packets/test/packet_view_test.cc
+++ /dev/null
@@ -1,507 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "packets/packet_view.h"
-
-#include <gtest/gtest.h>
-#include <forward_list>
-#include <memory>
-
-#include "types/address.h"
-
-using std::vector;
-
-namespace {
-vector<uint8_t> count_all = {
-    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
-    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
-};
-
-vector<uint8_t> count_1 = {
-    0x00,
-    0x01,
-    0x02,
-};
-
-vector<uint8_t> count_2 = {
-    0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
-};
-
-vector<uint8_t> count_3 = {
-    0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
-};
-}  // namespace
-
-namespace test_vendor_lib {
-namespace packets {
-
-template <typename T>
-class IteratorTest : public ::testing::Test {
- public:
-  IteratorTest() = default;
-  ~IteratorTest() = default;
-
-  void SetUp() {
-    packet = std::shared_ptr<T>(new T({View(std::make_shared<const vector<uint8_t>>(count_all), 0, count_all.size())}));
-  }
-
-  void TearDown() {
-    packet.reset();
-  }
-
-  std::shared_ptr<T> packet;
-};
-
-using PacketViewTypes = ::testing::Types<PacketView<true>, PacketView<false>>;
-TYPED_TEST_CASE(IteratorTest, PacketViewTypes);
-
-class IteratorExtractTest : public ::testing::Test {
- public:
-  IteratorExtractTest() = default;
-  ~IteratorExtractTest() = default;
-};
-
-template <typename T>
-class PacketViewTest : public IteratorTest<T> {
- public:
-  PacketViewTest() = default;
-  ~PacketViewTest() = default;
-};
-
-using PacketViewTypes = ::testing::Types<PacketView<true>, PacketView<false>>;
-TYPED_TEST_CASE(PacketViewTest, PacketViewTypes);
-
-class PacketViewMultiViewTest : public ::testing::Test {
- public:
-  PacketViewMultiViewTest() = default;
-  ~PacketViewMultiViewTest() = default;
-};
-
-class ViewTest : public ::testing::Test {
- public:
-  ViewTest() = default;
-  ~ViewTest() = default;
-};
-
-TEST(IteratorExtractTest, extractLeTest) {
-  PacketView<true> packet({View(std::make_shared<const vector<uint8_t>>(count_all), 0, count_all.size())});
-  auto general_case = packet.begin();
-
-  ASSERT_EQ(0x00, general_case.extract<uint8_t>());
-  ASSERT_EQ(0x0201, general_case.extract<uint16_t>());
-  ASSERT_EQ(0x06050403u, general_case.extract<uint32_t>());
-  ASSERT_EQ(0x0e0d0c0b0a090807u, general_case.extract<uint64_t>());
-  ASSERT_EQ(0x0f, general_case.extract<uint8_t>());
-  Address raw({0x10, 0x11, 0x12, 0x13, 0x14, 0x15});
-  ASSERT_EQ(raw, general_case.extract<Address>());
-  ASSERT_EQ(0x16, general_case.extract<uint8_t>());
-}
-
-TEST(IteratorExtractTest, extractBeTest) {
-  PacketView<false> packet({View(std::make_shared<const vector<uint8_t>>(count_all), 0, count_all.size())});
-  auto general_case = packet.begin();
-
-  ASSERT_EQ(0x00, general_case.extract<uint8_t>());
-  ASSERT_EQ(0x0102, general_case.extract<uint16_t>());
-  ASSERT_EQ(0x03040506u, general_case.extract<uint32_t>());
-  ASSERT_EQ(0x0708090a0b0c0d0eu, general_case.extract<uint64_t>());
-  ASSERT_EQ(0x0f, general_case.extract<uint8_t>());
-  Address raw({0x15, 0x14, 0x13, 0x12, 0x11, 0x10});
-  ASSERT_EQ(raw, general_case.extract<Address>());
-  ASSERT_EQ(0x16, general_case.extract<uint8_t>());
-}
-
-TYPED_TEST(IteratorTest, extractBoundsDeathTest) {
-  auto bounds_test = this->packet->end();
-
-  ASSERT_DEATH(bounds_test.template extract<uint8_t>(), "");
-  ASSERT_DEATH(bounds_test.template extract<uint16_t>(), "");
-  ASSERT_DEATH(bounds_test.template extract<uint32_t>(), "");
-  ASSERT_DEATH(bounds_test.template extract<uint64_t>(), "");
-}
-
-TYPED_TEST(IteratorTest, dereferenceDeathTest) {
-  auto dereference_test = this->packet->end();
-
-  ASSERT_DEATH(*dereference_test, "");
-  ASSERT_EQ(0x1f, *(dereference_test - 1));
-}
-
-TYPED_TEST(IteratorTest, plusEqTest) {
-  auto plus_eq = this->packet->begin();
-  for (size_t i = 0; i < count_all.size(); i += 2) {
-    ASSERT_EQ(count_all[i], *plus_eq) << "+= test: Dereferenced iterator does not equal expected at index " << i;
-    plus_eq += 2;
-  }
-}
-
-TYPED_TEST(IteratorTest, preIncrementTest) {
-  auto plus_plus = this->packet->begin();
-  for (size_t i = 0; i < count_all.size() - 1; i++) {
-    ASSERT_EQ(count_all[i + 1], *(++plus_plus)) << "Pre-increment test: Dereferenced iterator does not equal expected "
-                                                << "at index " << i;
-  }
-}
-
-TYPED_TEST(IteratorTest, postIncrementTest) {
-  auto plus_plus = this->packet->begin();
-  for (size_t i = 0; i < count_all.size(); i++) {
-    ASSERT_EQ(count_all[i], *(plus_plus++)) << "Post-increment test: Dereferenced iterator does not equal expected "
-                                            << "at index " << i;
-  }
-}
-
-TYPED_TEST(IteratorTest, additionTest) {
-  auto plus = this->packet->begin();
-  for (size_t i = 0; i < count_all.size(); i++) {
-    ASSERT_EQ(count_all[i], *plus) << "+ test: Dereferenced iterator does not equal expected at index " << i;
-    plus = plus + 1;
-  }
-}
-
-TYPED_TEST(IteratorTest, minusEqTest) {
-  auto minus_eq = this->packet->end();
-  minus_eq -= 1;
-  size_t index = count_all.size() - 1;
-  for (size_t i = 0; index > i; i++) {
-    ASSERT_EQ(count_all[index], *minus_eq)
-        << "-= test: Dereferenced iterator does not equal expected at index " << index;
-    index -= i;
-    minus_eq -= i;
-  }
-}
-
-TYPED_TEST(IteratorTest, preDecrementTest) {
-  auto minus_minus = this->packet->end();
-  for (size_t i = count_all.size(); i > 0; i--) {
-    ASSERT_EQ(count_all[i - 1], *(--minus_minus))
-        << "Pre-decrement test: Dereferenced iterator does not equal expected "
-        << "at index " << i;
-  }
-}
-
-TYPED_TEST(IteratorTest, postDecrementTest) {
-  auto minus_minus = this->packet->end();
-  minus_minus--;
-  for (size_t i = count_all.size() - 1; i > 0; i--) {
-    ASSERT_EQ(count_all[i], *(minus_minus--)) << "Post-decrement test: Dereferenced iterator does not equal expected "
-                                              << "at index " << i;
-  }
-}
-
-TYPED_TEST(IteratorTest, subtractionTest) {
-  auto minus = this->packet->end();
-  minus = minus - 1;
-  for (size_t i = count_all.size() - 1; i > 0; i--) {
-    ASSERT_EQ(count_all[i], *minus) << "- test: Dereferenced iterator does not equal expected at index " << i;
-    minus = minus - 1;
-  }
-}
-
-TYPED_TEST(IteratorTest, differenceTest) {
-  auto begin = this->packet->begin();
-  auto end = this->packet->end();
-  int difference = end - begin;
-  ASSERT_EQ(difference, static_cast<int>(count_all.size()));
-  int neg_difference = begin - end;
-  ASSERT_EQ(neg_difference, -static_cast<int>(count_all.size()));
-}
-
-TYPED_TEST(IteratorTest, equalityTest) {
-  auto begin = this->packet->begin();
-  auto end = this->packet->end();
-  auto begin_copy = this->packet->begin();
-  auto end_copy = this->packet->end();
-  ASSERT_EQ(begin_copy, begin);
-  ASSERT_EQ(end_copy, end);
-}
-
-TYPED_TEST(IteratorTest, comparisonsTest) {
-  auto begin = this->packet->begin();
-  auto end = this->packet->end();
-  auto begin_copy = this->packet->begin();
-  auto end_copy = this->packet->end();
-  ASSERT_EQ(begin_copy, begin);
-  ASSERT_EQ(end_copy, end);
-  ASSERT_NE(begin, end);
-  ASSERT_TRUE(begin < end);
-  ASSERT_FALSE(end < end);
-  ASSERT_FALSE(end < begin);
-  ASSERT_FALSE(begin > end);
-  ASSERT_FALSE(end > end);
-  ASSERT_TRUE(end > begin);
-  ASSERT_TRUE(begin <= end);
-  ASSERT_TRUE(end <= end);
-  ASSERT_FALSE(end <= begin);
-  ASSERT_FALSE(begin >= end);
-  ASSERT_TRUE(end >= end);
-  ASSERT_TRUE(end >= begin);
-}
-
-TYPED_TEST(PacketViewTest, getLengthTest) {
-  size_t length = this->packet->size();
-  ASSERT_EQ(length, count_all.size());
-}
-
-TYPED_TEST(PacketViewTest, getAtIndexTest) {
-  size_t past_end = this->packet->size();
-  ASSERT_DEATH(this->packet->at(past_end), "");
-  size_t working_index = 0x1f;
-  ASSERT_EQ(0x1f, this->packet->at(working_index));
-}
-
-TYPED_TEST(PacketViewTest, arrayOperatorTest) {
-  size_t past_end = this->packet->size();
-  ASSERT_DEATH((*(this->packet))[past_end], "");
-  size_t working_index = 0x1f;
-  ASSERT_EQ(0x1f, (*(this->packet))[working_index]);
-}
-
-TYPED_TEST(PacketViewTest, numBytesRemainingTest) {
-  auto all = this->packet->begin();
-  size_t remaining = all.NumBytesRemaining();
-  for (size_t n = remaining; n > 0; n--) {
-    ASSERT_EQ(remaining, all.NumBytesRemaining());
-    all++;
-    remaining--;
-  }
-  ASSERT_EQ(static_cast<size_t>(0), all.NumBytesRemaining());
-  ASSERT_DEATH(*(all++), "");
-  all++;
-  ASSERT_EQ(static_cast<size_t>(0), all.NumBytesRemaining());
-  ASSERT_DEATH(*(all++), "");
-}
-
-using SubViewTestParam = std::pair<size_t, size_t>;
-class SubViewBaseTest : public ::testing::TestWithParam<SubViewTestParam> {
- public:
-  class SubPacketView : public PacketView<true> {
-   public:
-    using PacketView<true>::PacketView;
-    PacketView<true> Slice(size_t header, size_t tail) {
-      return PacketView<true>::SubViewLittleEndian(header, tail);
-    }
-  };
-};
-
-class SubViewPassTest : public SubViewBaseTest {};
-
-TEST_P(SubViewPassTest, subViewTest) {
-  auto header = GetParam().first;
-  auto tail = GetParam().second;
-  SubPacketView single_view({View(std::make_shared<const vector<uint8_t>>(count_all), 0, count_all.size())});
-  SubPacketView multi_view({
-      View(std::make_shared<const vector<uint8_t>>(count_1), 0, count_1.size()),
-      View(std::make_shared<const vector<uint8_t>>(count_2), 0, count_2.size()),
-      View(std::make_shared<const vector<uint8_t>>(count_3), 0, count_3.size()),
-  });
-
-  auto single_slice = single_view.Slice(header, tail);
-  auto multi_slice = multi_view.Slice(header, tail);
-
-  ASSERT_EQ(single_slice.size(), tail - header);
-  ASSERT_EQ(single_slice.size(), multi_slice.size());
-  for (size_t i = 0; i < single_slice.size(); i++) {
-    ASSERT_EQ(single_slice[i], multi_slice[i]);
-  }
-}
-
-static const size_t boundary_1 = count_1.size();
-static const size_t boundary_2 = count_1.size() + count_2.size();
-
-INSTANTIATE_TEST_CASE_P(
-    chopomatic, SubViewPassTest,
-    ::testing::Values(
-        // {begin, end} pairs for subsets into the PacketView
-        SubViewTestParam{0, 0}, SubViewTestParam{0, boundary_1}, SubViewTestParam{0, boundary_1 + 1},
-        SubViewTestParam{0, boundary_2}, SubViewTestParam{0, boundary_2 + 1}, SubViewTestParam{0, count_all.size()},
-        SubViewTestParam{boundary_1 - 1, boundary_1}, SubViewTestParam{boundary_1 - 1, boundary_1 + 1},
-        SubViewTestParam{boundary_1 - 1, boundary_2}, SubViewTestParam{boundary_1 - 1, boundary_2 + 1},
-        SubViewTestParam{boundary_1 - 1, count_all.size()}, SubViewTestParam{boundary_1, boundary_1},
-        SubViewTestParam{boundary_1, boundary_2}, SubViewTestParam{boundary_1, boundary_2 + 1},
-        SubViewTestParam{boundary_1, count_all.size()}, SubViewTestParam{boundary_2 - 1, boundary_2},
-        SubViewTestParam{boundary_2 - 1, boundary_2 + 1}, SubViewTestParam{boundary_2 - 1, count_all.size()},
-        SubViewTestParam{boundary_2, boundary_2}, SubViewTestParam{boundary_2, boundary_2 + 1},
-        SubViewTestParam{boundary_2, count_all.size()}, SubViewTestParam{count_all.size() - 1, count_all.size()},
-        SubViewTestParam{count_all.size(), count_all.size()}));
-
-class SubViewDeathTest : public SubViewBaseTest {};
-
-TEST_P(SubViewDeathTest, subViewDeathTest) {
-  auto header = GetParam().first;
-  auto tail = GetParam().second;
-  SubPacketView single_view({View(std::make_shared<const vector<uint8_t>>(count_all), 0, count_all.size())});
-  SubPacketView multi_view({
-      View(std::make_shared<const vector<uint8_t>>(count_1), 0, count_1.size()),
-      View(std::make_shared<const vector<uint8_t>>(count_2), 0, count_2.size()),
-      View(std::make_shared<const vector<uint8_t>>(count_3), 0, count_3.size()),
-  });
-
-  ASSERT_DEATH(auto single_slice = single_view.Slice(header, tail), "");
-  ASSERT_DEATH(auto multi_slice = multi_view.Slice(header, tail), "");
-}
-
-INSTANTIATE_TEST_CASE_P(chopomaticDeath, SubViewDeathTest,
-                        ::testing::Values(
-                            // {begin, end} pairs for subsets into the PacketView
-                            SubViewTestParam{1, 0}, SubViewTestParam{count_all.size(), count_all.size() - 1},
-                            SubViewTestParam{count_all.size(), count_all.size() + 1}));
-
-TEST(SubViewTest, simpleSubViewTest) {
-  PacketView<true> view({View(std::make_shared<const vector<uint8_t>>(count_all), 0, count_all.size())});
-  PacketView<true> sub_1_view = view.SubViewLittleEndian(0, view.size());
-  PacketView<true> sub_2_view = sub_1_view.SubViewLittleEndian(0, sub_1_view.size());
-  PacketView<true> sub_3_view = sub_2_view.SubViewLittleEndian(0, sub_2_view.size());
-  PacketView<true> sub_4_view = sub_3_view.SubViewLittleEndian(0, sub_3_view.size());
-  ASSERT_EQ(sub_1_view.size(), view.size());
-  ASSERT_EQ(sub_2_view.size(), view.size());
-  ASSERT_EQ(sub_3_view.size(), view.size());
-  ASSERT_EQ(sub_4_view.size(), view.size());
-}
-
-TEST(SubViewTest, realSubViewTest) {
-  PacketView<true> view({View(std::make_shared<const vector<uint8_t>>(count_all), 0, count_all.size())});
-  std::vector<PacketView<true>> sub_views{view};
-  for (size_t i = 1; i < 6; i++) {
-    size_t parent_size = sub_views[i - 1].size();
-    sub_views.push_back(sub_views[i - 1].SubViewLittleEndian(1, parent_size - 1));
-    ASSERT_EQ(sub_views[i][0], i);
-    ASSERT_EQ(sub_views[i].size(), parent_size - 2);
-  }
-}
-
-TEST(SubViewTest, subSubViewTest) {
-  PacketView<true> single_view({View(std::make_shared<const vector<uint8_t>>(count_all), 0, count_all.size())});
-  PacketView<true> multi_view({
-      View(std::make_shared<const vector<uint8_t>>(count_1), 0, count_1.size()),
-      View(std::make_shared<const vector<uint8_t>>(count_2), 0, count_2.size()),
-      View(std::make_shared<const vector<uint8_t>>(count_3), 0, count_3.size()),
-  });
-  ASSERT_EQ(single_view.size(), multi_view.size());
-  for (size_t i = 0; i < count_all.size() / 2; i++) {
-    PacketView<true> sub_single_view = single_view.SubViewLittleEndian(i, count_all.size() - i);
-    PacketView<true> sub_multi_view = multi_view.SubViewLittleEndian(i, count_all.size() - i);
-    ASSERT_EQ(count_all.size() - 2 * i, sub_single_view.size());
-    ASSERT_EQ(sub_single_view.size(), sub_multi_view.size());
-    for (size_t j = 0; j < sub_single_view.size() / 2; j++) {
-      PacketView<true> sub_sub_single_view = sub_single_view.SubViewLittleEndian(j, sub_single_view.size() - j);
-      PacketView<true> sub_sub_multi_view = sub_multi_view.SubViewLittleEndian(j, sub_multi_view.size() - j);
-      ASSERT_EQ(sub_single_view.size() - 2 * j, sub_sub_single_view.size());
-      ASSERT_EQ(sub_sub_single_view.size(), sub_sub_multi_view.size());
-    }
-  }
-}
-
-TEST(PacketViewMultiViewTest, sizeTest) {
-  PacketView<true> single_view({View(std::make_shared<const vector<uint8_t>>(count_all), 0, count_all.size())});
-  PacketView<true> multi_view({
-      View(std::make_shared<const vector<uint8_t>>(count_1), 0, count_1.size()),
-      View(std::make_shared<const vector<uint8_t>>(count_2), 0, count_2.size()),
-      View(std::make_shared<const vector<uint8_t>>(count_3), 0, count_3.size()),
-  });
-  ASSERT_EQ(single_view.size(), multi_view.size());
-}
-
-TEST(PacketViewMultiViewTest, dereferenceTestLittleEndian) {
-  PacketView<true> single_view({View(std::make_shared<const vector<uint8_t>>(count_all), 0, count_all.size())});
-  PacketView<true> multi_view({
-      View(std::make_shared<const vector<uint8_t>>(count_1), 0, count_1.size()),
-      View(std::make_shared<const vector<uint8_t>>(count_2), 0, count_2.size()),
-      View(std::make_shared<const vector<uint8_t>>(count_3), 0, count_3.size()),
-  });
-  auto single_itr = single_view.begin();
-  auto multi_itr = multi_view.begin();
-  for (size_t i = 0; i < single_view.size(); i++) {
-    ASSERT_EQ(*(single_itr++), *(multi_itr++));
-  }
-  ASSERT_DEATH(*multi_itr, "");
-}
-
-TEST(PacketViewMultiViewTest, dereferenceTestBigEndian) {
-  PacketView<false> single_view({View(std::make_shared<const vector<uint8_t>>(count_all), 0, count_all.size())});
-  PacketView<false> multi_view({
-      View(std::make_shared<const vector<uint8_t>>(count_1), 0, count_1.size()),
-      View(std::make_shared<const vector<uint8_t>>(count_2), 0, count_2.size()),
-      View(std::make_shared<const vector<uint8_t>>(count_3), 0, count_3.size()),
-  });
-  auto single_itr = single_view.begin();
-  auto multi_itr = multi_view.begin();
-  for (size_t i = 0; i < single_view.size(); i++) {
-    ASSERT_EQ(*(single_itr++), *(multi_itr++));
-  }
-  ASSERT_DEATH(*multi_itr, "");
-}
-
-TEST(PacketViewMultiViewTest, arrayOperatorTest) {
-  PacketView<true> single_view({View(std::make_shared<const vector<uint8_t>>(count_all), 0, count_all.size())});
-  PacketView<true> multi_view({
-      View(std::make_shared<const vector<uint8_t>>(count_1), 0, count_1.size()),
-      View(std::make_shared<const vector<uint8_t>>(count_2), 0, count_2.size()),
-      View(std::make_shared<const vector<uint8_t>>(count_3), 0, count_3.size()),
-  });
-  for (size_t i = 0; i < single_view.size(); i++) {
-    ASSERT_EQ(single_view[i], multi_view[i]);
-  }
-  ASSERT_DEATH(multi_view[single_view.size()], "");
-}
-
-TEST(ViewTest, arrayOperatorTest) {
-  View view_all(std::make_shared<const vector<uint8_t>>(count_all), 0, count_all.size());
-  size_t past_end = view_all.size();
-  for (size_t i = 0; i < past_end; i++) {
-    ASSERT_EQ(view_all[i], count_all[i]);
-  }
-  ASSERT_DEATH(view_all[past_end], "");
-
-  size_t header_size = 2;
-  size_t tail_size = 3;
-  View view_subset(std::make_shared<const vector<uint8_t>>(count_all), header_size, count_all.size() - tail_size);
-  View view_subset2(view_all, header_size, count_all.size() - tail_size);
-  size_t subset_length = view_subset.size();
-  for (size_t i = 0; i < subset_length; i++) {
-    ASSERT_EQ(view_subset[i], count_all[header_size + i]);
-    ASSERT_EQ(view_subset[i], view_subset2[i]);
-  }
-  ASSERT_DEATH(view_subset[subset_length + 1], "");
-  ASSERT_DEATH(view_subset2[subset_length + 1], "");
-}
-
-TEST(ViewTest, earlySubSubViewTest) {
-  View view(std::make_shared<const vector<uint8_t>>(count_all), 0, count_all.size());
-  View sub_1_view(view, view.size() - 3, view.size() - 1);
-  View sub_2_view(sub_1_view, 1, 2);
-  ASSERT_EQ(sub_1_view.size(), 2u);
-  ASSERT_EQ(sub_2_view.size(), 1u);
-}
-
-TEST(ViewTest, subSubViewTest) {
-  View view(std::make_shared<const vector<uint8_t>>(count_all), 0, count_all.size());
-  std::vector<View> sub_views{view};
-  for (size_t i = 1; i < 6; i++) {
-    size_t parent_size = sub_views[i - 1].size();
-    sub_views.push_back({View(sub_views[i - 1], 1, parent_size - 1)});
-    ASSERT_EQ(sub_views[i][0], i);
-    ASSERT_EQ(sub_views[i].size(), parent_size - 2);
-  }
-}
-
-TEST(ViewTest, zeroSubViewTest) {
-  View view(std::make_shared<const vector<uint8_t>>(count_all), 0, count_all.size());
-  View subview(view, view.size(), view.size() + 1);
-  ASSERT_EQ(subview.size(), 0u);
-}
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/test/raw_builder_test.cc b/vendor_libs/test_vendor_lib/packets/test/raw_builder_test.cc
deleted file mode 100644
index f67ba0b..0000000
--- a/vendor_libs/test_vendor_lib/packets/test/raw_builder_test.cc
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "packets/raw_builder.h"
-
-#include <gtest/gtest.h>
-#include <forward_list>
-#include <memory>
-
-#include "types/address.h"
-
-using std::vector;
-
-namespace {
-vector<uint8_t> count = {
-    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
-    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
-};
-
-}  // namespace
-
-namespace test_vendor_lib {
-namespace packets {
-
-class RawBuilderTest : public ::testing::Test {
- public:
-  RawBuilderTest() = default;
-  ~RawBuilderTest() = default;
-};
-
-TEST(RawBuilderTest, buildCountTest) {
-  std::unique_ptr<RawBuilder> count_builder = std::make_unique<RawBuilder>();
-  ASSERT_EQ(0u, count_builder->size());
-  count_builder->AddOctets8(0x0706050403020100);
-  count_builder->AddOctets4(0x0b0a0908);
-  count_builder->AddOctets2(0x0d0c);
-  count_builder->AddOctets1(0x0e);
-  count_builder->AddOctets1(0x0f);
-  count_builder->AddAddress(Address({0x10, 0x11, 0x12, 0x13, 0x14, 0x15}));
-  std::vector<uint8_t> count_subset(count.begin() + 0x16, count.end());
-  count_builder->AddOctets(count_subset);
-
-  ASSERT_EQ(count.size(), count_builder->size());
-
-  std::vector<uint8_t> packet;
-  std::back_insert_iterator<std::vector<uint8_t>> it(packet);
-
-  count_builder->Serialize(it);
-
-  ASSERT_EQ(count, packet);
-}
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/view.cc b/vendor_libs/test_vendor_lib/packets/view.cc
deleted file mode 100644
index 7dbd8bd..0000000
--- a/vendor_libs/test_vendor_lib/packets/view.cc
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "view.h"
-
-#include <base/logging.h>
-
-namespace test_vendor_lib {
-namespace packets {
-
-View::View(std::shared_ptr<const std::vector<uint8_t>> data, size_t begin, size_t end)
-    : data_(data), begin_(begin < data_->size() ? begin : data_->size()),
-      end_(end < data_->size() ? end : data_->size()) {}
-
-View::View(const View& view, size_t begin, size_t end) : data_(view.data_) {
-  begin_ = (begin < view.size() ? begin : view.size());
-  begin_ += view.begin_;
-  end_ = (end < view.size() ? end : view.size());
-  end_ += view.begin_;
-}
-
-uint8_t View::operator[](size_t i) const {
-  CHECK(i + begin_ < end_) << "Out of bounds access at " << i;
-  return data_->operator[](i + begin_);
-}
-
-size_t View::size() const {
-  return end_ - begin_;
-}
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/view.h b/vendor_libs/test_vendor_lib/packets/view.h
deleted file mode 100644
index ca38875..0000000
--- a/vendor_libs/test_vendor_lib/packets/view.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cstdint>
-#include <vector>
-
-namespace test_vendor_lib {
-namespace packets {
-
-// Base class that holds a shared pointer to data with bounds.
-class View {
- public:
-  View(std::shared_ptr<const std::vector<uint8_t>> data, size_t begin, size_t end);
-  View(const View& view, size_t begin, size_t end);
-  View(const View& view) = default;
-  virtual ~View() = default;
-
-  uint8_t operator[](size_t i) const;
-
-  size_t size() const;
-
- private:
-  std::shared_ptr<const std::vector<uint8_t>> data_;
-  size_t begin_;
-  size_t end_;
-};
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/scripts/hci_socket.py b/vendor_libs/test_vendor_lib/scripts/hci_socket.py
index 0c425af..6833eeb 100644
--- a/vendor_libs/test_vendor_lib/scripts/hci_socket.py
+++ b/vendor_libs/test_vendor_lib/scripts/hci_socket.py
@@ -73,31 +73,31 @@
 
 
 class HCI_Cmd_Create_Connection(Packet):
-  name = 'Create Connection'
-  fields_desc = [
-      LEMACField('addr', None),
-      LEShortField('packet_type', 8),
-      ByteEnumField('page_scan_repetition_mode', 0, {
-          0: 'R0',
-          1: 'R1',
-          2: 'R2'
-      }),
-      ByteEnumField('rsvd', 0, {0: 'Reserved'}),
-      LEShortField('clock_offset', 0),
-      ByteEnumField('allow_role_switch', 1, {
-          0: 'false',
-          1: 'true'
-      }),
-  ]
+    name = 'Create Connection'
+    fields_desc = [
+        LEMACField('addr', None),
+        LEShortField('packet_type', 8),
+        ByteEnumField('page_scan_repetition_mode', 0, {
+            0: 'R0',
+            1: 'R1',
+            2: 'R2'
+        }),
+        ByteEnumField('rsvd', 0, {0: 'Reserved'}),
+        LEShortField('clock_offset', 0),
+        ByteEnumField('allow_role_switch', 1, {
+            0: 'false',
+            1: 'true'
+        }),
+    ]
 
 
 class HCI_Cmd_Inquiry(Packet):
-  name = 'Inquiry'
-  fields_desc = [
-      X3BytesField('LAP', 0x9e8b0),
-      ByteField('length', 1),
-      ByteField('max_responses', 0),
-  ]
+    name = 'Inquiry'
+    fields_desc = [
+        X3BytesField('LAP', 0x9e8b0),
+        ByteField('length', 1),
+        ByteField('max_responses', 0),
+    ]
 
 
 bind_layers(HCI_Command_Hdr, HCI_Cmd_Inquiry, opcode=0x0401)
@@ -105,96 +105,96 @@
 
 
 class HCI_Event_Inquiry_Result(Packet):
-  name = 'Inquiry Result'
-  fields_desc = [
-      ByteField('num_responses', 0),
-      LEMACField('addr', None),
-      ByteEnumField('page_scan_repetition_mode', 0, {
-          0: 'R0',
-          1: 'R1',
-          2: 'R2'
-      }),
-      LEShortEnumField('rsvd', 0, {0: 'Reserved'}),
-      X3BytesField('class_of_device', 0),
-      LEShortField('clock_offset', 0),
-  ]
+    name = 'Inquiry Result'
+    fields_desc = [
+        ByteField('num_responses', 0),
+        LEMACField('addr', None),
+        ByteEnumField('page_scan_repetition_mode', 0, {
+            0: 'R0',
+            1: 'R1',
+            2: 'R2'
+        }),
+        LEShortEnumField('rsvd', 0, {0: 'Reserved'}),
+        X3BytesField('class_of_device', 0),
+        LEShortField('clock_offset', 0),
+    ]
 
 
 class HCI_Event_Connection_Complete(Packet):
-  name = 'Connection Complete'
-  fields_desc = [
-      ByteField('status', 0),
-      LEShortField('handle', 0xffff),
-      LEMACField('addr', None),
-      ByteField('link_type', 1),
-      ByteField('encryption_mode', 0),
-  ]
+    name = 'Connection Complete'
+    fields_desc = [
+        ByteField('status', 0),
+        LEShortField('handle', 0xffff),
+        LEMACField('addr', None),
+        ByteField('link_type', 1),
+        ByteField('encryption_mode', 0),
+    ]
 
 
 class HCI_Event_Remote_Name_Request_Complete(Packet):
-  name = 'Remote Name Request Complete'
-  fields_desc = [
-      ByteField('status', 0),
-      LEMACField('addr', None),
-  ]
+    name = 'Remote Name Request Complete'
+    fields_desc = [
+        ByteField('status', 0),
+        LEMACField('addr', None),
+    ]
 
 
 class HCI_Event_Read_Remote_Supported_Features_Complete(Packet):
-  name = 'Read Remote Supported Features Complete'
-  fields_desc = [
-      ByteField('status', 0),
-      LEShortField('handle', 0xffff),
-      XLELongField('features', 0x0123456789abcdef),
-  ]
+    name = 'Read Remote Supported Features Complete'
+    fields_desc = [
+        ByteField('status', 0),
+        LEShortField('handle', 0xffff),
+        XLELongField('features', 0x0123456789abcdef),
+    ]
 
 
 class HCI_Event_Read_Remote_Version_Information_Complete(Packet):
-  name = 'Read Remote Version Information Complete'
-  fields_desc = [
-      ByteField('status', 0),
-      LEShortField('handle', 0xffff),
-      ByteField('version', 0),
-      LEShortField('manufacturer_name', 0),
-      LEShortField('subversion', 0),
-  ]
+    name = 'Read Remote Version Information Complete'
+    fields_desc = [
+        ByteField('status', 0),
+        LEShortField('handle', 0xffff),
+        ByteField('version', 0),
+        LEShortField('manufacturer_name', 0),
+        LEShortField('subversion', 0),
+    ]
 
 
 class HCI_Event_Read_Clock_Offset_Complete(Packet):
-  name = 'Read Clock Offset Complete'
-  fields_desc = [
-      ByteField('status', 0),
-      LEShortField('handle', 0xffff),
-      LEShortField('offset', 0xffff),
-  ]
+    name = 'Read Clock Offset Complete'
+    fields_desc = [
+        ByteField('status', 0),
+        LEShortField('handle', 0xffff),
+        LEShortField('offset', 0xffff),
+    ]
 
 
 class HCI_Event_Read_Remote_Extended_Features_Complete(Packet):
-  name = 'Read Remote Supported Features Complete'
-  fields_desc = [
-      ByteField('status', 0),
-      LEShortField('handle', 0xffff),
-      ByteField('page_number', 0),
-      ByteField('max_page_number', 0),
-      XLELongField('features', 0x0123456789abcdef),
-  ]
+    name = 'Read Remote Supported Features Complete'
+    fields_desc = [
+        ByteField('status', 0),
+        LEShortField('handle', 0xffff),
+        ByteField('page_number', 0),
+        ByteField('max_page_number', 0),
+        XLELongField('features', 0x0123456789abcdef),
+    ]
 
 
 class HCI_Event_Extended_Inquiry_Result(Packet):
-  name = 'Extended Inquiry Result'
-  fields_desc = [
-      ByteField('num_responses', 1),
-      LEMACField('addr', None),
-      ByteEnumField('page_scan_repetition_mode', 0, {
-          0: 'R0',
-          1: 'R1',
-          2: 'R2'
-      }),
-      ByteEnumField('rsvd', 0, {0: 'Reserved'}),
-      X3BytesField('class_of_device', 0),
-      LEShortField('clock_offset', 0),
-      SignedByteField('rssi', -20),
-      PacketListField('extended_inquiry_response', [], EIR_Hdr, 1),
-  ]
+    name = 'Extended Inquiry Result'
+    fields_desc = [
+        ByteField('num_responses', 1),
+        LEMACField('addr', None),
+        ByteEnumField('page_scan_repetition_mode', 0, {
+            0: 'R0',
+            1: 'R1',
+            2: 'R2'
+        }),
+        ByteEnumField('rsvd', 0, {0: 'Reserved'}),
+        X3BytesField('class_of_device', 0),
+        LEShortField('clock_offset', 0),
+        SignedByteField('rssi', -20),
+        PacketListField('extended_inquiry_response', [], EIR_Hdr, 1),
+    ]
 
 
 bind_layers(HCI_Event_Hdr, HCI_Event_Inquiry_Result, code=0x02)
@@ -214,228 +214,236 @@
 
 
 class HCISocket(SuperSocket):
-  """Simple wrapper class for a socket object.
+    """Simple wrapper class for a socket object.
 
   Attributes:
     socket: The underlying socket created for the specified address and port.
   """
 
-  def __init__(self, port):
-    self.done_ = False
-    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-    s.connect(('localhost', port))
-    self.ins = self.outs = s
-    self.packets_ = queue.Queue()
-    self.rx_thread_ = threading.Thread(target=self.rx_thread_body)
-    self.rx_thread_.start()
+    def __init__(self, port):
+        self.done_ = False
+        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        s.connect(('localhost', port))
+        self.ins = self.outs = s
+        self.packets_ = queue.Queue()
+        self.rx_thread_ = threading.Thread(target=self.rx_thread_body)
+        self.rx_thread_.start()
 
-  def rx_bytes(self, size):
-    while not self.done_:
-      raw_bytes = b''
-      while len(raw_bytes) < size and not self.done_:
-        more_raw_bytes = self.ins.recv(min(size - len(raw_bytes), 2048))
-        if more_raw_bytes:
-          raw_bytes += more_raw_bytes
-      return raw_bytes
+    def rx_bytes(self, size):
+        while not self.done_:
+            raw_bytes = b''
+            while len(raw_bytes) < size and not self.done_:
+                more_raw_bytes = self.ins.recv(min(size - len(raw_bytes), 2048))
+                if more_raw_bytes:
+                    raw_bytes += more_raw_bytes
+            return raw_bytes
 
-  def rx_thread_body(self):
-    while not self.done_:
-      payload_length = 0
-      # Read the type
-      type_byte = self.rx_bytes(1)
-      if not type_byte:
-        continue
-      # Read the Header
-      header = b''
-      if type_byte == b'\x01':  # Command
-        header = self.rx_bytes(3)
-        if not header:
-          continue
-        payload_length = header[2]
-      elif type_byte == b'\x02':  # ACL
-        header = self.rx_bytes(4)
-        if not header:
-          continue
-        payload_length = header[3] << 8
-        payload_length |= header[2]
-      elif type_byte == b'\x03':  # SCO
-        header = self.rx_bytes(3)
-        if not header:
-          continue
-        payload_length = header[2]
-      elif type_byte == b'\x04':  # Event
-        header = self.rx_bytes(2)
-        if not header:
-          continue
-        payload_length = header[1]
-      else:
+    def rx_thread_body(self):
+        while not self.done_:
+            payload_length = 0
+            # Read the type
+            type_byte = self.rx_bytes(1)
+            if not type_byte:
+                continue
+            # Read the Header
+            header = b''
+            if type_byte == b'\x01':  # Command
+                header = self.rx_bytes(3)
+                if not header:
+                    continue
+                payload_length = header[2]
+            elif type_byte == b'\x02':  # ACL
+                header = self.rx_bytes(4)
+                if not header:
+                    continue
+                payload_length = header[3] << 8
+                payload_length |= header[2]
+            elif type_byte == b'\x03':  # SCO
+                header = self.rx_bytes(3)
+                if not header:
+                    continue
+                payload_length = header[2]
+            elif type_byte == b'\x04':  # Event
+                header = self.rx_bytes(2)
+                if not header:
+                    continue
+                payload_length = header[1]
+            else:
+                self.done_ = True
+                print('Rx: type_byte ' + hex(type_byte[0]))
+            # Read the Payload
+            payload = self.rx_bytes(
+                payload_length) if payload_length != 0 else b''
+            packet_bytes = type_byte + header + payload
+            packet = HCI_Hdr(packet_bytes)
+            print('Rx: ' + packet.__repr__())
+            self.packets_.put(packet)
+
+    def get_packet(self):
+        if self.packets_.empty():
+            return False
+        return self.packets_.get()
+
+    def tell_rx_thread_to_quit(self):
         self.done_ = True
-        print('Rx: type_byte ' + hex(type_byte[0]))
-      # Read the Payload
-      payload = self.rx_bytes(payload_length) if payload_length != 0 else b''
-      packet_bytes = type_byte + header + payload
-      packet = HCI_Hdr(packet_bytes)
-      print('Rx: ' + packet.__repr__())
-      self.packets_.put(packet)
-
-  def get_packet(self):
-    if self.packets_.empty():
-      return False
-    return self.packets_.get()
-
-  def tell_rx_thread_to_quit(self):
-    self.done_ = True
-    self.rx_thread_.join()
+        self.rx_thread_.join()
 
 
 class HCIShell(cmd.Cmd):
-  """Shell for sending binary data to a port.
+    """Shell for sending binary data to a port.
 
   """
 
-  def __init__(self, hci):
-    cmd.Cmd.__init__(self)
-    self._hci = hci
+    def __init__(self, hci):
+        cmd.Cmd.__init__(self)
+        self._hci = hci
 
-  def do_send(self, args):
-    """Arguments: dev_type_str Add a new device of type dev_type_str.
+    def do_send(self, args):
+        """Arguments: dev_type_str Add a new device of type dev_type_str.
 
     """
-    self._hci.send_binary(args.split())
+        self._hci.send_binary(args.split())
 
-  def do_connect(self, args):
-    """Arguments: bluetooth_address xx:xx:xx:xx:xx:xx, timeout (seconds)
+    def do_connect(self, args):
+        """Arguments: bluetooth_address xx:xx:xx:xx:xx:xx, timeout (seconds)
 
     """
-    split_args = args.split()
-    address = split_args[0] if len(split_args) > 0 else 'NULL'
-    timeout = int(split_args[1]) if len(split_args) > 1 else 2
-    num_responses = 0
-    connect = HCI_Hdr(type='Command') / HCI_Command_Hdr(
-        opcode=0x0405) / HCI_Cmd_Create_Connection(addr=address)
-    self._hci.send(connect)
-    status = None
-    while status == None:
-      response = self._hci.get_packet()
-      if response == False:
-        continue
-      if response[HCI_Hdr].type == HCI_Hdr(
-          type='Event'
-      ).type and response[HCI_Event_Hdr].code == 0xf and response[
-          HCI_Event_Command_Status].opcode == connect[HCI_Command_Hdr].opcode:
-        status = response[HCI_Event_Command_Status].status
-    if status != HCI_Event_Command_Status(status='pending').status:
-      print('Connection failed with status = ' + str(status))
-      return
+        split_args = args.split()
+        address = split_args[0] if len(split_args) > 0 else 'NULL'
+        timeout = int(split_args[1]) if len(split_args) > 1 else 2
+        num_responses = 0
+        connect = HCI_Hdr(type='Command') / HCI_Command_Hdr(
+            opcode=0x0405) / HCI_Cmd_Create_Connection(addr=address)
+        self._hci.send(connect)
+        status = None
+        while status == None:
+            response = self._hci.get_packet()
+            if response == False:
+                continue
+            if response[HCI_Hdr].type == HCI_Hdr(
+                    type='Event'
+            ).type and response[HCI_Event_Hdr].code == 0xf and response[HCI_Event_Command_Status].opcode == connect[HCI_Command_Hdr].opcode:
+                status = response[HCI_Event_Command_Status].status
+        if status != HCI_Event_Command_Status(status='pending').status:
+            print('Connection failed with status = ' + str(status))
+            return
 
-    handle = None
-    while handle == None:
-      connection_complete = self._hci.get_packet()
-      if connection_complete == False:
-        continue
-      if (connection_complete[HCI_Hdr].type == HCI_Hdr(type='Event').type) and (
-          connection_complete[HCI_Event_Hdr].code == 0x3):
-        status = connection_complete[HCI_Event_Connection_Complete].status
-        if status != 0:
-          print('Connection complete with failed status = ' + str(status))
-          return
-        handle = connection_complete[HCI_Event_Connection_Complete].handle
-        print('Connection established with handle ' + str(handle))
-        connection_complete.show()
-        hexdump(connection_complete)
+        handle = None
+        while handle == None:
+            connection_complete = self._hci.get_packet()
+            if connection_complete == False:
+                continue
+            if (connection_complete[HCI_Hdr].type == HCI_Hdr(type='Event').type
+               ) and (connection_complete[HCI_Event_Hdr].code == 0x3):
+                status = connection_complete[
+                    HCI_Event_Connection_Complete].status
+                if status != 0:
+                    print('Connection complete with failed status = ' +
+                          str(status))
+                    return
+                handle = connection_complete[
+                    HCI_Event_Connection_Complete].handle
+                print('Connection established with handle ' + str(handle))
+                connection_complete.show()
+                hexdump(connection_complete)
 
-    l2cap_done = False
-    while l2cap_done == None:
-      l2cap_req = self._hci.get_packet()
-      if l2cap_req == False:
-        continue
-      if (l2cap_req[HCI_Hdr].type == HCI_Hdr(type='ACL Data').type) and (
-          l2cap_req[L2CAP_Hdr].cid == L2CAP_Hdr(cid='control').cid) and (
-              l2cap_req[L2CAP_CmdHdr].code == L2CAP_CmdHdr(code='info_req').code
-          ) and (l2cap_req[L2CAP_InfoReq].type == L2CAP_InfoReq(
-              type='FEAT_MASK').type):
-        print('Send Features packet' + HCI_Hdr(type='ACL Data') / HCI_ACL_Hdr(
-            handle=l2cap_req[HCI_ACL_Hdr].handle, PB=0, BC=2, len=16) /
-              L2CAP_Hdr(len=12, cid='control') /
-              L2CAP_CmdHdr(code='info_resp', id=146, len=8) / L2CAP_InfoResp(
-                  type=l2cap_req[L2CAP_InfoResp].type,
-                  result='success',
-                  data=b'\xb8\x00\x00\x00').__repr__())
-        self._hci.send(
-            HCI_Hdr(type='ACL Data') / HCI_ACL_Hdr(
-                handle=l2cap_req[HCI_ACL_Hdr].handle, PB=0, BC=2, len=16) /
-            L2CAP_Hdr(len=12, cid='control') / L2CAP_CmdHdr(
-                code='info_resp', id=146, len=8) / L2CAP_InfoResp(
-                    type=l2cap_req[L2CAP_InfoResp].type,
-                    result='success',
-                    data=b'\xb8\x00\x00\x00'))
+        l2cap_done = False
+        while l2cap_done == None:
+            l2cap_req = self._hci.get_packet()
+            if l2cap_req == False:
+                continue
+            if (l2cap_req[HCI_Hdr].type == HCI_Hdr(type='ACL Data').type) and (
+                    l2cap_req[L2CAP_Hdr].cid == L2CAP_Hdr(cid='control').cid
+            ) and (l2cap_req[L2CAP_CmdHdr].code == L2CAP_CmdHdr(code='info_req')
+                   .code) and (l2cap_req[L2CAP_InfoReq].type == L2CAP_InfoReq(
+                       type='FEAT_MASK').type):
+                print('Send Features packet' + HCI_Hdr(
+                    type='ACL Data'
+                ) / HCI_ACL_Hdr(
+                    handle=l2cap_req[HCI_ACL_Hdr].handle, PB=0, BC=2, len=16) /
+                      L2CAP_Hdr(len=12, cid='control') / L2CAP_CmdHdr(
+                          code='info_resp', id=146, len=8) / L2CAP_InfoResp(
+                              type=l2cap_req[L2CAP_InfoResp].type,
+                              result='success',
+                              data=b'\xb8\x00\x00\x00').__repr__())
+                self._hci.send(
+                    HCI_Hdr(type='ACL Data') / HCI_ACL_Hdr(
+                        handle=l2cap_req[HCI_ACL_Hdr].handle,
+                        PB=0,
+                        BC=2,
+                        len=16) /
+                    L2CAP_Hdr(len=12, cid='control') / L2CAP_CmdHdr(
+                        code='info_resp', id=146, len=8) / L2CAP_InfoResp(
+                            type=l2cap_req[L2CAP_InfoResp].type,
+                            result='success',
+                            data=b'\xb8\x00\x00\x00'))
 
-  def do_le_scan(self, args):
-    """Arguments: enable (0 or 1), filter duplicates (0 or 1) Print the scan responses from reachable devices
+    def do_le_scan(self, args):
+        """Arguments: enable (0 or 1), filter duplicates (0 or 1) Print the scan responses from reachable devices
 
     """
-    split_args = args.split()
-    enable = int(split_args[0]) if len(split_args) > 0 else 1
-    filter_dups = int(split_args[1]) if len(split_args) > 1 else 1
-    set_scan_enable = HCI_Hdr(type=1) / HCI_Command_Hdr(
-        opcode=0x200c) / HCI_Cmd_LE_Set_Scan_Enable(
-            enable=enable, filter_dups=filter_dups)
-    print('Tx: ' + set_scan_enable.__repr__())
-    self._hci.send(set_scan_enable)
+        split_args = args.split()
+        enable = int(split_args[0]) if len(split_args) > 0 else 1
+        filter_dups = int(split_args[1]) if len(split_args) > 1 else 1
+        set_scan_enable = HCI_Hdr(type=1) / HCI_Command_Hdr(
+            opcode=0x200c) / HCI_Cmd_LE_Set_Scan_Enable(
+                enable=enable, filter_dups=filter_dups)
+        print('Tx: ' + set_scan_enable.__repr__())
+        self._hci.send(set_scan_enable)
 
-  def do_scan(self, args):
-    """Arguments: timeout (seconds), max_results Print the scan responses from reachable devices
+    def do_scan(self, args):
+        """Arguments: timeout (seconds), max_results Print the scan responses from reachable devices
 
     """
-    split_args = args.split()
-    scan_time = int(split_args[0]) if len(split_args) > 0 else 0
-    max_responses = int(split_args[1]) if len(split_args) > 1 else 0
-    num_responses = 0
-    inquiry = HCI_Hdr(type='Command') / HCI_Command_Hdr(
-        opcode=0x0401) / HCI_Cmd_Inquiry(
-            length=scan_time, max_responses=max_responses)
-    print('Tx: ' + inquiry.__repr__())
-    self._hci.send(inquiry)
+        split_args = args.split()
+        scan_time = int(split_args[0]) if len(split_args) > 0 else 0
+        max_responses = int(split_args[1]) if len(split_args) > 1 else 0
+        num_responses = 0
+        inquiry = HCI_Hdr(type='Command') / HCI_Command_Hdr(
+            opcode=0x0401) / HCI_Cmd_Inquiry(
+                length=scan_time, max_responses=max_responses)
+        print('Tx: ' + inquiry.__repr__())
+        self._hci.send(inquiry)
 
-  def do_quit(self, args):
-    """Arguments: None.
+    def do_quit(self, args):
+        """Arguments: None.
 
     Exits.
     """
-    self._hci.tell_rx_thread_to_quit()
-    self._hci.close()
-    print('Goodbye.')
-    return True
+        self._hci.tell_rx_thread_to_quit()
+        self._hci.close()
+        print('Goodbye.')
+        return True
 
-  def do_help(self, args):
-    """Arguments: [dev_num [attr]] List the commands available, optionally filtered by device and attr.
+    def do_help(self, args):
+        """Arguments: [dev_num [attr]] List the commands available, optionally filtered by device and attr.
 
     """
-    if (len(args) == 0):
-      cmd.Cmd.do_help(self, args)
+        if (len(args) == 0):
+            cmd.Cmd.do_help(self, args)
 
 
 def main(argv):
-  if len(argv) != 2:
-    print('Usage: python hci_socket.py [port]')
-    return
-  try:
-    port = int(argv[1])
-  except ValueError:
-    print('Error parsing port.')
-  else:
+    if len(argv) != 2:
+        print('Usage: python hci_socket.py [port]')
+        return
     try:
-      hci = HCISocket(port)
-    except socket.error as e:
-      print('Error connecting to socket: %s' % e)
-    except:
-      print('Error creating (check arguments).')
+        port = int(argv[1])
+    except ValueError:
+        print('Error parsing port.')
     else:
-      hci_shell = HCIShell(hci)
-      hci_shell.prompt = '$ '
-      hci_shell.cmdloop('Welcome to the RootCanal HCI Console \n' +
-                        'Type \'help\' for more information.')
+        try:
+            hci = HCISocket(port)
+        except socket.error as e:
+            print('Error connecting to socket: %s' % e)
+        except:
+            print('Error creating (check arguments).')
+        else:
+            hci_shell = HCIShell(hci)
+            hci_shell.prompt = '$ '
+            hci_shell.cmdloop('Welcome to the RootCanal HCI Console \n' +
+                              'Type \'help\' for more information.')
 
 
 if __name__ == '__main__':
-  main(sys.argv)
+    main(sys.argv)
diff --git a/vendor_libs/test_vendor_lib/scripts/link_layer_socket.py b/vendor_libs/test_vendor_lib/scripts/link_layer_socket.py
index c7b3045..e9779cb 100644
--- a/vendor_libs/test_vendor_lib/scripts/link_layer_socket.py
+++ b/vendor_libs/test_vendor_lib/scripts/link_layer_socket.py
@@ -69,124 +69,132 @@
 import struct
 import sys
 
+
 class LinkLayerSocket(object):
-  """Simple wrapper class for a socket object.
+    """Simple wrapper class for a socket object.
 
   Attributes:
     socket: The underlying socket created for the specified address and port.
   """
 
-  def __init__(self, port):
-    print('port = ' + port)
-    self.done_ = False
-    self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-    self._socket.connect(('localhost', port))
-    # Should it be a non-blocking socket?
-    # self._socket.setblocking(0)
-    self.packets_ = queue.Queue()
-    self.rx_thread_ = threading.Thread(target=self.rx_thread_body)
-    self.rx_thread_.start()
+    def __init__(self, port):
+        print('port = ' + port)
+        self.done_ = False
+        self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        self._socket.connect(('localhost', port))
+        # Should it be a non-blocking socket?
+        # self._socket.setblocking(0)
+        self.packets_ = queue.Queue()
+        self.rx_thread_ = threading.Thread(target=self.rx_thread_body)
+        self.rx_thread_.start()
 
-  def rx_bytes(self, size):
-    while not self.done_:
-      raw_bytes = b''
-      while len(raw_bytes) < size and not self.done_:
-        more_raw_bytes = self._socket.recv(min(size - len(raw_bytes), 2048))
-        if more_raw_bytes:
-          raw_bytes += more_raw_bytes
-      return raw_bytes
+    def rx_bytes(self, size):
+        while not self.done_:
+            raw_bytes = b''
+            while len(raw_bytes) < size and not self.done_:
+                more_raw_bytes = self._socket.recv(
+                    min(size - len(raw_bytes), 2048))
+                if more_raw_bytes:
+                    raw_bytes += more_raw_bytes
+            return raw_bytes
 
-  def rx_thread_body(self):
-    while not self.done_:
-      payload_length = 0
-      # Read the size (4B), the type (1B), and the addresses (2*6B)
-      header = self.rx_bytes(17)
-      if not header:
-        continue
-      payload_length = header[0]
-      payload_length |= header[1] << 8
-      payload_length |= header[2] << 16
-      payload_length |= header[3] << 24
-      print('Rx: type_byte ' + hex(header[4]))
-      print('Rx: from ' + hex(header[5]) + ':' + hex(header[6]) + ':' + hex(header[7]) + ':' + hex(header[8]) + ':' + hex(header[9]) + ':' + hex(header[10]))
-      print('Rx: to ' + hex(header[11]) + ':' + hex(header[12]) + ':' + hex(header[13]) + ':' + hex(header[14]) + ':' + hex(header[15]) + ':' + hex(header[16]))
-      # Read the Payload
-      payload = self.rx_bytes(payload_length) if payload_length != 0 else b''
-      packet_bytes = header + payload
-      self.packets_.put(packet_bytes)
+    def rx_thread_body(self):
+        while not self.done_:
+            payload_length = 0
+            # Read the size (4B), the type (1B), and the addresses (2*6B)
+            header = self.rx_bytes(17)
+            if not header:
+                continue
+            payload_length = header[0]
+            payload_length |= header[1] << 8
+            payload_length |= header[2] << 16
+            payload_length |= header[3] << 24
+            print('Rx: type_byte ' + hex(header[4]))
+            print('Rx: from ' + hex(header[5]) + ':' + hex(header[6]) + ':' +
+                  hex(header[7]) + ':' + hex(header[8]) + ':' + hex(header[9]) +
+                  ':' + hex(header[10]))
+            print('Rx: to ' + hex(header[11]) + ':' + hex(header[12]) + ':' +
+                  hex(header[13]) + ':' + hex(header[14]) + ':' +
+                  hex(header[15]) + ':' + hex(header[16]))
+            # Read the Payload
+            payload = self.rx_bytes(
+                payload_length) if payload_length != 0 else b''
+            packet_bytes = header + payload
+            self.packets_.put(packet_bytes)
 
-  def get_packet(self):
-    if self.packets_.empty():
-      return False
-    return self.packets_.get()
+    def get_packet(self):
+        if self.packets_.empty():
+            return False
+        return self.packets_.get()
 
-  def send_binary(self, args):
-    joined_args = ''.join(arg for arg in args)
-    print(joined_args)
-    packet = binascii.a2b_hex(joined_args)
-    if self._done:
-      return
-    self._connection.send(packet)
+    def send_binary(self, args):
+        joined_args = ''.join(arg for arg in args)
+        print(joined_args)
+        packet = binascii.a2b_hex(joined_args)
+        if self._done:
+            return
+        self._connection.send(packet)
 
-  def tell_rx_thread_to_quit(self):
-    self.done_ = True
-    self.rx_thread_.join()
+    def tell_rx_thread_to_quit(self):
+        self.done_ = True
+        self.rx_thread_.join()
 
 
 class LinkLayerShell(cmd.Cmd):
-  """Shell for sending binary data to a port.
+    """Shell for sending binary data to a port.
 
   """
 
-  def __init__(self, link_layer):
-    cmd.Cmd.__init__(self)
-    self._link_layer = link_layer
+    def __init__(self, link_layer):
+        cmd.Cmd.__init__(self)
+        self._link_layer = link_layer
 
-  def do_send(self, args):
-    """Arguments: binary representation of a packet.
+    def do_send(self, args):
+        """Arguments: binary representation of a packet.
 
     """
-    self._link_layer.send_binary(args.split())
+        self._link_layer.send_binary(args.split())
 
-  def do_quit(self, args):
-    """Arguments: None.
+    def do_quit(self, args):
+        """Arguments: None.
 
     Exits.
     """
-    self._link_layer.tell_rx_thread_to_quit()
-    self._link_layer.close()
-    print('Goodbye.')
-    return True
+        self._link_layer.tell_rx_thread_to_quit()
+        self._link_layer.close()
+        print('Goodbye.')
+        return True
 
-  def do_help(self, args):
-    """Arguments: [dev_num [attr]] List the commands available, optionally filtered by device and attr.
+    def do_help(self, args):
+        """Arguments: [dev_num [attr]] List the commands available, optionally filtered by device and attr.
 
     """
-    if (len(args) == 0):
-      cmd.Cmd.do_help(self, args)
+        if (len(args) == 0):
+            cmd.Cmd.do_help(self, args)
 
 
 def main(argv):
-  if len(argv) != 2:
-    print('Usage: python link_layer_socket.py [port]')
-    return
-  try:
-    port = int(argv[1])
-  except ValueError:
-    print('Error parsing port.')
-  else:
+    if len(argv) != 2:
+        print('Usage: python link_layer_socket.py [port]')
+        return
     try:
-      link_layer = LinkLayerSocket(port)
-    except socket.error as e:
-      print('Error connecting to socket: %s' % e)
-    except:
-      print('Error creating (check arguments).')
+        port = int(argv[1])
+    except ValueError:
+        print('Error parsing port.')
     else:
-      link_layer_shell = LinkLayerShell(link_layer)
-      link_layer_shell.prompt = '$ '
-      link_layer_shell.cmdloop('Welcome to the RootCanal LinkLayer Console \n' +
-                        'Type \'help\' for more information.')
+        try:
+            link_layer = LinkLayerSocket(port)
+        except socket.error as e:
+            print('Error connecting to socket: %s' % e)
+        except:
+            print('Error creating (check arguments).')
+        else:
+            link_layer_shell = LinkLayerShell(link_layer)
+            link_layer_shell.prompt = '$ '
+            link_layer_shell.cmdloop(
+                'Welcome to the RootCanal LinkLayer Console \n' +
+                'Type \'help\' for more information.')
 
 
 if __name__ == '__main__':
-  main(sys.argv)
+    main(sys.argv)
diff --git a/vendor_libs/test_vendor_lib/scripts/send_simple_commands.py b/vendor_libs/test_vendor_lib/scripts/send_simple_commands.py
index cadeab3..b7b0670 100644
--- a/vendor_libs/test_vendor_lib/scripts/send_simple_commands.py
+++ b/vendor_libs/test_vendor_lib/scripts/send_simple_commands.py
@@ -75,146 +75,146 @@
 
 # Used to generate fake device names and addresses during discovery.
 def generate_random_name():
-  return ''.join(random.SystemRandom().choice(string.ascii_uppercase + \
-    string.digits) for _ in range(DEVICE_NAME_LENGTH))
+    return ''.join(random.SystemRandom().choice(string.ascii_uppercase + \
+      string.digits) for _ in range(DEVICE_NAME_LENGTH))
 
 
 def generate_random_address():
-  return ''.join(random.SystemRandom().choice(string.digits) for _ in \
-    range(DEVICE_ADDRESS_LENGTH))
+    return ''.join(random.SystemRandom().choice(string.digits) for _ in \
+      range(DEVICE_ADDRESS_LENGTH))
 
 
 class Connection(object):
-  """Simple wrapper class for a socket object.
+    """Simple wrapper class for a socket object.
 
   Attributes:
     socket: The underlying socket created for the specified address and port.
   """
 
-  def __init__(self, port):
-    self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-    self._socket.connect(('localhost', port))
-    self._socket.setblocking(0)
+    def __init__(self, port):
+        self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        self._socket.connect(('localhost', port))
+        self._socket.setblocking(0)
 
-  def close(self):
-    self._socket.close()
+    def close(self):
+        self._socket.close()
 
-  def send(self, data):
-    self._socket.sendall(data)
+    def send(self, data):
+        self._socket.sendall(data)
 
-  def receive(self, size):
-    return self._socket.recv(size)
+    def receive(self, size):
+        return self._socket.recv(size)
 
 
 class RawPort(object):
-  """Checks outgoing commands and sends them once verified.
+    """Checks outgoing commands and sends them once verified.
 
   Attributes:
     connection: The connection to the HCI port.
   """
 
-  def __init__(self, port):
-    self._connection = Connection(port)
-    self._closed = False
+    def __init__(self, port):
+        self._connection = Connection(port)
+        self._closed = False
 
-  def close(self):
-    self._connection.close()
-    self._closed = True
+    def close(self):
+        self._connection.close()
+        self._closed = True
 
-  def send_binary(self, args):
-    joined_args = ''.join(arg for arg in args)
-    print(joined_args)
-    packet = binascii.a2b_hex(joined_args)
-    if self._closed:
-      return
-    self._connection.send(packet)
-    received = self.receive_response()
-    received_bytes = bytearray(received)
-    print(raw(received_bytes))
+    def send_binary(self, args):
+        joined_args = ''.join(arg for arg in args)
+        print(joined_args)
+        packet = binascii.a2b_hex(joined_args)
+        if self._closed:
+            return
+        self._connection.send(packet)
+        received = self.receive_response()
+        received_bytes = bytearray(received)
+        print(raw(received_bytes))
 
-  def receive_response(self):
-    if self._closed:
-      return
-    size_chars = self._connection.receive(4)
-    if not size_chars:
-      print('Debug: No response')
-      return False
-    size_bytes = bytearray(size_chars)
-    response_size = 0
-    for i in range(0, len(size_chars) - 1):
-      response_size |= ord(size_chars[i]) << (8 * i)
-    response = self._connection.receive(response_size)
-    return response
+    def receive_response(self):
+        if self._closed:
+            return
+        size_chars = self._connection.receive(4)
+        if not size_chars:
+            print('Debug: No response')
+            return False
+        size_bytes = bytearray(size_chars)
+        response_size = 0
+        for i in range(0, len(size_chars) - 1):
+            response_size |= ord(size_chars[i]) << (8 * i)
+        response = self._connection.receive(response_size)
+        return response
 
-  def lint_command(self, name, args, name_size, args_size):
-    assert name_size == len(name) and args_size == len(args)
-    try:
-      name.encode('utf-8')
-      for arg in args:
-        arg.encode('utf-8')
-    except UnicodeError:
-      print('Unrecognized characters.')
-      raise
-    if name_size > 255 or args_size > 255:
-      raise ValueError  # Size must be encodable in one octet.
-    for arg in args:
-      if len(arg) > 255:
-        raise ValueError  # Size must be encodable in one octet.
+    def lint_command(self, name, args, name_size, args_size):
+        assert name_size == len(name) and args_size == len(args)
+        try:
+            name.encode('utf-8')
+            for arg in args:
+                arg.encode('utf-8')
+        except UnicodeError:
+            print('Unrecognized characters.')
+            raise
+        if name_size > 255 or args_size > 255:
+            raise ValueError  # Size must be encodable in one octet.
+        for arg in args:
+            if len(arg) > 255:
+                raise ValueError  # Size must be encodable in one octet.
 
 
 class RawPortShell(cmd.Cmd):
-  """Shell for sending binary data to a port.
+    """Shell for sending binary data to a port.
 
   """
 
-  def __init__(self, raw_port):
-    cmd.Cmd.__init__(self)
-    self._raw_port = raw_port
+    def __init__(self, raw_port):
+        cmd.Cmd.__init__(self)
+        self._raw_port = raw_port
 
-  def do_send(self, args):
-    """Arguments: dev_type_str Add a new device of type dev_type_str.
+    def do_send(self, args):
+        """Arguments: dev_type_str Add a new device of type dev_type_str.
 
     """
-    self._raw_port.send_binary(args.split())
+        self._raw_port.send_binary(args.split())
 
-  def do_quit(self, args):
-    """Arguments: None.
+    def do_quit(self, args):
+        """Arguments: None.
 
     Exits.
     """
-    self._raw_port.close()
-    print('Goodbye.')
-    return True
+        self._raw_port.close()
+        print('Goodbye.')
+        return True
 
-  def do_help(self, args):
-    """Arguments: [dev_num [attr]] List the commands available, optionally filtered by device and attr.
+    def do_help(self, args):
+        """Arguments: [dev_num [attr]] List the commands available, optionally filtered by device and attr.
 
     """
-    if (len(args) == 0):
-      cmd.Cmd.do_help(self, args)
+        if (len(args) == 0):
+            cmd.Cmd.do_help(self, args)
 
 
 def main(argv):
-  if len(argv) != 2:
-    print('Usage: python raw_port.py [port]')
-    return
-  try:
-    port = int(argv[1])
-  except ValueError:
-    print('Error parsing port.')
-  else:
+    if len(argv) != 2:
+        print('Usage: python raw_port.py [port]')
+        return
     try:
-      raw_port = RawPort(port)
-    except (socket.error, e):
-      print('Error connecting to socket: %s' % e)
-    except:
-      print('Error creating (check arguments).')
+        port = int(argv[1])
+    except ValueError:
+        print('Error parsing port.')
     else:
-      raw_port_shell = RawPortShell(raw_port)
-      raw_port_shell.prompt = '$ '
-      raw_port_shell.cmdloop('Welcome to the RootCanal Console \n' +
-                             'Type \'help\' for more information.')
+        try:
+            raw_port = RawPort(port)
+        except (socket.error, e):
+            print('Error connecting to socket: %s' % e)
+        except:
+            print('Error creating (check arguments).')
+        else:
+            raw_port_shell = RawPortShell(raw_port)
+            raw_port_shell.prompt = '$ '
+            raw_port_shell.cmdloop('Welcome to the RootCanal Console \n' +
+                                   'Type \'help\' for more information.')
 
 
 if __name__ == '__main__':
-  main(sys.argv)
+    main(sys.argv)
diff --git a/vendor_libs/test_vendor_lib/scripts/simple_link_layer_socket.py b/vendor_libs/test_vendor_lib/scripts/simple_link_layer_socket.py
index 982aee2..89552d9 100644
--- a/vendor_libs/test_vendor_lib/scripts/simple_link_layer_socket.py
+++ b/vendor_libs/test_vendor_lib/scripts/simple_link_layer_socket.py
@@ -61,137 +61,137 @@
 
 # Used to generate fake device names and addresses during discovery.
 def generate_random_name():
-  return ''.join(random.SystemRandom().choice(string.ascii_uppercase + \
-    string.digits) for _ in range(DEVICE_NAME_LENGTH))
+    return ''.join(random.SystemRandom().choice(string.ascii_uppercase + \
+      string.digits) for _ in range(DEVICE_NAME_LENGTH))
 
 
 def generate_random_address():
-  return ''.join(random.SystemRandom().choice(string.digits) for _ in \
-    range(DEVICE_ADDRESS_LENGTH))
+    return ''.join(random.SystemRandom().choice(string.digits) for _ in \
+      range(DEVICE_ADDRESS_LENGTH))
 
 
 class Connection(object):
-  """Simple wrapper class for a socket object.
+    """Simple wrapper class for a socket object.
 
   Attributes:
     socket: The underlying socket created for the specified address and port.
   """
 
-  def __init__(self, port):
-    self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-    self._socket.connect(('localhost', port))
-    self._socket.setblocking(0)
+    def __init__(self, port):
+        self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        self._socket.connect(('localhost', port))
+        self._socket.setblocking(0)
 
-  def close(self):
-    self._socket.close()
+    def close(self):
+        self._socket.close()
 
-  def send(self, data):
-    self._socket.sendall(data)
+    def send(self, data):
+        self._socket.sendall(data)
 
-  def receive(self, size):
-    return self._socket.recv(size)
+    def receive(self, size):
+        return self._socket.recv(size)
 
 
 class RawPort(object):
-  """Checks outgoing commands and sends them once verified.
+    """Checks outgoing commands and sends them once verified.
 
   Attributes:
     connection: The connection to the HCI port.
   """
 
-  def __init__(self, port):
-    self._connection = Connection(port)
-    self._closed = False
+    def __init__(self, port):
+        self._connection = Connection(port)
+        self._closed = False
 
-  def close(self):
-    self._connection.close()
-    self._closed = True
+    def close(self):
+        self._connection.close()
+        self._closed = True
 
-  def send_binary(self, args):
-    joined_args = ''.join(arg for arg in args)
-    print(joined_args)
-    packet = binascii.a2b_hex(joined_args)
-    if self._closed:
-      return
-    self._connection.send(packet)
+    def send_binary(self, args):
+        joined_args = ''.join(arg for arg in args)
+        print(joined_args)
+        packet = binascii.a2b_hex(joined_args)
+        if self._closed:
+            return
+        self._connection.send(packet)
 
-  def receive_response(self):
-    if self._closed:
-      return
-    size_chars = self._connection.receive(4)
-    if not size_chars:
-      print('Debug: No response')
-      return False
-    size_bytes = bytearray(size_chars)
-    response_size = 0
-    for i in range(0, len(size_chars) - 1):
-      response_size |= ord(size_chars[i]) << (8 * i)
-    response = self._connection.receive(response_size)
-    return response
+    def receive_response(self):
+        if self._closed:
+            return
+        size_chars = self._connection.receive(4)
+        if not size_chars:
+            print('Debug: No response')
+            return False
+        size_bytes = bytearray(size_chars)
+        response_size = 0
+        for i in range(0, len(size_chars) - 1):
+            response_size |= ord(size_chars[i]) << (8 * i)
+        response = self._connection.receive(response_size)
+        return response
 
-  def lint_command(self, name, args, name_size, args_size):
-    assert name_size == len(name) and args_size == len(args)
-    try:
-      name.encode('utf-8')
-      for arg in args:
-        arg.encode('utf-8')
-    except UnicodeError:
-      print('Unrecognized characters.')
-      raise
-    if name_size > 255 or args_size > 255:
-      raise ValueError  # Size must be encodable in one octet.
-    for arg in args:
-      if len(arg) > 255:
-        raise ValueError  # Size must be encodable in one octet.
+    def lint_command(self, name, args, name_size, args_size):
+        assert name_size == len(name) and args_size == len(args)
+        try:
+            name.encode('utf-8')
+            for arg in args:
+                arg.encode('utf-8')
+        except UnicodeError:
+            print('Unrecognized characters.')
+            raise
+        if name_size > 255 or args_size > 255:
+            raise ValueError  # Size must be encodable in one octet.
+        for arg in args:
+            if len(arg) > 255:
+                raise ValueError  # Size must be encodable in one octet.
 
 
 class RawPortShell(cmd.Cmd):
-  """Shell for sending binary data to a port."""
+    """Shell for sending binary data to a port."""
 
-  def __init__(self, raw_port):
-    cmd.Cmd.__init__(self)
-    self._raw_port = raw_port
+    def __init__(self, raw_port):
+        cmd.Cmd.__init__(self)
+        self._raw_port = raw_port
 
-  def do_send(self, args):
-    """Arguments: dev_type_str Add a new device of type dev_type_str."""
-    self._raw_port.send_binary(args.split())
+    def do_send(self, args):
+        """Arguments: dev_type_str Add a new device of type dev_type_str."""
+        self._raw_port.send_binary(args.split())
 
-  def do_quit(self, args):
-    """Arguments: None.
+    def do_quit(self, args):
+        """Arguments: None.
 
     Exits.
     """
-    self._raw_port.close()
-    print('Goodbye.')
-    return True
+        self._raw_port.close()
+        print('Goodbye.')
+        return True
 
-  def do_help(self, args):
-    """Arguments: [dev_num [attr]] List the commands available, optionally filtered by device and attr."""
-    if (len(args) == 0):
-      cmd.Cmd.do_help(self, args)
+    def do_help(self, args):
+        """Arguments: [dev_num [attr]] List the commands available, optionally filtered by device and attr."""
+        if (len(args) == 0):
+            cmd.Cmd.do_help(self, args)
 
 
 def main(argv):
-  if len(argv) != 2:
-    print('Usage: python raw_port.py [port]')
-    return
-  try:
-    port = int(argv[1])
-  except ValueError:
-    print('Error parsing port.')
-  else:
+    if len(argv) != 2:
+        print('Usage: python raw_port.py [port]')
+        return
     try:
-      raw_port = RawPort(port)
-    except (socket.error, e):
-      print('Error connecting to socket: %s' % e)
-    except:
-      print('Error creating (check arguments).')
+        port = int(argv[1])
+    except ValueError:
+        print('Error parsing port.')
     else:
-      raw_port_shell = RawPortShell(raw_port)
-      raw_port_shell.prompt = '$ '
-      raw_port_shell.cmdloop('Welcome to the RootCanal Console \n' +
-                             'Type \'help\' for more information.')
+        try:
+            raw_port = RawPort(port)
+        except (socket.error, e):
+            print('Error connecting to socket: %s' % e)
+        except:
+            print('Error creating (check arguments).')
+        else:
+            raw_port_shell = RawPortShell(raw_port)
+            raw_port_shell.prompt = '$ '
+            raw_port_shell.cmdloop('Welcome to the RootCanal Console \n' +
+                                   'Type \'help\' for more information.')
 
 
 if __name__ == '__main__':
-  main(sys.argv)
+    main(sys.argv)
diff --git a/vendor_libs/test_vendor_lib/scripts/simple_stack.py b/vendor_libs/test_vendor_lib/scripts/simple_stack.py
index 4f42bf2..52472e9 100644
--- a/vendor_libs/test_vendor_lib/scripts/simple_stack.py
+++ b/vendor_libs/test_vendor_lib/scripts/simple_stack.py
@@ -72,167 +72,167 @@
 
 
 class HCI_Cmd_Connect(Packet):
-  name = "Connect"
-  fields_desc = [
-      ByteEnumField("filter", 0, {0: "address"}),
-      LEShortField("packet_type", 8),
-      ByteEnumField("page_scan_repetition_mode", 0, {
-          0: "R0",
-          1: "R1",
-          2: "R2"
-      }),
-      ByteEnumField("page_scan_repetition_mode", 0, {0: "Reserved"}),
-      LEShortField("clock_offset", 0),
-      ByteEnumField("allow_role_switch", 0, {
-          0: "false",
-          1: "true"
-      }),
-  ]
+    name = "Connect"
+    fields_desc = [
+        ByteEnumField("filter", 0, {0: "address"}),
+        LEShortField("packet_type", 8),
+        ByteEnumField("page_scan_repetition_mode", 0, {
+            0: "R0",
+            1: "R1",
+            2: "R2"
+        }),
+        ByteEnumField("page_scan_repetition_mode", 0, {0: "Reserved"}),
+        LEShortField("clock_offset", 0),
+        ByteEnumField("allow_role_switch", 0, {
+            0: "false",
+            1: "true"
+        }),
+    ]
 
 
 class HCI_Cmd_Inquiry(Packet):
-  name = "Inquiry"
-  fields_desc = [
-      XByteField("LAP0", 0),
-      XByteField("LAP1", 0x8B),
-      XByteField("LAP2", 0x9E),
-      ByteField("length", 1),
-      ByteField("max_responses", 0),
-  ]
+    name = "Inquiry"
+    fields_desc = [
+        XByteField("LAP0", 0),
+        XByteField("LAP1", 0x8B),
+        XByteField("LAP2", 0x9E),
+        ByteField("length", 1),
+        ByteField("max_responses", 0),
+    ]
 
 
 """ END SCAPY stuff"""
 
 
 class Connection(object):
-  """Simple wrapper class for a socket object.
+    """Simple wrapper class for a socket object.
 
   Attributes:
     socket: The underlying socket created for the specified address and port.
   """
 
-  def __init__(self, port):
-    self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-    self._socket.connect(("localhost", port))
+    def __init__(self, port):
+        self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        self._socket.connect(("localhost", port))
 
-  def close(self):
-    self._socket.close()
+    def close(self):
+        self._socket.close()
 
-  def send(self, data):
-    self._socket.sendall(data)
+    def send(self, data):
+        self._socket.sendall(data)
 
-  def receive(self, size):
-    return self._socket.recv(size)
+    def receive(self, size):
+        return self._socket.recv(size)
 
 
 class RawPort(object):
-  """Converts outgoing packets to binary and sends them.
+    """Converts outgoing packets to binary and sends them.
 
   Attributes:
     connection: The connection to the HCI port.
   """
 
-  def __init__(self, port):
-    self._connection = Connection(port)
+    def __init__(self, port):
+        self._connection = Connection(port)
 
-  def close(self):
-    self._connection.close()
+    def close(self):
+        self._connection.close()
 
-  def send_binary(self, args):
-    joined_args = "".join(arg for arg in args)
-    print(joined_args)
-    packet = binascii.a2b_hex(joined_args)
-    self._connection.send(packet)
+    def send_binary(self, args):
+        joined_args = "".join(arg for arg in args)
+        print(joined_args)
+        packet = binascii.a2b_hex(joined_args)
+        self._connection.send(packet)
 
-  def receive_response(self):
-    ready_to_read, ready_to_write, in_error = \
-               select.select(
-                  [ self._connection._socket ],
-                  [ ],
-                  [ self._connection._socket ],
-                  1.5)
-    if len(in_error) > 0:
-      print("Error")
-      return False
-    if len(ready_to_read) > 0:
-      print("Ready to Read")
-      type_str = self._connection.receive(512)
-      print(len(type_str))
-      print(type_str)
-      return type_str
-    print("Returning false at the end")
-    return False
+    def receive_response(self):
+        ready_to_read, ready_to_write, in_error = \
+                   select(
+                      [ self._connection._socket ],
+                      [ ],
+                      [ self._connection._socket ],
+                      1.5)
+        if len(in_error) > 0:
+            print("Error")
+            return False
+        if len(ready_to_read) > 0:
+            print("Ready to Read")
+            type_str = self._connection.receive(512)
+            print(len(type_str))
+            print(type_str)
+            return type_str
+        print("Returning false at the end")
+        return False
 
 
 class RawPortShell(cmd.Cmd):
-  """Shell for sending binary data to a port.
+    """Shell for sending binary data to a port.
 
   """
 
-  def __init__(self, raw_port):
-    cmd.Cmd.__init__(self)
-    self._raw_port = raw_port
+    def __init__(self, raw_port):
+        cmd.Cmd.__init__(self)
+        self._raw_port = raw_port
 
-  def do_send(self, args):
-    """Arguments: dev_type_str Add a new device of type dev_type_str.
+    def do_send(self, args):
+        """Arguments: dev_type_str Add a new device of type dev_type_str.
 
     """
-    self._raw_port.send_binary(args.split())
+        self._raw_port.send_binary(args.split())
 
-  def do_scan(self, args):
-    """Arguments: timeout (seconds) Print the scan responses from reachable devices
+    def do_scan(self, args):
+        """Arguments: timeout (seconds) Print the scan responses from reachable devices
 
     """
-    self._raw_port.send_binary(args.split())
+        self._raw_port.send_binary(args.split())
 
-  def do_quit(self, args):
-    """Arguments: None.
+    def do_quit(self, args):
+        """Arguments: None.
 
     Exits.
     """
-    self._raw_port.close()
-    print("Goodbye.")
-    return True
+        self._raw_port.close()
+        print("Goodbye.")
+        return True
 
-  def do_help(self, args):
-    """Arguments: [dev_num [attr]] List the commands available, optionally filtered by device and attr.
+    def do_help(self, args):
+        """Arguments: [dev_num [attr]] List the commands available, optionally filtered by device and attr.
 
     """
-    if (len(args) == 0):
-      cmd.Cmd.do_help(self, args)
+        if (len(args) == 0):
+            cmd.Cmd.do_help(self, args)
 
-  def postcmd(self, stop, line):
-    """Called after each command stop : whether we will stop after this command line : the previous input line Return True to stop, False to continue
+    def postcmd(self, stop, line):
+        """Called after each command stop : whether we will stop after this command line : the previous input line Return True to stop, False to continue
 
     """
-    if stop:
-      return True
-    response = self._raw_port.receive_response()
-    print(response)
-    return False
+        if stop:
+            return True
+        response = self._raw_port.receive_response()
+        print(response)
+        return False
 
 
 def main(argv):
-  if len(argv) != 2:
-    print("Usage: python raw_port.py [port]")
-    return
-  try:
-    port = int(argv[1])
-  except ValueError:
-    print("Error parsing port.")
-  else:
+    if len(argv) != 2:
+        print("Usage: python raw_port.py [port]")
+        return
     try:
-      raw_port = RawPort(port)
-    except (socket.error, e):
-      print("Error connecting to socket: %s" % e)
-    except:
-      print("Error creating (check arguments).")
+        port = int(argv[1])
+    except ValueError:
+        print("Error parsing port.")
     else:
-      raw_port_shell = RawPortShell(raw_port)
-      raw_port_shell.prompt = "$ "
-      raw_port_shell.cmdloop("Welcome to the RootCanal Console \n" +
-                             'Type \'help\' for more information.')
+        try:
+            raw_port = RawPort(port)
+        except (socket.error, e):
+            print("Error connecting to socket: %s" % e)
+        except:
+            print("Error creating (check arguments).")
+        else:
+            raw_port_shell = RawPortShell(raw_port)
+            raw_port_shell.prompt = "$ "
+            raw_port_shell.cmdloop("Welcome to the RootCanal Console \n" +
+                                   'Type \'help\' for more information.')
 
 
 if __name__ == "__main__":
-  main(sys.argv)
+    main(sys.argv)
diff --git a/vendor_libs/test_vendor_lib/scripts/test_channel.py b/vendor_libs/test_vendor_lib/scripts/test_channel.py
index 15f5f1f..79b19b1 100644
--- a/vendor_libs/test_vendor_lib/scripts/test_channel.py
+++ b/vendor_libs/test_vendor_lib/scripts/test_channel.py
@@ -49,97 +49,98 @@
 
 # Used to generate fake device names and addresses during discovery.
 def generate_random_name():
-  return ''.join(random.SystemRandom().choice(string.ascii_uppercase + \
-    string.digits) for _ in range(DEVICE_NAME_LENGTH))
+    return ''.join(random.SystemRandom().choice(string.ascii_uppercase + \
+      string.digits) for _ in range(DEVICE_NAME_LENGTH))
 
 
 def generate_random_address():
-  return ''.join(random.SystemRandom().choice(string.digits) for _ in \
-    range(DEVICE_ADDRESS_LENGTH))
+    return ''.join(random.SystemRandom().choice(string.digits) for _ in \
+      range(DEVICE_ADDRESS_LENGTH))
 
 
 class Connection(object):
-  """Simple wrapper class for a socket object.
+    """Simple wrapper class for a socket object.
 
   Attributes:
     socket: The underlying socket created for the specified address and port.
   """
 
-  def __init__(self, port):
-    self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-    self._socket.connect(('localhost', port))
+    def __init__(self, port):
+        self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        self._socket.connect(('localhost', port))
 
-  def close(self):
-    self._socket.close()
+    def close(self):
+        self._socket.close()
 
-  def send(self, data):
-    self._socket.sendall(data)
+    def send(self, data):
+        self._socket.sendall(data)
 
-  def receive(self, size):
-    return self._socket.recv(size)
+    def receive(self, size):
+        return self._socket.recv(size)
 
 
 class TestChannel(object):
-  """Checks outgoing commands and sends them once verified.
+    """Checks outgoing commands and sends them once verified.
 
   Attributes:
     connection: The connection to the test vendor library that commands are sent
       on.
   """
 
-  def __init__(self, port):
-    self._connection = Connection(port)
-    self._closed = False
+    def __init__(self, port):
+        self._connection = Connection(port)
+        self._closed = False
 
-  def close(self):
-    self._connection.close()
-    self._closed = True
+    def close(self):
+        self._connection.close()
+        self._closed = True
 
-  def send_command(self, name, args):
-    name_size = len(name)
-    args_size = len(args)
-    self.lint_command(name, args, name_size, args_size)
-    encoded_name = chr(name_size) + name
-    encoded_args = chr(args_size) + ''.join(chr(len(arg)) + arg for arg in args)
-    command = encoded_name + encoded_args
-    if self._closed:
-      return
-    self._connection.send(command)
-    if name != 'CLOSE_TEST_CHANNEL':
-      print self.receive_response()
+    def send_command(self, name, args):
+        name_size = len(name)
+        args_size = len(args)
+        self.lint_command(name, args, name_size, args_size)
+        encoded_name = chr(name_size) + name
+        encoded_args = chr(args_size) + ''.join(
+            chr(len(arg)) + arg for arg in args)
+        command = encoded_name + encoded_args
+        if self._closed:
+            return
+        self._connection.send(command)
+        if name != 'CLOSE_TEST_CHANNEL':
+            print self.receive_response()
 
-  def receive_response(self):
-    if self._closed:
-      return
-    size_chars = self._connection.receive(4)
-    size_bytes = bytearray(size_chars)
-    if not size_chars:
-      print 'No response, assuming that the connection is broken'
-      return False
-    response_size = 0
-    for i in range(0, len(size_chars) - 1):
-      response_size |= ord(size_chars[i]) << (8 * i)
-    response = self._connection.receive(response_size)
-    return response
+    def receive_response(self):
+        if self._closed:
+            return
+        size_chars = self._connection.receive(4)
+        size_bytes = bytearray(size_chars)
+        if not size_chars:
+            print 'No response, assuming that the connection is broken'
+            return False
+        response_size = 0
+        for i in range(0, len(size_chars) - 1):
+            response_size |= ord(size_chars[i]) << (8 * i)
+        response = self._connection.receive(response_size)
+        return response
 
-  def lint_command(self, name, args, name_size, args_size):
-    assert name_size == len(name) and args_size == len(args)
-    try:
-      name.encode('utf-8')
-      for arg in args:
-        arg.encode('utf-8')
-    except UnicodeError:
-      print 'Unrecognized characters.'
-      raise
-    if name_size > 255 or args_size > 255:
-      raise ValueError  # Size must be encodable in one octet.
-    for arg in args:
-      if len(arg) > 255:
-        raise ValueError  # Size must be encodable in one octet.
+    def lint_command(self, name, args, name_size, args_size):
+        assert name_size == len(name) and args_size == len(args)
+        try:
+            name.encode('utf-8')
+            for arg in args:
+                arg.encode('utf-8')
+        except UnicodeError:
+            print 'Unrecognized characters.'
+            raise
+        if name_size > 255 or args_size > 255:
+            raise ValueError  # Size must be encodable in one octet.
+        for arg in args:
+            if len(arg) > 255:
+                raise ValueError  # Size must be encodable in one octet.
 
 
 class TestChannelShell(cmd.Cmd):
-  """Shell for sending test channel data to controller.
+    """Shell for sending test channel data to controller.
 
   Manages the test channel to the controller and defines a set of commands the
   user can send to the controller as well. These commands are processed parallel
@@ -150,132 +151,138 @@
     test_channel: The communication channel to send data to the controller.
   """
 
-  def __init__(self, test_channel):
-    cmd.Cmd.__init__(self)
-    self._test_channel = test_channel
+    def __init__(self, test_channel):
+        cmd.Cmd.__init__(self)
+        self._test_channel = test_channel
 
-  def do_add(self, args):
-    """Arguments: dev_type_str Add a new device of type dev_type_str.
+    def do_add(self, args):
+        """Arguments: dev_type_str Add a new device of type dev_type_str.
 
     """
-    self._test_channel.send_command('add', args.split())
+        self._test_channel.send_command('add', args.split())
 
-  def do_del(self, args):
-    """Arguments: device index Delete the device with the specified index.
+    def do_del(self, args):
+        """Arguments: device index Delete the device with the specified index.
 
     """
-    self._test_channel.send_command('del', args.split())
+        self._test_channel.send_command('del', args.split())
 
-  def do_add_phy(self, args):
-    """Arguments: dev_type_str Add a new device of type dev_type_str.
+    def do_add_phy(self, args):
+        """Arguments: dev_type_str Add a new device of type dev_type_str.
 
     """
-    self._test_channel.send_command('add_phy', args.split())
+        self._test_channel.send_command('add_phy', args.split())
 
-  def do_del_phy(self, args):
-    """Arguments: phy index Delete the phy with the specified index.
+    def do_del_phy(self, args):
+        """Arguments: phy index Delete the phy with the specified index.
 
     """
-    self._test_channel.send_command('del_phy', args.split())
+        self._test_channel.send_command('del_phy', args.split())
 
-  def do_add_device_to_phy(self, args):
-    """Arguments: device index phy index Add a new device of type dev_type_str.
+    def do_add_device_to_phy(self, args):
+        """Arguments: device index phy index Add a new device of type dev_type_str.
 
     """
-    self._test_channel.send_command('add_device_to_phy', args.split())
+        self._test_channel.send_command('add_device_to_phy', args.split())
 
-  def do_del_device_from_phy(self, args):
-    """Arguments: phy index Delete the phy with the specified index.
+    def do_del_device_from_phy(self, args):
+        """Arguments: phy index Delete the phy with the specified index.
 
     """
-    self._test_channel.send_command('del_device_from_phy', args.split())
+        self._test_channel.send_command('del_device_from_phy', args.split())
 
-  def do_add_remote(self, args):
-    """Arguments: dev_type_str Connect to a remote device at arg1@arg2.
+    def do_add_remote(self, args):
+        """Arguments: dev_type_str Connect to a remote device at arg1@arg2.
 
     """
-    self._test_channel.send_command('add_remote', args.split())
+        self._test_channel.send_command('add_remote', args.split())
 
-  def do_get(self, args):
-    """Arguments: dev_num attr_str Get the value of the attribute attr_str from device dev_num.
+    def do_get(self, args):
+        """Arguments: dev_num attr_str Get the value of the attribute attr_str from device dev_num.
 
     """
-    self._test_channel.send_command('get', args.split())
+        self._test_channel.send_command('get', args.split())
 
-  def do_set(self, args):
-    """Arguments: dev_num attr_str val Set the value of the attribute attr_str from device dev_num equal to val.
+    def do_set(self, args):
+        """Arguments: dev_num attr_str val Set the value of the attribute attr_str from device dev_num equal to val.
 
     """
-    self._test_channel.send_command('set', args.split())
+        self._test_channel.send_command('set', args.split())
 
-  def do_list(self, args):
-    """Arguments: [dev_num [attr]] List the devices from the controller, optionally filtered by device and attr.
+    def do_set_device_address(self, args):
+        """Arguments: dev_num addr Set the address of device dev_num equal to addr.
 
     """
-    self._test_channel.send_command('list', args.split())
+        self._test_channel.send_command('set_device_address', args.split())
 
-  def do_quit(self, args):
-    """Arguments: None.
+    def do_list(self, args):
+        """Arguments: [dev_num [attr]] List the devices from the controller, optionally filtered by device and attr.
+
+    """
+        self._test_channel.send_command('list', args.split())
+
+    def do_quit(self, args):
+        """Arguments: None.
 
     Exits the test channel.
     """
-    self._test_channel.send_command('CLOSE_TEST_CHANNEL', [])
-    self._test_channel.close()
-    print 'Goodbye.'
-    return True
+        self._test_channel.send_command('CLOSE_TEST_CHANNEL', [])
+        self._test_channel.close()
+        print 'Goodbye.'
+        return True
 
-  def do_help(self, args):
-    """Arguments: [dev_num [attr]] List the commands available, optionally filtered by device and attr.
+    def do_help(self, args):
+        """Arguments: [dev_num [attr]] List the commands available, optionally filtered by device and attr.
 
     """
-    if (len(args) == 0):
-      cmd.Cmd.do_help(self, args)
-    else:
-      self._test_channel.send_command('help', args.split())
+        if (len(args) == 0):
+            cmd.Cmd.do_help(self, args)
+        else:
+            self._test_channel.send_command('help', args.split())
 
-  def preloop(self):
-    """Clear out the buffer
+    def preloop(self):
+        """Clear out the buffer
 
     """
-    response = self._test_channel.receive_response()
+        response = self._test_channel.receive_response()
 
-  #def postcmd(self, stop, line):
-  #"""
-  #Called after each command
-  #stop : whether we will stop after this command
-  #line : the previous input line
-  #Return True to stop, False to continue
-  #"""
-  #if stop:
-  #return True
-  #response = self._test_channel.receive_response()
-  #if not response:
-  #return True
-  #print response
-  #return False
+    #def postcmd(self, stop, line):
+    #"""
+    #Called after each command
+    #stop : whether we will stop after this command
+    #line : the previous input line
+    #Return True to stop, False to continue
+    #"""
+    #if stop:
+    #return True
+    #response = self._test_channel.receive_response()
+    #if not response:
+    #return True
+    #print response
+    #return False
 
 
 def main(argv):
-  if len(argv) != 2:
-    print 'Usage: python test_channel.py [port]'
-    return
-  try:
-    port = int(argv[1])
-  except ValueError:
-    print 'Error parsing port.'
-  else:
+    if len(argv) != 2:
+        print 'Usage: python test_channel.py [port]'
+        return
     try:
-      test_channel = TestChannel(port)
-    except socket.error, e:
-      print 'Error connecting to socket: %s' % e
-    except:
-      print 'Error creating test channel (check argument).'
+        port = int(argv[1])
+    except ValueError:
+        print 'Error parsing port.'
     else:
-      test_channel_shell = TestChannelShell(test_channel)
-      test_channel_shell.prompt = '$ '
-      test_channel_shell.cmdloop('Welcome to the RootCanal Console \n' +
-                                 'Type \'help\' for more information.')
+        try:
+            test_channel = TestChannel(port)
+        except socket.error, e:
+            print 'Error connecting to socket: %s' % e
+        except:
+            print 'Error creating test channel (check argument).'
+        else:
+            test_channel_shell = TestChannelShell(test_channel)
+            test_channel_shell.prompt = '$ '
+            test_channel_shell.cmdloop('Welcome to the RootCanal Console \n' +
+                                       'Type \'help\' for more information.')
 
 
 if __name__ == '__main__':
-  main(sys.argv)
+    main(sys.argv)
diff --git a/vendor_libs/test_vendor_lib/test/iterator_test.cc b/vendor_libs/test_vendor_lib/test/iterator_test.cc
index 4a85068..0e7b67c 100644
--- a/vendor_libs/test_vendor_lib/test/iterator_test.cc
+++ b/vendor_libs/test_vendor_lib/test/iterator_test.cc
@@ -46,13 +46,13 @@
 class IteratorTest : public ::testing::Test {
  public:
   IteratorTest() = default;
-  ~IteratorTest() = default;
+  ~IteratorTest() override = default;
 
-  void SetUp() {
+  void SetUp() override {
     packet = TestPacket::make_new_packet(complete_l2cap_packet);
   }
 
-  void TearDown() {
+  void TearDown() override {
     packet.reset();
   }
 
diff --git a/vendor_libs/test_vendor_lib/test/l2cap_sdu_test.cc b/vendor_libs/test_vendor_lib/test/l2cap_sdu_test.cc
index 3a1cd5c..7aa0ec6 100644
--- a/vendor_libs/test_vendor_lib/test/l2cap_sdu_test.cc
+++ b/vendor_libs/test_vendor_lib/test/l2cap_sdu_test.cc
@@ -39,7 +39,7 @@
  public:
   L2capSduTest(){};
 
-  ~L2capSduTest() = default;
+  ~L2capSduTest() override = default;
 
 };  // L2capSduTest
 
diff --git a/vendor_libs/test_vendor_lib/test/link_layer_socket_device_test.cc b/vendor_libs/test_vendor_lib/test/link_layer_socket_device_test.cc
index f24bf0e..5b4ee49 100644
--- a/vendor_libs/test_vendor_lib/test/link_layer_socket_device_test.cc
+++ b/vendor_libs/test_vendor_lib/test/link_layer_socket_device_test.cc
@@ -27,11 +27,8 @@
 #include <sys/types.h>
 #include <unistd.h>
 
-#include "include/link.h"
 #include "model/setup/async_manager.h"
 #include "packets/link_layer/command_view.h"
-#include "packets/link_layer/link_layer_packet_builder.h"
-#include "packets/link_layer/link_layer_packet_view.h"
 
 std::vector<uint8_t> count = {
     0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
@@ -60,11 +57,11 @@
    public:
     MockPhyLayer(const std::function<void(std::shared_ptr<LinkLayerPacketBuilder>)>& on_receive)
         : PhyLayer(Phy::Type::LOW_ENERGY, 0, [](LinkLayerPacketView) {}), on_receive_(on_receive) {}
-    virtual void Send(const std::shared_ptr<LinkLayerPacketBuilder> packet) override {
+    void Send(const std::shared_ptr<LinkLayerPacketBuilder> packet) override {
       on_receive_(packet);
     }
-    virtual void Receive(LinkLayerPacketView) override {}
-    virtual void TimerTick() override {}
+    void Receive(LinkLayerPacketView) override {}
+    void TimerTick() override {}
 
    private:
     std::function<void(std::shared_ptr<LinkLayerPacketBuilder>)> on_receive_;
@@ -185,14 +182,7 @@
 
   LinkLayerPacketView NextPacket() {
     std::shared_ptr<std::vector<uint8_t>> count_shared = std::make_shared<std::vector<uint8_t>>(count);
-    View count_view(count_shared, 0, count_shared->size());
-    PacketView<true> args({count_view});
-    auto builder = CommandBuilder::Create(packet_id_++, args);
-    auto wrapped_command = LinkLayerPacketBuilder::WrapCommand(std::move(builder), source_, dest_);
-    std::shared_ptr<std::vector<uint8_t>> packet_ptr = std::make_shared<std::vector<uint8_t>>();
-    std::back_insert_iterator<std::vector<uint8_t>> it(*packet_ptr);
-    wrapped_command->Serialize(it);
-    LinkLayerPacketView view = LinkLayerPacketView::Create(packet_ptr);
+    LinkLayerPacketView view = LinkLayerPacketView::Create(count_shared);
     return view;
   }
 
diff --git a/vendor_libs/test_vendor_lib/test/packet_builder_test.cc b/vendor_libs/test_vendor_lib/test/packet_builder_test.cc
deleted file mode 100644
index 60e5d95..0000000
--- a/vendor_libs/test_vendor_lib/test/packet_builder_test.cc
+++ /dev/null
@@ -1,244 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "packets/packet_builder.h"
-
-#include <gtest/gtest.h>
-#include <forward_list>
-#include <memory>
-
-using std::vector;
-
-namespace {
-vector<uint8_t> count_all = {
-    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
-    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
-};
-
-vector<uint8_t> count_1 = {
-    0x00,
-    0x01,
-    0x02,
-};
-
-vector<uint8_t> count_2 = {
-    0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
-};
-
-vector<uint8_t> count_3 = {
-    0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
-};
-}  // namespace
-
-namespace test_vendor_lib {
-namespace packets {
-
-template <bool little_endian>
-class EndianBuilder : public PacketBuilder<little_endian> {
- public:
-  EndianBuilder(uint8_t byte, uint16_t two_bytes, uint32_t four_bytes, uint64_t eight_bytes)
-      : byte_(byte), two_bytes_(two_bytes), four_bytes_(four_bytes), eight_bytes_(eight_bytes) {}
-  ~EndianBuilder() = default;
-
-  virtual size_t size() const override {
-    return sizeof(signature_) + sizeof(byte_) + sizeof(two_bytes_) + sizeof(four_bytes_) + sizeof(eight_bytes_);
-  }
-
-  virtual const std::unique_ptr<std::vector<uint8_t>> FinalPacket() {
-    std::unique_ptr<std::vector<uint8_t>> packet = std::make_unique<std::vector<uint8_t>>();
-    packet->reserve(size());
-    std::back_insert_iterator<std::vector<uint8_t>> it(*packet);
-    Serialize(it);
-    return packet;
-  }
-
-  virtual void Serialize(std::back_insert_iterator<std::vector<uint8_t>> it) const override {
-    PacketBuilder<little_endian>::insert(signature_, it);
-    PacketBuilder<little_endian>::insert(byte_, it);
-    PacketBuilder<little_endian>::insert(two_bytes_, it);
-    PacketBuilder<little_endian>::insert(four_bytes_, it);
-    PacketBuilder<little_endian>::insert(eight_bytes_, it);
-  }
-
- private:
-  uint32_t signature_{(little_endian ? 0x03020100 : 0x00010203)};
-  uint8_t byte_;
-  uint16_t two_bytes_;
-  uint32_t four_bytes_;
-  uint64_t eight_bytes_;
-};
-
-class PacketBuilderEndianTest : public ::testing::Test {
- public:
-  PacketBuilderEndianTest() = default;
-  ~PacketBuilderEndianTest() = default;
-};
-
-TEST(PacketBuilderEndianTest, insertTest) {
-  EndianBuilder<true> little(0x04, 0x0605, 0x0a090807, 0x1211100f0e0d0c0b);
-  EndianBuilder<false> big(0x04, 0x0506, 0x0708090a, 0x0b0c0d0e0f101112);
-  ASSERT_EQ(*big.FinalPacket(), *little.FinalPacket());
-}
-
-template <typename T>
-class VectorBuilder : public PacketBuilder<true> {
- public:
-  VectorBuilder(std::vector<uint64_t> vect) {
-    for (uint64_t element : vect) {
-      vect.push_back(static_cast<T>(element));
-    }
-  }
-  ~VectorBuilder() = default;
-
-  virtual size_t size() const override {
-    return vect_.size() * sizeof(T);
-  }
-
-  virtual const std::unique_ptr<std::vector<uint8_t>> FinalPacket() {
-    std::unique_ptr<std::vector<uint8_t>> packet = std::make_unique<std::vector<uint8_t>>();
-    packet->reserve(size());
-    std::back_insert_iterator<std::vector<uint8_t>> it(*packet);
-    Serialize(it);
-    return packet;
-  }
-
-  virtual void Serialize(std::back_insert_iterator<std::vector<uint8_t>> it) const override {
-    PacketBuilder<true>::insert_vector(vect_, it);
-  }
-
- private:
-  std::vector<T> vect_;
-};
-
-template <typename T>
-class InsertElementsBuilder : public PacketBuilder<true> {
- public:
-  InsertElementsBuilder(std::vector<uint64_t> vect) {
-    for (uint64_t element : vect) {
-      vect.push_back(static_cast<T>(element));
-    }
-  }
-  virtual ~InsertElementsBuilder() = default;
-
-  virtual size_t size() const override {
-    return vect_.size() * sizeof(T);
-  }
-
-  virtual const std::unique_ptr<std::vector<uint8_t>> FinalPacket() {
-    std::unique_ptr<std::vector<uint8_t>> packet = std::make_unique<std::vector<uint8_t>>();
-    packet->reserve(size());
-    std::back_insert_iterator<std::vector<uint8_t>> it(*packet);
-    Serialize(it);
-    return packet;
-  }
-
-  virtual void Serialize(std::back_insert_iterator<std::vector<uint8_t>> it) const override {
-    for (T elem : vect_) {
-      PacketBuilder<true>::insert(elem, it);
-    }
-  }
-
- private:
-  std::vector<T> vect_;
-};
-
-std::vector<uint64_t> vector_data{
-    0x7060504030201000, 0x7161514131211101, 0x7262524232221202, 0x7363534333231303, 0x7464544434241404,
-    0x7565554535251505, 0x7666564636261606, 0x7767574737271707, 0x7868584838281808,
-};
-
-template <typename T>
-class VectorBuilderTest : public ::testing::Test {
- public:
-  VectorBuilderTest() = default;
-  ~VectorBuilderTest() = default;
-
-  void SetUp() {
-    packet_1_ = std::shared_ptr<VectorBuilder<T>>(new VectorBuilder<T>(vector_data));
-    packet_2_ = std::shared_ptr<InsertElementsBuilder<T>>(new InsertElementsBuilder<T>(vector_data));
-  }
-
-  void TearDown() {
-    packet_1_.reset();
-    packet_2_.reset();
-  }
-
-  std::shared_ptr<VectorBuilder<T>> packet_1_;
-  std::shared_ptr<InsertElementsBuilder<T>> packet_2_;
-};
-
-using VectorBaseTypes = ::testing::Types<uint8_t, uint16_t, uint32_t, uint64_t, int8_t, int16_t, int32_t, int64_t>;
-TYPED_TEST_CASE(VectorBuilderTest, VectorBaseTypes);
-
-TYPED_TEST(VectorBuilderTest, insertVectorTest) {
-  ASSERT_EQ(*(this->packet_1_->FinalPacket()), *(this->packet_2_->FinalPacket()));
-}
-
-class NestedBuilder : public PacketBuilder<true> {
- public:
-  ~NestedBuilder() = default;
-
-  virtual size_t size() const override {
-    size_t payload_size = (payload_ ? payload_->size() : 0);
-    return 1 + payload_size;
-  }
-
-  static std::unique_ptr<NestedBuilder> Create(uint8_t level) {
-    return std::unique_ptr<NestedBuilder>(new NestedBuilder(level));
-  }
-
-  static std::unique_ptr<NestedBuilder> CreateNested(std::unique_ptr<BasePacketBuilder> payload, uint8_t level) {
-    return std::unique_ptr<NestedBuilder>(new NestedBuilder(std::move(payload), level));
-  }
-
-  virtual const std::unique_ptr<std::vector<uint8_t>> FinalPacket() {
-    std::unique_ptr<std::vector<uint8_t>> packet = std::make_unique<std::vector<uint8_t>>();
-    packet->reserve(size());
-    std::back_insert_iterator<std::vector<uint8_t>> it(*packet);
-    Serialize(it);
-    return packet;
-  }
-
-  virtual void Serialize(std::back_insert_iterator<std::vector<uint8_t>> it) const override {
-    PacketBuilder<true>::insert(level_, it);
-    if (payload_) {
-      payload_->Serialize(it);
-    }
-  }
-
- private:
-  std::unique_ptr<BasePacketBuilder> payload_;
-  uint8_t level_;
-
-  NestedBuilder(std::unique_ptr<BasePacketBuilder> inner, uint8_t level) : payload_(std::move(inner)), level_(level) {}
-  NestedBuilder(uint8_t level) : level_(level) {}
-};
-
-class BuilderBuilderTest : public ::testing::Test {};
-
-TEST(BuilderBuilderTest, nestingTest) {
-  std::unique_ptr<BasePacketBuilder> innermost = NestedBuilder::Create(0);
-  std::unique_ptr<BasePacketBuilder> number_1 = NestedBuilder::CreateNested(std::move(innermost), 1);
-  std::unique_ptr<BasePacketBuilder> number_2 = NestedBuilder::CreateNested(std::move(number_1), 2);
-  std::unique_ptr<BasePacketBuilder> number_3 = NestedBuilder::CreateNested(std::move(number_2), 3);
-  std::unique_ptr<BasePacketBuilder> number_4 = NestedBuilder::CreateNested(std::move(number_3), 4);
-  std::unique_ptr<NestedBuilder> number_5 = NestedBuilder::CreateNested(std::move(number_4), 5);
-
-  std::vector<uint8_t> count_down{5, 4, 3, 2, 1, 0};
-  ASSERT_EQ(*number_5->FinalPacket(), count_down);
-}
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/test/packet_stream_unittest.cc b/vendor_libs/test_vendor_lib/test/packet_stream_unittest.cc
index 375c090..c67c95e 100644
--- a/vendor_libs/test_vendor_lib/test/packet_stream_unittest.cc
+++ b/vendor_libs/test_vendor_lib/test/packet_stream_unittest.cc
@@ -48,7 +48,7 @@
     CheckSocketpairInit();
   }
 
-  ~PacketStreamTest() {
+  ~PacketStreamTest() override {
     close(socketpair_fds_[0]);
     close(socketpair_fds_[1]);
   }
@@ -83,33 +83,6 @@
     for (int i = 0; i < payload_size; ++i) EXPECT_EQ(packet[4 + i], received_payload[i + 1]);
   }
 
-  void CheckedSendEvent(std::unique_ptr<EventPacket> event) {
-    const vector<uint8_t> expected_payload = event->GetPayload();
-    auto expected_size = event->GetPacketSize();
-    auto expected_code = event->GetEventCode();
-    auto expected_payload_size = event->GetPayloadSize();
-
-    EXPECT_TRUE(packet_stream_.SendEvent(std::move(event), socketpair_fds_[0]));
-
-    // Read the packet sent by |packet_stream_|.
-    uint8_t event_header[2];
-    read(socketpair_fds_[1], event_header, 2);
-
-    uint8_t return_parameters_size;
-    read(socketpair_fds_[1], &return_parameters_size, 1);
-
-    uint8_t return_parameters[return_parameters_size];
-    read(socketpair_fds_[1], return_parameters, sizeof(return_parameters));
-
-    // Validate the packet by checking that it's the
-    // appropriate size and then checking each byte.
-    EXPECT_EQ(expected_size, sizeof(event_header) + return_parameters_size + 1);
-    EXPECT_EQ(DATA_TYPE_EVENT, event_header[0]);
-    EXPECT_EQ(expected_code, event_header[1]);
-    EXPECT_EQ(expected_payload_size, static_cast<size_t>(return_parameters_size) + 1);
-    for (int i = 0; i < return_parameters_size; ++i) EXPECT_EQ(expected_payload[i + 1], return_parameters[i]);
-  }
-
  protected:
   PacketStream packet_stream_;
 
@@ -141,9 +114,4 @@
   CheckedReceiveCommand(large_payload, HCI_RESET);
 }
 
-TEST_F(PacketStreamTest, SendEvent) {
-  const vector<uint8_t> return_parameters = {0};
-  CheckedSendEvent(EventPacket::CreateCommandCompleteEvent(HCI_RESET, return_parameters));
-}
-
 }  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/test/security_manager_unittest.cc b/vendor_libs/test_vendor_lib/test/security_manager_unittest.cc
index 8fa3a03..9c19c4f 100644
--- a/vendor_libs/test_vendor_lib/test/security_manager_unittest.cc
+++ b/vendor_libs/test_vendor_lib/test/security_manager_unittest.cc
@@ -16,22 +16,20 @@
  *
  ******************************************************************************/
 
-#include <gtest/gtest.h>
-#include <string>
-#include <vector>
-using std::vector;
-
 #include "model/controller/security_manager.h"
 
+#include <gtest/gtest.h>
+#include <array>
+#include <string>
+
 namespace {
 const std::string kTestAddr1 = "12:34:56:78:9a:bc";
 const std::string kTestAddr2 = "cb:a9:87:65:43:21";
 const std::string kTestAddr3 = "cb:a9:56:78:9a:bc";
 const std::string kTestAddr4 = "12:34:56:78:9a:bc";
-const vector<uint8_t> kZeros_octets = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
-const vector<uint8_t> kTestAddr1_octets = {0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12};
-const vector<uint8_t> kTestKey = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
-                                  0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10};
+const std::array<uint8_t, 16> kTestKey = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+                                          0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
+                                          0x0d, 0x0e, 0x0f, 0x10};
 }  // namespace
 
 namespace test_vendor_lib {
@@ -39,7 +37,7 @@
 class SecurityManagerTest : public ::testing::Test {
  public:
   SecurityManagerTest() {}
-  ~SecurityManagerTest() {}
+  ~SecurityManagerTest() override {}
 };
 
 TEST_F(SecurityManagerTest, WriteKey) {
diff --git a/vendor_libs/test_vendor_lib/types/Android.bp b/vendor_libs/test_vendor_lib/types/Android.bp
index 07198e9..7e1193c 100644
--- a/vendor_libs/test_vendor_lib/types/Android.bp
+++ b/vendor_libs/test_vendor_lib/types/Android.bp
@@ -16,8 +16,6 @@
     ],
     host_supported: true,
     srcs: [
-        "class_of_device.cc",
-        "address.cc",
         "bluetooth/uuid.cc",
     ],
     header_libs: ["libbt-rootcanal-types-header"],
@@ -31,8 +29,6 @@
     defaults: ["fluoride_defaults"],
     host_supported: true,
     srcs: [
-        "test/class_of_device_unittest.cc",
-        "test/address_unittest.cc",
         "test/bluetooth/uuid_unittest.cc",
     ],
     static_libs: [
diff --git a/vendor_libs/test_vendor_lib/types/address.cc b/vendor_libs/test_vendor_lib/types/address.cc
deleted file mode 100644
index b3c1db4..0000000
--- a/vendor_libs/test_vendor_lib/types/address.cc
+++ /dev/null
@@ -1,71 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2017 The Android Open Source Project
- *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at:
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- *
- ******************************************************************************/
-
-#include "address.h"
-
-#include <base/strings/string_split.h>
-#include <base/strings/stringprintf.h>
-#include <stdint.h>
-#include <algorithm>
-#include <vector>
-
-static_assert(sizeof(Address) == 6, "Address must be 6 bytes long!");
-
-const Address Address::kAny{{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}};
-const Address Address::kEmpty{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
-
-Address::Address(const uint8_t (&addr)[6]) {
-  std::copy(addr, addr + kLength, address);
-};
-
-std::string Address::ToString() const {
-  return base::StringPrintf("%02x:%02x:%02x:%02x:%02x:%02x", address[5], address[4], address[3], address[2], address[1],
-                            address[0]);
-}
-
-bool Address::FromString(const std::string& from, Address& to) {
-  Address new_addr;
-  if (from.length() != 17) return false;
-
-  std::vector<std::string> byte_tokens = base::SplitString(from, ":", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
-
-  if (byte_tokens.size() != 6) return false;
-
-  for (int i = 0; i < 6; i++) {
-    const auto& token = byte_tokens[i];
-
-    if (token.length() != 2) return false;
-
-    char* temp = nullptr;
-    new_addr.address[5 - i] = strtol(token.c_str(), &temp, 16);
-    if (*temp != '\0') return false;
-  }
-
-  to = new_addr;
-  return true;
-}
-
-size_t Address::FromOctets(const uint8_t* from) {
-  std::copy(from, from + kLength, address);
-  return kLength;
-};
-
-bool Address::IsValidAddress(const std::string& address) {
-  Address tmp;
-  return Address::FromString(address, tmp);
-}
diff --git a/vendor_libs/test_vendor_lib/types/address.h b/vendor_libs/test_vendor_lib/types/address.h
deleted file mode 100644
index 1aa8c97..0000000
--- a/vendor_libs/test_vendor_lib/types/address.h
+++ /dev/null
@@ -1,76 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2017 The Android Open Source Project
- *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at:
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- *
- ******************************************************************************/
-
-#pragma once
-
-#include <string>
-
-/** Bluetooth Address */
-class Address final {
- public:
-  static constexpr unsigned int kLength = 6;
-
-  uint8_t address[kLength];
-
-  Address() = default;
-  Address(const uint8_t (&addr)[6]);
-
-  bool operator<(const Address& rhs) const {
-    return (std::memcmp(address, rhs.address, sizeof(address)) < 0);
-  }
-  bool operator==(const Address& rhs) const {
-    return (std::memcmp(address, rhs.address, sizeof(address)) == 0);
-  }
-  bool operator>(const Address& rhs) const {
-    return (rhs < *this);
-  }
-  bool operator<=(const Address& rhs) const {
-    return !(*this > rhs);
-  }
-  bool operator>=(const Address& rhs) const {
-    return !(*this < rhs);
-  }
-  bool operator!=(const Address& rhs) const {
-    return !(*this == rhs);
-  }
-
-  bool IsEmpty() const {
-    return *this == kEmpty;
-  }
-
-  std::string ToString() const;
-
-  // Converts |string| to Address and places it in |to|. If |from| does
-  // not represent a Bluetooth address, |to| is not modified and this function
-  // returns false. Otherwise, it returns true.
-  static bool FromString(const std::string& from, Address& to);
-
-  // Copies |from| raw Bluetooth address octets to the local object.
-  // Returns the number of copied octets - should be always Address::kLength
-  size_t FromOctets(const uint8_t* from);
-
-  static bool IsValidAddress(const std::string& address);
-
-  static const Address kEmpty;  // 00:00:00:00:00:00
-  static const Address kAny;    // FF:FF:FF:FF:FF:FF
-};
-
-inline std::ostream& operator<<(std::ostream& os, const Address& a) {
-  os << a.ToString();
-  return os;
-}
diff --git a/vendor_libs/test_vendor_lib/types/class_of_device.cc b/vendor_libs/test_vendor_lib/types/class_of_device.cc
deleted file mode 100644
index c5f1c3f..0000000
--- a/vendor_libs/test_vendor_lib/types/class_of_device.cc
+++ /dev/null
@@ -1,75 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2018 The Android Open Source Project
- *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at:
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- *
- ******************************************************************************/
-
-#include "class_of_device.h"
-
-#include <base/strings/string_split.h>
-#include <base/strings/stringprintf.h>
-#include <stdint.h>
-#include <algorithm>
-#include <vector>
-
-static_assert(sizeof(ClassOfDevice) == ClassOfDevice::kLength, "ClassOfDevice must be 3 bytes long!");
-
-ClassOfDevice::ClassOfDevice(const uint8_t (&class_of_device)[kLength]) {
-  std::copy(class_of_device, class_of_device + kLength, cod);
-};
-
-std::string ClassOfDevice::ToString() const {
-  return base::StringPrintf("%03x-%01x-%02x", (static_cast<uint16_t>(cod[2]) << 4) | cod[1] >> 4, cod[1] & 0x0f,
-                            cod[0]);
-}
-
-bool ClassOfDevice::FromString(const std::string& from, ClassOfDevice& to) {
-  ClassOfDevice new_cod;
-  if (from.length() != 8) return false;
-
-  std::vector<std::string> byte_tokens = base::SplitString(from, "-", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
-
-  if (byte_tokens.size() != 3) return false;
-  if (byte_tokens[0].length() != 3) return false;
-  if (byte_tokens[1].length() != 1) return false;
-  if (byte_tokens[2].length() != 2) return false;
-
-  uint16_t values[3];
-
-  for (size_t i = 0; i < kLength; i++) {
-    const auto& token = byte_tokens[i];
-
-    char* temp = nullptr;
-    values[i] = strtol(token.c_str(), &temp, 16);
-    if (*temp != '\0') return false;
-  }
-
-  new_cod.cod[0] = values[2];
-  new_cod.cod[1] = values[1] | ((values[0] & 0xf) << 4);
-  new_cod.cod[2] = values[0] >> 4;
-
-  to = new_cod;
-  return true;
-}
-
-size_t ClassOfDevice::FromOctets(const uint8_t* from) {
-  std::copy(from, from + kLength, cod);
-  return kLength;
-};
-
-bool ClassOfDevice::IsValid(const std::string& cod) {
-  ClassOfDevice tmp;
-  return ClassOfDevice::FromString(cod, tmp);
-}
diff --git a/vendor_libs/test_vendor_lib/types/class_of_device.h b/vendor_libs/test_vendor_lib/types/class_of_device.h
deleted file mode 100644
index 8c2ab37..0000000
--- a/vendor_libs/test_vendor_lib/types/class_of_device.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2018 The Android Open Source Project
- *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at:
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- *
- ******************************************************************************/
-
-#pragma once
-
-#include <string>
-
-/** Bluetooth Class of Device */
-class ClassOfDevice final {
- public:
-  static constexpr unsigned int kLength = 3;
-
-  uint8_t cod[kLength];
-
-  ClassOfDevice() = default;
-  ClassOfDevice(const uint8_t (&class_of_device)[kLength]);
-
-  bool operator==(const ClassOfDevice& rhs) const {
-    return (std::memcmp(cod, rhs.cod, sizeof(cod)) == 0);
-  }
-
-  std::string ToString() const;
-
-  // Converts |string| to ClassOfDevice and places it in |to|. If |from| does
-  // not represent a Class of Device, |to| is not modified and this function
-  // returns false. Otherwise, it returns true.
-  static bool FromString(const std::string& from, ClassOfDevice& to);
-
-  // Copies |from| raw Class of Device octets to the local object.
-  // Returns the number of copied octets (always ClassOfDevice::kLength)
-  size_t FromOctets(const uint8_t* from);
-
-  static bool IsValid(const std::string& class_of_device);
-};
-
-inline std::ostream& operator<<(std::ostream& os, const ClassOfDevice& c) {
-  os << c.ToString();
-  return os;
-}
diff --git a/vendor_libs/test_vendor_lib/types/test/address_unittest.cc b/vendor_libs/test_vendor_lib/types/test/address_unittest.cc
deleted file mode 100644
index 65fd26d..0000000
--- a/vendor_libs/test_vendor_lib/types/test/address_unittest.cc
+++ /dev/null
@@ -1,197 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2017 The Android Open Source Project
- *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at:
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- *
- ******************************************************************************/
-
-#include <gtest/gtest.h>
-
-#include "address.h"
-
-static const char* test_addr = "bc:9a:78:56:34:12";
-static const char* test_addr2 = "21:43:65:87:a9:cb";
-
-TEST(AddressUnittest, test_constructor_array) {
-  Address bdaddr({0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc});
-
-  ASSERT_EQ(0x12, bdaddr.address[0]);
-  ASSERT_EQ(0x34, bdaddr.address[1]);
-  ASSERT_EQ(0x56, bdaddr.address[2]);
-  ASSERT_EQ(0x78, bdaddr.address[3]);
-  ASSERT_EQ(0x9A, bdaddr.address[4]);
-  ASSERT_EQ(0xBC, bdaddr.address[5]);
-
-  std::string ret = bdaddr.ToString();
-
-  ASSERT_STREQ(test_addr, ret.c_str());
-}
-
-TEST(AddressUnittest, test_is_empty) {
-  Address empty;
-  Address::FromString("00:00:00:00:00:00", empty);
-  ASSERT_TRUE(empty.IsEmpty());
-
-  Address not_empty;
-  Address::FromString("00:00:00:00:00:01", not_empty);
-  ASSERT_FALSE(not_empty.IsEmpty());
-}
-
-TEST(AddressUnittest, test_to_from_str) {
-  Address bdaddr;
-  Address::FromString(test_addr, bdaddr);
-
-  ASSERT_EQ(0x12, bdaddr.address[0]);
-  ASSERT_EQ(0x34, bdaddr.address[1]);
-  ASSERT_EQ(0x56, bdaddr.address[2]);
-  ASSERT_EQ(0x78, bdaddr.address[3]);
-  ASSERT_EQ(0x9A, bdaddr.address[4]);
-  ASSERT_EQ(0xBC, bdaddr.address[5]);
-
-  std::string ret = bdaddr.ToString();
-
-  ASSERT_STREQ(test_addr, ret.c_str());
-}
-
-TEST(AddressUnittest, test_from_octets) {
-  static const uint8_t test_addr_array[] = {0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc};
-
-  Address bdaddr;
-  size_t expected_result = Address::kLength;
-  ASSERT_EQ(expected_result, bdaddr.FromOctets(test_addr_array));
-
-  ASSERT_EQ(0x12, bdaddr.address[0]);
-  ASSERT_EQ(0x34, bdaddr.address[1]);
-  ASSERT_EQ(0x56, bdaddr.address[2]);
-  ASSERT_EQ(0x78, bdaddr.address[3]);
-  ASSERT_EQ(0x9A, bdaddr.address[4]);
-  ASSERT_EQ(0xBC, bdaddr.address[5]);
-
-  std::string ret = bdaddr.ToString();
-
-  ASSERT_STREQ(test_addr, ret.c_str());
-}
-
-TEST(AddressTest, test_equals) {
-  Address bdaddr1;
-  Address bdaddr2;
-  Address bdaddr3;
-  Address::FromString(test_addr, bdaddr1);
-  Address::FromString(test_addr, bdaddr2);
-  EXPECT_TRUE(bdaddr1 == bdaddr2);
-  EXPECT_FALSE(bdaddr1 != bdaddr2);
-  EXPECT_TRUE(bdaddr1 == bdaddr1);
-  EXPECT_FALSE(bdaddr1 != bdaddr1);
-
-  Address::FromString(test_addr2, bdaddr3);
-  EXPECT_FALSE(bdaddr2 == bdaddr3);
-  EXPECT_TRUE(bdaddr2 != bdaddr3);
-}
-
-TEST(AddressTest, test_less_than) {
-  Address bdaddr1;
-  Address bdaddr2;
-  Address bdaddr3;
-  Address::FromString(test_addr, bdaddr1);
-  Address::FromString(test_addr, bdaddr2);
-  EXPECT_FALSE(bdaddr1 < bdaddr2);
-  EXPECT_FALSE(bdaddr1 < bdaddr1);
-
-  Address::FromString(test_addr2, bdaddr3);
-  EXPECT_TRUE(bdaddr2 < bdaddr3);
-  EXPECT_FALSE(bdaddr3 < bdaddr2);
-}
-
-TEST(AddressTest, test_more_than) {
-  Address bdaddr1;
-  Address bdaddr2;
-  Address bdaddr3;
-  Address::FromString(test_addr, bdaddr1);
-  Address::FromString(test_addr, bdaddr2);
-  EXPECT_FALSE(bdaddr1 > bdaddr2);
-  EXPECT_FALSE(bdaddr1 > bdaddr1);
-
-  Address::FromString(test_addr2, bdaddr3);
-  EXPECT_FALSE(bdaddr2 > bdaddr3);
-  EXPECT_TRUE(bdaddr3 > bdaddr2);
-}
-
-TEST(AddressTest, test_less_than_or_equal) {
-  Address bdaddr1;
-  Address bdaddr2;
-  Address bdaddr3;
-  Address::FromString(test_addr, bdaddr1);
-  Address::FromString(test_addr, bdaddr2);
-  EXPECT_TRUE(bdaddr1 <= bdaddr2);
-  EXPECT_TRUE(bdaddr1 <= bdaddr1);
-
-  Address::FromString(test_addr2, bdaddr3);
-  EXPECT_TRUE(bdaddr2 <= bdaddr3);
-  EXPECT_FALSE(bdaddr3 <= bdaddr2);
-}
-
-TEST(AddressTest, test_more_than_or_equal) {
-  Address bdaddr1;
-  Address bdaddr2;
-  Address bdaddr3;
-  Address::FromString(test_addr, bdaddr1);
-  Address::FromString(test_addr, bdaddr2);
-  EXPECT_TRUE(bdaddr1 >= bdaddr2);
-  EXPECT_TRUE(bdaddr1 >= bdaddr1);
-
-  Address::FromString(test_addr2, bdaddr3);
-  EXPECT_FALSE(bdaddr2 >= bdaddr3);
-  EXPECT_TRUE(bdaddr3 >= bdaddr2);
-}
-
-TEST(AddressTest, test_copy) {
-  Address bdaddr1;
-  Address bdaddr2;
-  Address::FromString(test_addr, bdaddr1);
-  bdaddr2 = bdaddr1;
-
-  EXPECT_TRUE(bdaddr1 == bdaddr2);
-}
-
-TEST(AddressTest, IsValidAddress) {
-  EXPECT_FALSE(Address::IsValidAddress(""));
-  EXPECT_FALSE(Address::IsValidAddress("000000000000"));
-  EXPECT_FALSE(Address::IsValidAddress("00:00:00:00:0000"));
-  EXPECT_FALSE(Address::IsValidAddress("00:00:00:00:00:0"));
-  EXPECT_FALSE(Address::IsValidAddress("00:00:00:00:00:0;"));
-  EXPECT_TRUE(Address::IsValidAddress("00:00:00:00:00:00"));
-  EXPECT_TRUE(Address::IsValidAddress("AB:cd:00:00:00:00"));
-  EXPECT_FALSE(Address::IsValidAddress("aB:cD:eF:Gh:iJ:Kl"));
-}
-
-TEST(AddressTest, BdAddrFromString) {
-  Address addr;
-  memset(&addr, 0, sizeof(addr));
-
-  EXPECT_TRUE(Address::FromString("00:00:00:00:00:00", addr));
-  const Address result0 = {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
-  EXPECT_EQ(0, memcmp(&addr, &result0, sizeof(addr)));
-
-  EXPECT_TRUE(Address::FromString("ab:01:4C:d5:21:9f", addr));
-  const Address result1 = {{0x9f, 0x21, 0xd5, 0x4c, 0x01, 0xab}};
-  EXPECT_EQ(0, memcmp(&addr, &result1, sizeof(addr)));
-}
-
-TEST(AddressTest, BdAddrFromStringToStringEquivalent) {
-  std::string address = "c1:c2:c3:d1:d2:d3";
-  Address addr;
-
-  EXPECT_TRUE(Address::FromString(address, addr));
-  EXPECT_EQ(addr.ToString(), address);
-}
diff --git a/vendor_libs/test_vendor_lib/types/test/class_of_device_unittest.cc b/vendor_libs/test_vendor_lib/types/test/class_of_device_unittest.cc
deleted file mode 100644
index 5d652a5..0000000
--- a/vendor_libs/test_vendor_lib/types/test/class_of_device_unittest.cc
+++ /dev/null
@@ -1,96 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2018 The Android Open Source Project
- *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at:
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- *
- ******************************************************************************/
-
-#include <gtest/gtest.h>
-
-#include "class_of_device.h"
-
-static const char* test_class = "efc-d-ab";
-static const uint8_t test_bytes[]{0xab, 0xcd, 0xef};
-
-TEST(ClassOfDeviceUnittest, test_constructor_array) {
-  ClassOfDevice cod(test_bytes);
-
-  ASSERT_EQ(test_bytes[0], cod.cod[0]);
-  ASSERT_EQ(test_bytes[1], cod.cod[1]);
-  ASSERT_EQ(test_bytes[2], cod.cod[2]);
-
-  std::string ret = cod.ToString();
-
-  ASSERT_STREQ(test_class, ret.c_str());
-}
-
-TEST(ClassOfDeviceUnittest, test_to_from_str) {
-  ClassOfDevice cod;
-  ClassOfDevice::FromString(test_class, cod);
-
-  ASSERT_EQ(test_bytes[0], cod.cod[0]);
-  ASSERT_EQ(test_bytes[1], cod.cod[1]);
-  ASSERT_EQ(test_bytes[2], cod.cod[2]);
-
-  std::string ret = cod.ToString();
-
-  ASSERT_STREQ(test_class, ret.c_str());
-}
-
-TEST(ClassOfDeviceUnittest, test_from_octets) {
-  ClassOfDevice cod;
-  size_t expected_result = ClassOfDevice::kLength;
-  ASSERT_EQ(expected_result, cod.FromOctets(test_bytes));
-
-  ASSERT_EQ(test_bytes[0], cod.cod[0]);
-  ASSERT_EQ(test_bytes[1], cod.cod[1]);
-  ASSERT_EQ(test_bytes[2], cod.cod[2]);
-
-  std::string ret = cod.ToString();
-
-  ASSERT_STREQ(test_class, ret.c_str());
-}
-
-TEST(ClassOfDeviceTest, test_copy) {
-  ClassOfDevice cod1;
-  ClassOfDevice cod2;
-  ClassOfDevice::FromString(test_class, cod1);
-  cod2 = cod1;
-
-  ASSERT_EQ(cod1.cod[0], cod2.cod[0]);
-  ASSERT_EQ(cod1.cod[1], cod2.cod[1]);
-  ASSERT_EQ(cod1.cod[2], cod2.cod[2]);
-}
-
-TEST(ClassOfDeviceTest, IsValid) {
-  EXPECT_FALSE(ClassOfDevice::IsValid(""));
-  EXPECT_FALSE(ClassOfDevice::IsValid("000000"));
-  EXPECT_FALSE(ClassOfDevice::IsValid("00-00-00"));
-  EXPECT_FALSE(ClassOfDevice::IsValid("000-0-0"));
-  EXPECT_TRUE(ClassOfDevice::IsValid("000-0-00"));
-  EXPECT_TRUE(ClassOfDevice::IsValid("ABc-d-00"));
-  EXPECT_TRUE(ClassOfDevice::IsValid("aBc-D-eF"));
-}
-
-TEST(ClassOfDeviceTest, classOfDeviceFromString) {
-  ClassOfDevice cod;
-
-  EXPECT_TRUE(ClassOfDevice::FromString("000-0-00", cod));
-  const ClassOfDevice result0 = {{0x00, 0x00, 0x00}};
-  EXPECT_EQ(0, memcmp(&cod, &result0, sizeof(cod)));
-
-  EXPECT_TRUE(ClassOfDevice::FromString("ab2-1-4C", cod));
-  const ClassOfDevice result1 = {{0x4c, 0x21, 0xab}};
-  EXPECT_EQ(0, memcmp(&cod, &result1, sizeof(cod)));
-}