Wednesday, May 15, 2013

How to Implement Pair and Unpair with other Bluetooth Devices Programmable on Android.

Recently, based on a project's requirement, the application has to repair with device if this android device has paired with that device recently. This really brought me bunch of challenges I had never experienced. Anyway, "There is always a way with such a strong will.". After a few days' research and experience, the door is open now.

Here I made a app to demonstrate how I handle each challenge and maybe other related problem. The link to the source code is at the bottom of this article.




This demo firstly have a list view to display all of the available devices around your android device. The paired devices is labeled with pink color while the unpaired is labeled with brown color. The paired device is always listed at the top. For each item in the list literally describes the device's name, 16 hex digits MAC address and the status of it's paired or not.

The device will pop on the list view one by one after user clicks on "Scan Device" button at the bottom. With the time Bluetooth adapter found new devices, it will check if it's paired, if it's paired, then it's already in the list, otherwise, add it to the list as a new device.








If user clicks on a new device, the app will pair it for user, while if the user clicks on the paired device, the app will unpair the device first, after the device is unpaired, the app will pair with device again with a request from Bluetooth adapter asking for the pass code to get paired. Once the device is newly paired, the app will connect with it.






Then, it will works like a SPP app on the Android device.






















The following are the main challenges I had when I went through the whole process.

Challenge #1: How to pair and unpair programmable with other Bluetooth Device on Android?

private void pairDevice(BluetoothDevice device) {
  try {
     if (D) Log.d(TAG, "Start Pairing...");

     Method m = device.getClass()
      .getMethod("createBond", (Class[]) null);
     m.invoke(device, (Object[]) null);
  } catch (Exception e) {
     Log.e(TAG, e.getMessage());
  }
}
private void unpairDevice(BluetoothDevice device) {
 try {
     Method m = device.getClass()
      .getMethod("removeBond", (Class[]) null);
     m.invoke(device, (Object[]) null);
 } catch (Exception e) {
     Log.e(TAG, e.getMessage());
 }
}
This code totally solve the challenge we have here. It's pretty tricky to use Reflection here.

Challenge #2: How to make unpair -> pair -> connect in order ?

         This challenge is raised when I try to connect the Bluetooth device right after unpair with it. I thought the Bluetooth Adapter will have my application enough time to let user input the that looks short but feels damn long four digits passcode, while the adapter will give you a new passcode request each time the application try to connect with the device. So finally I think maybe it's better for my application to handle corresponding event at each state.
1st, The first state change is from paired to unpaired state, when this state change is done, the app is ready to pair with that device again.
2nd, The second state change is from paring to paired state, when this is done, the app finally is ready to connect with the device.
The state change monitor is implemented with Broadcast Receiver.


    // The BroadcastReceiver that listens for discovered devices and
    // changes the title when discovery is finished
    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
 @Override
 public void onReceive(Context context, Intent intent) {
     String action = intent.getAction();

     // When discovery finds a device
     if (BluetoothDevice.ACTION_FOUND.equals(action)) {
  // Get the BluetoothDevice object from the Intent
  BluetoothDevice device = intent
   .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
  // If it's already paired, skip it, because it's been listed
  // already
  CustomizedBluetoothDevice mDevice = new CustomizedBluetoothDevice(
   device);
  if (device.getBondState() != BluetoothDevice.BOND_BONDED) {

      if (mDeviceList.contains(mDevice) == false) {
   mDeviceList.add(mDevice);
   updateUI();
      }
  }
     }
     // When the device bond state changed.
     else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) {
  int prevBondState = intent.getIntExtra(
   BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, -1);
  int bondState = intent.getIntExtra(
   BluetoothDevice.EXTRA_BOND_STATE, -1);

  if (prevBondState == BluetoothDevice.BOND_BONDED
   && bondState == BluetoothDevice.BOND_NONE) {
      BluetoothDevice device = intent
       .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
      if (currentPosition != -1
       && currentPosition < mDeviceList.size()) {
   CustomizedBluetoothDevice mDevice = mDeviceList
    .get(currentPosition);
   if (device.getAddress().compareTo(mDevice.getAddress()) == 0) {
       mDevice.setStatusPaired(false);
       updateUI();
       pairDevice(device);
   }
      }
  } else if (prevBondState == BluetoothDevice.BOND_BONDING
   && bondState == BluetoothDevice.BOND_BONDED) {
      BluetoothDevice device = intent
       .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
      if (currentPosition != -1
       && currentPosition < mDeviceList.size()) {
   CustomizedBluetoothDevice mDevice = mDeviceList
    .get(currentPosition);
   if (device.getAddress().compareTo(mDevice.getAddress()) == 0) {
       mDevice.setStatusPaired(true);
       updateUI();
       startConnect(mDevice);
   }
      }
  }
     }
 }
    };


The whole source code of this demo on Android is published on Github.

No comments :

Post a Comment