summaryrefslogtreecommitdiff
path: root/shared
diff options
context:
space:
mode:
Diffstat (limited to 'shared')
-rw-r--r--shared/utils/bdaddr/Android.bp28
-rw-r--r--shared/utils/bdaddr/bdaddr.c215
-rw-r--r--shared/utils/bdaddr/bdaddr.rc24
-rw-r--r--shared/utils/bdaddr/set_bdaddr.sh33
4 files changed, 300 insertions, 0 deletions
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}"