Beacon Detector on Android

Beacon is one of the most trending technologies now. As retailers are always looking for the next great way to generate sales. Beacons are one technology that have taken the spotlight. Beacons have been featured in the news, mostly under the name “iBeacon” – Apple’s protocol using Beacon technologies.

To understand how beacon work, we need to know about Bluetooth Low Energy, or Bluetooth Smart or Bluetooth 4.0. It is a wireless persona area which target novel applications in the healthcare, fitness, beacons, security, and home entertainment industries. Today, most of major mobile platforms support for BLE capability in two form:

  • Advertising Mode: BLE devices broadcast a packet of data around it, this is 1-n connection
  • Central Mode: BLE devices connect and send/receive data with a client (like smartphones), this is 1-1 connection

BLE devices can act like a beacon when it broadcasts a data packet which conform to Bluetooth Specification. So with a beacon detector application, we need to scan for advertisement packet and check if the packet is belong to beacon.

Using BluetoothAdapter and ScanCallback

We can scan for beacon using BluetoothAdapter. First, make sure that you are going to implement on Android 4.3+, and your device has Bluetooth turned on. Then we acquire BluetoothAdapter instance:

final BluetoothManager bluetoothManager = (BluetoothManager) getApplicationContext().getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();

If we are on Lollipop and later, we should use new API for BLE callback:

mCallback = new ScanCallback() {
 @Override public void onScanResult(int callbackType, ScanResult result) {
  if (result != null && result.getDevice() != null && result.getScanRecord() != null) {
   appendDevice(result.getDevice(), result.getRssi(), result.getScanRecord().getBytes());
  }
 }
 @Override public void onBatchScanResults(List < ScanResult > results) {
  if (results != null) {
   for (ScanResult result: results) {
    if (result != null && result.getDevice() != null && result.getScanRecord() != null) {
     appendDevice(result.getDevice(), result.getRssi(), result.getScanRecord().getBytes());
    }
   }
  }
 }
 @Override public void onScanFailed(int errorCode) {}
};

However if we not, ensure that you use the legacy callback:

private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
 @Override public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
  if (device != null) {
   appendDevice(device, rssi, scanRecord);
  }
 }
};

Finally inside appendDevice function, we are good to check for scanRecord byte array to ensure our packet is from a beacon. The data layout of that beacon depends on its manufacture. For example, iBeacon, AltBeacon and others will have different layouts and internal byte values.

Start Scanning

mScanner = mBluetoothAdapter.getBluetoothLeScanner();
mScanner.startScan(Collections. < ScanFilter > emptyList(), new ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build(), mCallback);

Note that we use ScanSettings.SCAN_MODE_LOW_LATENCY to ensure highest performance for our scanning. Based on your requirements, please change this value as you need.

With before Lollipop, call legacy method to start scanning:

mBluetoothAdapter.startLeScan(mLeScanCallback);

When you’re done, dont forget to call stop scanning to avoid battery drain:

// Android 5.0+
mScanner.stopScan(mCallback);
// else
mBluetoothAdapter.stopLeScan(mLeScanCallback);

You’re good to go from here. In next post, we will use another third-party library to abstract Bluetooth layer and only care for beacon regioning.