Beacon Implementation in Android
In our previous post we have given a overview of what Beacon is, so here we are with its practical implementation.
There are two applications required to create Beacon experience.
1. Transmitter
2. Receiver
For Transmitter –
1. Bluetooth should be on
getSystemService(Context.BLUETOOTH_SERVICE)
2. BLE supported device
hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE))
<uses-permission android:name=”android.permission.BLUETOOTH” />
<uses-permission android:name=”android.permission.BLUETOOTH_ADMIN” />
If device supports BLE, then the Beacon Transmitter transmits the advertisements like this,
/**
* Simulates a new beacon every 10 seconds until it runs out of new ones to add.
*/
Beacon beacon = new Beacon.Builder() .setId1(“2f234454-cf6d-4a0f-adf2-f4911ba9ffa6”) .setId2(“1”) .setId3(“2”) .setManufacturer(0x0118) .setTxPower(-59) .setDataFields(Arrays.asList(new Long[] {0l})) .build(); BeaconParser beaconParser = new BeaconParser() .setBeaconLayout("m:2-3=beac,i:4-19,i:20-21,i:22-23,p:24-24,d:25-25"); BeaconTransmitter beaconTransmitter = new BeaconTransmitter(getApplicationContext(), beaconParser); beaconTransmitter.startAdvertising(beacon); scheduleTaskExecutor= Executors.newScheduledThreadPool(5); // This schedules an beacon to appear every 10 seconds: scheduleTaskExecutor.scheduleAtFixedRate(new Runnable(){ public void run() { try{ //putting a single beacon back into the beacons list. if (finalBeacons.size() > beacons.size()) beacons.add(finalBeacons.get(beacons.size())); else scheduleTaskExecutor.shutdown(); }catch(Exception e){ e.printStackTrace(); } } }, 0, 10, TimeUnit.SECONDS); } }
Beacon Advertisement Receiver
Two classes are main in this receiver
1. Beacon Parser
2. Beacon Manager
Beacon Parser – It parses the advertised data into relevant information.
@TargetApi(5) protected Beacon fromScanData(byte[] scanData, int rssi, BluetoothDevice device, Beacon beacon) { int startByte = 2; boolean patternFound = false; byte[] typeCodeBytes = longToByteArray(getMatchingBeaconTypeCode(), mMatchingBeaconTypeCodeEndOffset-mMatchingBeaconTypeCodeStartOffset+1); while (startByte <= 5) { if (byteArraysMatch(scanData, startByte+mMatchingBeaconTypeCodeStartOffset, typeCodeBytes, 0)) { patternFound = true; break; } startByte++; } if (patternFound == false) { // This is not an beacon BeaconManager.logDebug(TAG, "This is not a matching Beacon advertisement. (Was expecting "+byteArrayToString(typeCodeBytes)+". The bytes I see are: "+bytesToHex(scanData)); return null; } else { //BeaconManager.logDebug(TAG, "This is a recognized beacon advertisement -- "+String.format("%04x", getMatchingBeaconTypeCode())+" seen"); Log.e("rec beacon**", "This is a recognized beacon advertisement -- "+String.format("%04x", getMatchingBeaconTypeCode())+" seen"); } ArrayList<Identifier> identifiers = new ArrayList<Identifier>(); for (int i = 0; i < mIdentifierEndOffsets.size(); i++) { String idString = byteArrayToFormattedString(scanData, mIdentifierStartOffsets.get(i)+startByte, mIdentifierEndOffsets.get(i)+startByte, mIdentifierLittleEndianFlags.get(i)); Log.e("idString",idString); identifiers.add(Identifier.parse(idString)); } ArrayList<Long> dataFields = new ArrayList<Long>(); for (int i = 0; i < mDataEndOffsets.size(); i++) { String dataString = byteArrayToFormattedString(scanData, mDataStartOffsets.get(i)+startByte, mDataEndOffsets.get(i)+startByte, mDataLittleEndianFlags.get(i)); dataFields.add(Long.parseLong(dataString)); BeaconManager.logDebug(TAG, "parsing found data field "+i); // TODO: error handling needed here on the parse } int txPower = 0; String powerString = byteArrayToFormattedString(scanData, mPowerStartOffset+startByte, mPowerEndOffset+startByte, false); txPower = Integer.parseInt(powerString); // make sure it is a signed integer if (txPower > 127) { txPower -= 256; } Log.e("txPower data",txPower+""); // TODO: error handling needed on the parse int beaconTypeCode = 0; String beaconTypeString = byteArrayToFormattedString(scanData, mMatchingBeaconTypeCodeStartOffset+startByte, mMatchingBeaconTypeCodeEndOffset+startByte, false); beaconTypeCode = Integer.parseInt(beaconTypeString); // TODO: error handling needed on the parse int manufacturer = 0; String manufacturerString = byteArrayToFormattedString(scanData, startByte, startByte+1, true); manufacturer = Integer.parseInt(manufacturerString); String macAddress = null; String name = null; if (device != null) { macAddress = device.getAddress(); name = device.getName(); } beacon.mIdentifiers = identifiers; beacon.mDataFields = dataFields; beacon.mTxPower = txPower; beacon.mRssi = rssi; beacon.mBeaconTypeCode = beaconTypeCode; beacon.mBluetoothAddress = macAddress; beacon.mBluetoothName= name; beacon.mManufacturer = manufacturer; return beacon; }
Beacon Manager – It specifies the time interval for scanning Beacons in range.
public static final long DEFAULT_FOREGROUND_SCAN_PERIOD = 1000;
/**
* The default duration in milliseconds spent not scanning between each bluetooth scan cycle
*/
public static final long DEFAULT_FOREGROUND_BETWEEN_SCAN_PERIOD = 2*60*1000; // 2 minutes in foreground
/**
* The default duration in milliseconds of the bluetooth scan cycle when no ranging/monitoring clients
*/ are in the foreground
public static final long DEFAULT_BACKGROUND_SCAN_PERIOD = 10000;
/**
* The default duration in milliseconds spent not scanning between each bluetooth scan cycle when no */ ranging/monitoring clients are in the foreground
public static final long DEFAULT_BACKGROUND_BETWEEN_SCAN_PERIOD = 2*60*1000; // 2 minutes in background