Click here to Skip to main content
15,868,141 members
Articles / Programming Languages / Java

Windows Authentication tools for BlueCove

Rate me:
Please Sign up or sign in to vote.
4.94/5 (4 votes)
11 Jun 2013CPOL5 min read 35.3K   625   3   11
The article describes adding native functionality to BlueCove applications.

Contents

Introduction

BlueCove[^] is a popular development kit that enables rapid development of Bluetooth applications in Java. It has a comprehensive feature set and allows a developer to do almost anything related to Bluetooth communications on Windows, Mac, and Linux systems. I say almost because there is one desirable feature that is apparently supported in Linux Bluez versions of BlueCove but is lacking in Windows: that of pass code authentication during the pairing process.

BlueCove Bluez provides a passkey agent that responds to remote authentication requests with a passkey in order to facilitate device pairing. This functionality is handled natively in Windows by the use of the Windows API method BluetoothRegisterForAuthenticationEx() but I was unable to find a BlueCove passkey agent for Windows and so I set out to create something that would fulfill this role for my application.

Designing a Windows authenticator

The first thing I considered before setting out to write the authenticator class was how BlueCove handled device discovery in general. For instance, if I have a room full of devices that are discoverable and want to search for them, I can use the DiscoveryAgent.startInquiry() method which takes, as an argument, a callback of type DiscoveryListener. In this case, I initiate an active search for remote devices instead of passively responding to remote device pairing requests. Here is an example of how this works (Source: bluecove.org)[^].

Java
import java.io.IOException;
import java.util.Vector;
import javax.bluetooth.*;

/**
 * Minimal Device Discovery example.
 */
public class RemoteDeviceDiscovery {

    public static final Vector/*<RemoteDevice>*/ devicesDiscovered = new Vector();

    public static void main(String[] args) throws IOException, InterruptedException {

        final Object inquiryCompletedEvent = new Object();

        devicesDiscovered.clear();

        DiscoveryListener listener = new DiscoveryListener() {

            public void deviceDiscovered(RemoteDevice btDevice, DeviceClass cod) {
                System.out.println("Device " + btDevice.getBluetoothAddress() + " found");
                devicesDiscovered.addElement(btDevice);
                try {
                    System.out.println("     name " + btDevice.getFriendlyName(false));
                } catch (IOException cantGetDeviceName) {
                }
            }

            public void inquiryCompleted(int discType) {
                System.out.println("Device Inquiry completed!");
                synchronized(inquiryCompletedEvent){
                    inquiryCompletedEvent.notifyAll();
                }
            }

            public void serviceSearchCompleted(int transID, int respCode) {
            }

            public void servicesDiscovered(int transID, ServiceRecord[] servRecord) {
            }
        };

        synchronized(inquiryCompletedEvent) {
            boolean started = LocalDevice.getLocalDevice().getDiscoveryAgent().
                                    startInquiry(DiscoveryAgent.GIAC, listener);
            if (started) {
                System.out.println("wait for device inquiry to complete...");
                inquiryCompletedEvent.wait();
                System.out.println(devicesDiscovered.size() +  " device(s) found");
            }
        }
    }
}

If I desire to authenticate a discovered device, I could perform this action in the DiscoveryListener method deviceDiscovered() using the following:

Java
public void deviceDiscovered(RemoteDevice btDevice, DeviceClass cod) {
    try {
        RemoteDeviceHelper.authenticate(btDevice, "1234");
    } catch (IOException CantAuthenticate) {
    }
}

Unlike the DiscoveryAgent.startInquiry() method's DiscoveryListener callback, I only needed one method since services were not involved and I would only respond to each remote device request in turn. However I patterned my Win32AuthenticationListener classes single method authenticationRequest() after DiscoveryListener's deviceDiscovered() method. Here's an example of how the Win32Authenticator is used:

Java
import java.io.IOException;
import javax.bluetooth.*;
import com.intel.bluetooth.RemoteDeviceHelper;

public class Win32AuthenticationTest {

     //Must declare globally in order to avoid premature garbage collection.
    static Win32Authenticator pairing;
    
