Friday, April 27, 2007

Using the Java APIs for Bluetooth

Bluetooth is a low-cost, short-range wireless technology that has become popular
among those who want to create personal area networks (PANs). Each PAN is a dynamically
created network built around an individual, that enables devices such as cellular
phones and personal digital assistants (PDAs) to connect automatically and share
data immediately. To support development of Bluetooth-enabled software on the
Java platform, the Java Community Process (JCP) has defined JSR 82, the Java APIs
for Bluetooth Wireless Technology (JABWT).

Part 1 of this article presented an overview of Bluetooth technology and JABWT,
along with use cases, activities, and elements of a typical Bluetooth application.
It also introduced the core JABWT APIs defined in the javax.bluetooth package.
This second part of the article will focus on the how-to aspects of JABWT. Code
examples will show you how to use the core Bluetooth APIs to initialize a Bluetooth
application, deal with connections, set up a service, discover nearby devices
and services, connect to a service, and make a connection secure.

Introduction


Let's begin by reviewing briefly the typical use cases and activities of a
Bluetooth-enabled application. A Bluetooth application can be either a server
or a client - a producer of services or a consumer - or it can behave as a true
peer-to-peer endpoint by exposing both server and client behavior. Typical Bluetooth
applications have three categories of use cases:



Figure 1: Bluetooth Use Cases

Figure 1: Bluetooth Use Cases

Click to enlarge


* Initialization - Any Bluetooth-enabled application, server or client, must
first initialize the Bluetooth stack.


* Client - A client consumes remote services. It first discovers any nearby
devices, then for each discovered device it searches for services of interest.


* Server - A server makes services available to clients. It registers them
in the Service Discovery Database (SDDB), in effect advertising them. It then
waits for incoming connections, accepts them as they come in, and serves the
clients that make them. Finally, when the service is no longer needed the application
removes it from the SDDB.


Figure 2 diagrams the typical activities of Bluetooth client and server applications:



Figure 2: Server and Client Activities

Figure 2: Server and Client Activities

Click to enlarge


You see in this figure that both client and server perform initialization,
that the server application prepares a service and waits for connections, and
that the client discovers devices and services, then connects to specific device
to consume a particular service.


Now let's drill down into some sample code that shows how you implement these
activities using the Java APIs for Bluetooth.

Initializing the Bluetooth Application


As you can see in Figure 3, initialization is a very simple activity:



Figure 3: Initializing the Bluetooth Application

Figure 3: Initializing the Bluetooth Application


First the application retrieves a reference to the Bluetooth Manager from the
LocalDevice. Client applications retrieve a reference to the DiscoveryAgent,
which provides all the discovery-related services. Server applications make
the device discoverable. In the following code snippet, the initialization method
btInit() performs both client and server initialization:


...

private LocalDevice localDevice; // local Bluetooth Manager

private DiscoveryAgent discoveryAgent; // discovery agent

...

/**

* Initialize

*/

public void btInit() throws BluetoothStateException {

localDevice = null;

discoveryAgent = null;

// Retrieve the local device to get to the Bluetooth Manager

localDevice = LocalDevice.getLocalDevice();

// Servers set the discoverable mode to GIAC

localDevice.setDiscoverable(DiscoveryAgent.GIAC);

// Clients retrieve the discovery agent

discoveryAgent = localDevice.getDiscoveryAgent();

}

...




Not all applications serve as both server and client at the same time; the roles
they play depend on your application requirements. Server applications set themselves
to be discoverable, while client applications get a reference to the discovery
agent for service discovery. When you set the device's discoverable mode by
calling LocalDevice.setDiscoverable() you must specify the inquiry access code
(IAC). JABWT supports two access modes:


* DiscoveryAgent.LIAC specifies Limited Inquiry Access Code. The device will
be discoverable for only a limited period of time, typically one minute. After
the limited period, the device automatically reverts to undiscoverable mode.


* DiscoveryAgent.GIAC specifies General Inquiry Access Code. No limit is set
on how long the device remains in the discoverable mode.


To force a device back to undiscoverable mode, simply call LocalDevice.setDiscoverable()
with DiscoveryAgent.NOT_DISCOVERABLE as the argument.


