OPP

The purpose of this document is to give an overview of the Object Push Profile. The OPP defines the requirements for the protocols and procedures that shall be used by the applications providing the Object Push usage model. This profile makes use of the Generic Object Exchange Profile, to define the interoperability requirements for the protocols needed by applications. Common devices using these models are notebook PCs, PDAs, and mobile phones.

Terminology and Concepts

OPP Roles

OPP roles are typically defined as bellow.

  • Device A (DevA) - This device pushes and pulls objects to and from the Push Server.

    • OPP client

  • Device B (DevB) - This device provides an object exchange server.

    • OPP server

OPP Profile Stack

Protocols and entities used in OPP profile are shown below.

Feature Set

The functions provided by OPP APIs are as below.

  1. Connecting and Disconnecting: Including functions to connect/disconnect OPP.

  2. Sending and Receiving Object: Including functions to send objects through OPP between devices.

Provided APIs

The table below shows a simple description of OPP APIs. OPP related APIs are provided in the sdk\inc\framework\bt\bt_opp.h.

Header File

Description

API Reference

bt_opp.h

Connect OPP, send object etc.

Bluetooth OPP Profile

Functions

OPP Init

bool bt_opp_init()

Initialize OPP profile, and set RFCOMM channel number and L2CAP PSM.

void app_opp_init(void)
{
    bt_sdp_record_add((void *)opp_sdp_record);
    bt_opp_init(RFC_OPP_CHANN_NUM, L2CAP_OPP_PSM);
    bt_mgr_cback_register(app_opp_bt_cback);
}

OPP Connect

bool bt_opp_connect_over_l2c_req()

Initiate OPP connection over L2CAP to a remote device , as shown below.

bool bt_opp_connect_over_rfc_req()

Initiate OPP connection over RFCOMM to a remote device , as shown below.

bool bt_opp_disconnect_req()

Request OPP disconnection, as shown below.

bool bt_opp_connect_cfm()

Accept or reject the incoming OPP connection from the remote device.

OPP Data Transfer

bool bt_opp_push_data_header_req()

OPP client request to push data header to OPP server. The type of data header is MIME type, which can be found in https://datatracker.ietf.org/doc/html/rfc6838.

bool bt_opp_push_data_req()

OPP client request to push data to OPP server.

bool bt_opp_send_data_rsp()

OPP server acknowledgment on receiving an OPP data Indication.

bool bt_opp_push_abort()

Request to terminate the push operation.

OPP data transfer between OPP client and OPP server is shown below.

Source Code Overview

The following sections describe important parts of OPP application.

As OPP client, after sending data, the next data cannot be sent until the BT_EVENT_OPP_DATA_RSP is received.

As server, after receiving data BT_EVENT_OPP_DATA_IND, application must use bt_opp_send_data_rsp to send response.

#define RFC_OPP_CHANN_NUM               24
#define L2CAP_OPP_PSM                   0x1003

const uint8_t opp_sdp_record[] =
{
    SDP_DATA_ELEM_SEQ_HDR,
    0x50,
    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,
    0x03,
    SDP_UUID16_HDR,
    (uint8_t)(UUID_OBEX_OBJECT_PUSH >> 8),
    (uint8_t)(UUID_OBEX_OBJECT_PUSH),

    SDP_UNSIGNED_TWO_BYTE,
    (uint8_t)(SDP_ATTR_PROTO_DESC_LIST >> 8),
    (uint8_t)SDP_ATTR_PROTO_DESC_LIST,
    SDP_DATA_ELEM_SEQ_HDR,
    0x11,
    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_OPP_CHANN_NUM,
    SDP_DATA_ELEM_SEQ_HDR,
    0x03,
    SDP_UUID16_HDR,
    (uint8_t)(UUID_OBEX >> 8),
    (uint8_t)(UUID_OBEX),

    SDP_UNSIGNED_TWO_BYTE,
    (uint8_t)(SDP_ATTR_PROFILE_DESC_LIST >> 8),
    (uint8_t)SDP_ATTR_PROFILE_DESC_LIST,
    SDP_DATA_ELEM_SEQ_HDR,
    0x08,
    SDP_DATA_ELEM_SEQ_HDR,
    0x06,
    SDP_UUID16_HDR,
    (uint8_t)(UUID_OBEX_OBJECT_PUSH >> 8),
    (uint8_t)UUID_OBEX_OBJECT_PUSH,
    SDP_UNSIGNED_TWO_BYTE,
    0x01,
    0x02,   /* version 1.2 */

    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,
    0x0B,
    'R', 'e', 'a', 'l', 't', 'e', 'k', '-', 'O', 'P', 'P',

    SDP_UNSIGNED_TWO_BYTE,
    (uint8_t)((SDP_ATTR_L2C_PSM) >> 8),
    (uint8_t)(SDP_ATTR_L2C_PSM),
    SDP_UNSIGNED_TWO_BYTE,
    (uint8_t)(L2CAP_OPP_PSM >> 8),
    (uint8_t)(L2CAP_OPP_PSM),

    SDP_UNSIGNED_TWO_BYTE,
    (uint8_t)((SDP_ATTR_SUPPORTED_FORMATS_LIST) >> 8),
    (uint8_t)(SDP_ATTR_SUPPORTED_FORMATS_LIST),
    SDP_DATA_ELEM_SEQ_HDR,
    0x0a,
    SDP_UNSIGNED_ONE_BYTE,
    0x01, /* vCard 2.1 */
    SDP_UNSIGNED_ONE_BYTE,
    0x02, /* vCard 3.0 */
    SDP_UNSIGNED_ONE_BYTE,
    0x03, /* vCal 1.0 */
    SDP_UNSIGNED_ONE_BYTE,
    0x04, /* vCal 2.0 */
    SDP_UNSIGNED_ONE_BYTE,
    0xff, /* any type of object */
};

