Starting to abstract more so we can support more beacon types

This commit is contained in:
Alexandros Schillings
2015-07-14 14:01:32 +01:00
parent cb43be74ef
commit 459eb1d2d4
11 changed files with 136 additions and 84 deletions
@@ -216,15 +216,6 @@ public class BluetoothLeDevice implements Parcelable {
return BluetoothClassResolver.resolveDeviceClass(mDevice.getBluetoothClass().getDeviceClass());
}
/**
* Gets the bluetooth device major class name.
*
* @return the bluetooth device major class name
*/
public String getBluetoothDeviceMajorClassName() {
return BluetoothClassResolver.resolveMajorDeviceClass(mDevice.getBluetoothClass().getMajorDeviceClass());
}
public Set<BluetoothService> getBluetoothDeviceKnownSupportedServices() {
if (mServiceSet == null) {
synchronized (this) {
@@ -244,6 +235,15 @@ public class BluetoothLeDevice implements Parcelable {
return mServiceSet;
}
/**
* Gets the bluetooth device major class name.
*
* @return the bluetooth device major class name
*/
public String getBluetoothDeviceMajorClassName() {
return BluetoothClassResolver.resolveMajorDeviceClass(mDevice.getBluetoothClass().getMajorDeviceClass());
}
/**
* Gets the device.
*
@@ -0,0 +1,31 @@
package uk.co.alt236.bluetoothlelib.device.beacon;
import java.util.Arrays;
/**
*
*/
public abstract class BeaconManufacturerData {
private final BeaconType mBeaconType;
private final byte[] mData;
protected BeaconManufacturerData(final BeaconType expectedType, final byte[] data){
if (BeaconUtils.getBeaconType(data) != expectedType) {
throw new IllegalArgumentException(
"Manufacturer record '"
+ Arrays.toString(data)
+ "' is not from a " + expectedType);
}
this.mData = data;
this.mBeaconType = expectedType;
}
public BeaconType getBeaconType(){
return mBeaconType;
}
public byte[] getData(){
return mData;
}
}
@@ -5,5 +5,5 @@ package uk.co.alt236.bluetoothlelib.device.beacon;
*/
public enum BeaconType {
NOT_A_BEACON,
IBEACON
IBEACON,
}
@@ -21,7 +21,7 @@ public final class BeaconUtils {
* @return the {@link BeaconType}
*/
public static BeaconType getBeaconType(final byte[] manufacturerData) {
if (manufacturerData == null) {
if (manufacturerData == null || manufacturerData.length == 0) {
return BeaconType.NOT_A_BEACON;
}
@@ -25,7 +25,6 @@ public class IBeaconDevice extends BluetoothLeDevice implements BeaconDevice{
*/
public IBeaconDevice(final BluetoothDevice device, final int rssi, final byte[] scanRecord) {
super(device, rssi, scanRecord, 0);
validate();
mIBeaconData = new IBeaconManufacturerData(this);
}
@@ -40,7 +39,6 @@ public class IBeaconDevice extends BluetoothLeDevice implements BeaconDevice{
*/
public IBeaconDevice(final BluetoothDevice device, final int rssi, final byte[] scanRecord, final long timestamp) {
super(device, rssi, scanRecord, timestamp);
validate();
mIBeaconData = new IBeaconManufacturerData(this);
}
@@ -53,13 +51,11 @@ public class IBeaconDevice extends BluetoothLeDevice implements BeaconDevice{
*/
public IBeaconDevice(final BluetoothLeDevice device) {
super(device);
validate();
mIBeaconData = new IBeaconManufacturerData(this);
}
private IBeaconDevice(final Parcel in) {
super(in);
validate();
mIBeaconData = new IBeaconManufacturerData(this);
}
@@ -143,10 +139,4 @@ public class IBeaconDevice extends BluetoothLeDevice implements BeaconDevice{
public String getUUID() {
return getIBeaconData().getUUID();
}
private void validate() {
if (BeaconUtils.getBeaconType(this) != BeaconType.IBEACON) {
throw new IllegalArgumentException("Device " + getDevice() + " is not an iBeacon.");
}
}
}
@@ -4,8 +4,8 @@ import java.util.Arrays;
import uk.co.alt236.bluetoothlelib.device.BluetoothLeDevice;
import uk.co.alt236.bluetoothlelib.device.adrecord.AdRecord;
import uk.co.alt236.bluetoothlelib.device.beacon.BeaconManufacturerData;
import uk.co.alt236.bluetoothlelib.device.beacon.BeaconType;
import uk.co.alt236.bluetoothlelib.device.beacon.BeaconUtils;
import uk.co.alt236.bluetoothlelib.util.ByteUtils;
/**
@@ -45,8 +45,7 @@ import uk.co.alt236.bluetoothlelib.util.ByteUtils;
* @author Alexandros Schillings
*/
public final class IBeaconManufacturerData {
private final byte[] mData;
public final class IBeaconManufacturerData extends BeaconManufacturerData{
private final int mCalibratedTxPower;
private final int mCompanyIdentidier;
private final int mIBeaconAdvertisment;
@@ -71,24 +70,17 @@ public final class IBeaconManufacturerData {
* @throws IllegalArgumentException if the data is not from an iBeacon.
*/
public IBeaconManufacturerData(final byte[] manufacturerData) {
mData = manufacturerData;
super(BeaconType.IBEACON, manufacturerData);
if (BeaconUtils.getBeaconType(mData) != BeaconType.IBEACON) {
throw new IllegalArgumentException(
"Manufacturer record '"
+ Arrays.toString(manufacturerData)
+ "' is not from an iBeacon.");
}
final byte[] intArray = Arrays.copyOfRange(mData, 0, 2);
final byte[] intArray = Arrays.copyOfRange(manufacturerData, 0, 2);
ByteUtils.invertArray(intArray);
mCompanyIdentidier = ByteUtils.getIntFrom2ByteArray(intArray);
mIBeaconAdvertisment = ByteUtils.getIntFrom2ByteArray(Arrays.copyOfRange(mData, 2, 4));
mUUID = IBeaconUtils.calculateUuidString(Arrays.copyOfRange(mData, 4, 20));
mMajor = ByteUtils.getIntFrom2ByteArray(Arrays.copyOfRange(mData, 20, 22));
mMinor = ByteUtils.getIntFrom2ByteArray(Arrays.copyOfRange(mData, 22, 24));
mCalibratedTxPower = mData[24];
mIBeaconAdvertisment = ByteUtils.getIntFrom2ByteArray(Arrays.copyOfRange(manufacturerData, 2, 4));
mUUID = IBeaconUtils.calculateUuidString(Arrays.copyOfRange(manufacturerData, 4, 20));
mMajor = ByteUtils.getIntFrom2ByteArray(Arrays.copyOfRange(manufacturerData, 20, 22));
mMinor = ByteUtils.getIntFrom2ByteArray(Arrays.copyOfRange(manufacturerData, 22, 24));
mCalibratedTxPower = manufacturerData[24];
}
/**
@@ -7,12 +7,6 @@ import junit.framework.TestCase;
*/
public class BeaconUtilsTest extends TestCase {
public void testGetBeaconTypeInvalid() throws Exception {
assertEquals(BeaconType.NOT_A_BEACON, BeaconUtils.getBeaconType((byte[]) null));
assertEquals(BeaconType.NOT_A_BEACON, BeaconUtils.getBeaconType(new byte[0]));
assertEquals(BeaconType.NOT_A_BEACON, BeaconUtils.getBeaconType(new byte[25]));
}
public void testGetBeaconTypeIBeacon() throws Exception {
assertEquals(BeaconType.IBEACON, BeaconUtils.getBeaconType(new byte[]{
0x4C, 0x00, 0x02, 0x15, 0x00, // <- Magic iBeacon header
@@ -22,4 +16,10 @@ public class BeaconUtilsTest extends TestCase {
0x00, 0x00, 0x00, 0x00, 0x00
}));
}
public void testGetBeaconTypeInvalid() throws Exception {
assertEquals(BeaconType.NOT_A_BEACON, BeaconUtils.getBeaconType((byte[]) null));
assertEquals(BeaconType.NOT_A_BEACON, BeaconUtils.getBeaconType(new byte[0]));
assertEquals(BeaconType.NOT_A_BEACON, BeaconUtils.getBeaconType(new byte[25]));
}
}
@@ -0,0 +1,39 @@
package uk.co.alt236.bluetoothlelib.device.beacon.ibeacon;
import junit.framework.TestCase;
import uk.co.alt236.bluetoothlelib.device.beacon.BeaconManufacturerData;
/**
*
*/
public class IBeaconManufacturerDataTest extends TestCase {
private static final byte[] NON_BEACON =
{2, 1, 26, 11, -1, 76, 0, 9, 6, 3, -32, -64, -88,
1, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
public void testNonIBeaconData() throws Exception{
try {
BeaconManufacturerData data = new IBeaconManufacturerData(NON_BEACON);
fail("Should have thrown an exception");
} catch (final IllegalArgumentException e){
// EXPECTED
}
try {
BeaconManufacturerData data = new IBeaconManufacturerData((byte[]) null);
fail("Should have thrown an exception");
} catch (final IllegalArgumentException e){
// EXPECTED
}
try {
BeaconManufacturerData data = new IBeaconManufacturerData(new byte[0]);
fail("Should have thrown an exception");
} catch (final IllegalArgumentException e){
// EXPECTED
}
}
}
@@ -7,6 +7,17 @@ import junit.framework.TestCase;
*/
public class IBeaconUtilsTest extends TestCase {
public void testCalculateUuidString() throws Exception {
assertEquals("00", IBeaconUtils.calculateUuidString(new byte[]{0}));
assertEquals("0a", IBeaconUtils.calculateUuidString(new byte[]{10}));
assertEquals("0f", IBeaconUtils.calculateUuidString(new byte[]{15}));
assertEquals("10", IBeaconUtils.calculateUuidString(new byte[]{16}));
assertEquals("7f", IBeaconUtils.calculateUuidString(new byte[]{127}));
assertEquals(
"00000000-0000-0000-0000-00",
IBeaconUtils.calculateUuidString(new byte[]{0,0,0,0,0,0,0,0,0,0,0}));
}
public void testGetDistanceDescriptor() throws Exception {
assertEquals(IBeaconDistanceDescriptor.UNKNOWN, IBeaconUtils.getDistanceDescriptor(-1));
@@ -18,15 +29,4 @@ public class IBeaconUtilsTest extends TestCase {
assertEquals(IBeaconDistanceDescriptor.FAR, IBeaconUtils.getDistanceDescriptor(3));
}
public void testCalculateUuidString() throws Exception {
assertEquals("00", IBeaconUtils.calculateUuidString(new byte[]{0}));
assertEquals("0a", IBeaconUtils.calculateUuidString(new byte[]{10}));
assertEquals("0f", IBeaconUtils.calculateUuidString(new byte[]{15}));
assertEquals("10", IBeaconUtils.calculateUuidString(new byte[]{16}));
assertEquals("7f", IBeaconUtils.calculateUuidString(new byte[]{127}));
assertEquals(
"00000000-0000-0000-0000-00",
IBeaconUtils.calculateUuidString(new byte[]{0,0,0,0,0,0,0,0,0,0,0}));
}
}
@@ -49,10 +49,10 @@ public class AdRecordUtilsTest extends TestCase {
//
// Cannot be tested here as it relies on Android code...
//
// final SparseArray<AdRecord> adRecords = AdRecordUtils.parseScanRecordAsSparseArray(NON_IBEACON);
// assertNotNull(adRecords);
// assertEquals(2, adRecords.size());
// assertEquals(AdRecord.TYPE_FLAGS, adRecords.get(AdRecord.TYPE_FLAGS).getType());
// assertEquals(AdRecord.TYPE_MANUFACTURER_SPECIFIC_DATA, adRecords.get(AdRecord.TYPE_MANUFACTURER_SPECIFIC_DATA).getType());
// final SparseArray<AdRecord> adRecords = AdRecordUtils.parseScanRecordAsSparseArray(NON_IBEACON);
// assertNotNull(adRecords);
// assertEquals(2, adRecords.size());
// assertEquals(AdRecord.TYPE_FLAGS, adRecords.get(AdRecord.TYPE_FLAGS).getType());
// assertEquals(AdRecord.TYPE_MANUFACTURER_SPECIFIC_DATA, adRecords.get(AdRecord.TYPE_MANUFACTURER_SPECIFIC_DATA).getType());
}
}
@@ -7,27 +7,11 @@ import junit.framework.TestCase;
*/
public class ByteUtilsTest extends TestCase {
public void testInvertArray() throws Exception {
final byte[] original = {1, 2 ,3 ,4};
final byte[] out = new byte[original.length];
public void testByteArrayToHexString() throws Exception {
assertEquals("[]", ByteUtils.byteArrayToHexString(new byte[0]));
System.arraycopy( original, 0, out, 0, original.length);
ByteUtils.invertArray(out);
assertEquals(original[0], out[3]);
assertEquals(original[1], out[2]);
assertEquals(original[2], out[1]);
assertEquals(original[3], out[0]);
}
public void testGetIntFromByte() throws Exception {
byte bite = 127;
int integer = ByteUtils.getIntFromByte(bite);
assertEquals(127, integer);
bite = -1;
integer = ByteUtils.getIntFromByte(bite);
assertEquals(255, integer);
final byte[] one = {1, 10, 15, 127};
assertEquals("[01, 0A, 0F, 7F]", ByteUtils.byteArrayToHexString(one));
}
public void testDoesArrayBeginWith() throws Exception {
@@ -51,10 +35,26 @@ public class ByteUtilsTest extends TestCase {
assertTrue(ByteUtils.doesArrayBeginWith(array, prefix));
}
public void testByteArrayToHexString() throws Exception {
assertEquals("[]", ByteUtils.byteArrayToHexString(new byte[0]));
public void testGetIntFromByte() throws Exception {
byte bite = 127;
int integer = ByteUtils.getIntFromByte(bite);
assertEquals(127, integer);
final byte[] one = {1, 10, 15, 127};
assertEquals("[01, 0A, 0F, 7F]", ByteUtils.byteArrayToHexString(one));
bite = -1;
integer = ByteUtils.getIntFromByte(bite);
assertEquals(255, integer);
}
public void testInvertArray() throws Exception {
final byte[] original = {1, 2 ,3 ,4};
final byte[] out = new byte[original.length];
System.arraycopy( original, 0, out, 0, original.length);
ByteUtils.invertArray(out);
assertEquals(original[0], out[3]);
assertEquals(original[1], out[2]);
assertEquals(original[2], out[1]);
assertEquals(original[3], out[0]);
}
}