Documentation  |   Table of Contents   |  < Previous   |  Next >   |  Index

11    Developing Bluetooth-enabled Applications

Low-Level Communications

Exploring Palm OS®

Palm OS® exposes Bluetooth through multiple interfaces, allowing you to choose the interface that is best suited for the task at hand. Bluetooth development is supported through IOS STDIO calls. Object transfer is supported through the Exchange Manager using the Bluetooth Exchange Library, which is discussed in Chapter 12, "Bluetooth Exchange Library Support." Finally, you can program directly with the Bluetooth Library APIs, which is the subject of this section.

Regardless of which approach you take, your applications should check if the Bluetooth system is running on the device before using any Bluetooth APIs. To do so, use the code shown in Listing 11.1:

Listing 11.1  Making sure the device has Bluetooth support


UInt32 btVersion; 
 
// Make sure Bluetooth components are installed 
if (FtrGet(btLibFeatureCreator, btLibFeatureVersion, 
					&btVersion) != errNone) { 
	// Alert the user if it's the active application 
	if ((launchFlags & sysAppLaunchFlagNewGlobals) && 
		(launchFlags & sysAppLaunchFlagUIApp)) 
		FrmAlert (MissingBtComponentsAlert); 
	return sysErrRomIncompatible; 
} 

Overview of the Bluetooth Library ^TOP^

From a programmer's perspective, the functions of the Bluetooth library fall into six areas: management entity, sockets, service discovery, security, persistent services, and utility.

  • The management entity functions deal with the radio, baseband, and link manager parts of the Bluetooth specification. You use them to find nearby devices and to establish ACL links.
  • The socket functions enable communication with L2CAP, RFCOMM, and SDP protocols, as well as with SCO links.
  • The service discovery functions manage the local service database and query remote devices' service databases.
  • The security functions manage a set of trusted devices—devices that do not have to authenticate when they create a secure connection with the Palm OS device.
  • The persistent service functions provide a means of installing applications that run in the background and respond to inbound connections from remote devices.
  • The utility functions perform useful data conversions.

Compatibility ^TOP^

The entire communications architecture has changed with Palm OS Cobalt. While existing applications will continue to run, using a compatibility library, applications written to the Palm OS Cobalt and later Bluetooth need to conform to a few modest changes to the API.

Deprecated Functions

The functions BtLibRegisterManagementNotification() and BtLibUnregisterManagementNotification() no longer exist; instead, applications read events from the Management Entity device directly by polling its file descriptor.

The BtLibServicesOpen(), BtLibServicesClose(), and BtLibServicesIndicateSessionStart() functions have been removed as well. Services are no longer a special case.

Additionally, the BtLibDiscoverSingleDevice(), BtLibDiscoverMultipleDevices(), and BtLibGetSelectedDevices() functions have been replaced by one function: BtLibDiscoverDevices().

Parameter Changes

Functions that used to take a Bluetooth library reference number as an input parameter now require a file descriptor to one of the Management Entity, L2CAP, RFCOMM, or SDP device, depending on the specific function.

The BtLibOpen() function now returns an IOS file descriptor to the Management Entity device, and BtLibClose() closes that file descriptor. BtLibOpen() also no longer necessarily causes a radio state event; applications should not wait for a radio state event after calling BtLibOpen(). If the hardware is not available, the call to BtLibOpen() will simply fail. Likewise, BtLibOpen() will no longer necessarily cause an accessibility event; if the application needs to know the accessibility state, it should call BtLibGetGeneralPreference().

BtLibSocketCreate() no longer has callback procedure and callback context parameters. The function now returns a file descriptor opened to an L2CAP, RFCOMM, or SDP device. BtLibSocketClose() closes the file descriptor.

The BtLibSocketRef type is now a 32-bit value. It is a file descriptor to the underlying STREAMS device.

New Functions

There are several new functions:

Events

Applications now obtain events by polling IOS file descriptors, instead of through a callback function. See "Polling for Management Entity Events".

The Management Entity ^TOP^

Three basic management tasks common among Bluetooth applications are finding the Bluetooth devices in range, establishing ACL links, and working with piconets. However, in order for your code to use any of the functions that do these operations, you need to poll for events on the STREAMS devices for the relevant protocols.

Opening the Library ^TOP^

To open the Bluetooth library, use the BtLibOpen() function. If this returns without error, the Bluetooth Management Entity device is open and ready to go.

BtLibOpen() returns a file descriptor to the Management Entity. Every Management Entity file descriptor sees the same Management Entity; every program monitoring ME events receives the same events.

Polling for Management Entity Events ^TOP^

Most management calls are asynchronous. In other words, they start an operation and return before the operation actually completes. When the operation completes, the Bluetooth Library notifies the application by way of events posted on the Management Entity's event queue.

In some cases, a management function fails before starting the asynchronous operation. In this case, an event does not get sent. You can tell whether or not to expect to receive an event as a result of the call by looking at the management function's return code:

btLibErrNoError
The operation has completed and no event will be sent.
btLibErrPending
The operation has started successfully and an appropriate event will be sent,
any other error code
The operation failed and no event will be sent.

You can poll for these events either by calling IOSPoll() directly, or by using a PollBox. See Chapter 18, "Polling STREAMS File Descriptors," for an introduction to event polling.

As a simple example, consider the task of finding nearby devices, discussed in the next section. The callback function must respond to four events: btLibManagementEventInquiryResult, btLibManagementEventInquiryComplete, btLibManagementEventInquiryCanceled, and btLibManagementEventRadioState. The code in Listing 11.2 is a skeleton of the PollBox callback you need.

Listing 11.2  Polling for Management Entity events using a PollBox


void HandlePbxMEEvent( struct PollBox *pbx, struct pollfd *pollFd, void * ) { 
	status_t error; 
	int32_t flags; 
 
	static BtLibManagementEventType	mEvent; 
	static char mData[sizeof(BtLibFriendlyNameType)]; 
	static struct strbuf ctlBuf = { sizeof(mEvent), 0, (char*)&mEvent }; 
	static struct strbuf datBuf = { sizeof(mData),  0, (char*)&mData[0] }; 
 
	// We must be here for a reason... 
 
	ErrFatalErrorIf(!(pollFd->revents & (POLLIN|POLLERR|POLLHUP|POLLNVAL)), 
					"no event flag" ); 
 
	// We must have the Management Entity file descriptor. 
	ErrFatalErrorIf( pollFd->fd != gFdME, "not the ME fd" ); 
	ErrFatalErrorIf( pollFd->fd < 0, "ME fd closed" ); 
 
	// Check for error/eof from poll, read the event message. 
	flags = 0; 
	if ((pollFd->revents & (POLLERR|POLLHUP|POLLNVAL)) ||  
		IOSGetmsg(pollFd->fd, &ctlBuf, &datBuf, &flags, &error) != 0) { 
		PbxRemoveFd(pbx, pollFd->fd); 
		BtLibClose(pollFd->fd); 
		gFdME = -1; 
		return; 
	} 
 
	// We must have an event struct in the control part. 
	ErrFatalErrorIf(ctlBuf.len != sizeof(BtLibManagementEventType), 
					"no event struct"); 
 
	// Decode the event. 
	 
	switch (mEvent.event) { 
		case btLibManagementEventRadioState: 
			// The radio state has changed. 
			break; 
	 
		case btLibManagementEventInquiryResult: 
			// A device has been found.  Save it in a list. 
			break; 
	 
		case btLibManagementEventInquiryComplete: 
			// The inquiry is finished. 
			break; 
	 
		case btLibManagementEventInquiryCanceled: 
			// The inquiry has been canceled. 
			break; 
	} 
} 

This example includes some simple error condition checks for the PollBox callback being called with events that aren't Management Entity events.

To install this PollBox event handler, you would use code similar to that shown in Listing 11.3.

Listing 11.3  Installing the Management Event handler PollBox callback


int32_t gFdME; 
 
error = BtLibOpen(&gFdME); 
if (error) { 
	// Unable to open the Bluetooth Library. 
} else { 
	PbxAddFd(gPollBox, gFdME, POLLIN, HandlePbxMEEvent, 0); 
} 

For a list of management events, see "BtLibManagementEventEnum" in Chapter 13, "Bluetooth Reference."

Finding Nearby Devices ^TOP^

There are two ways to find Bluetooth devices that are within range:

  • Use the BtLibDiscoverDevices() function to find nearby devices. These functions bring up a user interface that allows the user to choose one or more devices.
  • Perform a device inquiry using BtLibStartInquiry(). This is more difficult to do than using the discovery function, but provides more flexibility.

When you call BtLibStartInquiry(), the Bluetooth Library searches for all devices in range. Whenever it finds a device, it generates a btLibManagementEventInquiryResult event. When the inquiry has completed, a btLibManagementEventInquiryComplete event is generated. To cancel the inquiry, call BtLibCancelInquiry(). The btLibManagementEventInquiryCanceled event is generated when the cancellation succeeds.

