OSDN Git Service

Merge "Merge "Disable sniff in link policy before a2dp streaming" am: f532661162...
authorTreeHugger Robot <treehugger-gerrit@google.com>
Fri, 10 Dec 2021 07:43:47 +0000 (07:43 +0000)
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
Fri, 10 Dec 2021 07:43:47 +0000 (07:43 +0000)
Original change: https://googleplex-android-review.googlesource.com/c/platform/system/bt/+/16417864

Change-Id: I23fc9fae451686480f20def5b857aa1bde6b2e9a

15 files changed:
btif/src/btif_debug_btsnoop.cc
btif/src/btif_util.cc
stack/avrc/avrc_pars_tg.cc
stack/btm/btm_ble_adv_filter.cc
stack/l2cap/l2c_link.cc
stack/smp/smp_act.cc
stack/smp/smp_api.cc
stack/smp/smp_int.h
stack/smp/smp_keys.cc
vendor_libs/test_vendor_lib/Android.bp
vendor_libs/test_vendor_lib/model/devices/h4_packetizer.cc
vendor_libs/test_vendor_lib/model/devices/h4_packetizer.h
vendor_libs/test_vendor_lib/model/devices/h4_parser.cc [new file with mode: 0644]
vendor_libs/test_vendor_lib/model/devices/h4_parser.h [new file with mode: 0644]
vendor_libs/test_vendor_lib/test/h4_parser_unittest.cc [new file with mode: 0644]

index 5ebfdae..e7cf611 100644 (file)
@@ -26,6 +26,7 @@
 #include "btif/include/btif_debug_btsnoop.h"
 #include "hci/include/btsnoop_mem.h"
 #include "internal_include/bt_target.h"
+#include "osi/include/properties.h"
 #include "osi/include/ringbuffer.h"
 
 #define REDUCE_HCI_TYPE_TO_SIGNIFICANT_BITS(type) ((type) >> 8)
@@ -38,6 +39,7 @@ static const size_t BTSNOOP_MEM_BUFFER_SIZE = (256 * 1024);
 static std::mutex buffer_mutex;
 static ringbuffer_t* buffer = NULL;
 static uint64_t last_timestamp_ms = 0;
