diff --git a/src/main/java/com/neovisionaries/bluetooth/ble/advertising/LocalName.java b/src/main/java/com/neovisionaries/bluetooth/ble/advertising/LocalName.java index e8c3af9..f6f2890 100644 --- a/src/main/java/com/neovisionaries/bluetooth/ble/advertising/LocalName.java +++ b/src/main/java/com/neovisionaries/bluetooth/ble/advertising/LocalName.java @@ -18,6 +18,8 @@ import java.io.UnsupportedEncodingException; +import static com.neovisionaries.bluetooth.ble.util.Bytes.removeTrailingNulls; + /** * An AD structure of type "Shortened Local Name" (type = 0x08) @@ -71,14 +73,16 @@ public LocalName(int length, int type, byte[] data) private void parse(byte[] data) { - if (data == null || data.length < 1) + byte[] dataStr = removeTrailingNulls(data); + + if (dataStr == null || dataStr.length < 1) { return; } try { - mLocalName = new String(data, "UTF-8"); + mLocalName = new String(dataStr, "UTF-8"); } catch (UnsupportedEncodingException e) { diff --git a/src/main/java/com/neovisionaries/bluetooth/ble/util/Bytes.java b/src/main/java/com/neovisionaries/bluetooth/ble/util/Bytes.java index 6d3573e..204d140 100644 --- a/src/main/java/com/neovisionaries/bluetooth/ble/util/Bytes.java +++ b/src/main/java/com/neovisionaries/bluetooth/ble/util/Bytes.java @@ -16,6 +16,7 @@ package com.neovisionaries.bluetooth.ble.util; + /** * Utility for byte arrays. * @@ -164,4 +165,34 @@ public static byte[] copyOfRange(byte[] source, int from, int to) return destination; } + + /** + * Remove trailing nulls from a byte array. + * + * @param data + * An input byte array. + + * @return + * A byte array with any trailing null bytes removed. + */ + public static byte[] removeTrailingNulls(byte[] data) + { + int length = lengthWithoutNulls(data); + byte[] result = new byte[length]; + + System.arraycopy(data, 0, result, 0, length); + + return result; + } + + private static int lengthWithoutNulls(byte[] data) { + if (data == null || data.length == 0) + return 0; + + for (int i = 0; i < data.length; i++) + if (data[i] == '\0') + return i; + + return data.length; + } } diff --git a/src/test/java/com/neovisionaries/bluetooth/ble/StandardGattServiceTest.java b/src/test/java/com/neovisionaries/bluetooth/ble/StandardGattServiceTest.java index 4971500..5fcb7a3 100644 --- a/src/test/java/com/neovisionaries/bluetooth/ble/StandardGattServiceTest.java +++ b/src/test/java/com/neovisionaries/bluetooth/ble/StandardGattServiceTest.java @@ -27,9 +27,17 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; + +import java.util.List; import java.util.UUID; + +import com.neovisionaries.bluetooth.ble.advertising.ADPayloadParser; +import com.neovisionaries.bluetooth.ble.advertising.ADStructure; +import com.neovisionaries.bluetooth.ble.advertising.LocalName; import org.junit.Test; +import javax.xml.bind.DatatypeConverter; + public class StandardGattServiceTest { @@ -51,6 +59,18 @@ private void uuidTest(String uuid, StandardGattService expected) compareInstances(expected, getByUuid(uuid)); } + private String nameFromScanRecord(String hex) + { + byte[] scanRecord = DatatypeConverter.parseHexBinary(hex); + List structures = ADPayloadParser.getInstance().parse(scanRecord); + for (ADStructure structure : structures) { + if (structure instanceof LocalName) { + LocalName name = (LocalName) structure; + return name.getLocalName(); + } + } + return null; + } private void uuidTest(UUID uuid, StandardGattService expected) { @@ -126,4 +146,12 @@ public void test10() { uuidTest(UUID.fromString("0000181F-0000-1000-8000-00805f9b34fb"), CONTINUOUS_GLUCOSE_MONITORING); } + + @Test + public void test11() + { + // test extracting a name with a null terminated string + // "Name" = 4E616D65 + assertEquals("Name", nameFromScanRecord("020102020A0C0319800006094E616D6500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")); + } }