Note: Behind the scenes, the Bluetooth Control Center (BCC) serves as the
central authority for all local Bluetooth settings. The BCC enables the user
to define and override discoverable modes and other settings. The BCC also exists
to keep any application from adversely affecting others. For more information,
see the specification.


Whenever a device is discoverable it's visible to other Bluetooth devices,
and thus open to attack. Even though GIAC is the most commonly used inquiry
access code, to reduce risk you should consider making the device discoverable
only when necessary, and perhaps only for short periods of time, by using LIAC.
The BCC allows you to disable the discovery mode.

Dealing with Connections


In Part 1 of this article you learned that JABWT connections are based on the
Logical Link Control and Adaptation Layer Protocol (L2CAP), a low-level data-packet
protocol, and that a serial emulation protocol over L2CAP is supported by the
Serial Port Profile (SPP) RFCOMM. JABWT connections are based on the Generic
Connection Framework (GCF) and are represented by the L2CAPConnection and StreamConnection
types respectively. Part 1 also noted that, while L2CAPConnection was introduced
with JSR 82, StreamConnection was defined as part of the original javax.microedition.io
GCF, in the Connected Limited Device Configuration (CLDC).


As with all GCF connection types, you create a Bluetooth connection using the
GCF connection factory javax.microedition.io.Connector, passing to its open()
method a connection URL argument describing the connection endpoint to create.
The connection URL scheme determines the connection type to create:


* The URL format for an L2CAPConnection:


btl2cap://hostname:[PSM | UUID];parameters


* The URL format for an RFCOMM StreamConnection:


btspp://hostname:[CN | UUID];parameters


Where:


* btl2cap is the URL scheme for an L2CAPConnection.


* btspp is the URL scheme for an RFCOMM StreamConnection.


* hostname is either localhost to set up a server connection, or the Bluetooth
address to create a client connection.


* PSM is the Protocol/Service Multiplexer value, used by a client connecting
to a server. This is similar in concept to a TCP/IP port.


* CN is the Channel Number value, used by a client connecting to a server
- also similar in concept to a TCP/IP port.


* UUID is the Universally Unique Identifier used when setting up a service
on a server. Each UUID is guaranteed to be unique across all time and space.


* parameters include name to describe the service name, and the security parameters
authenticate, authorize, and encrypt.


For example:


* A server RFCOMM URL:


btspp://localhost:2D26618601FB47C28D9F10B8EC891363;authenticate=false;

encrypt=false;name=MyBtService


* A client RFCOMM URL:


btspp://0123456789AF:1;master=false;encrypt=false;authenticate=false


Using localhost as a hostname indicates you want a server connection. To create
a client connection to a known device and service, use the service's connection
URL, found in its ServiceRecord.


Handling L2CAP connections is more involved than handling stream connections;
developers must deal with maximum message sizes (maximum transmission unit,
or MTU), and with breaking up and reassembling long messages. These complexities
are hidden from developers who use stream connections, making them preferable
for Bluetooth connectivity. Code samples in this article cover only stream connections.


The following code snippet shows how to create an RFCOMM server connection:


...

// Bluetooth Service name

private static final String myServiceName = "MyBtService";

// Bluetooth Service UUID of interest

private static final String myServiceUUID = "2d26618601fb47c28d9f10b8ec891363";

private UUID MYSERVICEUUID_UUID = new UUID(myServiceUUID, false);

...

// Define the server connection URL

String connURL = "btspp://localhost:"+MYSERVICEUUID_UUID.toString()+";"+name="+myServiceName;

...

// Create a server connection (a notifier)

StreamConnectionNotifier scn = (StreamConnectionNotifier) Connector.open(connURL);

...

// Accept a new client connection

StreamConnection sc = scn.acceptAndOpen();

...




The following code snippet shows how to create a client connection to a given
service of interest, using its service record:


...

// Given a service of interest, get its service record

ServiceRecord sr = (ServiceRecord)discoveredServices.elementAt(i);

// Then get the service's connection URL

String connURL = sr.getConnectionURL(ServiceRecord.NOAUTHENTICATE_NOENCRYPT,
false);

// Open connection

StreamConnection sc = (StreamConnection) Connector.open(connURL);

...




