Beacon Detector on Android with Third-Party Library

In previous post, we are able to scan for BLE devices using Android native BluetoothAdapter. For beacon detector, we also use scan record byte array to identify layout and data frame of beacon.

In this post, we are going to implement the same feature but by using a third-party library. We will use the awesome beacon library from AltBeacon team here.

Installation

Open your build.gradle and add JCenter as a repository:

repositories {  
  jcenter()
}

Then add to dependencies block:

compile 'org.altbeacon:android-beacon-library:2.5.1'  

Defining Your Beacon Layout

Based on your need, you may want to create your own Beacon model for storing more data, just like me. Here I allow the model to change bluetooth name, manufacturer ID and replace identifier with a specific UUID:

public class MyBeacon extends Beacon {  
 public void setBluetoothName(String name) {
  this.mBluetoothName = name;
 }
 public void setManufacturerId(int id) {
  this.mManufacturer = id;
 }
 public void setUuid(UUID uuid) {
  if (this.mIdentifiers == null) {
   this.mIdentifiers = new ArrayList < > ();
  }
  this.mIdentifiers.add(Identifier.fromUuid(uuid));
 }
}

Note that this step is not necessary, we usually need next step.

Defining Your Beacon Parser

The library uses custom parser to construct beacon from advertisement packet. It also allows us to extend and create our own parser too. The base parser scans for AltBeacon by default. To scan for iBeacon and others, we need to provide parser layout, or completely ignore it and write the parse method by ourself.

Example of a custom parser is like this:

public class MyBeaconParser extends BeaconParser {  
 @Override public Beacon fromScanData(byte[] scanData, int rssi, BluetoothDevice device) {
  return fromScanData(scanData, rssi, device, new MyBeacon());
 }
 @Override protected Beacon fromScanData(byte[] bytesToProcess, int rssi, BluetoothDevice device, Beacon other) {
  if (bytesToProcess == null || bytesToProcess.length < 7) {
   return null;
  }
  MyBeacon beacon = new MyBeacon();
  beacon.setRssi(rssi);
  beacon.setBluetoothName(device.getName());
  ByteBuffer bb = ByteBuffer.wrap(bytesToProcess, 5, 2);
  bb.order(ByteOrder.LITTLE_ENDIAN);
  beacon.setManufacturerId(bb.getShort());
  if (beacon.getManufacturer() != 0x11aa || bytesToProcess[3] != 0x13) {
   return null; // we return null, means this is not our desired beacon 
  }
  bb = ByteBuffer.wrap(bytesToProcess, 7, 16);
  beacon.setUuid(new UUID(bb.getLong(), bb.getLong()));
  return beacon;
 }
}

Or, to scan for iBeacon, we simply create the layout and add it to default parser:

mBeaconManager.getBeaconParsers().add(new BeaconParser().setBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24"));  

The string you have just see can be understood as:

  • 2nd and 3nd bytes are manufacturer ID, and their values are exactly 0x0215
  • First identifier goes from 4th byte to 19th byte
  • Second identifier goes from 20th byte to 21st byte
  • Third identifier goes from 22nd byte to 23rd byte
  • Finally, 24th byte is TxPower value

Initialization and Scanning

First, we obtain BeaconManager instance from an activity or context:

mBeaconManager = BeaconManager.getInstanceForApplication(this);  

We bind this manager to activity to receive callback whenever beacon service is ready:

mBeaconManager.bind(this);  

Our activity should implement the BeaconConsumer interface:

public class BeaconActivity extends Activity implements BeaconConsumer {  
  ...
  @Override public void onBeaconServiceConnect() {
    // here we are good to start scanning
  }
  ...
}

Then, before we start scan, do not forget to setRangeNotifier so we can know about updated beacon list:

mBeaconManager.setRangeNotifier(new RangeNotifier() {  
 @Override public void didRangeBeaconsInRegion(Collection < Beacon > beacons, Region region) {
    // manipulate beacons here 
 }
});

Start the region ranging is simple:

mRegion = new Region("MyRegion", null, null, null);  
mBeaconManager.startRangingBeaconsInRegion(mRegion);  

Library will do the hardwork by scanning for data packet, parsing and give you the list of discovered beacons through the notifier which you created earlier. Later, when you are done with beacon scanning, do not forget to stop the ranging:

mBeaconManager.stopRangingBeaconsInRegion(mRegion);  

Conclusion

You can see that, although Apple has created iBeacon, Android developers can totally scan and work with those beacons. It is very helpful for us to design and develop proximity and location-based application as well as other type of apps as well. Using a good library is also a good choice, beside manually taking care of native Android Bluetooth component. Going with which way is up to your own choice.