Push files to your mobile using Tcl

Published

In my previous post, I described how to connect to Bluetooth devices from Tcl. That however only went as far as discovering devices and establishing network connections and left unanswered the question of what you do next once a connection is established. This post, and succeeding ones, will build on that by showing how you can transfer files, contact information etc. to Bluetooth devices from Windows using the OBEX protocol.

To follow along this post, you will need the following packages:

Load the packages as below. For convenience, the proc pdict prints a dictionary in an easier to read form.

% package require iocp_bt
0.1
% package require obex
0.1
% proc pdict {d} {dict for {k v} $d {puts "$k: $v"}}

As implied by the version numbers, both packages, like my own understanding of the protocol, are in their infancy but nevertheless suffice to do useful work.

The OBEX protocol

The OBject EXchange (OBEX) protocol is similar in function to HTTP with commands like PUT, GET etc. for transferring data objects. It is geared towards smaller resource-constrained devices and thus differs from HTTP in design. The salient features of the protocol are:

  • It is transport-independent. Although originally developed for IrDA and now primarily run over Bluetooth transports, it can be used as an application level protocol over other transports.

  • It is primarily a binary request-response protocol. An OBEX conversation consists of a client sending a series of requests to a service which sends back responses. The protocol only allows at most one request outstanding from a client to a service using a specific transport connection.

  • A request logically consists of a request operation code, or opcode, and a sequence of typed headers which carry the actual object, such as contents of file, as well as meta information, such as file name, creation time and so on. The headers are typed and specify semantics as well, for example the Name header will hold the name of the object in big-endian UTF-16 encoding. The actual object content is carried in a sequence of one or more Body headers with an optional EndOfBody header.

  • A response similarly consists of a response code analogous to HTTP response codes followed by zero or more headers.

  • Both requests and responses are broken up into packets based on an optionally negotiated maximum size. Every request packet from the client by a response packet from the server and there can be at most one packet "in-flight" at a time. A special final marker in a request packet indicates it is the final packet in a request.

  • Optionally, a CONNECT request can be used to negotiate some parameters like maximum packet size to be used in a conversation. This is not necessary in all use cases but can improve efficiency as otherwise packets are restricted to 255 bytes.

Bluetooth OBEX profiles

Bluetooth specifications include a set of profiles targeted towards specific usage scenarios. These profiles specify the protocol stacks, messages and procedures that an implementation must conform to in order for devices to interoperate for that scenario. This includes profiles related to OBEX which define which headers are used for each scenario, the sequencing of request, authentication mechanisms etc.

The most general of these OBEX-related profiles, the Generic Object Exchange Profile (GOEP), defines the base requirements for all OBEX-based data exchange over Bluetooth. This includes specification of how devices are paired, the transport protocols (RFCOMM etc.) supported, channel establishment, usage of headers and service discovery.

Other OBEX profiles build on the GOEP for specific scenarios. They specify the subset of the GOEP features that must be supported and further detail the application-specific pieces, e.g. the use of vCard format for transferring contact information. Some commonly used profiles are

  • The Object Push Profile (OPP) is the simplest of the profiles. It provides for transferring contacts and calendars in vCard/iCal format, files and other objects to Bluetooth devices. Retrieval capabilities are very limited. The example in this post uses this profile.

  • The Phone Book Access Profile (PBAP) is a more featureful profile targetted specifically towards accessing and storing phone book objects such as contacts and call histories.

  • The Message Access Profile (MAP) addresses exchange of messages between devices. Message types include SMS, MMS, email and others.

  • The File Transfer Profile (FTP) provides functionality similar that provided by the more well known File Transfer Protocol over TCP/IP.

Transferring a file

With that introduction over, we can move on to a basic example of transferring a file to a mobile phone over Bluetooth. NOTE: This assumes that the phone has already been paired with the Windows system running the example below.

Most phones support services conforming to the Object Push Profile so that is what we will use. The obex package is broken up into namespaces based on the various profiles. The obex::opp obviously implements the OPP profile.

Pushing a file to the phone requires us to first establish a transport connection to the phone to carry our OBEX conversation. As discussed in the previous post, we have to first resolve address of the device and the port that the OPP service is listening on. The address is resolved as

% set addr [iocp::bt::device address ak]
ac:c1:ee:b3:e2:e8

where ak is my phone's Bluetooth name. To resolve the service's port, we need its UUID. The easiest way is to ask OPP for it since it is fixed for a profile.

% obex::opp::bt_uuid
00001105-0000-1000-8000-00805f9b34fb

Resolving the port is as simple as

% set port [iocp::bt::device port $addr [obex::opp::bt_uuid]]
7

We can then establish a Bluetooth connection to the service.

% set chan [iocp::bt::socket $addr $port]
bt000002462D0405C0

Now that the transport connection is established, we can create an Obex client object passing it the created channel and then call its push_file method to transfer a file.

% obex::opp::Client create client $chan
::client
% client push_file test.txt

At this point, you should see a pop-up on your phone asking you to accept the file. Make sure you tap the OK or Accept buttons. The directory in which the file is placed depends on the mobile platform and version. On my Redmi, incoming files are placed in the bluetooth folder. You may need to refresh the file manager application to see the file.

You can call push_file multiple times to send additional files.

Some services, like the File Manager on my Android phone, will not accept files with extensions that they do not recognize.

% file rename test.txt test.foo
% client push_file test.foo
OBEX CLIENT {ResponseStatus clienterror ResponseCode 207 ResponseCodeName unsupportedmediatype ErrorMessage {File push failed.}}

Note the unsupportedmediatype error. For such cases, the MIME type of the file content has to be explicitly passed. Below, the error is first cleared with a call to clear and then the push retried with an explicit MIME type.

% client clear
% client push_file test.foo text/plain

When done, the OBEX conversation and the transport connection must be closed. The OBEX conversation is ended by calling the close method on the client object. The transport channel is closed as always with the Tcl close command.

% client close
% client destroy
% close $chan

NOTE: Not all systems require the OBEX conversation to be explicitly closed. Pushing to another Windows 10 system however does require a graceful close as otherwise the sent files are discarded.

The above illustrated the simplest possible use of OBEX — pushing a file using the OPP profile. (This profile does not offer the capability to pull files.) It does not illustrate various other OBEX operations or options as well as is limited to synchronous behaviour meaning the application is blocked for the duration of the file transfer. Future posts will go into more complex modes of operation.

References