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.
*/
[code language=”java”]
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);
}
}
[/code]
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.
[code language=”java”]
@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;
}
[/code]
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
*/
[code language=”java”]
public static final long DEFAULT_FOREGROUND_BETWEEN_SCAN_PERIOD = 2*60*1000; // 2 minutes in foreground
[/code]
/**
* The default duration in milliseconds of the bluetooth scan cycle when no ranging/monitoring clients
*/ are in the foreground
[code language=”java”]
public static final long DEFAULT_BACKGROUND_SCAN_PERIOD = 10000;
[/code]
/**
* The default duration in milliseconds spent not scanning between each bluetooth scan cycle when no */ ranging/monitoring clients are in the foreground
[code language=”java”]
public static final long DEFAULT_BACKGROUND_BETWEEN_SCAN_PERIOD = 2*60*1000; // 2 minutes in background
[/code]