+static bool qualcomm_debug_log_enable = false;
 
 static size_t btsnoop_calculate_packet_length(uint16_t type,
                                               const uint8_t* data,
@@ -80,6 +82,9 @@ static size_t btsnoop_calculate_packet_length(uint16_t type,
   static const size_t L2CAP_CID_OFFSET = (HCI_ACL_HEADER_SIZE + 2);
   static const uint16_t L2CAP_SIGNALING_CID = 0x0001;
 
+  static const size_t HCI_ACL_HANDLE_OFFSET = 0;
+  static const uint16_t QUALCOMM_DEBUG_LOG_HANDLE = 0xedc;
+
   // Maximum amount of ACL data to log.
   // Enough for an RFCOMM frame up to the frame check;
   // not enough for a HID report or audio data.
@@ -101,11 +106,19 @@ static size_t btsnoop_calculate_packet_length(uint16_t type,
       if (length > len_hci_acl) {
         uint16_t l2cap_cid =
             data[L2CAP_CID_OFFSET] | (data[L2CAP_CID_OFFSET + 1] << 8);
+        uint16_t hci_acl_packet_handle = data[HCI_ACL_HANDLE_OFFSET] |
+                                         (data[HCI_ACL_HANDLE_OFFSET + 1] << 8);
+        hci_acl_packet_handle &= 0x0FFF;
+
         if (l2cap_cid == L2CAP_SIGNALING_CID) {
           // For the signaling CID, take the full packet.
           // That way, the PSM setup is captured, allowing decoding of PSMs down
           // the road.
           return length;
+        } else if (qualcomm_debug_log_enable &&
+                   hci_acl_packet_handle == QUALCOMM_DEBUG_LOG_HANDLE) {
+          // For the enhanced controller debug log, take the full packet.
+          return length;
         } else {
           // Otherwise, return as much as we reasonably can
           len_hci_acl = MAX_HCI_ACL_LEN;
@@ -131,6 +144,12 @@ static size_t btsnoop_calculate_packet_length(uint16_t type,
 void btif_debug_btsnoop_init(void) {
   if (buffer == NULL) buffer = ringbuffer_init(BTSNOOP_MEM_BUFFER_SIZE);
   btsnoop_mem_set_callback(btsnoop_cb);
+
+  char value[PROPERTY_VALUE_MAX] = {0};
+  int ret = osi_property_get("ro.soc.manufacturer", value, "");
+  if (ret > 0 && strncmp(value, "Qualcomm", ret) == 0) {
+    qualcomm_debug_log_enable = true;
+  }
 }
 
 #ifndef OS_ANDROID
index ae49edb..9030f9a 100644 (file)
@@ -83,7 +83,7 @@ void uint2devclass(uint32_t cod, DEV_CLASS dev_class) {
  *  Description     This function converts an ASCII string into HEX
  *
  *  Returns         the number of hex bytes filled.
-*/
+ */
 int ascii_2_hex(const char* p_ascii, int len, uint8_t* p_hex) {
   int x;
   uint8_t c;
@@ -155,6 +155,7 @@ const char* dump_dm_event(uint16_t event) {
     CASE_RETURN_STR(BTA_DM_BLE_PASSKEY_NOTIF_EVT)
     CASE_RETURN_STR(BTA_DM_BLE_PASSKEY_REQ_EVT)
     CASE_RETURN_STR(BTA_DM_BLE_OOB_REQ_EVT)
+    CASE_RETURN_STR(BTA_DM_BLE_SC_OOB_REQ_EVT)
     CASE_RETURN_STR(BTA_DM_BLE_LOCAL_IR_EVT)
     CASE_RETURN_STR(BTA_DM_BLE_LOCAL_ER_EVT)
     CASE_RETURN_STR(BTA_DM_BLE_AUTH_CMPL_EVT)
index c59c18d..db13bd5 100644 (file)
@@ -119,13 +119,6 @@ static tAVRC_STS avrc_pars_vendor_cmd(tAVRC_MSG_VENDOR* p_msg,
   if (p_msg->vendor_len == 0) return AVRC_STS_NO_ERROR;
   if (p_msg->p_vendor_data == NULL) return AVRC_STS_INTERNAL_ERR;
 
-  if (p_msg->vendor_len < 4) {
-    android_errorWriteLog(0x534e4554, "168712382");
-    AVRC_TRACE_WARNING("%s: message length %d too short: must be at least 4",
-                       __func__, p_msg->vendor_len);
-    return AVRC_STS_INTERNAL_ERR;
-  }
-
   p = p_msg->p_vendor_data;
   p_result->pdu = *p++;
   AVRC_TRACE_DEBUG("%s pdu:0x%x", __func__, p_result->pdu);
index 08773ab..710cca7 100644 (file)
@@ -607,7 +607,7 @@ void BTM_LE_PF_set(tBTM_BLE_PF_FILT_INDEX filt_index,
       case BTM_BLE_PF_ADDR_FILTER: {
         tBLE_BD_ADDR target_addr;
         target_addr.bda = cmd.address;
-        target_addr.type = cmd.addr_type;
+        target_addr.type = (cmd.addr_type & (~BLE_ADDR_TYPE_ID_BIT));
 
         BTM_LE_PF_addr_filter(action, filt_index, target_addr,
                               base::DoNothing());
@@ -641,12 +641,14 @@ void BTM_LE_PF_set(tBTM_BLE_PF_FILT_INDEX filt_index,
             return;
           }
           // Allocate a new "temporary" device record
+
           btm_sec_alloc_dev(cmd.address);
           remove_me_later_map.emplace(filt_index, cmd.address);
           // Set the IRK
           tBTM_LE_PID_KEYS pid_keys;
           pid_keys.irk = cmd.irk;
-          pid_keys.identity_addr_type = cmd.addr_type;
+          pid_keys.identity_addr_type =
+              (cmd.addr_type & (~BLE_ADDR_TYPE_ID_BIT));
           pid_keys.identity_addr = cmd.address;
           // Add it to the union to pass to SecAddBleKey
           tBTM_LE_KEY_VALUE le_key;
@@ -729,15 +731,6 @@ void BTM_LE_PF_clear(tBTM_BLE_PF_FILT_INDEX filt_index,
     /* clear service data filter */
     BTM_LE_PF_srvc_data_pattern(BTM_BLE_SCAN_COND_CLEAR, filt_index, {}, {},
                                 fDoNothing);
-
-    // If we have an entry, lets remove the device if it isn't bonded
-    auto entry = remove_me_later_map.find(filt_index);
-    if (entry != remove_me_later_map.end()) {
-      auto entry = remove_me_later_map.find(filt_index);
-      if (!btm_sec_is_a_bonded_dev(entry->second)) {
-        BTM_SecDeleteDevice(entry->second);
-      }
-    }
   }
 
   uint8_t len = BTM_BLE_ADV_FILT_META_HDR_LENGTH + BTM_BLE_PF_FEAT_SEL_LEN;
@@ -855,24 +848,6 @@ void BTM_BleAdvFilterParamSetup(
         (uint8_t)(BTM_BLE_ADV_FILT_META_HDR_LENGTH),
         base::Bind(&btm_flt_update_cb, BTM_BLE_META_PF_FEAT_SEL, cb));
 
-    auto entry = remove_me_later_map.find(filt_index);
-    if (entry != remove_me_later_map.end()) {
-      LOG_WARN("Replacing existing filter index entry with new address");
-      // If device is not bonded, then try removing the device
-      // If the device doesn't get removed then it is currently connected
-      // (may be pairing?) If we do delete the device we want to erase the
-      // filter index so we can replace it If the device is bonded, we
-      // want to erase the filter index so we don't delete it in the later
-      // BTM_LE_PF_clear call.
-      if (!btm_sec_is_a_bonded_dev(entry->second)) {
-        if (!BTM_SecDeleteDevice(entry->second)) {
-          LOG_WARN("Unable to remove device, still connected.");
-          return;
-        }
-      }
-      remove_me_later_map.erase(filt_index);
-    }
-
   } else if (BTM_BLE_SCAN_COND_CLEAR == action) {
     /* Deallocate all filters here */
     btm_ble_dealloc_addr_filter_counter(NULL, BTM_BLE_PF_TYPE_ALL);
index 70adbb6..3f56eff 100644 (file)
@@ -1047,6 +1047,8 @@ void l2c_OnHciModeChangeSendPendingPackets(RawAddress remote) {
 static void l2c_link_send_to_lower_br_edr(tL2C_LCB* p_lcb, BT_HDR* p_buf) {
   const uint16_t acl_packet_size_classic =
       controller_get_interface()->get_acl_packet_size_classic();
+  const uint16_t acl_data_size_classic =
+      controller_get_interface()->get_acl_data_size_classic();
   const uint16_t link_xmit_quota = p_lcb->link_xmit_quota;
   const bool is_bdr_and_fits_in_buffer =
       bluetooth::shim::is_gd_acl_enabled()
@@ -1062,8 +1064,8 @@ static void l2c_link_send_to_lower_br_edr(tL2C_LCB* p_lcb, BT_HDR* p_buf) {
     l2cb.controller_xmit_window--;
   } else {
     uint16_t num_segs =
-        (p_buf->len - HCI_DATA_PREAMBLE_SIZE + acl_packet_size_classic - 1) /
-        acl_packet_size_classic;
+        (p_buf->len - HCI_DATA_PREAMBLE_SIZE + acl_data_size_classic - 1) /
+        acl_data_size_classic;
 
     /* If doing round-robin, then only 1 segment each time */
     if (p_lcb->link_xmit_quota == 0) {
@@ -1097,6 +1099,8 @@ static void l2c_link_send_to_lower_br_edr(tL2C_LCB* p_lcb, BT_HDR* p_buf) {
 static void l2c_link_send_to_lower_ble(tL2C_LCB* p_lcb, BT_HDR* p_buf) {
   const uint16_t acl_packet_size_ble =
       controller_get_interface()->get_acl_packet_size_ble();
+  const uint16_t acl_data_size_ble =
+      controller_get_interface()->get_acl_data_size_ble();
   const uint16_t link_xmit_quota = p_lcb->link_xmit_quota;
   const bool is_ble_and_fits_in_buffer = (p_buf->len <= acl_packet_size_ble);
 
@@ -1109,8 +1113,8 @@ static void l2c_link_send_to_lower_ble(tL2C_LCB* p_lcb, BT_HDR* p_buf) {
     l2cb.controller_le_xmit_window--;
   } else {
     uint16_t num_segs =
-        (p_buf->len - HCI_DATA_PREAMBLE_SIZE + acl_packet_size_ble - 1) /
-        acl_packet_size_ble;
+        (p_buf->len - HCI_DATA_PREAMBLE_SIZE + acl_data_size_ble - 1) /
+        acl_data_size_ble;
 
     /* If doing round-robin, then only 1 segment each time */
     if (p_lcb->link_xmit_quota == 0) {
index 3afae8b..24f3d8a 100644 (file)
@@ -1417,7 +1417,6 @@ void smp_decide_association_model(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) {
  * Description  process IO response for a peripheral device.
  ******************************************************************************/
 void smp_process_io_response(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) {
-
   SMP_TRACE_DEBUG("%s", __func__);
   if (p_cb->flags & SMP_PAIR_FLAGS_WE_STARTED_DD) {
     /* pairing started by local (peripheral) Security Request */
@@ -1442,12 +1441,40 @@ void smp_process_io_response(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) {
 
     // If we are doing SMP_MODEL_SEC_CONN_OOB we don't need to request OOB data
     // locally if loc_oob_flag == 0x00 b/c there is no OOB data to give.  In the
-    // event the loc_oob_flag is another value, we should request the OOB data
-    // locally.  Which seems to cause it to make a TK REQUEST which is used for
-    // the legacy flow which requires both sides to have OOB data.
-    if (p_cb->selected_association_model == SMP_MODEL_SEC_CONN_OOB &&
-        p_cb->loc_oob_flag != 0x00) {
-      if (smp_request_oob_data(p_cb)) return;
+    // event the loc_oob_flag is present value, we should request the OOB data
+    // locally; otherwise fail.
+    // If we are the initiator the OOB data has already been stored and will be
+    // collected in the statemachine later.
+    //
+    // loc_oob_flag could be one of the following tSMP_OOB_FLAG enum values:
+    // SMP_OOB_NONE = 0
+    // SMP_OOB_PRESENT = 1
+    // SMP_OOB_UNKNOWN = 2
+    //
+    // The only time Android cares about needing to provide the peer oob data
+    // here would be in the advertiser situation or role.  If the
+    // device is doing the connecting it will not need to get the data again as
+    // it was already provided in the initiation call.
+    //
+    // loc_oob_flag should only equal SMP_OOB_PRESENT when PEER DATA exists and
+    // device is the advertiser as opposed to being the connector.
+    if (p_cb->selected_association_model == SMP_MODEL_SEC_CONN_OOB) {
+      switch (p_cb->loc_oob_flag) {
+        case SMP_OOB_NONE:
+          LOG_INFO("SMP_MODEL_SEC_CONN_OOB with SMP_OOB_NONE");
+          smp_send_pair_rsp(p_cb, NULL);
+          break;
+        case SMP_OOB_PRESENT:
+          LOG_INFO("SMP_MODEL_SEC_CONN_OOB with SMP_OOB_PRESENT");
+          if (smp_request_oob_data(p_cb)) return;
+          break;
+        case SMP_OOB_UNKNOWN:
+          LOG_WARN("SMP_MODEL_SEC_CONN_OOB with SMP_OOB_UNKNOWN");
+          tSMP_INT_DATA smp_int_data;
+          smp_int_data.status = SMP_PAIR_AUTH_FAIL;
+          smp_send_pair_fail(p_cb, &smp_int_data);
+          return;
+      }
     }
 
     // PTS Testing failure modes
index 164971d..dc05e1d 100644 (file)
@@ -530,6 +530,12 @@ void SMP_SecureConnectionOobDataReply(uint8_t* p_data) {
       if (!p_oob->loc_oob_data.present) data_missing = true;
       break;
     case SMP_OOB_BOTH:
+      // Check for previous local OOB data in cache
+      // This would be in the case data was generated BEFORE pairing was
+      // attempted and this instance is the connector or pairing initiator.
+      // [NOTICE]: Overridding data present here if the data exists so state
+      // machine asks for it later
+      p_oob->loc_oob_data.present = smp_has_local_oob_data();
       if (!p_oob->loc_oob_data.present || !p_oob->peer_oob_data.present)
         data_missing = true;
       break;
index 54fdfdf..c131201 100644 (file)
@@ -465,5 +465,5 @@ extern void smp_xor_128(Octet16* a, const Octet16& b);
  * cleaned up */
 extern void smp_save_local_oob_data(tSMP_CB* p_cb);
 extern void smp_clear_local_oob_data();
-
+extern bool smp_has_local_oob_data();
 #endif /* SMP_INT_H */
index 5614499..5d0e614 100644 (file)
@@ -70,9 +70,10 @@ static bool is_empty(tSMP_LOC_OOB_DATA* data) {
   return memcmp(data, &empty_data, sizeof(tSMP_LOC_OOB_DATA)) == 0;
 }
 
+bool smp_has_local_oob_data() { return !is_empty(&saved_local_oob_data); }
+
 void smp_debug_print_nbyte_little_endian(uint8_t* p, const char* key_name,
-                                         uint8_t len) {
-}
+                                         uint8_t len) {}
 
 inline void smp_debug_print_nbyte_little_endian(const Octet16& p,
                                                 const char* key_name,
@@ -82,8 +83,7 @@ inline void smp_debug_print_nbyte_little_endian(const Octet16& p,
 }
 
 void smp_debug_print_nbyte_big_endian(uint8_t* p, const char* key_name,
-                                      uint8_t len) {
-}
+                                      uint8_t len) {}
 
 /** This function is called to process a passkey. */
 void smp_proc_passkey(tSMP_CB* p_cb, BT_OCTET8 rand) {
@@ -657,6 +657,29 @@ void smp_use_oob_private_key(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) {
     case SMP_OOB_BOTH:
     case SMP_OOB_LOCAL:
       LOG_INFO("restore secret key");
+      // Only use the stored OOB data if we are in an oob association model
+      if (p_cb->selected_association_model == SMP_MODEL_SEC_CONN_OOB) {
+        LOG_INFO("OOB Association Model");
+        // Make sure our data isn't empty, otherwise we generate new and
+        // eventually pairing will fail Not much we can do about it at this
+        // point, just have to generate new data The data will be cleared after
+        // the advertiser times out, so if the advertiser times out we want the
+        // pairing to fail anyway.
+        if (!is_empty(&saved_local_oob_data)) {
+          LOG_INFO("Found OOB data, loading keys");
+          for (int i = 0; i < BT_OCTET32_LEN; i++) {
+            p_cb->private_key[i] = saved_local_oob_data.private_key_used[i];
+            p_cb->loc_publ_key.x[i] = saved_local_oob_data.publ_key_used.x[i];
+            p_cb->loc_publ_key.y[i] = saved_local_oob_data.publ_key_used.y[i];
+          }
+          p_cb->sc_oob_data.loc_oob_data = saved_local_oob_data;
+          p_cb->local_random = saved_local_oob_data.randomizer;
+          smp_process_private_key(p_cb);
+          return;
+        }
+        LOG_INFO("OOB Association Model with no saved data present");
+      }
+
       memcpy(p_cb->private_key, p_cb->sc_oob_data.loc_oob_data.private_key_used,
              BT_OCTET32_LEN);
       smp_process_private_key(p_cb);
@@ -853,7 +876,6 @@ void smp_calculate_numeric_comparison_display_number(tSMP_CB* p_cb,
   return;
 }
 
-
 /*******************************************************************************
  *
  * Function         smp_calculate_local_dhkey_check
@@ -915,7 +937,6 @@ void smp_calculate_peer_dhkey_check(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) {
   smp_sm_event(p_cb, SMP_SC_KEY_READY_EVT, &smp_int_data);
 }
 
-
 /*******************************************************************************
  *
  * Function         smp_calculate_link_key_from_long_term_key
index 160dca8..96d0053 100644 (file)
@@ -33,6 +33,7 @@ cc_library_static {
         "model/devices/device.cc",
         "model/devices/device_properties.cc",
         "model/devices/h4_packetizer.cc",
+        "model/devices/h4_parser.cc",
         "model/devices/hci_protocol.cc",
         "model/devices/hci_socket_device.cc",
         "model/devices/keyboard.cc",
@@ -104,6 +105,7 @@ cc_test_host {
     ],
     srcs: [
         "test/async_manager_unittest.cc",
+        "test/h4_parser_unittest.cc",
         "test/security_manager_unittest.cc",
     ],
     header_libs: [
@@ -196,3 +198,32 @@ genrule {
         "packets/link_layer_packets.h",
     ],
 }
+
+// bt_vhci_forwarder in cuttlefish depends on this H4Packetizer implementation.
+cc_library_static {
+    name: "h4_packetizer_lib",
+    vendor: true,
+    defaults: [
+        "gd_defaults",
+    ],
+    srcs: [
+        "model/devices/h4_packetizer.cc",
+        "model/devices/h4_parser.cc",
+        "model/devices/hci_protocol.cc",
+    ],
+
+    local_include_dirs: [
+        "include",
+    ],
+    export_include_dirs: [
+        "include",
+        ".",
+    ],
+    generated_headers: [
+        "libbt_init_flags_bridge_header",
+    ],
+    include_dirs: [
+        "system/bt",
+        "system/bt/gd",
+    ],
+}
index 721a1f9..7cdec69 100644 (file)
 #include "h4_packetizer.h"
 
 #include <cerrno>
-
 #include <dlfcn.h>
 #include <fcntl.h>
 #include <sys/uio.h>
 #include <unistd.h>
 
 #include "os/log.h"
+#include "osi/include/osi.h"
 
 namespace test_vendor_lib {
-size_t H4Packetizer::HciGetPacketLengthForType(PacketType type,
-                                               const uint8_t* preamble) const {
-  static const size_t
-      packet_length_offset[static_cast<size_t>(PacketType::ISO) + 1] = {
-          0,
-          H4Packetizer::COMMAND_LENGTH_OFFSET,
-          H4Packetizer::ACL_LENGTH_OFFSET,
-          H4Packetizer::SCO_LENGTH_OFFSET,
-          H4Packetizer::EVENT_LENGTH_OFFSET,
-          H4Packetizer::ISO_LENGTH_OFFSET,
-      };
-
-  size_t offset = packet_length_offset[static_cast<size_t>(type)];
-  size_t size = preamble[offset];
-  if (type == PacketType::ACL) {
-    size |= ((size_t)preamble[offset + 1]) << 8u;
-  }
-  if (type == PacketType::ISO) {
-    size |= ((size_t)preamble[offset + 1] & 0x0fu) << 8u;
-  }
-  return size;
-}
 
 H4Packetizer::H4Packetizer(int fd, PacketReadCallback command_cb,
                            PacketReadCallback event_cb,
@@ -55,18 +33,14 @@ H4Packetizer::H4Packetizer(int fd, PacketReadCallback command_cb,
                            PacketReadCallback iso_cb,
                            ClientDisconnectCallback disconnect_cb)
     : uart_fd_(fd),
-      command_cb_(std::move(command_cb)),
-      event_cb_(std::move(event_cb)),
-      acl_cb_(std::move(acl_cb)),
-      sco_cb_(std::move(sco_cb)),
-      iso_cb_(std::move(iso_cb)),
+      h4_parser_(command_cb, event_cb, acl_cb, sco_cb, iso_cb),
       disconnect_cb_(std::move(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}};
   ssize_t ret = 0;
   do {
-    ret = TEMP_FAILURE_RETRY(writev(uart_fd_, iov, sizeof(iov) / sizeof(iov[0])));
+    OSI_NO_INTR(ret = writev(uart_fd_, iov, sizeof(iov) / sizeof(iov[0])));
   } while (-1 == ret && EAGAIN == errno);
 
   if (ret == -1) {
@@ -78,59 +52,14 @@ size_t H4Packetizer::Send(uint8_t type, const uint8_t* data, size_t length) {
   return ret;
 }
 
-void H4Packetizer::OnPacketReady() {
-  switch (hci_packet_type_) {
-    case PacketType::COMMAND:
-      command_cb_(packet_);
-      break;
-    case PacketType::ACL:
-      acl_cb_(packet_);
-      break;
-    case PacketType::SCO:
-      sco_cb_(packet_);
-      break;
-    case PacketType::EVENT:
-      event_cb_(packet_);
-      break;
-    case PacketType::ISO:
-      iso_cb_(packet_);
-      break;
-    default:
-      LOG_ALWAYS_FATAL("Unimplemented packet type %d",
-                       static_cast<int>(hci_packet_type_));
-  }
-  // Get ready for the next type byte.
-  hci_packet_type_ = PacketType::UNKNOWN;
-}
 
 void H4Packetizer::OnDataReady(int fd) {
   if (disconnected_) return;
-  ssize_t bytes_to_read = 0;
-  uint8_t* buffer_pointer = nullptr;
-
-  static const size_t preamble_size[static_cast<size_t>(PacketType::ISO) + 1] =
-      {
-          0,
-          H4Packetizer::COMMAND_PREAMBLE_SIZE,
-          H4Packetizer::ACL_PREAMBLE_SIZE,
-          H4Packetizer::SCO_PREAMBLE_SIZE,
-          H4Packetizer::EVENT_PREAMBLE_SIZE,
-          H4Packetizer::ISO_PREAMBLE_SIZE,
-      };
-  switch (state_) {
-    case HCI_TYPE:
-      bytes_to_read = 1;
-      buffer_pointer = &packet_type_;
-      break;
-    case HCI_PREAMBLE:
-    case HCI_PAYLOAD:
-      bytes_to_read = packet_.size() - bytes_read_;
-      buffer_pointer = packet_.data() + bytes_read_;
-      break;
-  }
+  ssize_t bytes_to_read = h4_parser_.BytesRequested();
+  std::vector<uint8_t> buffer(bytes_to_read);
 
-  ssize_t bytes_read =
-      TEMP_FAILURE_RETRY(read(fd, buffer_pointer, bytes_to_read));
+  ssize_t bytes_read;
+  OSI_NO_INTR(bytes_read = read(fd, buffer.data(), bytes_to_read));
   if (bytes_read == 0) {
     LOG_INFO("remote disconnected!");
     disconnected_ = true;
@@ -146,55 +75,11 @@ void H4Packetizer::OnDataReady(int fd) {
       disconnect_cb_();
       return;
     } else {
-      LOG_ALWAYS_FATAL(
-          "Read error in %s: %s",
-          state_ == HCI_TYPE
-              ? "HCI_TYPE"
-              : state_ == HCI_PREAMBLE ? "HCI_PREAMBLE" : "HCI_PAYLOAD",
-          strerror(errno));
+      LOG_ALWAYS_FATAL("Read error in %d: %s", h4_parser_.CurrentState(),
+                       strerror(errno));
     }
-  } else if (bytes_read > bytes_to_read) {
-    LOG_ALWAYS_FATAL("More bytes read (%u) than expected (%u)!",
-                     static_cast<int>(bytes_read),
-                     static_cast<int>(bytes_to_read));
-  }
-
-  switch (state_) {
-    case HCI_TYPE:
-      hci_packet_type_ = static_cast<PacketType>(packet_type_);
-      if (hci_packet_type_ != PacketType::ACL &&
-          hci_packet_type_ != PacketType::SCO &&
-          hci_packet_type_ != PacketType::COMMAND &&
-          hci_packet_type_ != PacketType::EVENT &&
-          hci_packet_type_ != PacketType::ISO) {
-        LOG_ALWAYS_FATAL("Unimplemented packet type %hhd", packet_type_);
-      }
-      state_ = HCI_PREAMBLE;
-      bytes_read_ = 0;
-      packet_.resize(preamble_size[static_cast<size_t>(hci_packet_type_)]);
-      break;
-    case HCI_PREAMBLE:
-      bytes_read_ += bytes_read;
-      if (bytes_read_ == packet_.size()) {
-        size_t payload_size =
-            HciGetPacketLengthForType(hci_packet_type_, packet_.data());
-        if (payload_size == 0) {
-          OnPacketReady();
-          state_ = HCI_TYPE;
-        } else {
-          packet_.resize(packet_.size() + payload_size);
-          state_ = HCI_PAYLOAD;
-        }
-      }
-      break;
-    case HCI_PAYLOAD:
-      bytes_read_ += bytes_read;
-      if (bytes_read_ == packet_.size()) {
-        OnPacketReady();
-        state_ = HCI_TYPE;
-      }
-      break;
   }
+  h4_parser_.Consume(buffer.data(), bytes_read);
 }
 
 }  // namespace test_vendor_lib
index 2f73007..440dcd3 100644 (file)
 #include <functional>
 #include <vector>
 
+#include "h4_parser.h"
 #include "hci_protocol.h"
 
 namespace test_vendor_lib {
 
-using HciPacketReadyCallback = std::function<void(void)>;
-using ClientDisconnectCallback = std::function<void()>;
-
-enum class PacketType : uint8_t {
-  UNKNOWN = 0,
-  COMMAND = 1,
-  ACL = 2,
-  SCO = 3,
-  EVENT = 4,
-  ISO = 5,
-};
-
+// A socket based H4Packetizer. Call OnDataReady whenever
+// data can be read from file descriptor fd.
+//
+// This is only supported on unix.
 class H4Packetizer : public HciProtocol {
  public:
   H4Packetizer(int fd, PacketReadCallback command_cb,
@@ -44,53 +37,14 @@ class H4Packetizer : public HciProtocol {
 
   size_t Send(uint8_t type, const uint8_t* data, size_t length) override;
 
-  void OnPacketReady();
-
   void OnDataReady(int fd);
 
-  // 2 bytes for opcode, 1 byte for parameter length (Volume 2, Part E, 5.4.1)
-  static constexpr size_t COMMAND_PREAMBLE_SIZE = 3;
-  static constexpr size_t COMMAND_LENGTH_OFFSET = 2;
-
-  // 2 bytes for handle, 2 bytes for data length (Volume 2, Part E, 5.4.2)
-  static constexpr size_t ACL_PREAMBLE_SIZE = 4;
-  static constexpr size_t ACL_LENGTH_OFFSET = 2;
-
-  // 2 bytes for handle, 1 byte for data length (Volume 2, Part E, 5.4.3)
-  static constexpr size_t SCO_PREAMBLE_SIZE = 3;
-  static constexpr size_t SCO_LENGTH_OFFSET = 2;
-
-  // 1 byte for event code, 1 byte for parameter length (Volume 2, Part
-  // E, 5.4.4)
-  static constexpr size_t EVENT_PREAMBLE_SIZE = 2;
-  static constexpr size_t EVENT_LENGTH_OFFSET = 1;
-
-  // 2 bytes for handle and flags, 12 bits for length (Volume 2, Part E, 5.4.5)
-  static constexpr size_t ISO_PREAMBLE_SIZE = 4;
-  static constexpr size_t ISO_LENGTH_OFFSET = 2;
-
  private:
   int uart_fd_;
-
-  PacketReadCallback command_cb_;
-  PacketReadCallback event_cb_;
-  PacketReadCallback acl_cb_;
-  PacketReadCallback sco_cb_;
-  PacketReadCallback iso_cb_;
+  H4Parser h4_parser_;
 
   ClientDisconnectCallback disconnect_cb_;
   bool disconnected_{false};
-
-  size_t HciGetPacketLengthForType(PacketType type,
-                                   const uint8_t* preamble) const;
-
-  PacketType hci_packet_type_{PacketType::UNKNOWN};
-
-  enum State { HCI_TYPE, HCI_PREAMBLE, HCI_PAYLOAD };
-  State state_{HCI_TYPE};
-  uint8_t packet_type_{};
-  std::vector<uint8_t> packet_;
-  size_t bytes_read_{0};
 };
 
 }  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/model/devices/h4_parser.cc b/vendor_libs/test_vendor_lib/model/devices/h4_parser.cc
new file mode 100644 (file)
index 0000000..e87bc12
--- /dev/null
@@ -0,0 +1,173 @@
+//
+// Copyright 20 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 "model/devices/h4_parser.h"  // for H4Parser, PacketType, H4Pars...
+
+#include <stddef.h>    // for size_t
+#include <cstdint>     // for uint8_t, int32_t
+#include <functional>  // for function
+#include <utility>     // for move
+#include <vector>      // for vector
+
+#include "model/devices/hci_protocol.h"  // for PacketReadCallback
+#include "os/log.h"                      // for LOG_ALWAYS_FATAL, LOG_INFO
+
+namespace test_vendor_lib {
+
+void H4Parser::Reset() {
+  state_ = HCI_TYPE;
+  packet_.clear();
+  bytes_wanted_ = 0;
+  packet_type_ = 0;
+}
+
+size_t H4Parser::HciGetPacketLengthForType(PacketType type,
+                                           const uint8_t* preamble) const {
+  static const size_t
+      packet_length_offset[static_cast<size_t>(PacketType::ISO) + 1] = {
+          0,
+          H4Parser::COMMAND_LENGTH_OFFSET,
+          H4Parser::ACL_LENGTH_OFFSET,
+          H4Parser::SCO_LENGTH_OFFSET,
+          H4Parser::EVENT_LENGTH_OFFSET,
+          H4Parser::ISO_LENGTH_OFFSET,
+      };
+
+  size_t offset = packet_length_offset[static_cast<size_t>(type)];
+  size_t size = preamble[offset];
+  if (type == PacketType::ACL) {
+    size |= ((size_t)preamble[offset + 1]) << 8u;
+  }
+  if (type == PacketType::ISO) {
+    size |= ((size_t)preamble[offset + 1] & 0x0fu) << 8u;
+  }
+  return size;
+}
+
+H4Parser::H4Parser(PacketReadCallback command_cb, PacketReadCallback event_cb,
+                   PacketReadCallback acl_cb, PacketReadCallback sco_cb,
+                   PacketReadCallback iso_cb)
+    : command_cb_(std::move(command_cb)),
+      event_cb_(std::move(event_cb)),
+      acl_cb_(std::move(acl_cb)),
+      sco_cb_(std::move(sco_cb)),
+      iso_cb_(std::move(iso_cb)) {}
+
+void H4Parser::OnPacketReady() {
+  switch (hci_packet_type_) {
+    case PacketType::COMMAND:
+      command_cb_(packet_);
+      break;
+    case PacketType::ACL:
+      acl_cb_(packet_);
+      break;
+    case PacketType::SCO:
+      sco_cb_(packet_);
+      break;
+    case PacketType::EVENT:
+      event_cb_(packet_);
+      break;
+    case PacketType::ISO:
+      iso_cb_(packet_);
+      break;
+    default:
+      LOG_ALWAYS_FATAL("Unimplemented packet type %d",
+                       static_cast<int>(hci_packet_type_));
+  }
+  // Get ready for the next type byte.
+  hci_packet_type_ = PacketType::UNKNOWN;
+}
+
+size_t H4Parser::BytesRequested() {
+  switch (state_) {
+    case HCI_TYPE:
+      return 1;
+    case HCI_PREAMBLE:
+    case HCI_PAYLOAD:
+      return bytes_wanted_;
+  }
+}
+
+bool H4Parser::Consume(uint8_t* buffer, int32_t bytes_read) {
+  size_t bytes_to_read = BytesRequested();
+  if (bytes_read <= 0) {
+    LOG_INFO("remote disconnected, or unhandled error?");
+    return false;
+  } else if (bytes_read > BytesRequested()) {
+    LOG_ALWAYS_FATAL("More bytes read (%u) than expected (%u)!",
+                     static_cast<int>(bytes_read),
+                     static_cast<int>(bytes_to_read));
+  }
+
+  static const size_t preamble_size[static_cast<size_t>(PacketType::ISO) + 1] =
+      {
+          0,
+          H4Parser::COMMAND_PREAMBLE_SIZE,
+          H4Parser::ACL_PREAMBLE_SIZE,
+          H4Parser::SCO_PREAMBLE_SIZE,
+          H4Parser::EVENT_PREAMBLE_SIZE,
+          H4Parser::ISO_PREAMBLE_SIZE,
+      };
+  switch (state_) {
+    case HCI_TYPE:
+      // bytes_read >= 1
+      packet_type_ = *buffer;
+      packet_.clear();
+      break;
+    case HCI_PREAMBLE:
+    case HCI_PAYLOAD:
+      packet_.insert(packet_.end(), buffer, buffer + bytes_read);
+      bytes_wanted_ -= bytes_read;
+      break;
+  }
+
+  switch (state_) {
+    case HCI_TYPE:
+      hci_packet_type_ = static_cast<PacketType>(packet_type_);
+      if (hci_packet_type_ != PacketType::ACL &&
+          hci_packet_type_ != PacketType::SCO &&
+          hci_packet_type_ != PacketType::COMMAND &&
+          hci_packet_type_ != PacketType::EVENT &&
+          hci_packet_type_ != PacketType::ISO) {
+        LOG_ALWAYS_FATAL("Unimplemented packet type %hhd", packet_type_);
+      }
+      state_ = HCI_PREAMBLE;
+      bytes_wanted_ = preamble_size[static_cast<size_t>(hci_packet_type_)];
+      break;
+    case HCI_PREAMBLE:
+
+      if (bytes_wanted_ == 0) {
+        size_t payload_size =
+            HciGetPacketLengthForType(hci_packet_type_, packet_.data());
+        if (payload_size == 0) {
+          OnPacketReady();
+          state_ = HCI_TYPE;
+        } else {
+          bytes_wanted_ = payload_size;
+          state_ = HCI_PAYLOAD;
+        }
+      }
+      break;
+    case HCI_PAYLOAD:
+      if (bytes_wanted_ == 0) {
+        OnPacketReady();
+        state_ = HCI_TYPE;
+      }
+      break;
+  }
+  return true;
+}
+}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/model/devices/h4_parser.h b/vendor_libs/test_vendor_lib/model/devices/h4_parser.h
new file mode 100644 (file)
index 0000000..6f2f79e
--- /dev/null
@@ -0,0 +1,122 @@
+//
+// Copyright 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#pragma once
+
+#include <stddef.h>    // for size_t
+#include <cstdint>     // for uint8_t, int32_t
+#include <functional>  // for function
+#include <ostream>     // for operator<<, ostream
+#include <vector>      // for vector
+
+#include "model/devices/hci_protocol.h"  // for PacketReadCallback
+
+namespace test_vendor_lib {
+
+using HciPacketReadyCallback = std::function<void(void)>;
+using ClientDisconnectCallback = std::function<void()>;
+
+enum class PacketType : uint8_t {
+  UNKNOWN = 0,
+  COMMAND = 1,
+  ACL = 2,
+  SCO = 3,
+  EVENT = 4,
+  ISO = 5,
+};
+
+// An H4 Parser can parse H4 Packets and will invoke the proper callback
+// once a packet has been parsed.
+//
+// You use it as follows:
+//
+// H4Parser h4(....);
+// size_t nr_bytes = h4.BytesRequested();
+// std::vector fill_this_vector_with_at_most_nr_bytes(nr_bytes);
+// h4.Consume(fill_this_vector_with_at_most_nr_bytes.data(), nr_bytes.size());
+//
+// The parser will invoke the proper callbacks once a packet has been parsed.
+// The parser keeps internal state and is not thread safe.
+class H4Parser {
+ public:
+  enum State { HCI_TYPE, HCI_PREAMBLE, HCI_PAYLOAD };
+
+  H4Parser(PacketReadCallback command_cb, PacketReadCallback event_cb,
+           PacketReadCallback acl_cb, PacketReadCallback sco_cb,
+           PacketReadCallback iso_cb);
+
+  // Consumes the given number of bytes, returns true on success.
+  bool Consume(uint8_t* buffer, int32_t bytes);
+
+  // The maximum number of bytes the parser can consume in the current state.
+  size_t BytesRequested();
+
+  // Resets the parser to the empty, initial state.
+  void Reset();
+
+  State CurrentState() { return state_; };
+
+ private:
+  void OnPacketReady();
+
+  // 2 bytes for opcode, 1 byte for parameter length (Volume 2, Part E, 5.4.1)
+  static constexpr size_t COMMAND_PREAMBLE_SIZE = 3;
+  static constexpr size_t COMMAND_LENGTH_OFFSET = 2;
+
+  // 2 bytes for handle, 2 bytes for data length (Volume 2, Part E, 5.4.2)
+  static constexpr size_t ACL_PREAMBLE_SIZE = 4;
+  static constexpr size_t ACL_LENGTH_OFFSET = 2;
+
+  // 2 bytes for handle, 1 byte for data length (Volume 2, Part E, 5.4.3)
+  static constexpr size_t SCO_PREAMBLE_SIZE = 3;
+  static constexpr size_t SCO_LENGTH_OFFSET = 2;
+
+  // 1 byte for event code, 1 byte for parameter length (Volume 2, Part
+  // E, 5.4.4)
+  static constexpr size_t EVENT_PREAMBLE_SIZE = 2;
+  static constexpr size_t EVENT_LENGTH_OFFSET = 1;
+
+  // 2 bytes for handle and flags, 12 bits for length (Volume 2, Part E, 5.4.5)
+  static constexpr size_t ISO_PREAMBLE_SIZE = 4;
+  static constexpr size_t ISO_LENGTH_OFFSET = 2;
+
+  PacketReadCallback command_cb_;
+  PacketReadCallback event_cb_;
+  PacketReadCallback acl_cb_;
+  PacketReadCallback sco_cb_;
+  PacketReadCallback iso_cb_;
+
+  size_t HciGetPacketLengthForType(PacketType type,
+                                   const uint8_t* preamble) const;
+
+  PacketType hci_packet_type_{PacketType::UNKNOWN};
+
+  State state_{HCI_TYPE};
+  uint8_t packet_type_{};
+  std::vector<uint8_t> packet_;
+  size_t bytes_wanted_{0};
+};
+
+inline std::ostream& operator<<(std::ostream& os,
+                                H4Parser::State const& state_) {
+  os << (state_ == H4Parser::State::HCI_TYPE
+             ? "HCI_TYPE"
+             : state_ == H4Parser::State::HCI_PREAMBLE ? "HCI_PREAMBLE"
+                                                       : "HCI_PAYLOAD");
+  return os;
+}
+
+}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/test/h4_parser_unittest.cc b/vendor_libs/test_vendor_lib/test/h4_parser_unittest.cc
new file mode 100644 (file)
index 0000000..b5efe5f
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "model/devices/h4_parser.h"
+
+#include <gtest/gtest.h>
+
+#include "osi/include/osi.h"  // for OSI_NO_INTR
+
+namespace test_vendor_lib {
+using PacketData = std::vector<uint8_t>;
+
+class H4ParserTest : public ::testing::Test {
+ public:
+ protected:
+  void SetUp() override {
+    packet_.clear();
+    parser_.Reset();
+  }
+
+  void TearDown() override { parser_.Reset(); }
+
+  void PacketReadCallback(const std::vector<uint8_t>& packet) {
+    packet_ = std::move(packet);
+  }
+
+ protected:
+  H4Parser parser_{
+      [&](auto p) {
+        type_ = PacketType::COMMAND;
+        PacketReadCallback(p);
+      },
+      [&](auto p) {
+        type_ = PacketType::EVENT;
+        PacketReadCallback(p);
+      },
+      [&](auto p) {
+        type_ = PacketType::ACL;
+        PacketReadCallback(p);
+      },
+      [&](auto p) {
+        type_ = PacketType::SCO;
+        PacketReadCallback(p);
+      },
+      [&](auto p) {
+        type_ = PacketType::ISO;
+        PacketReadCallback(p);
+      },
+  };
+  PacketData packet_;
+  PacketType type_;
+};
+
+TEST_F(H4ParserTest, InitiallyExpectOneByte) {
+  ASSERT_EQ(1, (int)parser_.BytesRequested());
+}
+
+TEST_F(H4ParserTest, SwitchStateAfterType) {
+  uint8_t typ = (uint8_t)PacketType::ACL;
+  ASSERT_TRUE(parser_.Consume(&typ, 1));
+  ASSERT_EQ(parser_.CurrentState(), H4Parser::State::HCI_PREAMBLE);
+}
+
+TEST_F(H4ParserTest, RequestedBytesDecreases) {
+  // Make sure that the requested bytes is monotonically decreasing
+  // for the requested types.
+  uint8_t typ = (uint8_t)PacketType::ACL;
+  ASSERT_TRUE(parser_.Consume(&typ, 1));
+  auto wanted = parser_.BytesRequested();
+  while (wanted > 0) {
+    ASSERT_EQ(wanted, parser_.BytesRequested());
+    ASSERT_TRUE(parser_.Consume(&typ, 1));
+    wanted--;
+  }
+
+  ASSERT_EQ(parser_.CurrentState(), H4Parser::State::HCI_PAYLOAD);
+  wanted = parser_.BytesRequested();
+  while (wanted > 0) {
+    ASSERT_EQ(wanted, parser_.BytesRequested());
+    ASSERT_TRUE(parser_.Consume(&typ, 1));
+    wanted--;
+  }
+
+  // A callback should have been invoked.
+  ASSERT_LT(0, (int)packet_.size());
+}
+
+TEST_F(H4ParserTest, RejectNoData) {
+  // You need to give us something!
+  PacketData bad_bit;
+  ASSERT_FALSE(parser_.Consume(bad_bit.data(), bad_bit.size()));
+}
+
+TEST_F(H4ParserTest, TooMuchIsDeath) {
+  PacketData bad_bit({0xfd});
+  ASSERT_DEATH(parser_.Consume(bad_bit.data(), parser_.BytesRequested() + 1),
+               "More bytes read (.*) than expected (.*)!");
+}
+
+TEST_F(H4ParserTest, WrongTypeIsDeath) {
+  PacketData bad_bit({0xfd});
+  ASSERT_DEATH(parser_.Consume(bad_bit.data(), bad_bit.size()),
+               "Unimplemented packet type.*");
+}
+
+TEST_F(H4ParserTest, CallsTheRightCallbacks) {
+  // Make sure that the proper type of callback is invoked.
+  std::vector<PacketType> types({PacketType::ACL, PacketType::SCO,
+                                 PacketType::COMMAND, PacketType::EVENT,
+                                 PacketType::ISO});
+  for (auto packetType : types) {
+    // Configure the incoming packet.
+    uint8_t typ = (uint8_t)packetType;
+    ASSERT_TRUE(parser_.Consume(&typ, 1));
+
+    // Feed data as long as this packet is not complete.
+    while (parser_.CurrentState() != H4Parser::State::HCI_TYPE) {
+      PacketData data;
+      for (uint32_t i = 0; i < parser_.BytesRequested(); i++) {
+        data.push_back((uint8_t)i);
+      }
+      ASSERT_TRUE(parser_.Consume(data.data(), data.size()));
+    }
+
+    // The proper callbacks should have been invoked.
+    ASSERT_LT(0, (int)packet_.size());
+    ASSERT_EQ(packetType, type_);
+  }
+}
+
+}  // namespace test_vendor_lib