If you're deploying your JABWT application on MIDP 2.0 handsets, it must request
the appropriate permissions before attempting to connect, otherwise Connector.open()
will throw a SecurityException. I'll tell you how to request permissions later,
in the "JABWT and MIDP 2.0 Security" section of this article.

Setting up a Bluetooth Server




Figure 4: Setting up a Bluetooth Server

Figure 4: Setting up a Bluetooth Server


You set up a Bluetooth server to make a service available for consumption.
There are four main steps:


1. Creating a service record for the service you want to make available

2. Adding the new service record to the Service Discovery Database

3. Registering the service

4. Waiting for incoming client connections


Two related operations are significant:


* Modifying the service record, if the service attributes that are visible
to clients need to change

* When all done, removing the service record from the SDDB.


Let's look at each of these operations closely.

Creating a Service Record


The Bluetooth implementation automatically creates a service record when your
application creates a connection notifier, either a StreamConnectionNotifier
or an L2CAPConnectionNotifier.


Every Bluetooth service and service attribute has its own Universally Unique
Identifier. You can't create a service without first assigning it a UUID. Easy
enough: The UUID class represents short (16- or 32-bit) and long (128-bit) UUIDs.
To generate a UUID, use the command uuidgen -t if you're running Linux, or uuidgen
if you're running Windows. The uuidgen utility includes hyphens in the generated
UUID; remove these hyphens when you copy the output into your source code.


To get a UUID to use in the sample code for this article I ran uuidgen -t and
it generated 2d266186-01fb-47c2-8d9f-10b8ec891363. The following code snippet
defines some private members for the service, such as the service name and service
UUID members:


...

// Bluetooth service name

private static final String myServiceName = "MyBtService";

// Bluetooth service UUID

private static final String myServiceUUID = "2d26618601fb47c28d9f10b8ec891363";

private UUID MYSERVICEUUID_UUID = new UUID(myServiceUUID, false);

private UUID[] uuids = {MYSERVICEUUID_UUID};

...




The next snippet defines and instantiates an RFCOMM connection notifier, resulting
in the creation of the service record:


...

StreamConnectionNotifier streamConnectionNotifier;

// Create notifier (and service record)

streamConnectionNotifier = (StreamConnectionNotifier)

Connector.open(connectionURL);

...




Registering the Service and Waiting for Incoming Connections


Once you've created the connection notifier and the service record, the server
is ready to register the service and wait for clients. Invoking the notifier's
acceptAndOpen() method causes the Bluetooth implementation to insert the service
record for the associated connection into the SDDB, making the service visible
to clients. The acceptAndOpen() method then blocks, waiting for incoming connections,
which are accepted as they come in:


...

// Insert service record into SDDB and wait for an incoming client

StreamConnection conn = streamConnectionNotifier.acceptAndOpen();

...




When a client connects, acceptAndOpen() returns a connection, in our example
a StreamConnection, that represents the client endpoint that the server will
read data from. The following code snippet waits for and accepts an incoming
client connection, then reads from it:


...

// Wait for client connection

StreamConnection conn = streamConnectionNotifier.acceptAndOpen();

// New client connection accepted; get a handle on it

RemoteDevice rd = RemoteDevice.getRemoteDevice(conn);

System.out.println("New client connection... " +

rd.getFriendlyName(false));

// Read input message, in this example a String

DataInputStream dataIn = conn.openDataInputStream();

String s = dataIn.readUTF();

// Pass received message to incoming message listener

...




This snippet reads a simple String from the connection. Different applications
have different data-encoding needs, of course. While a String might suffice
for a chat application, a multimedia application would probably use a combination
of character and binary data.


Note that this code blocks while waiting for incoming client connections, and
must be dispatched in its own thread of execution; if it's called from the system
thread, the user interface will freeze, and your application may deadlock.

Updating the Service Record


There are occasions when the attributes for a registered service must be changed.
You can update records in the SDDB using the local Bluetooth manager. As the
next snippet shows, you retrieve the record from the SDDB by calling LocalDevice.getRecord(),
add or change attributes of interest by calling ServiceRecord.setAttributeValue(),
and write the service record back to the SDDB with a call to LocalDevice.updateRecord():


...