Creating ACL Links ^TOP^

Once you have the device address of a remote device, you can attempt to create an ACL link to it using the BtLibLinkConnect() function. This causes the btLibManagementEventACLConnectOutbound event to be generated, and the status code within that event indicates whether or not the link was successfully established.

To disconnect a link, use the BtLibLinkDisconnect() function. This causes the btLibManagementEventACLDisconnect event to be generated. Note that the same event is generated when the remote device initiates the disconnection; the status code will indicate why the connection was terminated.

Your program must also respond to btLibManagementEventACLConnectInbound events that indicate that a remote device has established a link with the handheld. You can disconnect an inbound link with the BtLibLinkDisconnect() function.

Working With Piconets ^TOP^

Bluetooth supports up to seven slaves in a piconet. The Bluetooth Library provides simplified APIs to create and destroy piconets.

Note that the Bluetooth 1.1 specification suggests that the upper software layers place slaves in hold or park mode while new connections are established. This isn't well–defined in the specification, and is difficult to do because of timing. The Bluetooth Library expects the radio baseband to handle piconet timing.

To create a piconet, the "master" calls BtLibPiconetCreate(). Slaves can then discover the master and join the piconet, or the master can discover and connect to the slaves. The master stops advertising once the limit of seven slaves has been reached. Note that any device should be capable of acting as a slave.

The piconet can be locked to prevent additional slaves from joining. The master can still discover and add slaves, however. With the piconet locked, there is a bandwidth improvement of approximately 10%.

In the Bluetooth Library, the following functions support the management of piconets:

Remember the following limitations of piconets: Slave-to-slave communication is not permitted. The master cannot "broadcast" to slaves.

Closing the Management Entity ^TOP^

When you're finished using the Bluetooth Library, you should call BtLibClose(), passing the Management Entity's file descriptor. When you do this, and there are no longer any open ME file descriptors or open and connected L2CAP or RFCOMM file descriptors, any remaining ACL links will be disconnected.

Bluetooth Sockets ^TOP^

The Bluetooth Library uses the concept of sockets to manage communication between Bluetooth devices. A socket represents a bidirectional packet-based link to a remote device. Sockets run over ACL connections. The Bluetooth library can accommodate up to 16 simultaneous sockets.

Five types of sockets are supported by the Bluetooth Library. L2CAP and RFCOMM sockets establish data channels and send and receive arbitrary data over those channels. SDP sockets allow you to query remote devices about the services those devices provide.

To send a packet of data over an L2CAP or RFCOMM socket, use the BtLibSocketSend() function.

SCO links are seen as a new socket type in BtLib. You can use BtLibSocketCreate() and BtLibSocketClose() to establish and break SCO links; however, once they're established, all data transfer is managed in hardware, so there is nothing further for software to do with them.

BNEP sockets are only used within the Bluetooth system and are generally not useful to developers. Sending data over a BNEP socket using BtLibSocketSend() must involve sending valid ethernet frames containing a 14-byte ethernet header followed by data.


NOTE: Versions of Palm OS prior to 6.0 required that the data buffer remain unchanged until the btLibSocketEventSendComplete event arrives. This is no longer the case; you can immediately release or reuse the buffer after BtLibSocketSend() returns.

When incoming data arrives, the IOSGetmsg() function returns a message with no control part and a data part containing the received data.

L2CAP ^TOP^

L2CAP sockets don't allow for flow control.

Establishing Inbound L2CAP Connections

To set up for inbound L2CAP connections, you call the following:

  1. BtLibSocketCreate(): create an L2CAP socket.
  2. BtLibSocketListen(): set up an L2CAP socket as a listener.
  3. BtLibSdpServiceRecordCreate(): allocate a memory chunk that represents an SDP service record.
  4. BtLibSdpServiceRecordSetAttributesForSocket(): initialize an SDP memory record so it can represent the newly-created L2CAP listener socket as a service
  5. BtLibSdpServiceRecordStartAdvertising(): make an SDP memory record representing a local SDP service record visible to remote devices.

When you get a btLibSocketEventConnectRequest event, you need to respond with a call to BtLibSocketRespondToConnection(). You then receive a btLibSocketEventConnectedInbound event with an inbound socket with which you can send and receive data.