    /**
     * @param args
     * @throws BluetoothStateException 
     */
    public static void main(String[] args) throws BluetoothStateException {

        java.util.Scanner scanner = new java.util.Scanner(System.in);

        try {
            pairing = new Win32Authenticator(
                    new Win32Authenticator.Win32AuthenticationListener() {

                        @Override
                        public void authenticationRequest(
                                RemoteDevice btDevice, DeviceClass cod) {

                            try {
                                System.out.println("Auth Request Recieved");
                                RemoteDeviceHelper.authenticate(btDevice, "1234");
                            } catch (IOException cantAuthenticate) {
                            }
                        }
                    });

            System.out.println("Waiting for authentications...");
            System.out.println("Press Enter to quit.");
            scanner.nextLine();
            pairing.dispose();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Authenticator nuts and bolts

The actual Win32Authenticator class implements JNA or Java Native Access[^] to call into the native Win32 library methods. In this case, the only native methods I needed to use were BluetoothRegisterForAuthenticationEx() and BluetoothUnregisterAuthentication(). The majority of the code in the module consists of native compatible struct/class definitions as well as some operating code.

In order to get the BlueCove side of things working, I did have to employ a few tricks. The code pattern for the callback required two arguments: one of type RemoteDevice and the other of type DeviceClass. Of these two BlueCove classes, I had no trouble creating an instance of DeviceClass from the callback thread; RemoteDevice however, was another matter entirely. For whatever reason, instantiating this class on any thread other than the main thread would cause a deadlock. I spent quite a bit of time researching a workaround for this without much success. Then I hit upon a solution that I figured might work, that is, to create an instance of the class in the main thread and reuse it each time the callback fired off.

Before I attempted this feat however, I decided to take a look at the BlueCove RemoteDevice class to see what I would be dealing with. I fired up Java decompiler[^] and opened the BlueCove jar to take a look, and this is what I saw:

Java
public class RemoteDevice
{
  private String addressStr;
  private long addressLong;

  protected RemoteDevice(String address)
  {...

Sweet! Only two fields and all of the class methods that follow return values based upon these two fields! Now notice the protected constructor; this class can only be instantiated from within its parent package. Because of this, the Win32Authenticator class is a part of the javax.bluetooth package. Thus I created a re-usable instance of RemoteDevice in the class constructor (called on the main thread) which is updated via Reflection each time a device requests authentication.

Java
this.m_callback = new _Bthprops.BluetoothAuthenticationCallback() {
    //
    // This callback executed on the callback thread
    //
    @Override
    public boolean callback(
            Pointer param,
            _Bthprops.BLUETOOTH_AUTHENTICATION_CALLBACK_PARAMS pAuthCallbackParams) {

        DeviceClass dc = new DeviceClass(
                pAuthCallbackParams.deviceInfo.ulClassofDevice);

        setRemoteDeviceViaReflection(rd, pAuthCallbackParams.deviceInfo
                .getBluetoothAddress());

        if (m_userCallback != null) {
            m_userCallback.authenticationRequest(rd, dc);
            return true;
        }
        return false;
    }
};

Now here's how I use Reflection to update the RemoteDevice object:

Java
private void setRemoteDeviceViaReflection(RemoteDevice rd, String address) {

    String formattedAddress = RemoteDeviceHelper
            .formatBluetoothAddress(address);
    Class<? extends RemoteDevice> c = rd.getClass();

    if (address == null) {
        throw new NullPointerException("address is null");
    }
    if (address.length() != 12) {
        throw new IllegalArgumentException("Malformed address: " + address
                + "; should be 12 characters");
    }
    if (address.startsWith("-")) {
        throw new IllegalArgumentException("Malformed address: " + address
                + "; can't be negative");
    }
    try {
        Field f = c.getDeclaredField("addressStr");
        f.setAccessible(true);
        f.set(rd, formattedAddress);
        f.setAccessible(false);
    } catch (Exception e) {
        throw ((RuntimeException) UtilsJavaSE.initCause(
                new RuntimeException("Can't set RemoteDevice field addressStr"),
                e));
    }
    try {
        if (formattedAddress.equals(LocalDevice.getLocalDevice()
                .getBluetoothAddress()))
            throw new IllegalArgumentException(
                    "can't use the LocalDevice address.");
    } catch (BluetoothStateException e) {
        throw ((RuntimeException) UtilsJavaSE.initCause(
                new RuntimeException("Can't initialize bluetooth support"),
                e));
    }
    try {
        Field f = c.getDeclaredField("addressLong");
        f.setAccessible(true);
        f.setLong(rd, RemoteDeviceHelper.getAddress(address));
        f.setAccessible(false);
    } catch (Exception e) {
        throw ((RuntimeException) UtilsJavaSE.initCause(
                new RuntimeException("Can't set RemoteDevice field addressLong"),
                e));
    }
}

Bonus: Win32LocalDeviceHelper

The BlueCove LocalDevice allows you to get the friendly name of your Bluetooth antenna. This is probably not all that helpful if you really care about the friendly name since there is no method to programmatically set the friendly name of your antenna. In my case, I had a remote device that would go active periodically and seek to link to a host with a specific friendly name. The application running on the host needed to ensure that the friendly name was correct and if not, reset it. I did quite a bit of digging on this one and found two solutions, one buried many pages deep in the comments of a blog post that became an informal go-to site for swapping Bluetooth solutions. The other on a help forum. Neither of these solutions worked correctly out of the box, however combining elements of both of them did the trick.

In a nutshell, on a Windows machine, the process of changing your local device friendly name involves finding a certain Registry key. Once found, write the desired name to that key. Finally, it is necessary to trigger the driver to read the Registry key with the new name. Smarter developers than me figured that one out and I am grateful to them. I created the Win32LocalDeviceHelper class to easily set the LocalDevice friendly name. It also employs JNA to call the native Win32 library methods used.

Final comments

The added functionality of the two classes presented here fill in some gaps in the the BlueCove development kit. Until such time that equivalent features should be added to BlueCove, it is my hope that these two classes prove to be as useful to some of you as they have been to me.

History

  • Mar 15, 2012: Version 1.0.0.0.
  • June 11, 2012: Version 1.1.0.0 - Modified code to use BluetoothRegisterForAuthenticationEx() instead of BluetoothAuthenticationCallback() to support Windows 7

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionHELP!! please!!! Pin
Member 933905611-Aug-12 12:48
Member 933905611-Aug-12 12:48 
AnswerRe: HELP!! please!!! Pin
David MacDermot13-Aug-12 6:29
David MacDermot13-Aug-12 6:29 
QuestionHelp!!! Pin
Member 93390566-Aug-12 10:57
Member 93390566-Aug-12 10:57 
AnswerRe: Help!!! Pin
David MacDermot7-Aug-12 6:46
David MacDermot7-Aug-12 6:46 
GeneralRe: Help!!! Pin
Member 93390567-Aug-12 12:02
Member 93390567-Aug-12 12:02 
GeneralRe: Help!!! Pin
David MacDermot7-Aug-12 13:52
David MacDermot7-Aug-12 13:52 
GeneralRe: Help!!! Pin
Member 93390567-Aug-12 14:03
Member 93390567-Aug-12 14:03 
QuestionauthenticationRequest can't callback Pin
chenpeilei200323-Jul-12 18:18
chenpeilei200323-Jul-12 18:18 
QuestionRSSI?? Pin
Ave-Duke9-Jul-12 8:39
Ave-Duke9-Jul-12 8:39 
When I compile this i get an error that says : Exception in thread "main" java.lang.RuntimeException: WIDCOMM BluetoothStack not found

can you help me pleaaseee??

also does this RSSI feed look right ?? here is the code

C#
import java.io.IOException;
import java.util.Vector;
import javax.bluetooth.*;
import com.intel.bluetooth.*;

/**
 * Minimal Device Discovery example.
 */
public class RemoteDeviceDiscovery {

    public static final Vector/*<RemoteDevice>*/ devicesDiscovered = new Vector();

    public static void main(String[] args) throws IOException, InterruptedException {

        final Object inquiryCompletedEvent = new Object();

        devicesDiscovered.clear();

        DiscoveryListener listener = new DiscoveryListener() {

            public void deviceDiscovered(RemoteDevice btDevice, DeviceClass cod) {
                int deviceRSSI = 1;
                System.out.println("Device " + btDevice.getBluetoothAddress() + " found");
                devicesDiscovered.addElement(btDevice);
                try {
                    System.out.println("     name " + btDevice.getFriendlyName(false));
                } catch (IOException cantGetDeviceName) {
                }
                try {
                    deviceRSSI = RemoteDeviceHelper.readRSSI(btDevice);
                } catch (IOException cantReadRSSI) {
                    System.out.println(cantReadRSSI);
                }
                System.out.println("     rssi " + deviceRSSI);
                //System.out.println("     rssi " + RemoteDeviceHelper.readRSSI(btDevice));

            }

            public void inquiryCompleted(int discType) {
                System.out.println("Device Inquiry completed!");
                synchronized(inquiryCompletedEvent){
                    inquiryCompletedEvent.notifyAll();
                }
            }

            public void serviceSearchCompleted(int transID, int respCode) {
            }

            public void servicesDiscovered(int transID, ServiceRecord[] servRecord) {
            }
        };

        synchronized(inquiryCompletedEvent) {
            boolean started = LocalDevice.getLocalDevice().getDiscoveryAgent().startInquiry(DiscoveryAgent.GIAC, listener);
            if (started) {
                System.out.println("wait for device inquiry to complete...");
                inquiryCompletedEvent.wait();
                System.out.println(devicesDiscovered.size() +  " device(s) found");
            }
        }
    }

}

AnswerRe: RSSI?? Pin
David MacDermot10-Jul-12 7:57
David MacDermot10-Jul-12 7:57 
GeneralMy vote of 5 Pin
grasp28-Mar-12 3:01
grasp28-Mar-12 3:01 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.