try {

// Retrieve service record and set/update optional attributes,

// for example, ServiceAvailability, indicating service is available

sr = localDevice.getRecord(streamConnectionNotifier);

sr.setAttributeValue(SDP_SERVICEAVAILABILITY,

new DataElement(DataElement.U_INT_1, 0xFF));

localDevice.updateRecord(sr);

} catch (IOException ioe) {

// Catch exception, display error

}

...




Closing the Connection and Removing the Service Record


When the service is no longer needed, remove it from the SDDB by closing the
connection notifier:


...

streamConnectionNotifier.close();

...




Discovering Nearby Devices and Services




Figure 5: Discovering Devices and Services

Figure 5: Discovering Devices and Services


A client can't consume services until it finds them. The search cycle consists
of discovering nearby devices, then for each discovered device searching for
services of interest. Discovering devices - inquiry - is expensive and time-consuming.
The client can often avoid this overhead by finding out whether known or cached
devices already provide the services of interest, and initiating a new inquiry
only when they don't.


Discovery is the responsibility of the DiscoveryAgent. This class allows the
client to initiate and cancel device and service discovery. The DiscoveryAgent
notifies the client application of any discovered devices and services through
the DiscoveryListener interface. Figure 6 illustrates the relationships between
the Bluetooth client, the DiscoveryAgent, and the DiscoveryListener.



Figure 6: The DiscoveryAgent and DiscoveryListener

Figure 6: The DiscoveryAgent and DiscoveryListener


To retrieve already known or cached devices the client calls the DiscoveryAgent
method retrieveDevices():


RemoteDevice[] retrieveDevices(int option);


...Where option is one of the following:


* CACHED if the method should return previously found devices


* PREKNOWN if it should return devices already known


The client initiates a device discovery cycle by calling startInquiry():


boolean startInquiry(int accessCode, DiscoveryListener listener);


...Where


* accessCode is DiscoveryAgent.LIAC or DiscoveryAgent.GIAC, as you saw earlier


* listener is the discovery listener.


To receive discovery notifications from the DiscoveryAgent the client application
must implement the DiscoveryListener interface and its four discovery callbacks
deviceDiscovered(), inquiryCompleted(), servicesDiscovered(), and serviceSearchCompleted(),
as illustrated in this sample class:


public class BtClient implements DiscoveryListener {

...

Vector discoveredDevices = new Vector();

...


// DiscoveryListener Callbacks ///////////////////////


/**

* deviceDiscovered() is called by the DiscoveryAgent when

* it discovers a device during an inquiry.

*/

public void deviceDiscovered(

javax.bluetooth.RemoteDevice remoteDevice,

javax.bluetooth.DeviceClass deviceClass) {

// Keep track of discovered remote devices by inserting

// them into a Vector

...

}


/**

* inquiryCompleted() is called by the DiscoveryAgent when

* a device discovery cycle finishes.

*/

public void inquiryCompleted(int param) {

// Now that the inquiry has been completed, if any

// devices were discovered trigger the search for services

...

}


/**

* servicesDiscovered() is called by the DiscoveryAgent when

* a service search finds services.

* transID identifies the service search that returned results.

* serviceRecord holds references to the services found.

*/

public void servicesDiscovered(int transID,

javax.bluetooth.ServiceRecord[] serviceRecord) {

// Keep track of discovered services, adding them

// to a Vector

...

}


/**

* serviceSearchCompleted() is called by the DiscoveryAgent

* implementation when a service search finishes.

* transID identifies a particular service search.

* responseCode indicates why the service search ended.

*/

public void serviceSearchCompleted

(int transID, int responseCode) {

// Now that the service discovery has been completed,

// dispatch thread to handle the discovered services

...

}

...

}




The helper method btInitiateDeviceSearch(), which can be called in response
to a user request, retrieves any cached or previously found devices, then kicks
off an inquiry:


/**

* btInitiateDeviceSearch() kicks off the device discovery

*/

