utils: bdaddr: Add service to set Bluetooth device (MAC) address

The Bluetooth chip does not come with a unique Bluetooth device (MAC)
address configured out of the box. This means that we manually need
to configure the device address.

Otherwise BT will stop working from v6.5 kernel version onwards.
Reference: https://bugs.linaro.org/show_bug.cgi?id=5998

Link: https://github.com/me176c-dev/android_device_asus_K013/commit/cbb7066
[AmitP: Kanged the service from above link, updated the commit
        message, built an rc and shell script around this service,
        and added SELinux policies.]
Signed-off-by: Amit Pundir <amit.pundir@linaro.org>
Change-Id: I79a364917e0a4d879e4319b45b4c0f7cc7df83d3
diff --git a/Android.bp b/Android.bp
index edefe22..4572d82 100644
--- a/Android.bp
+++ b/Android.bp
@@ -49,6 +49,7 @@
 }
 
 subdirs = [
+    "shared/utils/bdaddr",
     "shared/utils/pd-mapper",
     "shared/utils/qrtr",
     "shared/utils/rmtfs",
diff --git a/db845c/device.mk b/db845c/device.mk
index 46bc452..7dac78b 100644
--- a/db845c/device.mk
+++ b/db845c/device.mk
@@ -33,6 +33,13 @@
     android.hardware.boot@1.2-impl.recovery \
     android.hardware.boot@1.2-service
 
+# Set BT address
+PRODUCT_PACKAGES += bdaddr
+
+# Install bdaddr script
+PRODUCT_COPY_FILES += \
+    device/linaro/dragonboard/shared/utils/bdaddr/set_bdaddr.sh:$(TARGET_COPY_OUT_VENDOR)/bin/set_bdaddr.sh
+
 # Install scripts to set vendor.* properties
 PRODUCT_COPY_FILES += \
     device/linaro/dragonboard/shared/utils/set_hw.sh:$(TARGET_COPY_OUT_VENDOR)/bin/set_hw.sh \
diff --git a/rb5/device.mk b/rb5/device.mk
index 5aed224..656b7aa 100644
--- a/rb5/device.mk
+++ b/rb5/device.mk
@@ -33,6 +33,13 @@
     android.hardware.boot@1.2-impl.recovery \
     android.hardware.boot@1.2-service
 
+# Set BT address
+PRODUCT_PACKAGES += bdaddr
+
+# Install bdaddr script
+PRODUCT_COPY_FILES += \
+    device/linaro/dragonboard/shared/utils/bdaddr/set_bdaddr.sh:$(TARGET_COPY_OUT_VENDOR)/bin/set_bdaddr.sh
+
 # Install scripts to set vendor.* properties
 PRODUCT_COPY_FILES += \
     device/linaro/dragonboard/shared/utils/set_hw.sh:$(TARGET_COPY_OUT_VENDOR)/bin/set_hw.sh
diff --git a/sepolicy/file_contexts b/sepolicy/file_contexts
index c7eb6db..a74f361 100644
--- a/sepolicy/file_contexts
+++ b/sepolicy/file_contexts
@@ -61,6 +61,7 @@
 /vendor/bin/rmtfs									u:object_r:rmtfs_exec:s0
 /vendor/bin/tqftpserv									u:object_r:tqftpserv_exec:s0
 /vendor/bin/suspend_blocker								u:object_r:suspend_blocker_exec:s0
+/vendor/bin/set_bdaddr\.sh								u:object_r:set_bdaddr_exec:s0
 /vendor/bin/set_hw\.sh									u:object_r:set_hw_exec:s0
 /vendor/bin/set_udc\.sh									u:object_r:set_udc_exec:s0
 
diff --git a/sepolicy/set_bdaddr.te b/sepolicy/set_bdaddr.te
new file mode 100644
index 0000000..e81d6ab
--- /dev/null
+++ b/sepolicy/set_bdaddr.te
@@ -0,0 +1,11 @@
+type set_bdaddr, domain;
+type set_bdaddr_exec, exec_type, vendor_file_type, file_type;
+init_daemon_domain(set_bdaddr);
+
+# audit2allow
+allow set_bdaddr proc_cmdline:file { open read };
+allow set_bdaddr rootfs:dir { open read };
+allow set_bdaddr self:bluetooth_socket { bind create read write };
+allow set_bdaddr self:capability net_admin;
+allow set_bdaddr vendor_file:file execute_no_trans;
+allow set_bdaddr vendor_toolbox_exec:file execute_no_trans;
diff --git a/shared/utils/bdaddr/Android.bp b/shared/utils/bdaddr/Android.bp
new file mode 100644
index 0000000..fe1eb29
--- /dev/null
+++ b/shared/utils/bdaddr/Android.bp
@@ -0,0 +1,28 @@
+//
+// Copyright (C) 2023 The Android Open-Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_binary {
+    name: "bdaddr",
+    relative_install_path: "hw",
+    vendor: true,
+
+    srcs: ["bdaddr.c"],
+    shared_libs: ["liblog"],
+    init_rc: ["bdaddr.rc"],
+}
diff --git a/shared/utils/bdaddr/bdaddr.c b/shared/utils/bdaddr/bdaddr.c
new file mode 100644
index 0000000..d71fcfc
--- /dev/null
+++ b/shared/utils/bdaddr/bdaddr.c
@@ -0,0 +1,215 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "bdaddr"
+
+#include <stdint.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/socket.h>
+#include <log/log.h>
+
+#define HCI_DEV_NONE  0xffff
+#define HCI_CHANNEL_CONTROL  3
+#define BTPROTO_HCI  1
+
+struct sockaddr_hci {
+    sa_family_t hci_family;
+    uint16_t hci_dev;
+    uint16_t hci_channel;
+};
+
+#define BTMGMT_CMD_READ_CONFIG_INFO  0x0037
+#define BTMGMT_CMD_SET_PUBLIC_ADDR  0x0039
+#define BTMGMT_EV_CMD_COMPLETE  0x0001
+#define BTMGMT_EV_CMD_STATUS  0x0002
+#define BTMGMT_EV_UNCONF_INDEX_ADDED  0x001d
+#define BTMGMT_OPT_PUBLIC_ADDRESS  (1 << 1)
+#define BTMGMT_ERR_INVALID_INDEX  0x11
+
+struct btmgmt_hdr {
+    uint16_t cmd;
+    uint16_t id;
+    uint16_t len;
+} __attribute__((packed));
+
+struct btmgmt_cmd_set_public_addr {
+    struct btmgmt_hdr hdr;
+    uint8_t addr[6];
+} __attribute__((packed));
+
+struct btmgmt_ev_cmd_status {
+    struct btmgmt_hdr hdr;
+    uint16_t cmd;
+    uint8_t status;
+} __attribute__((packed));
+
+struct btmgmt_ev_cc_config_info {
+    struct btmgmt_ev_cmd_status ev;
+    uint16_t manufacturer;
+    uint32_t supported_options;
+    uint32_t missing_options;
+} __attribute__((packed));
+
+// TODO: Make this configurable
+#define HCI_CONTROLLER  0
+
+#define MAC_ADDRESS_SIZE    6
+#define MAC_ADDRESS_LENGTH  (MAC_ADDRESS_SIZE*2 + MAC_ADDRESS_SIZE-1)
+#define MAC_ADDRESS_FORMAT  "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx"
+#define MAC_ADDRESS_ARGS(addr) \
+    (addr)[5], (addr)[4], (addr)[3], (addr)[2], (addr)[1], (addr)[0]
+
+#define MESSAGE_BUFFER  512
+
+static int btmgmt_connect() {
+    int s = socket(PF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC, BTPROTO_HCI);
+    if (s < 0)
+        return -1;
+
+    struct sockaddr_hci addr = {
+        .hci_family = AF_BLUETOOTH,
+        .hci_dev = HCI_DEV_NONE,
+        .hci_channel = HCI_CHANNEL_CONTROL,
+    };
+    if (bind(s, (struct sockaddr*) &addr, sizeof(addr)) < 0) {
+        close(s);
+        return -1;
+    }
+
+    return s;
+}
+
+static void btmgmt_request_config_info(int s) {
+    struct btmgmt_hdr cmd = {
+        .cmd = BTMGMT_CMD_READ_CONFIG_INFO,
+        .id = HCI_CONTROLLER,
+        .len = 0,
+    };
+
+    if (write(s, &cmd, sizeof(cmd)) < 0) {
+        ALOGE("Failed to request controller configuration information: %s",
+            strerror(errno));
+    }
+}
+
+static void btmgmt_set_public_addr(int s, const uint8_t bdaddr[MAC_ADDRESS_SIZE]) {
+    struct btmgmt_cmd_set_public_addr cmd = {
+        .hdr = {
+            .cmd = BTMGMT_CMD_SET_PUBLIC_ADDR,
+            .id = HCI_CONTROLLER,
+            .len = sizeof(cmd) - sizeof(cmd.hdr),
+        },
+    };
+    memcpy(cmd.addr, bdaddr, sizeof(cmd.addr));
+
+    if (write(s, &cmd, sizeof(cmd)) != sizeof(cmd)) {
+        ALOGE("Failed to write set public address command: %s", strerror(errno));
+    }
+}
+
+static void btmgmt_complete_set_public_addr(struct btmgmt_ev_cmd_status* ev,
+        const uint8_t bdaddr[MAC_ADDRESS_SIZE]) {
+    if (ev->status == 0) {
+        ALOGI("Updated public address to " MAC_ADDRESS_FORMAT,
+            MAC_ADDRESS_ARGS(bdaddr));
+    } else {
+        ALOGE("Failed to update public address to " MAC_ADDRESS_FORMAT
+            ": error 0x%x", MAC_ADDRESS_ARGS(bdaddr), ev->status);
+    }
+}
+
+static bool btmgmt_config_needs_public_addr(struct btmgmt_ev_cmd_status* ev) {
+    struct btmgmt_ev_cc_config_info* info = (struct btmgmt_ev_cc_config_info*) ev;
+    if (info->ev.status) {
+        if (info->ev.status != BTMGMT_ERR_INVALID_INDEX)
+            ALOGE("Failed to read controller configuration information: 0x%x",
+                info->ev.status);
+        return false;
+    }
+
+    if (info->ev.hdr.cmd != BTMGMT_EV_CMD_COMPLETE
+            || info->ev.hdr.len < sizeof(*info) - sizeof(info->ev.hdr))
+        return false;
+
+    if (info->missing_options & BTMGMT_OPT_PUBLIC_ADDRESS) {
+        return true;
+    } else {
+        ALOGD("Controller is already configured with a public address");
+        return false;
+    }
+}
+
+int main(int argc, char* argv[]) {
+    if (argc < 2) {
+        ALOGE("Usage: bdaddr <bdaddr>");
+        return 1;
+    }
+
+    uint8_t bdaddr[MAC_ADDRESS_SIZE];
+    if (strlen(argv[1]) != MAC_ADDRESS_LENGTH
+        || sscanf(argv[1], MAC_ADDRESS_FORMAT,
+            &bdaddr[5], &bdaddr[4], &bdaddr[3],
+            &bdaddr[2], &bdaddr[1], &bdaddr[0]) != MAC_ADDRESS_SIZE) {
+        ALOGE("Invalid MAC address: %s", argv[1]);
+        return 1;
+    }
+
+    int s = btmgmt_connect();
+    if (s < 0) {
+        ALOGE("Failed to create Bluetooth management socket: %s", strerror(errno));
+        return 1;
+    }
+
+    btmgmt_request_config_info(s);
+
+    char buf[MESSAGE_BUFFER];
+    struct btmgmt_hdr* hdr = (struct btmgmt_hdr*) buf;
+    struct btmgmt_ev_cmd_status* ev = (struct btmgmt_ev_cmd_status*) hdr;
+
+    while (true) {
+        ssize_t len = read(s, buf, sizeof(buf));
+        if (len < (ssize_t) sizeof(struct btmgmt_hdr))
+            continue;
+        if (len < (ssize_t) sizeof(struct btmgmt_hdr) + hdr->len)
+            continue;
+        if (hdr->id != HCI_CONTROLLER)
+            continue;
+
+        switch (hdr->cmd) {
+        case BTMGMT_EV_CMD_COMPLETE:
+        case BTMGMT_EV_CMD_STATUS:
+            if (hdr->len < sizeof(*ev) - sizeof(hdr))
+                continue;
+
+            switch (ev->cmd) {
+            case BTMGMT_CMD_READ_CONFIG_INFO:
+                if (btmgmt_config_needs_public_addr(ev))
+                    btmgmt_set_public_addr(s, bdaddr);
+                break;
+            case BTMGMT_CMD_SET_PUBLIC_ADDR:
+                btmgmt_complete_set_public_addr(ev, bdaddr);
+                break;
+            }
+
+            break;
+        case BTMGMT_EV_UNCONF_INDEX_ADDED:
+            btmgmt_request_config_info(s);
+            break;
+        }
+    }
+}
diff --git a/shared/utils/bdaddr/bdaddr.rc b/shared/utils/bdaddr/bdaddr.rc
new file mode 100644
index 0000000..d4f007d
--- /dev/null
+++ b/shared/utils/bdaddr/bdaddr.rc
@@ -0,0 +1,24 @@
+#
+# Copyright (C) 2023 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+service set_bdaddr /vendor/bin/set_bdaddr.sh
+    class core
+    user root
+    group system
+    capabilities NET_ADMIN
+
+on post-fs
+    start set_bdaddr
diff --git a/shared/utils/bdaddr/set_bdaddr.sh b/shared/utils/bdaddr/set_bdaddr.sh
new file mode 100644
index 0000000..299626d
--- /dev/null
+++ b/shared/utils/bdaddr/set_bdaddr.sh
@@ -0,0 +1,33 @@
+#! /vendor/bin/sh
+# Set Bluetooth address (BT_ADDR).
+
+#
+# Copyright (C) 2023 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# Get the unique board serial number from /proc/cmdline,
+# prepend '0's to the serial number to fill 5 LSBs of the
+# BT address and prepend "C0" as MSB to prepare a 6 byte
+# Bluetooth Random Static Address. Reference:
+# https://www.bluetooth.com/wp-content/uploads/2022/05/Bluetooth_LE_Primer_Paper.pdf [Page 23]
+#
+# Format the output in xx:xx:xx:xx:xx:xx format for the
+# "bdaddr" command to work.
+
+BTADDR=`/vendor/bin/cat /proc/cmdline | vendor/bin/grep -o serialno.* |\
+	/vendor/bin/cut -f2 -d'=' | /vendor/bin/awk '{printf("C0%010s\n", $1)}' |\
+	/vendor/bin/sed 's/\(..\)/\1:/g' | /vendor/bin/sed '$s/:$//'`
+
+/vendor/bin/hw/bdaddr "${BTADDR}"