The listening socket remains open and will notify you of further connection attempts. In other words, you can use a single L2CAP listening socket to spawn several inbound sockets. You cannot close the listening socket until after you close its inbound sockets.

Establishing Outbound L2CAP Connections

To establish an outbound L2CAP connection, you first establish an ACL link to the remote device. Then you call:

  1. BtLibSocketCreate(): create an SDP socket.
  2. BtLibSdpGetPsmByUuid(): get an available L2CAP PSM using SDP.
  3. BtLibSocketClose(): close the SDP socket.
  4. BtLibSocketCreate(): create an L2CAP socket.
  5. BtLibSocketConnect(): create an outbound L2CAP connection.

RFCOMM ^TOP^

RFCOMM emulates a serial connection. It is used when using the Serial Manager API to perform Bluetooth communications, as well as by the Bluetooth Exchange Library.

When using RFCOMM, you can only have one inbound connection per listener socket. Flow control uses a "credit" system: you need to advance a credit to the far end before you can receive a data packet.

RFCOMM defines the notions of server and client. A server uses SDP to advertise its existence and listens for inbound connections. A client creates an outbound RFCOMM connection to a server.

Establishing Inbound RFCOMM Connections

To set up for inbound RFCOMM connections, call the following:

  1. BtLibSocketCreate(): create an RFCOMM socket.
  2. BtLibSocketListen(): set up the RFCOMM socket as a listener.
  3. BtLibSdpServiceRecordCreate(): allocate a memory chunk that represents an SDP service record.
  4. BtLibSdpServiceRecordSetAttributesForSocket(): initialize an SDP memory record so it can represent the newly-created RFCOMM listener socket as a service
  5. BtLibSdpServiceRecordStartAdvertising(): make the SDP memory record representing your local SDP service record visible to remote devices.

When you get a btLibSocketEventConnectRequest event, you need to respond with a call to BtLibSocketRespondToConnection(). You then receive a btLibSocketEventConnectedInbound event with an inbound socket with which you can send and receive data. To send data, use the BtLibSocketSend() function. When incoming data arrives, the IOSGetmsg() function returns a message with no control part and a data part containing the received data.

The listening socket will not notify you of further connection attempts. In other words, a single RFCOMM listening socket can only spawn a single inbound RFCOMM socket. You cannot close the listening socket until after you close its inbound socket.

Establishing Outbound RFCOMM Connections

To establish an outbound RFCOMM connection, you first establish an ACL link to the remote device. Then you call:

  1. BtLibSocketCreate(): create an SDP socket.
  2. BtLibSdpGetServerChannelByUuid(): get an available RFCOMM server channel using SDP.
  3. BtLibSocketCreate(): create an RFCOMM socket.
  4. BtLibSocketConnect(): Create an outbound RFCOMM connection.

Using Serial-on-L2CAP and Serial-on-RFCOMM

The Serial-on-L2CAP and Serial-on-RFCOMM modules, whose names are btModSerL2cName and btModSerRfcName in BtLibTypes.h, are STREAMS modules that can be pushed onto an L2CAP or RFCOMM file descriptor.

These modules can be pushed onto the file descriptor either before or after connecting the socket. If pushed before connecting, BtLibSocketXXX() functions and events will be transparent to the module until the connection is established. In particular, the connection event will be visible to the application.

Once the socket is connected, or if the module is pushed after establishing the connection, then only pure data can be read or written by the application; the module handles things like flow control for you.

A disconnect event appears as an error condition from IOSRead() or IOSWrite(). Closing the file descriptor will disconnect the socket if it's connected.

SCO ^TOP^

SCO sockets are used to transmit audio between a Palm OS smart phone and a hands-free kit or headset. The only operations that can be performed on SCO sockets are to create, connect, and close them. Everything else is done in hardware.

BSD Sockets ^TOP^

You can use the standard BSD Sockets API to perform Bluetooth communications using the RFCOMM protocol. Use the sockaddr_bth structure to define a Bluetooth device address when using the BSD Sockets API.

Creating a Socket ^TOP^

You obtain a Bluetooth RFCOMM sotcket by specifying the address family AF_BTH, the socket type SOCK_STREAM, and the protocol BTHPROTO_RFCOMM when calling socket():


myBtSocket = socket( AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM ); 

Restrictions ^TOP^

A listening socket can have no backlog, and can only accept a single incoming connection, after which it becomes dead (meaning that it doesn't listen for incoming connections anymore). In addition, once accept() has been called and has returned a newly connected socket, the listening socket must not be closed until the accepted connection is closed first.

Listing 11.4  Listening for an incoming connection


listenerSocket = socket(); 
bind(listenerSocket); 
listen(listenerSocket); 
select(listenerSocket); 
dataSocket = accept(listenerSocket); 
... 
close(dataSocket); 
close(listenerSocket); 

If the application wishes to listen for further connections, it needs to explicitly start listening again by calling listen().

For more information about Palm OS support for the BSD Sockets API, see Part IV, "Networking and Sockets."

Service Discovery ^TOP^

The service discovery functions are used to create and advertise service records to remote devices, and to discover services available on remote devices.


NOTE: While Palm OS Cobalt, version 6.1 supports service discovery, it does not support the full Service Discovery Application Profile, since there is no service browser provided.

Service Records ^TOP^

A service record is a sequence of service attributes. A service attribute consists of two components: an attribute ID and an attribute value.

Universal attributes are those service attributes whose definitions are common to all service records. Among them is the ServiceClassIDList, which is a list of service class identifiers. Every service record must have a ServiceClassIDList.

An attribute ID is a 16-bit unsigned integer that distinguishes each service attribute from other service attributes within a service record. The attribute ID also identifies the semantics of the associated attribute value.

An attribute value is a data element whose meaning is determined by the corresponding attribute ID and the service class of the service record in which the attribute is contained.

A data element is a typed data representation consisting of two fields: a header field and a data field. The header field, in turn, is composed of two parts: a type descriptor and a size descriptor. The data is a sequence of bytes whose length is specified by the size descriptor and whose meaning is partially specified by the type descriptor.

To fully understand SDP service records, how they are encoded, interpreted, and so forth, see the Service Discovery Protocol section in Volume 1 of the Bluetooth Specification, version 1.2.


NOTE: Only one outstanding query at a time is allowed per SDP socket.

Creating Persistent Services ^TOP^

Applications that support Bluetooth can register themselves as persistent Bluetooth services, that are automatically started in a thread in the System Process when other Bluetooth enabled devices connect to them.


NOTE: For examples of Bluetooth persistent service applications, see the samples/Bluetooth/BtHeadset and samples/Bluetooth/BtHandsfree directories in the SDK. The code snippets shown here are adapted from these examples, but the complete source code is a valuable resource you should review.

Registering a Persistent Service

To register as a persistent service, an application must register the service at boot and system reset time, as shown in Listing 11.5.

Listing 11.5  Registering a persistent service


uint32_t PilotMain(uint16_t cmd, MemPtr cmdPBP, uint16_t launchFlags) { 
	BtLibServiceRegistrationParamsType params; 
	status_t error = errNone; 
	... 
	switch(cmd) { 
		case sysLaunchCmdBoot: 
		case sysLaunchCmdSystemReset: 
			params.appCodeRscId = sysResIDDefault; 
			params.appType = myAppRscType; 
			params.appCreator = myAppCreator; 
			params.stackSize = 5000; 
			params.protocol = btLibRfCommProtocol; 
			params.pushSerialModule = false; 
			params.execAsNormalApp = false; 
			error = BtLibRegisterService(&params); 
			break; 
		... 
	} 
	return error; 
} 

Describing a Persistent Service

The Bluetooth system may shut down or reinitialize itself at any time for a variety of reasons: the on/auto/off preference may be set to "off,", the device may be powered off, the radio hardware may be physically detached, or some application may close its last Bluetooth file descriptor that had been used to communicate with remote devices. To support this, the service application needs to implement the sysBtLaunchCmdDescribeService launch code to fill out a BtLibServiceDescriptionType structure. This is demonstrated in Listing 11.6.

Listing 11.6  Describing a Bluetooth persistent service


		case sysBtLaunchCmdDescribeService: 
			int size; 
			MemHandle theResHdl; 
			MemPtr theResPtr; 
			 
			// Describe our service for the Bluetooth panel services view. 
			((BtLibServiceDescriptionType*)cmdPBP)->flags = 0; 
 
			// Get the profile service name str 
			theResHdl = DmGetResource(myDmOpenRef, (DmResourceType) strRsc, 
						infoNameStrRscId); 
			theResPtr = MemHandleLock(theResHdl); 
			size = strlen(theResPtr) + 1; 
			if ((((BtLibServiceDescriptionType*)cmdPBP)->nameP = MemPtrNew(size)) 
						== NULL ) { 
				return btLibErrOutOfMemory; 
			} 
			MemMove(((BtLibServiceDescriptionType*)cmdPBP)->nameP, theResPtr, 
						size ); 
			MemHandleUnlock(theResHdl); 
			DmReleaseResource(theResHdl); 
			 
			// Get the profile service description str 
			theResHdl = DmGetResource(myDmOpenRef, (DmResourceType) strRsc, 
						infoDescStrRscId); 
			theResPtr = MemHandleLock(theResHdl); 
			size = strlen(theResPtr) + 1; 
			if ((((BtLibServiceDescriptionType*)cmdPBP)->descriptionP = 
						MemPtrNew( size )) == NULL ) { 
				return btLibErrOutOfMemory; 
			} 
			MemMove(((BtLibServiceDescriptionType*)cmdPBP)->descriptionP, 
						theResPtr, size); 
			MemHandleUnlock(theResHdl); 
			DmReleaseResource(theResHdl); 
			break; 

The example loads the strings from resources, copies them into the BtLibServiceDescriptionType structure, and releases the resources.

Providing Advanced Configuration Options

If the persistent service needs to provide the ability for the user to configure it, it should implement the sysBtLaunchCmdDoServiceUI launch code, which is sent when the user clicks the "Advanced" button in the Bluetooth services view. In response to this launch code, the application can present user interface to configure the service.

Preparing the Service to Listen for Incoming Connections

When the system is ready for the service application to begin listening for incoming connections, it sends the application the sysBtLaunchCmdPrepareService launch code with a BtLibServicePreparationParamsType structure as input.

This structure includes a handle to an empty SDP service record, which the service application needs to fill out to describe the offered serice, as well as the file descriptor for the socket on which the service application should listen for incoming connections.

Listing 11.7  Preparing the service


		BtLibServicePreparationParamsType *params = 
					(BtLibServicePreparationParamsType *) cmdPBP; 
		BtLibSdpUuidType gGWSdpUUIDList[3]; 
		status_t error; 
		... 
		case sysBtLaunchCmdPrepareService: 
			BtLibSdpUuidInitialize(gGWSdpUUIDList[0], 
					btLibSdpUUID_SC_HEADSET_AUDIO_GATEWAY, btLibUuidSize16); 
			BtLibSdpUuidInitialize(gGWSdpUUIDList[1], 
					btLibSdpUUID_SC_GENERIC_AUDIO, btLibUuidSize16); 
 
			error = BtLibSdpServiceRecordSetAttributesForSocket( 
						params->fdListener, 
						gGWSdpUUIDList, 
						2, 
						HEADSET_GW_SERVICE_NAME, 
						strlen(HEADSET_GW_SERVICE_NAME); 
						params->serviceRecH 
						); 
			break; 

The code snippet in Listing 11.7 comes from the BtHeadset sample program. It supports the audio gateway and generic audio services, for which it builds UUIDs using the BtLibSdpUuidInitialize() macro and inserts them into an array. It then sets the attributes on the listener socket to watch for attempts to connect to those particular services, and sets the name of the service, by calling BtLibSdpServiceRecordSetAttributesForSocket().

Executing the Service

When a connection attempt arrives at the listener socket, Palm OS sends the sysBtLaunchCmdExecuteService launch code to the service application. This sublaunch occurs in a thread within the System Process, and the application should not exit until the service finishes the transaction.

Listing 11.8  Processing the service transaction


	BtLibServiceExecutionParamsType *params; 
	... 
		case sysBtLaunchCmdExecuteService: 
			params = (BtLibServiceExecutionParamsType *) cmdPBP; 
			if (AppStart() == errNone) { 
				... 
				/* perform the transaction */ 
				... 
			} 
			AppStop(); 
			break; 
	... 

When the service begins running, it needs to initialize itself by opening its database, creating a PollBox to process events, open the Bluetooth Management Entity file descriptor, and so forth. In this example, this is all done by the AppStart() function.

Then the application can run an event loop to process incoming data and respond to that data, as well as to provide progress user interface and so forth.

Once the transaction is finished, the service application must close the data socket, which is specified by the fdData field in the BtLibServiceExecutionParamsType structure, before exiting.

Dealing with Bluetooth Shutdown ^TOP^

The Bluetooth system shuts down when the user changes the on/off preference or the radio hardware is physically detached. When this happens, all opened Bluetooth file descriptors start to produce errors.

When an application detects M_ERROR on any Bluetooth file descriptor, it must immediately close all its Bluetooth file descriptors.