public void btInitiateDeviceSearch() {

System.out.println("BTMIDlet.btInitiateDeviceSearch");

int i, s;

...

remoteDevices.clear();

discoveredDevices.clear();

...

RemoteDevice[] cachedDevices =

discoveryAgent.retrieveDevices(DiscoveryAgent.CACHED);

if (cachedDevices != null) {

s = cachedDevices.length;

for (i=0; i<s; i++) {

remoteDevices.put(

cachedDevices[i].getBluetoothAddress(),

cachedDevices[i]);

}

}

...

RemoteDevice[] preknownDevices =

discoveryAgent.retrieveDevices(DiscoveryAgent.PREKNOWN);

if (preknownDevices != null) {

s = preknownDevices.length;

for (i=0; i<s; i++) {

remoteDevices.put(

preknownDevices[i].getBluetoothAddress(),

preknownDevices[i]);

}

}

...

try {

inquiryStarted =

discoveryAgent.startInquiry(

DiscoveryAgent.GIAC, this);

} catch(BluetoothStateException bse) {

// Display error message

System.out.println("Inquiry Failed");

return;

}


if (inquiryStarted == true) {

// Display progress message

System.out.println("Inquiry in Progress");

} else {

// Display error message

System.out.println("Inquiry Failed");

}

}




Let's take a more detailed look at two callbacks the DiscoveryAgent invokes,
and see how the client should process discovered devices:


/**

* deviceDiscovered() is called by the DiscoveryAgent when

* it discovers a device during an inquiry.

*/

public void deviceDiscovered(

javax.bluetooth.RemoteDevice remoteDevice,

javax.bluetooth.DeviceClass deviceClass) {

System.out.println("BTMIDlet.deviceDiscovered");

// Keep track of discovered remote devices

discoveredDevices.put(

remoteDevice.getBluetoothAddress(), remoteDevice);

}


/**

* inquiryCompleted() is called by the DiscoveryAgent when

* device discovery finishes.

* @param type is the type of request that was completed:

* INQUIRY_COMPLETED, INQUIRY_TERMINATED, or INQUIRY_ERROR

*/

public void inquiryCompleted(int type) {

System.out.println("BTMIDlet.inquiryCompleted");

int i, s;


// After each inquiry is completed, update the screen


// Now that the inquiry has been completed, move newly

// discovered devices into the remoteDevices Vector

s = discoveredDevices.size();

if (s > 0) {

System.out.println(s + " device(s) found");

for (i=0; i<s; i++) {

RemoteDevice rd = (RemoteDevice)

discoveredDevices.elementAt(i);

// Add device only if not already known or cached

RemoteDevice rd2 = (RemoteDevice)

remoteDevices.get(rd.getBluetoothAddress());

if (rd2 == null) {

remoteDevices.put(rd.getBluetoothAddress(), rd);

}

}

} else {

System.out.println("No devices found");

}


// Show remote devices

String friendlyName;

s = remoteDevices.size();

if (s > 0) {

System.out.println(s + "device(s) found");

for (i=0; i<s; i++) {

RemoteDevice rd = (RemoteDevice)

remoteDevices.elementAt(i);

try {

friendlyName = rd.getFriendlyName(false);

} catch (IOException ioe) {

friendlyName = null;

}

if (friendlyName == null) {

System.out.println(rd.getBluetoothAddress());

} else {

System.out.println(friendlyName);

}

}

}

}




Once nearby devices have been discovered, the client can search for services
of interest. The following code snippet shows the helper method btInitiateServiceSearch()
that is responsible for kicking off the service search:


/**

* btInitiateServiceSearch() kicks off the service discovery

*/

public void btInitiateServiceSearch() {

System.out.println("BTMIDlet.btInitiateServiceSearch");

int s, i;


...

discoveredServices.clear();


// Initiate the service search on the remote device

s = remoteDevices.size();

if (s==0) {

System.out.println("No devices to search...");

} else {

for (i=0; i<s; i++) {

RemoteDevice rd = (RemoteDevice)

remoteDevices.elementAt(i);

try {

transID = discoveryAgent.searchServices(

attrs, uuids, rd, this);

// Display progress message

System.out.println(

"Service Search in Progress ("+transID+")");

synchronized (serviceSearchSemaphore) {

serviceSearchSemaphore.wait();

}

} catch (InterruptedException ie) {

// Ignore

} catch (BluetoothStateException bse) {

// ...

System.out.println("Service Search Failed");

return;

}

}

}

}




Connecting to a Service


Once a service of interest has been found, the client application can connect
to it. As you learned earlier, the client can retrieve the service's connection
URL from its service record. The next method shows how to connect to a service:


