Initial entry for headless

Bug: 147316415
Test: Compiles and sdp test runs

Change-Id: I2db01df68fb8807ce934b59aab5a4f593d4f8074
diff --git a/system/test/headless/Android.bp b/system/test/headless/Android.bp
new file mode 100644
index 0000000..bc91652
--- /dev/null
+++ b/system/test/headless/Android.bp
@@ -0,0 +1,55 @@
+cc_test {
+    name: "bt_headless",
+    test_suites: ["device-tests"],
+    defaults: ["fluoride_defaults"],
+    srcs: [
+        "get_options.cc",
+        "headless.cc",
+        "sdp/main.cc",
+    ],
+    include_dirs: [
+        "packages/modules/Bluetooth/system",
+        "packages/modules/Bluetooth/system/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/system/test/headless/get_options.cc b/system/test/headless/get_options.cc
new file mode 100644
index 0000000..c28e30e
--- /dev/null
+++ b/system/test/headless/get_options.cc
@@ -0,0 +1,127 @@
+/*
+ * 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);
+}
diff --git a/system/test/headless/get_options.h b/system/test/headless/get_options.h
new file mode 100644
index 0000000..65ab920
--- /dev/null
+++ b/system/test/headless/get_options.h
@@ -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.
+ */
+
+#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
diff --git a/system/test/headless/headless.cc b/system/test/headless/headless.cc
new file mode 100644
index 0000000..2cf6334
--- /dev/null
+++ b/system/test/headless/headless.cc
@@ -0,0 +1,173 @@
+/*
+ * 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__);
+}
diff --git a/system/test/headless/headless.h b/system/test/headless/headless.h
new file mode 100644
index 0000000..c51d640
--- /dev/null
+++ b/system/test/headless/headless.h
@@ -0,0 +1,47 @@
+/*
+ * 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
diff --git a/system/test/headless/sdp/main.cc b/system/test/headless/sdp/main.cc
new file mode 100644
index 0000000..b031e28
--- /dev/null
+++ b/system/test/headless/sdp/main.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_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;
+}