iAP
The purpose of this document is to provide an overview of the iAP profile.
The iAP profile is always used in authenticated products with an MFi License. It provides a mechanism for reliable and ordered delivery of packetized data belonging to one or more iAP external accessory sessions.
The figure below shows the protocols and entities used in this profile.
As shown above, the iAP profile is based on the RFCOMM protocol. Applications can interact with the iAP profile through iAP profile APIs and callback messages.
Terminology and Concepts
iAP Role
Accessory - Refers to any product that connects to an iPhone, iPad, or iPod device.
Device - Refers to an iPhone, iPad, or iPod.
According to the definition above, only the accessory role is supported in this profile.
iAP Authentication
iAP accessory authentication provides a mechanism for devices to trust the identities and feature sets of accessories. iAP accessory authentication requires an Apple Authentication 3.0 Coprocessor. Earlier versions of the Apple Authentication Coprocessor are not allowed. An accessory may use one authentication processor to authenticate itself to more than one device. Conversely, multiple accessories shall not share one Apple Authentication Coprocessor or authenticate on behalf of each other to the same device.
Feature Set
The iAP profile version and supported ICs are detailed in BR/EDR Supported Profiles in Supported Features. The provided functions are as below:
iAP initialization: Including functions to initialize iAP profile.
iAP Connection: Including functions to connect/disconnect iAP profile.
iAP Communication: Including functions to transmit data via iAP profile.
Provided APIs
The iAP profile APIs can be divided into several parts:
iAP initialization APIs: The user must call iAP initialization function before calling other iAP profile APIs.
iAP connection control APIs: The user can call these APIs to connect and disconnect iAP profile.
iAP control session APIs: The user can call these APIs to send data over iAP control session.
iAP external accessory session APIs: The user can call these APIs to send data over iAP external accessory session.
The table below shows a simple description of iAP profile APIs.
Header file |
Description |
API Reference |
---|---|---|
bt_iap.h |
Initialize/Establish/Control iAP profile, etc. |
Functions
iAP Init
bt_iap_init() - Initialize iAP profile, including the RFCOMM channel number, CP write hook, and CP read hook used for iAP.
bt_iap_param_set() - Set local iAP parameter. Application can call this function after initiating iAP or use default settings in framework.
void app_iap_init(void)
{
if (app_cfg_const.supported_profile_mask & IAP_PROFILE_MASK)
{
if (app_cfg_const.enable_multi_link)
{
bt_iap_init(2, RFC_IAP_CHANN_NUM, app_cp_write, app_cp_burst_read);
}
else
{
bt_iap_init(1, RFC_IAP_CHANN_NUM, app_cp_write, app_cp_burst_read);
}
bt_mgr_cback_register(app_iap_bt_cback);
}
}
Application should register iAP SDP table when initializing iAP. Application can call bt_sdp_record_add() to register iAP SDP table.
static const uint8_t iap_sdp_record[] =
{
SDP_DATA_ELEM_SEQ_HDR,
0x4F,
//attribute SDP_ATTR_SRV_CLASS_ID_LIST
SDP_UNSIGNED_TWO_BYTE,
(uint8_t)(SDP_ATTR_SRV_CLASS_ID_LIST >> 8),
(uint8_t)SDP_ATTR_SRV_CLASS_ID_LIST,
SDP_DATA_ELEM_SEQ_HDR,
0x11,
SDP_UUID128_HDR,
0x00, 0x00, 0x00, 0x00, 0xde, 0xca, 0xfa, 0xde, 0xde, 0xca, 0xde, 0xaf, 0xde, 0xca, 0xca, 0xff,
//attribute SDP_ATTR_PROTO_DESC_LIST
SDP_UNSIGNED_TWO_BYTE,
(uint8_t)(SDP_ATTR_PROTO_DESC_LIST >> 8),
(uint8_t)SDP_ATTR_PROTO_DESC_LIST,
SDP_DATA_ELEM_SEQ_HDR,
0x0c,
SDP_DATA_ELEM_SEQ_HDR,
0x03,
SDP_UUID16_HDR,
(uint8_t)(UUID_L2CAP >> 8),
(uint8_t)UUID_L2CAP,
SDP_DATA_ELEM_SEQ_HDR,
0x05,
SDP_UUID16_HDR,
(uint8_t)(UUID_RFCOMM >> 8),
(uint8_t)UUID_RFCOMM,
SDP_UNSIGNED_ONE_BYTE,
RFC_IAP_CHANN_NUM,
//attribute SDP_ATTR_BROWSE_GROUP_LIST
SDP_UNSIGNED_TWO_BYTE,
(uint8_t)(SDP_ATTR_BROWSE_GROUP_LIST >> 8),
(uint8_t)SDP_ATTR_BROWSE_GROUP_LIST,
SDP_DATA_ELEM_SEQ_HDR,
0x03,
SDP_UUID16_HDR,
(uint8_t)(UUID_PUBLIC_BROWSE_GROUP >> 8),
(uint8_t)UUID_PUBLIC_BROWSE_GROUP,
//attribute SDP_ATTR_LANG_BASE_ATTR_ID_LIST
SDP_UNSIGNED_TWO_BYTE,
(uint8_t)(SDP_ATTR_LANG_BASE_ATTR_ID_LIST >> 8),
(uint8_t)SDP_ATTR_LANG_BASE_ATTR_ID_LIST,
SDP_DATA_ELEM_SEQ_HDR,
0x09,
SDP_UNSIGNED_TWO_BYTE,
(uint8_t)(SDP_LANG_ENGLISH >> 8),
(uint8_t)SDP_LANG_ENGLISH,
SDP_UNSIGNED_TWO_BYTE,
(uint8_t)(SDP_CHARACTER_UTF8 >> 8),
(uint8_t)SDP_CHARACTER_UTF8,
SDP_UNSIGNED_TWO_BYTE,
(uint8_t)(SDP_BASE_LANG_OFFSET >> 8),
(uint8_t)SDP_BASE_LANG_OFFSET,
//attribute SDP_ATTR_SRV_NAME
SDP_UNSIGNED_TWO_BYTE,
(uint8_t)((SDP_ATTR_SRV_NAME + SDP_BASE_LANG_OFFSET) >> 8),
(uint8_t)(SDP_ATTR_SRV_NAME + SDP_BASE_LANG_OFFSET),
SDP_STRING_HDR,
0x0C,
0x57, 0x69, 0x72, 0x65, 0x6c, 0x65, 0x73, 0x73, 0x20, 0x69, 0x41, 0x50 //"Wireless iAP"
};
void app_main_task(void *p_param)
{
if (supported_profile_mask & IAP_PROFILE_MASK)
{
bt_sdp_record_add((void *)iap_sdp_record);
}
}
Note
The remote device will connect iAP using the RFCOMM channel number contained in iAP SDP table; so the user must choose a unique RFCOMM channel number for iAP.
iAP Communication
The flow diagram for the establishment and detachment of an iAP connection between two devices is shown below:
iAP Connect
As shown in the figure of the flow diagram, step 1 ~ step 5 describe the flow of iAP establishment. Both local and remote device can initiate the iAP connection. As shown below, an iAP connection can be initiated by the local application.
If the iAP connection is initiated by the local device, the application should first discover the remote iAP RFCOMM channel by sdp. Then the application can call bt_iap_connect_req() to initiate the iAP connection.
As shown below, an iAP connection can also be initiated by the remote device. After the remote device starts a RFCOMM connect request for iAP, the local app will receive BT_EVENT_IAP_CONN_IND. The local application should call bt_iap_connect_cfm() to reply to the received iAP connect indication event, whether to accept or reject.
After the iAP2 parameters negotiation is finished successfully, the application will receive BT_EVENT_IAP_CONN_CMPL.
If failed in step 1 or step 2 as shown in the figure of connection flow, the application will receive BT_EVENT_IAP_CONN_FAIL.
If failed in step 3 as shown in the figure of connection flow, the application will receive BT_EVENT_IAP_AUTHEN_CMPL with result false.
If success in step 3, the application will receive BT_EVENT_IAP_IDENTITY_INFO_REQ and should call bt_iap_ident_info_send() to respond with identification information.
If the remote device accepts the identification information, the application will receive BT_EVENT_IAP_AUTHEN_CMPL with result true. Otherwise, BT_EVENT_IAP_AUTHEN_CMPL with result false will be received.
void app_main_task(void *p_param)
{
/* after discovering the remote iAP RFCOMM channel */
if(bt_iap_connect_req(bd_addr, remote_server_chann, frame_size, init_credits) == true)
{
/*connect request has been sent successfully, do something*/
}
else
{
/*connect request failed to send, do something*/
}
}
void app_iap_send_ident_info(uint8_t *bd_addr)
{
uint8_t *p_buf;
uint8_t *p;
uint8_t *p_head;
p_buf = os_mem_alloc(RAM_TYPE_DATA_ON, 300);
if (p_buf == NULL)
{
return;
}
APP_PRINT_TRACE1("app_iap_send_ident_info: p_buf %p", p_buf);
p_head = p_buf;
p_buf = bt_iap2_prep_param(p_buf, BT_IAP_ID_NAME, (void *)app_cfg_nv.device_name_legacy,
strlen((const char *)app_cfg_nv.device_name_legacy) + 1);
char module_identifier[] = "1.0";
p_buf = bt_iap2_prep_param(p_buf, BT_IAP_ID_MODEL_IDENTIFIER, module_identifier,
sizeof(module_identifier));
char manufacturer[] = "realtek";
p_buf = bt_iap2_prep_param(p_buf, BT_IAP_ID_MANUFACTURER, manufacturer, sizeof(manufacturer));
char serial_num[] = "1.0";
p_buf = bt_iap2_prep_param(p_buf, BT_IAP_ID_SERIAL_NUMBER, serial_num, sizeof(serial_num));
char fw_version[6] = "1.0.0";
p_buf = bt_iap2_prep_param(p_buf, BT_IAP_ID_FIRMWARE_VERSION, fw_version, sizeof(fw_version));
char hw_version[6] = "2.0.0";
p_buf = bt_iap2_prep_param(p_buf, BT_IAP_ID_HARDWARE_VERSION, hw_version, sizeof(hw_version));
uint8_t msg_sent_by_accessory[2];
p = msg_sent_by_accessory;
BE_UINT16_TO_STREAM(p, BT_IAP_MSG_REQUEST_APP_LAUNCH);
p_buf = bt_iap2_prep_param(p_buf, BT_IAP_ID_MESSAGES_SENT_BY_ACCESSORY, msg_sent_by_accessory,
sizeof(msg_sent_by_accessory));
uint8_t msg_received_from_device[4];
p = msg_received_from_device;
BE_UINT16_TO_STREAM(p, BT_IAP_MSG_START_EA_PROTOCOL_SESSION);
BE_UINT16_TO_STREAM(p, BT_IAP_MSG_STOP_EA_PROTOCOL_SESSION);
p_buf = bt_iap2_prep_param(p_buf, BT_IAP_ID_MESSAGES_RECEIVED_FROM_DEVICE, msg_received_from_device,
sizeof(msg_received_from_device));
//BT_IAP_ID_POWER_SOURCETYPE
uint8_t power_source = 0;
p_buf = bt_iap2_prep_param(p_buf, BT_IAP_ID_POWER_SOURCETYPE, &power_source, sizeof(power_source));
//BT_IAP_ID_MAXIMUM_CURRENT_DRAWN_FROM_DEVICE
uint16_t max_current_drawn_from_device = 0;
p_buf = bt_iap2_prep_param(p_buf, BT_IAP_ID_MAXIMUM_CURRENT_DRAWN_FROM_DEVICE,
&max_current_drawn_from_device,
sizeof(max_current_drawn_from_device));
//Supported EA protocol group
{
uint8_t *p = p_buf;
p_buf = bt_iap2_prep_param(p_buf, BT_IAP_ID_SUPPORTED_EXTERNAL_ACCESSORY_PROTOCOL, NULL, 0);
//group : EA_PROTOCOL_ID
uint8_t ea_protocol_id = 1;
p_buf = bt_iap2_prep_param(p_buf, BT_IAP_EXT_ACC_PROTOCOL_ID, &ea_protocol_id,
sizeof(ea_protocol_id));
char ea_protocol_name[] = "com.rtk.datapath";
p_buf = bt_iap2_prep_param(p_buf, BT_IAP_EXT_ACC_PROTOCOL_NAME, ea_protocol_name,
sizeof(ea_protocol_name));
//group EA_PROTOCOL_MATCHACTION
uint8_t ea_protocol_match_action = 0x02;
p_buf = bt_iap2_prep_param(p_buf, BT_IAP_EXT_ACC_PROTOCOL_MATCHACTION, &ea_protocol_match_action,
sizeof(ea_protocol_match_action));
//calculate total Length
uint16_t total_len = p_buf - p;
BE_UINT16_TO_STREAM(p, total_len);
}
//BT_IAP_ID_PREFERRED_APP_MATCH_TEAM_ID
char app_match_team_id[] = "C6P64J2MZX";
p_buf = bt_iap2_prep_param(p_buf, BT_IAP_ID_PREFERRED_APP_MATCH_TEAM_ID, app_match_team_id,
sizeof(app_match_team_id));
//BT_IAP_ID_CURRENT_LANGUAGE
char current_language[] = "en";
p_buf = bt_iap2_prep_param(p_buf, BT_IAP_ID_CURRENT_LANGUAGE, current_language, sizeof(current_language));
//BT_IAP_ID_SUPPORTED_LANGUAGE
char supported_language[] = "en";
p_buf = bt_iap2_prep_param(p_buf, BT_IAP_ID_SUPPORTED_LANGUAGE, &supported_language,
sizeof(supported_language));
//BT_COMPONENT_ID group
{
uint8_t *p = p_buf;
p_buf = bt_iap2_prep_param(p_buf, BT_IAP_ID_BLUETOOTH_TRANSPORT_COMPONENT, NULL, 0);
//group : BT_COMPONENT_ID
uint16_t component_id = 0x1234;
p_buf = bt_iap2_prep_param(p_buf, BT_IAP_TRANSPORT_COMPONENT_IDENTIFIER, &component_id,
sizeof(component_id));
//group BT_IAP_TRANSPORT_COMPONENT_NAME
char acc_name[] = "component_name";
p_buf = bt_iap2_prep_param(p_buf, BT_IAP_TRANSPORT_COMPONENT_NAME, acc_name, sizeof(acc_name));
//group BT_IAP_TRANSPORT_SUPPORTS_IAP2_CONNECTION
p_buf = bt_iap2_prep_param(p_buf, BT_IAP_TRANSPORT_SUPPORTS_IAP2_CONNECTION, NULL, 0);
uint8_t local_bt_addr[6] = {0x00};
uint8_t mac_addr[6] = {0x00};
gap_get_param(GAP_PARAM_BD_ADDR, local_bt_addr);
mac_addr[0] = local_bt_addr[5];
mac_addr[1] = local_bt_addr[4];
mac_addr[2] = local_bt_addr[3];
mac_addr[3] = local_bt_addr[2];
mac_addr[4] = local_bt_addr[1];
mac_addr[5] = local_bt_addr[0];
p_buf = bt_iap2_prep_param(p_buf, BT_IAP_BLUETOOTH_TRANSPORT_MEDIA_ACCESS_CONTROL_ADDRESS, mac_addr,
sizeof(mac_addr));
//calculate total Length
uint16_t total_len = p_buf - p;
BE_UINT16_TO_STREAM(p, total_len);
}
APP_PRINT_TRACE2("app_iap_send_ident_info: p_buf %p, p_head %p", p_buf, p_head);
bt_iap_send_ident_info(bd_addr, p_head, p_buf - p_head);
os_mem_free(p_head);
}
void app_iap_bt_cback(T_BT_EVENT event_type, void *event_buf, uint16_t buf_len)
{
T_BT_EVENT_PARAM *param = event_buf;
switch (event_type)
{
case BT_EVENT_IAP_AUTHEN_CMPL:
{
T_APP_BR_LINK *p_link;
p_link = app_find_br_link(param->iap_authen_cmpl.bd_addr);
if (p_link != NULL)
{
if (param->iap_authen_cmpl.result == true)
{
p_link->iap_authen_flag = true;
}
else
{
APP_PRINT_ERROR0("app_iap_bt_cback: iAP authen fail");
p_link->iap_authen_flag = false;
}
}
}
break;
case BT_EVENT_IAP_CONN_IND:
{
T_APP_BR_LINK *p_link;
p_link = app_find_br_link(param->iap_conn_ind.bd_addr);
if (p_link != NULL)
{
p_link->rfc_iap_frame_size = param->iap_conn_ind.frame_size;
bt_iap_connect_cfm(param->iap_conn_ind.bd_addr, true, param->iap_conn_ind.frame_size, 7);
}
}
break;
case BT_EVENT_IAP_CONN_CMPL:
{
T_APP_BR_LINK *p_link;
p_link = app_find_br_link(param->iap_conn_cmpl.bd_addr);
if (p_link != NULL)
{
p_link->rfc_iap_frame_size = param->iap_conn_cmpl.frame_size;
}
}
break;
case BT_EVENT_IAP_IDENTITY_INFO_REQ:
{
app_iap_send_ident_info(param->iap_identity_info_req.bd_addr);
}
break;
default:
break;
}
}
iAP External Accessory Session
External accessory(EA) session communication is an optional step as shown in step 6a of the figure of flow diagram.
The EA session is always initiated by the remote device. To apply this feature, the application should support receiving BT_IAP_MSG_START_EA_PROTOCOL_SESSION and BT_IAP_MSG_STOP_EA_PROTOCOL_SESSION messages from the remote device in identification information. The normal EA session communication flow as shown below:
After the remote device sends a start EA session message, the application will receive BT_EVENT_IAP_DATA_SESSION_OPEN event which means EA session messages can be transferred in this EA session.
After the EA session opened, the application can call bt_iap_data_send() to send messages.
If the remote device sends a message in the opened EA session, the application will receive BT_EVENT_IAP_DATA_IND event.
The application should call bt_iap_ack_send() to acknowledge the received messages if there is no message to reply. The application can also call bt_iap_data_send() to send messages instead of sending the acknowledge packet.
The remote device may close the EA session at any time, and the application will receive BT_EVENT_IAP_DATA_SESSION_CLOSE.
void app_iap_bt_cback(T_BT_EVENT event_type, void *event_buf, uint16_t buf_len)
{
T_BT_EVENT_PARAM *param = event_buf;
switch (event_type)
{
case BT_EVENT_IAP_DATA_SESSION_OPEN:
{
if(need_send_something_after_session_open)
{
bt_iap_data_send(param->iap_data_session_open.bd_addr, param->iap_data_session_open.session_id, buf, len_buf);
}
}
break;
case BT_EVENT_IAP_DATA_IND:
{
/*handle received data*/
if(need_reply_something)
{
bt_iap_data_send(param->iap_data_ind.bd_addr, param->iap_data_ind.eap_session_id, buf, len_buf);
}
else
{
bt_iap_ack_send(param->iap_data_ind.bd_addr, param->iap_data_ind.dev_seq_num);
}
}
break;
case BT_EVENT_IAP_DATA_SESSION_CLOSE:
{
/*do something after session close*/
}
break;
default:
break;
}
}
iAP Control Session
Control session communication is also an optional step as shown in step 6b of the flow diagram figure.
The control session is opened by default after iAP connect complete. All control messages are transferred in the control session.
Bluetooth Connection Status
The application can register messages from the remote device that report changes in connection status.
To apply this feature, the application should support sending MSG_BLUETOOTH_COMPONENT_INFO
(0x4E01), MSG_START_BT_CONNECTION_UPDATES
(0x4E03) and MSG_STOP_BT_CONNECTION_UPDATES
(0x4E05)
messages in identification information. And it should also support receiving MSG_BT_CONNECTION_UPDATE
(0x4E04) messages from remote device in identification information.
The normal connection status update flow is shown below:
After iAP link is established, the application can register connection status update.
According to the iAP specification,
MSG_BLUETOOTH_COMPONENT_INFO
is only supported in R34 version and earlier, which means iOS 14.2 and later do not support this message.The application can call bt_iap_comp_info_send() to send component identifier.
The application can call bt_iap_conn_update_start() to start Bluetooth connection update.
One or more connection update messages will be sent from the remote device after the Bluetooth connection update is started, and the application will receive the corresponding BT_EVENT_IAP_CTRL_MSG_IND event.
void app_iap_bt_cback(T_BT_EVENT event_type, void *event_buf, uint16_t buf_len)
{
T_BT_EVENT_PARAM *param = event_buf;
switch (event_type)
{
case BT_EVENT_IAP_DATA_SESSION_OPEN:
{
if(need_start_connection_update)
{
bt_iap_comp_info_send(param->iap_data_session_open.bd_addr, comp_id, true);
bt_iap_conn_update_start(param->iap_data_session_open.bd_addr, comp_id);
}
}
break;
case BT_EVENT_IAP_CTRL_MSG_IND:
{
/*handle received control message*/
}
break;
default:
break;
}
}
Mute Status
The application can apply communication status to notify mute status. To apply this feature, the application
should support sending MSG_START_COMM_UPDATES
(0x4157) and MSG_MUTE_STATUS_UPDATE
(0x4160) messages in identification information. If the application wants to register mute status from the remote device, receiving MSG_MUTE_STATUS_UPDATE
(0x4160) messages should be supported in identification information.
The normal mute status update flow is shown below:
After the iAP link is established, the application can start mute status updates by calling bt_iap_comm_update_start() with param_id set to 0x09.
The application can call bt_iap_mute_status_update to update mute status at any time after the mute status update has started.
The application may receive BT_EVENT_IAP_CTRL_MSG_IND event from the remote device that carries mute status once the mute status update has started.
void app_iap_bt_cback(T_BT_EVENT event_type, void *event_buf, uint16_t buf_len)
{
T_BT_EVENT_PARAM *param = event_buf;
switch (event_type)
{
case BT_EVENT_IAP_DATA_SESSION_OPEN:
{
if(need_start_mute_status_update)
{
bt_iap_comm_update_start(param->iap_data_session_open.bd_addr, 0x09);
bt_iap_mute_status_update(param->iap_data_session_open.bd_addr, current_mute_status);
}
}
break;
case BT_EVENT_IAP_CTRL_MSG_IND:
{
/*handle received control message*/
}
break;
default:
break;
}
}
Note
Mute status is global, it is not a per-call status.
APP Launch
The application can launch an APP installed on the remote device after iAP authentication successfully.
To enable this feature, the application should support sending BT_IAP_MSG_REQUEST_APP_LAUNCH message in identification information.
void app_iap_bt_cback(T_BT_EVENT event_type, void *event_buf, uint16_t buf_len)
{
T_BT_EVENT_PARAM *param = event_buf;
switch (event_type)
{
case BT_EVENT_IAP_AUTHEN_CMPL:
{
if (param->iap_authen_cmpl.result == true)
{
if(need_launch_app)
{
bt_iap_launch_app(param->iap_authen_cmpl.bd_addr, boundle_id, sizeof(boundle_id), method);
}
}
}
break;
default:
break;
}
}
iAP Disconnect
As shown in the figure of flow diagram, step 7 describes the flow of iAP detachment. An iAP connection can be initiated by the local application. The normal flow is shown below:
The application can call bt_iap_disconnect_req() to disconnect the iAP link. After the iAP link is disconnected, BT_EVENT_IAP_DISCONN_CMPL will be received, regardless of whether the disconnection procedure was initiated by the local application or the remote device.
void app_main_task(void *p_param)
{
/* after iAP is connected */
if(bt_iap_disconnect_req(bd_addr) == true)
{
/*disconnect request has been sent successfully, do something*/
}
else
{
/*disconnect request failed to send, do something*/
}
}
void app_iap_bt_cback(T_BT_EVENT event_type, void *event_buf, uint16_t buf_len)
{
T_BT_EVENT_PARAM *param = event_buf;
if(event_type == BT_EVENT_IAP_DISCONN_CMPL)
{
/*do something after iAP is disconnected*/
}
}