--- /dev/null
+cc_test {
+ name: "bt_headless",
+ test_suites: ["device-tests"],
+ defaults: ["fluoride_defaults"],
+ srcs: [
+ "get_options.cc",
+ "headless.cc",
+ "sdp/main.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"],
+}
--- /dev/null
+/*
+ * 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},
+ {0, 0, 0, 0}};
+
+enum OptionType {
+ kOptionDevice = 0,
+ kOptionLoop = 1,
+ kOptionUuid = 2,
+};
+
+} // namespace
+
+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);
+
+ if (!optarg) return;
+ switch (option_type) {
+ case kOptionDevice:
+ 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:
+ ParseValue(optarg, string_list);
+ for (auto& entry : string_list) {
+ uuid_.push_back(
+ bluetooth::Uuid::From16Bit(std::stoul(entry.c_str(), nullptr, 0)));
+ }
+ 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);
+ }
+ }
+
+ if (optind < argc) {
+ printf("non-option ARGV-elements: ");
+ while (optind < argc) printf("%s ", argv[optind++]);
+ printf("\n");
+ valid_ = false;
+ }
+ fflush(nullptr);
+}
+
+void bluetooth::test::headless::GetOpt::Usage() const {
+ printf("%s: Usage:\n", name_);
+ printf("%s --device=<device,> Comma separated list of remote devices\n",
+ name_);
+ printf("%s --uuid=<uuid,> Comma separated list of uuids\n", name_);
+ printf("%s --loop=<loop> Number of loops\n", name_);
+ fflush(nullptr);
+}
--- /dev/null
+/*
+ * 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::list<RawAddress> device_;
+ std::list<bluetooth::Uuid> uuid_;
+ int loop_;
+
+ 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
--- /dev/null
+/*
+ * 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/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 Headless::SetUp() {
+ LOG(INFO) << __func__ << " Entry";
+
+ int status = bluetoothInterface.init(&bt_callbacks, false, 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 Headless 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 Headless stack is operational", __func__);
+}
+
+void Headless::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 Headless stack has exited", __func__);
+}
--- /dev/null
+/*
+ * 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 test {
+namespace headless {
+
+template <typename T>
+using ExecutionUnit = std::function<T()>;
+
+class Headless {
+ public:
+ Headless() = default;
+ virtual ~Headless() = default;
+
+ protected:
+ virtual void SetUp();
+ virtual void TearDown();
+};
+
+class Test : public Headless {
+ public:
+ template <typename T>
+ T Run(ExecutionUnit<T> func) {
+ SetUp();
+ T rc = func();
+ TearDown();
+ return rc;
+ }
+};
+
+} // namespace headless
+} // namespace test
+} // namespace bluetooth
--- /dev/null
+/*
+ * 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 "types/raw_address.h"
+
+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);
+}
+
+constexpr size_t kMaxDiscoveryRecords = 16;
+
+int sdp_query_uuid(int num_loops, const RawAddress& raw_address,
+ const bluetooth::Uuid& uuid) {
+ for (int i = 0; i < num_loops; i++) {
+ tSDP_DISCOVERY_DB* sdp_discovery_db = (tSDP_DISCOVERY_DB*)malloc(
+ sizeof(tSDP_DISCOVERY_DB) +
+ sizeof(tSDP_DISC_REC) * kMaxDiscoveryRecords);
+
+ if (!SDP_InitDiscoveryDb(sdp_discovery_db,
+ sizeof(tSDP_DISCOVERY_DB) +
+ sizeof(tSDP_DISC_REC) * kMaxDiscoveryRecords,
+ 1, // num_uuid,
+ &uuid, 0, nullptr)) {
+ LOG(ERROR) << __func__ << " Unable to initialize sdp discovery";
+ return -1;
+ }
+
+ std::promise<uint16_t> promise;
+ auto future = promise.get_future();
+
+ if (!SDP_ServiceSearchAttributeRequest2(raw_address, sdp_discovery_db,
+ bta_jv_start_discovery_callback,
+ (void*)&promise)) {
+ LOG(ERROR) << __func__
+ << " Failed to start search attribute request.. waiting";
+ return -2;
+ }
+ uint16_t result = future.get();
+ LOG(INFO) << __func__ << " connection result:" << result;
+
+ tSDP_DISC_REC* rec =
+ SDP_FindServiceInDb(sdp_discovery_db, uuid.As16Bit(), nullptr);
+ if (rec == nullptr) {
+ LOG(INFO) << __func__ << " iter:" << i << " discovery record is null"
+ << " from:" << raw_address.ToString() << " uuid:" << uuid;
+ } else {
+ printf("iter:%d result:%d attr_id:%x from:%s uuid:%s", i, result,
+ rec->p_first_attr->attr_id, rec->remote_bd_addr.ToString().c_str(),
+ uuid.ToString().c_str());
+
+ LOG(INFO) << __func__ << " iter:" << i << " result:" << result
+ << " discovery record found attr_id:"
+ << rec->p_first_attr->attr_id
+ << " len_type:" << rec->p_first_attr->attr_len_type << " time"
+ << rec->time_read << " from:" << rec->remote_bd_addr.ToString()
+ << " uuid:" << uuid;
+ fflush(nullptr);
+ }
+ free(sdp_discovery_db);
+ }
+ return 0;
+}
+
+int main(int argc, char** argv) {
+ printf("Hello world\n");
+ fflush(nullptr);
+
+ LOG(INFO) << "bt_headless start up";
+
+ bluetooth::test::headless::GetOpt options(argc, argv);
+ if (!options.IsValid()) {
+ return -1;
+ }
+ if (options.loop_ < 1) {
+ LOG(INFO) << "This test requires at least a single loop";
+ options.Usage();
+ return -1;
+ }
+ if (options.device_.size() != 1) {
+ LOG(INFO) << "This test requires a single device specified";
+ options.Usage();
+ return -1;
+ }
+ if (options.uuid_.size() != 1) {
+ LOG(INFO) << "This test requires a single uuid specified";
+ options.Usage();
+ return -1;
+ }
+
+ bluetooth::test::headless::Test test;
+ int rc = test.Run<int>([options]() {
+ return sdp_query_uuid(options.loop_, options.device_.front(),
+ options.uuid_.front());
+ });
+ LOG(INFO) << "bt_headless shut down";
+ return rc;
+}