static void app_opp_bt_cback(T_BT_EVENT event_type, void *event_buf, uint16_t buf_len)
{
    T_BT_EVENT_PARAM *param = event_buf;
    bool handle = true;

    switch (event_type)
    {
    case BT_EVENT_OPP_CONN_IND:
        {
            bt_opp_connect_cfm(param->opp_conn_ind.bd_addr, true);
        }
        break;

    case BT_EVENT_OPP_CONN_CMPL:
        {
            // bt_opp_push_data_header_req(param->opp_conn_cmpl.bd_addr, total_len, name, sizeof(name), type, sizeof(type), false);
            // data_idx += data_len;
        }
        break;

    case BT_EVENT_OPP_DATA_HEADER_IND:
        {
            APP_PRINT_INFO5("app_opp_bt_cback: DATA_HEADER_IND %x, %s, %s, %x, %x",
                            param->opp_data_header_ind.total_len,
                            TRACE_STRING(param->opp_data_header_ind.name), TRACE_STRING(param->opp_data_header_ind.type),
                            param->opp_data_header_ind.name_len, param->opp_data_header_ind.type_len);
        }
        break;

    case BT_EVENT_OPP_DATA_IND:
        {
            if (!param->opp_data_ind.data_end)
            {
                //bt_opp_send_data_rsp(param->opp_data_ind.bd_addr, BT_OPP_RSP_CONTINUE);
            }
            else
            {
                //bt_opp_send_data_rsp(param->opp_data_ind.bd_addr, BT_OPP_RSP_SUCCESS);
            }

            APP_PRINT_INFO3("app_opp_bt_cback: DATA_IND 0x%04x, end %x, first_data %x",
                            param->opp_data_ind.data_len,
                            param->opp_data_ind.data_end, *(uint8_t *)param->opp_data_ind.p_data);
        }
        break;

    case BT_EVENT_OPP_DATA_RSP:
        {
            if (param->opp_data_rsp.cause == BT_OPP_RSP_CONTINUE)
            {
                if (data_idx != 0)
                {
                    if (data_idx + data_len < total_len)
                    {
                        bt_opp_push_data_req(param->opp_data_rsp.bd_addr, &data[data_idx], data_len, true);
                        data_idx += data_len;
                    }
                    else
                    {
                        bt_opp_push_data_req(param->opp_data_rsp.bd_addr, &data[data_idx], total_len - data_idx, false);
                        data_idx = 0;
                    }
                }
            }
        }
        break;

    case BT_EVENT_SDP_ATTR_INFO:
        {
            T_BT_SDP_ATTR_INFO *sdp_info = &param->sdp_attr_info.info;

            if (sdp_info->srv_class_uuid_data.uuid_16 == UUID_OBEX_OBJECT_PUSH)
            {
                if (sdp_info->profile_version >= 0x0102)
                {
                    bt_opp_connect_over_l2c_req(param->sdp_attr_info.bd_addr, sdp_info->l2c_psm);
                }
                else
                {
                    bt_opp_connect_over_rfc_req(param->sdp_attr_info.bd_addr, sdp_info->server_channel);
                }
            }
        }
        break;

    default:
        handle = false;
        break;
    }

    if (handle == true)
    {
        APP_PRINT_INFO1("app_opp_bt_cback: event_type 0x%04x", event_type);
    }
}

bool app_opp_connect_req(uint8_t *bd_addr)
{
    T_BT_SDP_UUID_DATA uuid;

    uuid.uuid_16 = UUID_OBEX_OBJECT_PUSH;

    return bt_sdp_discov_start(bd_addr, BT_SDP_UUID16, uuid);
}