Starting to abstract more so we can support more beacon types
This commit is contained in:
@@ -216,15 +216,6 @@ public class BluetoothLeDevice implements Parcelable {
|
|||||||
return BluetoothClassResolver.resolveDeviceClass(mDevice.getBluetoothClass().getDeviceClass());
|
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() {
|
public Set<BluetoothService> getBluetoothDeviceKnownSupportedServices() {
|
||||||
if (mServiceSet == null) {
|
if (mServiceSet == null) {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
@@ -244,6 +235,15 @@ public class BluetoothLeDevice implements Parcelable {
|
|||||||
return mServiceSet;
|
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.
|
* Gets the device.
|
||||||
*
|
*
|
||||||
|
|||||||
+31
@@ -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 {
|
public enum BeaconType {
|
||||||
NOT_A_BEACON,
|
NOT_A_BEACON,
|
||||||
IBEACON
|
IBEACON,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ public final class BeaconUtils {
|
|||||||
* @return the {@link BeaconType}
|
* @return the {@link BeaconType}
|
||||||
*/
|
*/
|
||||||
public static BeaconType getBeaconType(final byte[] manufacturerData) {
|
public static BeaconType getBeaconType(final byte[] manufacturerData) {
|
||||||
if (manufacturerData == null) {
|
if (manufacturerData == null || manufacturerData.length == 0) {
|
||||||
return BeaconType.NOT_A_BEACON;
|
return BeaconType.NOT_A_BEACON;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
-10
@@ -25,7 +25,6 @@ public class IBeaconDevice extends BluetoothLeDevice implements BeaconDevice{
|
|||||||
*/
|
*/
|
||||||
public IBeaconDevice(final BluetoothDevice device, final int rssi, final byte[] scanRecord) {
|
public IBeaconDevice(final BluetoothDevice device, final int rssi, final byte[] scanRecord) {
|
||||||
super(device, rssi, scanRecord, 0);
|
super(device, rssi, scanRecord, 0);
|
||||||
validate();
|
|
||||||
mIBeaconData = new IBeaconManufacturerData(this);
|
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) {
|
public IBeaconDevice(final BluetoothDevice device, final int rssi, final byte[] scanRecord, final long timestamp) {
|
||||||
super(device, rssi, scanRecord, timestamp);
|
super(device, rssi, scanRecord, timestamp);
|
||||||
validate();
|
|
||||||
mIBeaconData = new IBeaconManufacturerData(this);
|
mIBeaconData = new IBeaconManufacturerData(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,13 +51,11 @@ public class IBeaconDevice extends BluetoothLeDevice implements BeaconDevice{
|
|||||||
*/
|
*/
|
||||||
public IBeaconDevice(final BluetoothLeDevice device) {
|
public IBeaconDevice(final BluetoothLeDevice device) {
|
||||||
super(device);
|
super(device);
|
||||||
validate();
|
|
||||||
mIBeaconData = new IBeaconManufacturerData(this);
|
mIBeaconData = new IBeaconManufacturerData(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private IBeaconDevice(final Parcel in) {
|
private IBeaconDevice(final Parcel in) {
|
||||||
super(in);
|
super(in);
|
||||||
validate();
|
|
||||||
mIBeaconData = new IBeaconManufacturerData(this);
|
mIBeaconData = new IBeaconManufacturerData(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,10 +139,4 @@ public class IBeaconDevice extends BluetoothLeDevice implements BeaconDevice{
|
|||||||
public String getUUID() {
|
public String getUUID() {
|
||||||
return getIBeaconData().getUUID();
|
return getIBeaconData().getUUID();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validate() {
|
|
||||||
if (BeaconUtils.getBeaconType(this) != BeaconType.IBEACON) {
|
|
||||||
throw new IllegalArgumentException("Device " + getDevice() + " is not an iBeacon.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-17
@@ -4,8 +4,8 @@ import java.util.Arrays;
|
|||||||
|
|
||||||
import uk.co.alt236.bluetoothlelib.device.BluetoothLeDevice;
|
import uk.co.alt236.bluetoothlelib.device.BluetoothLeDevice;
|
||||||
import uk.co.alt236.bluetoothlelib.device.adrecord.AdRecord;
|
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.BeaconType;
|
||||||
import uk.co.alt236.bluetoothlelib.device.beacon.BeaconUtils;
|
|
||||||
import uk.co.alt236.bluetoothlelib.util.ByteUtils;
|
import uk.co.alt236.bluetoothlelib.util.ByteUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -45,8 +45,7 @@ import uk.co.alt236.bluetoothlelib.util.ByteUtils;
|
|||||||
* @author Alexandros Schillings
|
* @author Alexandros Schillings
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public final class IBeaconManufacturerData {
|
public final class IBeaconManufacturerData extends BeaconManufacturerData{
|
||||||
private final byte[] mData;
|
|
||||||
private final int mCalibratedTxPower;
|
private final int mCalibratedTxPower;
|
||||||
private final int mCompanyIdentidier;
|
private final int mCompanyIdentidier;
|
||||||
private final int mIBeaconAdvertisment;
|
private final int mIBeaconAdvertisment;
|
||||||
@@ -71,24 +70,17 @@ public final class IBeaconManufacturerData {
|
|||||||
* @throws IllegalArgumentException if the data is not from an iBeacon.
|
* @throws IllegalArgumentException if the data is not from an iBeacon.
|
||||||
*/
|
*/
|
||||||
public IBeaconManufacturerData(final byte[] manufacturerData) {
|
public IBeaconManufacturerData(final byte[] manufacturerData) {
|
||||||
mData = manufacturerData;
|
super(BeaconType.IBEACON, manufacturerData);
|
||||||
|
|
||||||
if (BeaconUtils.getBeaconType(mData) != BeaconType.IBEACON) {
|
final byte[] intArray = Arrays.copyOfRange(manufacturerData, 0, 2);
|
||||||
throw new IllegalArgumentException(
|
|
||||||
"Manufacturer record '"
|
|
||||||
+ Arrays.toString(manufacturerData)
|
|
||||||
+ "' is not from an iBeacon.");
|
|
||||||
}
|
|
||||||
|
|
||||||
final byte[] intArray = Arrays.copyOfRange(mData, 0, 2);
|
|
||||||
ByteUtils.invertArray(intArray);
|
ByteUtils.invertArray(intArray);
|
||||||
|
|
||||||
mCompanyIdentidier = ByteUtils.getIntFrom2ByteArray(intArray);
|
mCompanyIdentidier = ByteUtils.getIntFrom2ByteArray(intArray);
|
||||||
mIBeaconAdvertisment = ByteUtils.getIntFrom2ByteArray(Arrays.copyOfRange(mData, 2, 4));
|
mIBeaconAdvertisment = ByteUtils.getIntFrom2ByteArray(Arrays.copyOfRange(manufacturerData, 2, 4));
|
||||||
mUUID = IBeaconUtils.calculateUuidString(Arrays.copyOfRange(mData, 4, 20));
|
mUUID = IBeaconUtils.calculateUuidString(Arrays.copyOfRange(manufacturerData, 4, 20));
|
||||||
mMajor = ByteUtils.getIntFrom2ByteArray(Arrays.copyOfRange(mData, 20, 22));
|
mMajor = ByteUtils.getIntFrom2ByteArray(Arrays.copyOfRange(manufacturerData, 20, 22));
|
||||||
mMinor = ByteUtils.getIntFrom2ByteArray(Arrays.copyOfRange(mData, 22, 24));
|
mMinor = ByteUtils.getIntFrom2ByteArray(Arrays.copyOfRange(manufacturerData, 22, 24));
|
||||||
mCalibratedTxPower = mData[24];
|
mCalibratedTxPower = manufacturerData[24];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+6
-6
@@ -7,12 +7,6 @@ import junit.framework.TestCase;
|
|||||||
*/
|
*/
|
||||||
public class BeaconUtilsTest extends 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 {
|
public void testGetBeaconTypeIBeacon() throws Exception {
|
||||||
assertEquals(BeaconType.IBEACON, BeaconUtils.getBeaconType(new byte[]{
|
assertEquals(BeaconType.IBEACON, BeaconUtils.getBeaconType(new byte[]{
|
||||||
0x4C, 0x00, 0x02, 0x15, 0x00, // <- Magic iBeacon header
|
0x4C, 0x00, 0x02, 0x15, 0x00, // <- Magic iBeacon header
|
||||||
@@ -22,4 +16,10 @@ public class BeaconUtilsTest extends TestCase {
|
|||||||
0x00, 0x00, 0x00, 0x00, 0x00
|
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]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+39
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+11
-11
@@ -7,6 +7,17 @@ import junit.framework.TestCase;
|
|||||||
*/
|
*/
|
||||||
public class IBeaconUtilsTest extends 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 {
|
public void testGetDistanceDescriptor() throws Exception {
|
||||||
assertEquals(IBeaconDistanceDescriptor.UNKNOWN, IBeaconUtils.getDistanceDescriptor(-1));
|
assertEquals(IBeaconDistanceDescriptor.UNKNOWN, IBeaconUtils.getDistanceDescriptor(-1));
|
||||||
|
|
||||||
@@ -18,15 +29,4 @@ public class IBeaconUtilsTest extends TestCase {
|
|||||||
|
|
||||||
assertEquals(IBeaconDistanceDescriptor.FAR, IBeaconUtils.getDistanceDescriptor(3));
|
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...
|
// Cannot be tested here as it relies on Android code...
|
||||||
//
|
//
|
||||||
// final SparseArray<AdRecord> adRecords = AdRecordUtils.parseScanRecordAsSparseArray(NON_IBEACON);
|
// final SparseArray<AdRecord> adRecords = AdRecordUtils.parseScanRecordAsSparseArray(NON_IBEACON);
|
||||||
// assertNotNull(adRecords);
|
// assertNotNull(adRecords);
|
||||||
// assertEquals(2, adRecords.size());
|
// assertEquals(2, adRecords.size());
|
||||||
// assertEquals(AdRecord.TYPE_FLAGS, adRecords.get(AdRecord.TYPE_FLAGS).getType());
|
// assertEquals(AdRecord.TYPE_FLAGS, adRecords.get(AdRecord.TYPE_FLAGS).getType());
|
||||||
// assertEquals(AdRecord.TYPE_MANUFACTURER_SPECIFIC_DATA, adRecords.get(AdRecord.TYPE_MANUFACTURER_SPECIFIC_DATA).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 class ByteUtilsTest extends TestCase {
|
||||||
|
|
||||||
public void testInvertArray() throws Exception {
|
public void testByteArrayToHexString() throws Exception {
|
||||||
final byte[] original = {1, 2 ,3 ,4};
|
assertEquals("[]", ByteUtils.byteArrayToHexString(new byte[0]));
|
||||||
final byte[] out = new byte[original.length];
|
|
||||||
|
|
||||||
System.arraycopy( original, 0, out, 0, original.length);
|
final byte[] one = {1, 10, 15, 127};
|
||||||
ByteUtils.invertArray(out);
|
assertEquals("[01, 0A, 0F, 7F]", ByteUtils.byteArrayToHexString(one));
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testDoesArrayBeginWith() throws Exception {
|
public void testDoesArrayBeginWith() throws Exception {
|
||||||
@@ -51,10 +35,26 @@ public class ByteUtilsTest extends TestCase {
|
|||||||
assertTrue(ByteUtils.doesArrayBeginWith(array, prefix));
|
assertTrue(ByteUtils.doesArrayBeginWith(array, prefix));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testByteArrayToHexString() throws Exception {
|
public void testGetIntFromByte() throws Exception {
|
||||||
assertEquals("[]", ByteUtils.byteArrayToHexString(new byte[0]));
|
byte bite = 127;
|
||||||
|
int integer = ByteUtils.getIntFromByte(bite);
|
||||||
|
assertEquals(127, integer);
|
||||||
|
|
||||||
final byte[] one = {1, 10, 15, 127};
|
bite = -1;
|
||||||
assertEquals("[01, 0A, 0F, 7F]", ByteUtils.byteArrayToHexString(one));
|
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]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user