diff --git a/android/tester-gatt.c b/android/tester-gatt.c
index 2ceb295..6655f2d 100644
--- a/android/tester-gatt.c
+++ b/android/tester-gatt.c
#include "tester-main.h"
#include "src/shared/util.h"
+#define ATT_HANDLE_SIZE 2
+
#define L2CAP_ATT_EXCHANGE_MTU_REQ 0x02
#define L2CAP_ATT_EXCHANGE_MTU_RSP 0x03
+#define L2CAP_ATT_READ_REQ 0x0a
+#define L2CAP_ATT_READ_RSP 0x0b
#define L2CAP_ATT_HANDLE_VALUE_NOTIFY 0x1b
#define L2CAP_ATT_HANDLE_VALUE_IND 0x1d
#define CONN1_ID 1
#define CONN2_ID 2
+#define TRANS1_ID 1
+
#define GATT_SERVER_TRANSPORT_LE 0x00
#define GATT_SERVER_TRANSPORT_BREDR 0x01
#define GATT_SERVER_TRANSPORT_LE_BREDR 0x02
static int srvc1_handle;
static int inc_srvc1_handle;
+static int char1_handle;
+
+struct set_att_data {
+ char *to;
+ char *from;
+ int len;
+};
static bt_uuid_t app1_uuid = {
.uu = { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
char *p_value;
};
+struct send_resp_data {
+ int conn_id;
+ int trans_id;
+ int status;
+ btgatt_response_t *response;
+};
+
static bt_bdaddr_t emu_remote_bdaddr_val = {
.address = { 0x00, 0xaa, 0x01, 0x01, 0x00, 0x00 },
};
.bdaddr = &emu_remote_bdaddr_val
};
+static btgatt_response_t response_1 = {
+ .handle = 0x1c,
+ .attr_value.auth_req = 0,
+ .attr_value.handle = 0x1d,
+ .attr_value.len = 0,
+ .attr_value.offset = 0,
+};
+
+static struct send_resp_data send_resp_data_1 = {
+ .conn_id = CONN1_ID,
+ .trans_id = TRANS1_ID,
+ .status = BT_STATUS_SUCCESS,
+ .response = &response_1,
+};
+
static struct iovec search_service[] = {
raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28),
raw_pdu(0x11, 0x06, 0x01, 0x00, 0x10, 0x00, 0x00, 0x18),
end_pdu
};
+/* att commands define raw pdus */
+static struct iovec att_read_req = raw_pdu(0x0a, 0x00, 0x00);
+
+static void gatt_att_pdu_modify(void)
+{
+ struct test_data *data = tester_get_data();
+ struct step *current_data_step = queue_peek_head(data->steps);
+ struct iovec *store_pdu = current_data_step->set_data_to;
+ struct step *step = g_new0(struct step, 1);
+ unsigned char *raw_pdu = store_pdu->iov_base;
+ int set_data_len = current_data_step->set_data_len;
+
+ switch (raw_pdu[0]) {
+ case L2CAP_ATT_READ_REQ: {
+ uint16_t handle = *((int *)current_data_step->set_data);
+
+ memcpy(raw_pdu + 1, &handle, set_data_len);
+ tester_debug("gatt: modify pdu read request handle to 0x%02x",
+ handle);
+
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ schedule_action_verification(step);
+}
+
static void gatt_client_register_action(void)
{
struct test_data *data = tester_get_data();
schedule_action_verification(step);
}
+static void gatt_server_send_response_action(void)
+{
+ struct test_data *data = tester_get_data();
+ struct step *current_data_step = queue_peek_head(data->steps);
+ struct send_resp_data *send_resp_data = current_data_step->set_data;
+ struct step *step = g_new0(struct step, 1);
+
+ step->action_status = data->if_gatt->server->send_response(
+ send_resp_data->conn_id,
+ send_resp_data->trans_id,
+ send_resp_data->status,
+ send_resp_data->response);
+
+ schedule_action_verification(step);
+}
+
static void gatt_cid_hook_cb(const void *data, uint16_t len, void *user_data)
{
struct test_data *t_data = tester_get_data();
schedule_callback_verification(step);
break;
+ case L2CAP_ATT_READ_RSP:
+ /* TODO - More complicated cases should also verify pdu data */
+ step = g_new0(struct step, 1);
+
+ step->callback = CB_EMU_READ_RESPONSE;
+
+ schedule_callback_verification(step);
+ break;
default:
if (!gatt_pdu || !gatt_pdu->iov_base) {
tester_print("Unknown ATT packet.");
schedule_action_verification(step);
}
+static void gatt_remote_send_raw_pdu_action(void)
+{
+ struct test_data *data = tester_get_data();
+ struct bthost *bthost = hciemu_client_get_host(data->hciemu);
+ struct step *current_data_step = queue_peek_head(data->steps);
+ struct iovec *pdu = current_data_step->set_data;
+ struct step *step = g_new0(struct step, 1);
+
+ if (cid_data.handle && cid_data.cid) {
+ bthost_send_cid_v(bthost, cid_data.handle, cid_data.cid,
+ pdu, 1);
+ step->action_status = BT_STATUS_SUCCESS;
+ } else {
+ tester_debug("No connection set up");
+ step->action_status = BT_STATUS_FAIL;
+ }
+
+ schedule_action_verification(step);
+}
+
static void gatt_conn_cb(uint16_t handle, void *user_data)
{
struct test_data *data = tester_get_data();
ACTION_SUCCESS(bluetooth_disable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
),
+ TEST_CASE_BREDRLE("Gatt Server - Send response to read char request",
+ ACTION_SUCCESS(bluetooth_enable_action, NULL),
+ CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
+ ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+ ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
+ ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
+ ACTION_SUCCESS(gatt_server_register_action, &app1_uuid),
+ CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS),
+ ACTION_SUCCESS(gatt_server_add_service_action,
+ &add_service_data_5),
+ CALLBACK_GATTS_SERVICE_ADDED(GATT_STATUS_SUCCESS, APP1_ID,
+ &service_add_1, NULL,
+ &srvc1_handle),
+ ACTION_SUCCESS(gatt_server_add_char_action, &add_char_data_1),
+ CALLBACK_GATTS_CHARACTERISTIC_ADDED(GATT_STATUS_SUCCESS,
+ APP1_ID, &app1_uuid,
+ &srvc1_handle, NULL,
+ &char1_handle),
+ ACTION_SUCCESS(gatt_server_start_srvc_action,
+ &start_srvc_data_2),
+ CALLBACK_GATTS_SERVICE_STARTED(GATT_STATUS_SUCCESS, APP1_ID,
+ &srvc1_handle),
+ ACTION_SUCCESS(bt_start_discovery_action, NULL),
+ CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
+ BT_DISCOVERY_STARTED),
+ CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_le_set, 2),
+ ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
+ ACTION_SUCCESS(gatt_server_connect_action, &app1_conn_req),
+ CALLBACK_GATTS_CONNECTION(GATT_SERVER_CONNECTED,
+ prop_emu_remotes_default_set,
+ CONN1_ID, APP1_ID),
+ MODIFY_DATA(GATT_STATUS_SUCCESS, gatt_att_pdu_modify,
+ &char1_handle, &att_read_req,
+ ATT_HANDLE_SIZE),
+ ACTION_SUCCESS(gatt_remote_send_raw_pdu_action, &att_read_req),
+ CALLBACK_GATTS_REQUEST_READ(CONN1_ID, TRANS1_ID,
+ prop_emu_remotes_default_set,
+ &char1_handle, 0, false),
+ ACTION_SUCCESS(gatt_server_send_response_action,
+ &send_resp_data_1),
+ CALLBACK(CB_EMU_READ_RESPONSE),
+ ACTION_SUCCESS(bluetooth_disable_action, NULL),
+ CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+ ),
};
struct queue *get_gatt_tests(void)
diff --git a/android/tester-main.c b/android/tester-main.c
index 58f9e9f..ea76869 100644
--- a/android/tester-main.c
+++ b/android/tester-main.c
DBG_CB(CB_EMU_CONNECTION_REJECTED),
DBG_CB(CB_EMU_VALUE_INDICATION),
DBG_CB(CB_EMU_VALUE_NOTIFICATION),
+ DBG_CB(CB_EMU_READ_RESPONSE),
};
static gboolean check_callbacks_called(gpointer user_data)
return false;
}
+ if (exp->callback_result.attr_handle &&
+ step->callback_result.attr_handle)
+ if (*exp->callback_result.attr_handle !=
+ *step->callback_result.attr_handle) {
+ tester_debug("Gatt attribute handle mismatch: %d vs %d",
+ *step->callback_result.attr_handle,
+ *exp->callback_result.attr_handle);
+ return false;
+ }
+
if (exp->callback_result.srvc_handle &&
step->callback_result.srvc_handle)
if (*exp->callback_result.srvc_handle !=
return false;
}
+ if (exp->callback_result.trans_id != step->callback_result.trans_id) {
+ tester_debug("Gatt trans id mismatch: %d vs %d",
+ exp->callback_result.trans_id,
+ step->callback_result.trans_id);
+ return false;
+ }
+
+ if (exp->callback_result.offset != step->callback_result.offset) {
+ tester_debug("Gatt offset mismatch: %d vs %d",
+ exp->callback_result.offset,
+ step->callback_result.offset);
+ return false;
+ }
+
+ if (exp->callback_result.is_long != step->callback_result.is_long) {
+ tester_debug("Gatt is long attr value flag mismatch: %d vs %d",
+ exp->callback_result.is_long,
+ step->callback_result.is_long);
+ return false;
+ }
+
if (exp->store_srvc_handle)
memcpy(exp->store_srvc_handle,
step->callback_result.srvc_handle,
if (step->callback_result.desc_handle)
free(step->callback_result.desc_handle);
+ if (step->callback_result.attr_handle)
+ free(step->callback_result.attr_handle);
+
g_free(step);
g_atomic_int_dec_and_test(&scheduled_cbacks_num);
}
schedule_callback_verification(step);
}
+static void gatts_request_read_cb(int conn_id, int trans_id, bt_bdaddr_t *bda,
+ int attr_handle, int offset,
+ bool is_long)
+{
+ struct step *step = g_new0(struct step, 1);
+ bt_property_t *props[1];
+
+ step->callback = CB_GATTS_REQUEST_READ;
+
+ step->callback_result.conn_id = conn_id;
+ step->callback_result.trans_id = trans_id;
+ step->callback_result.attr_handle = g_memdup(&attr_handle,
+ sizeof(attr_handle));
+ step->callback_result.offset = offset;
+ step->callback_result.is_long = is_long;
+
+ /* Utilize property verification mechanism for bdaddr */
+ props[0] = create_property(BT_PROPERTY_BDADDR, bda, sizeof(*bda));
+
+ step->callback_result.num_properties = 1;
+ step->callback_result.properties = repack_properties(1, props);
+
+ g_free(props[0]->val);
+ g_free(props[0]);
+
+ schedule_callback_verification(step);
+}
+
static void pan_control_state_cb(btpan_control_state_t state,
bt_status_t error, int local_role,
const char *ifname)
.service_started_cb = gatts_service_started_cb,
.service_stopped_cb = gatts_service_stopped_cb,
.service_deleted_cb = gatts_service_deleted_cb,
- .request_read_cb = NULL,
+ .request_read_cb = gatts_request_read_cb,
.request_write_cb = NULL,
.request_exec_write_cb = NULL,
.response_confirmation_cb = NULL
diff --git a/android/tester-main.h b/android/tester-main.h
index 31d271a..e710a03 100644
--- a/android/tester-main.h
+++ b/android/tester-main.h
(struct step[]) {__VA_ARGS__}, \
}
+#define MODIFY_DATA(status, modif_fun, from, to, len) { \
+ .action_status = status, \
+ .action = modif_fun, \
+ .set_data = from, \
+ .set_data_to = to, \
+ .set_data_len = len, \
+ }
+
#define ACTION(status, act_fun, data_set) { \
.action_status = status, \
.action = act_fun, \
.callback_result.srvc_handle = cb_srvc_handle, \
}
+#define CALLBACK_GATTS_REQUEST_READ(cb_conn_id, cb_trans_id, cb_prop, \
+ cb_attr_handle, cb_offset, \
+ cb_is_long) { \
+ .callback = CB_GATTS_REQUEST_READ, \
+ .callback_result.conn_id = cb_conn_id, \
+ .callback_result.trans_id = cb_trans_id, \
+ .callback_result.properties = cb_prop, \
+ .callback_result.num_properties = 1, \
+ .callback_result.attr_handle = cb_attr_handle, \
+ .callback_result.offset = cb_offset, \
+ .callback_result.is_long = cb_is_long, \
+ }
+
#define CALLBACK_PAN_CTRL_STATE(cb, cb_res, cb_state, cb_local_role) { \
.callback = cb, \
.callback_result.status = cb_res, \
CB_EMU_CONNECTION_REJECTED,
CB_EMU_VALUE_INDICATION,
CB_EMU_VALUE_NOTIFICATION,
+ CB_EMU_READ_RESPONSE,
} expected_bt_callback_t;
struct test_data {
int gatt_app_id;
int conn_id;
+ int trans_id;
+ int offset;
+ bool is_long;
int connected;
+ int *attr_handle;
int *srvc_handle;
int *inc_srvc_handle;
int *char_handle;
struct bt_callback_data callback_result;
void *set_data;
+ void *set_data_to;
int set_data_len;
int *store_srvc_handle;