package org.altbeacon.beacon;
import android.annotation.TargetApi;
import android.bluetooth.BluetoothDevice;
import android.os.Build;
import android.os.Parcel;
import org.altbeacon.beacon.logging.LogManager;
import org.altbeacon.beacon.logging.Loggers;
import org.junit.Test;
import java.util.ArrayList;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
import org.robolectric.annotation.Config;
@Config(sdk = 18)
/**
* Created by dyoung on 7/22/14.
*/
public class SBeaconTest {
@Test
public void testDetectsSBeacon() {
org.robolectric.shadows.ShadowLog.stream = System.err;
byte[] bytes = hexStringToByteArray("02011a1bff1801031501000100c502000000000000000003");
SBeaconParser parser = new SBeaconParser();
SBeacon sBeacon = (SBeacon) parser.fromScanData(bytes, -55, null);
assertNotNull("SBeacon should be not null if parsed successfully", sBeacon);
assertEquals("id should be parsed", "0x000000000003", sBeacon.getId());
assertEquals("group should be parsed", 1, sBeacon.getGroup());
assertEquals("time should be parsed", 2, sBeacon.getTime());
assertEquals("txPower should be parsed", -59, sBeacon.getTxPower());
}
public static byte[] hexStringToByteArray(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i+1), 16));
}
return data;
}
class SBeacon extends Beacon {
private static final String TAG = "SBeacon";
private int mTime;
protected SBeacon(int group, String id, int time, int txPower, int rssi, int beaconTypeCode, String bluetoothAddress) {
super();
mTxPower = txPower;
mRssi = rssi;
mBeaconTypeCode = beaconTypeCode;
mBluetoothAddress = bluetoothAddress;
mIdentifiers = new ArrayList<Identifier>(2);
mIdentifiers.add(Identifier.fromInt(group));
mIdentifiers.add(Identifier.parse(id));
mTime = time;
//BeaconManager.logDebug(TAG, "constructed a new sbeacon with id2: " + getIdentifier(2));
}
public int getGroup() {
return mIdentifiers.get(0).toInt();
}
public int getTime() {
return mTime;
}
public String getId() {
return mIdentifiers.get(1).toString();
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
// TODO: Implement me
}
protected SBeacon(Parcel in) {
// TODO: Implement me
}
}
class SBeaconParser extends BeaconParser {
private static final String TAG = "SBeaconParser";
@TargetApi(Build.VERSION_CODES.ECLAIR)
@Override
public Beacon fromScanData(byte[] scanData, int rssi, BluetoothDevice device) {
int startByte = 2;
while (startByte <= 5) {
// "m:2-3=0203,i:2-2,i:7-8,i:14-19,d:10-13,p:9-9"
if (((int)scanData[startByte+3] & 0xff) == 0x03 &&
((int)scanData[startByte+4] & 0xff) == 0x15) {
//BeaconManager.logDebug(TAG, "This is a SBeacon beacon advertisement");
// startByte+0 company id (2 bytes)
// startByte+2 = 02 (1) byte header
// startByte+3 = 0315 (2 bytes) header
// startByte+5 = Beacon Type 0x01
// startByte+6 = Reserved (1 bytes)
// startByte+7 = Security Code (2 bytes) => Major little endian
// startByte+9 = Tx Power => Tx Power
// startByte+10 = Timestamp (4 bytes) => Minor (2 LSBs) little endian
// startByte+14 = Beacon ID (6 bytes) -> UUID little endian
int group = (scanData[startByte+8] & 0xff) * 0x100 + (scanData[startByte+7] & 0xff);
int clock = (scanData[startByte+13] & 0xff) * 0x1000000 + (scanData[startByte+12] & 0xff) * 0x10000 + (scanData[startByte+11] & 0xff) * 0x100 + (scanData[startByte+10] & 0xff);
int txPower = (int)scanData[startByte+9]; // this one is signed
byte[] beaconId = new byte[6];
System.arraycopy(scanData, startByte+14, beaconId, 0, 6);
String hexString = bytesToHex(beaconId);
StringBuilder sb = new StringBuilder();
sb.append(hexString.substring(0,12));
String id = "0x" + sb.toString();
int beaconTypeCode = (scanData[startByte+3] & 0xff) * 0x100 + (scanData[startByte+2] & 0xff);
String mac = null;
if (device != null) {
mac = device.getAddress();
}
Beacon beacon = new SBeacon(group, id, clock, txPower, rssi, beaconTypeCode, mac);
return beacon;
}
startByte++;
}
return null;
}
}
}