/**

* Connect to service represented by service record

* @param sr is the service record for the service of interest

*/

public void btConnect(final ServiceRecord sr) {

Thread th = new Thread() {

public void run() {

System.out.println("BTMIDlet.btConnect.run()");

RemoteDevice rd = sr.getHostDevice();

String connectionURL = sr.getConnectionURL(

ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false);

try {

System.out.println(

"Connecting to " + rd.getFriendlyName(false));

System.out.println(

"Connecting to " + rd.getFriendlyName(false) +

", " + connectionURL);

StreamConnection streamConnection =

(StreamConnection) Connector.open(connectionURL);

DataOutputStream dataout =

streamConnection.openDataOutputStream();

dataout.writeUTF(messageOut);

System.out.println("Closing");

streamConnection.close();

} catch (IOException ioe) {

System.out.println(

"BTMIDlet.btConnect, exception & + ioe);

}

}

};

th.start();

}




There's another way to connect to a service. If you don't care which device
offers the desired service, you can use DiscoveryAgent.selectService(). This
method searches all nearby Bluetooth devices for the service indicated by a
UUID, and if successful returns the service's connection URL. Here's a helper
method that uses this approach:


/**

* Connect to service represented by UUID

* @param uuid is the UUID for the service of interest

*/

public void btConnect2(final UUID uuid) {

Thread th = new Thread() {

public void run() {

System.out.println("BTMIDlet.btConnect2.run()");

try {

// Select the service. Indicate no

// authentication or encryption is required.

String connectionURL =

discoveryAgent.selectService(uuid,

ServiceRecord.NOAUTHENTICATE_NOENCRYPT,

false);

mainScreen.setTitle(

"Connecting to " + uuid.toString());

StreamConnection streamConnection =

(StreamConnection)

Connector.open(connectionURL);

System.out.println("Sending message out");

DataOutputStream dataout =

streamConnection.openDataOutputStream();

dataout.writeUTF(messageOut);

System.out.println("Closing");

streamConnection.close();

} catch (BluetoothStateException bse) {

System.out.println("BTMIDlet.btConnect2,

exception " + bse);

} catch (IOException ioe) {

System.out.println("BTMIDlet.btConnect2,

exception " + ioe);

}

}

};

th.start();

}




Note that, because connecting to a server is a long operation, the method dispatches
this task in its own thread of execution.

Securing a Bluetooth Connection


A secure Bluetooth connection is one that is authenticated, and optionally
authorized, and encrypted. A Bluetooth connection can be secured when it's established,
or later.


Note: Not all Bluetooth implementations provide secure connections.


To make a Bluetooth connection secure when you establish it you must ensure
that the javax.microedition.io.Connector connection URL string has the appropriate
security parameters:


btspp://hostname:[CN | UUID];authenticate=true;authorize=true;encrypt=true


...Where:


* authenticate verifies the identity of a connecting device.


* authorize verifies its access to a given service. Authorize is not allowed
on client URL connection strings.


* encrypt specifies that the connection must be encrypted.


For example:


btspp://localhost:2D26618601FB47C28D9F10B8EC891363;

authenticate=true;encrypt=true;name=MyBtService


A client can retrieve a service's connection URL by calling ServiceRecord.getConnectionURL().
One of this method's arguments, requiredSecurity, determines whether the returned
connection URL should include the optional authenticate and encrypt security
parameters. The valid values for requiredSecurity are:


* ServiceRecord.AUTHENTICATE_NOENCRYPT indicates authenticate=true; encrypt=false.


* ServiceRecord.AUTHENTICATE_ENCRYPT indicates authenticate=true; encrypt=true.


For example:


...

ServiceRecord sr = ...;

...

String connURL =

sr.getConnectionURL(

ServiceRecord.AUTHENTICATE_ENCRYPT, false);

// Open connection

StreamConnection sc = (StreamConnection) Connector.open(connURL);

...




You can secure a connection after you establish it by invoking the RemoteDevice
security methods authenticate(), authorize(), and encrypt(); for example:


To authenticate a remote device:


StreamConnection sc = ...;

// Authenticate the remote device for the indicated connection

RemoteDevice rd = RemoteDevice.getRemoteDevice(sc);

rd.authenticate();




To authorize a remote device's access to a given service:


// Authorize client access to service indicated by connection

StreamConnection sc = ...;

RemoteDevice rd = RemoteDevice.getRemoteDevice(sc);

rd.authorize(sc);




To encrypt a connection:


// Encrypt connection

StreamConnection sc = ...;

RemoteDevice rd = RemoteDevice.getRemoteDevice(sc);

rd.encrypt(sc, true);




Note that authentication must be performed before authorization and encryption.
Turning encryption on will force authentication, if it hasn't already been done.

JABWT and MIDP 2.0 Security


MIDP 2.0 introduces a robust model for access to restricted resources and APIs
that are based on permissions and policies. A Bluetooth application using JABWT
and running under MIDP 2.0 may need to request permission to use network resources.
Failing to request permission may cause Connection.open() to throw a SecurityException.
You request permissions by creating MIDlet-Permissions property entries in the
JAD file or the JAR manifest:


* To request permission to open a Bluetooth server connection:


Connector.open("btspp://localhost:..."):

MIDlet-Permissions:javax.microedition.io.Connector.bluetooth.server


* To request permission to open a Bluetooth client connection:


Connector.open("btspp://... "):

MIDlet-Permissions:javax.microedition.io.Connector.bluetooth.client


* To request permission to open both:


MIDlet-Permissions:javax.microedition.io.Connector.bluetooth.client, javax.microedition.io.Connector.bluetooth.server


JABWT and MIDP 2.0 PushRegistry


MIDP 2.0's push registry manages network- and timer-initiated MIDlet activation;
that is, it enables an inbound network connection or a timer-based alarm to
wake a MIDlet up. MIDlets can thus use the PushRegistry class to set themselves
up to be launched automatically, without user initiation, if the underlying
implementation supports push operations.


MIDlets can be activated by incoming Bluetooth connections, if they first register
with the push registry, statically or dynamically.


For static registration, place MIDlet-Push-1 entries in the JAD or manifest:


...

MIDlet-Push-1: btspp://localhost:2D26618601FB47C28D9F10B8EC891363;

name=MyBtService,com.j2medeveloper.MyMIDlet, *

...




Dynamic registration is done at runtime, using the PushRegistry's registerConnection()
API:


...

// MIDlet class name

String midletClassName = this.getClass().getName();

// Register a static connection.

String url = "btspp://localhost:2D26618601FB47C28D9F10B8EC891363;name=MyBtService"

// Use an unrestricted filter.

String filter = "*";

...

PushRegistry.registerConnection(url, midletClassName, filter);

...




The MIDP 2.0 permission model may require applications to request permissions.
Creating the following MIDlet-Permissions property entries in the JAD or the
manifest requests permission to use PushRegistry activation by a Bluetooth server
connection:


MIDlet-Permissions: javax.microedition.io.PushRegistry,

javax.microedition.io.PushRegistry.bluetooth.server




See the article "The MIDP 2.0 Push Registry" to learn how to use the
PushRegistry API.

Summary


You now should have a good understanding of Bluetooth networking and JSR 82,
and be able to use the core Java APIs for Bluetooth Wireless Technology effectively.
Bluetooth is an exciting wireless technology for personal networks that allows
personal devices to share data and services. This article has covered a lot
of ground, including some background information on Bluetooth, an overview of
the typical elements of a Bluetooth-enabled MIDlet application, an introduction
to the core JABWT interfaces in the javax.bluetooth package, and some code that
showed you how to use the core Java Bluetooth APIs.

Acknowledgements


Thanks to Jim Trudeau and Sony Ericsson for loaning me the devices that I used
to run and test the code samples for this article. Thanks to Roger Riggs and
Kirill Kounik and Brian Christeson for their feedback and helping improve this
article.

Resources


* JABWT Specification (JSR 82)


* MIDP 2.0 Specification (JSR 118)


About the Author


C. Enrique Ortiz is a software architect and developer, and a mobility technologist
and writer. He is author or co-author of many publications, a co-designer of
Sun Microsystems' the Mobile Java Developer Certification Exam, and has been
an active participant in the wireless Java community and in various J2ME expert
groups. Enrique holds a B.S. in Computer Science from the University of Puerto
Rico and has more than 15 years of software engineering, product development,
and management experience.

No comments: