diff --git a/src/main/java/org/altbeacon/beacon/service/ExtraDataBeaconTracker.java b/src/main/java/org/altbeacon/beacon/service/ExtraDataBeaconTracker.java index 303efdd4d..ae1bbbb19 100644 --- a/src/main/java/org/altbeacon/beacon/service/ExtraDataBeaconTracker.java +++ b/src/main/java/org/altbeacon/beacon/service/ExtraDataBeaconTracker.java @@ -1,5 +1,8 @@ package org.altbeacon.beacon.service; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + import org.altbeacon.beacon.Beacon; import java.io.Serializable; @@ -12,11 +15,17 @@ */ public class ExtraDataBeaconTracker implements Serializable { private static final String TAG = "BeaconTracker"; - // This is a lookup table to find tracked beacons by the calculated beacon key - private HashMap> mBeaconsByKey = new HashMap>(); - private boolean matchBeaconsByServiceUUID = true; + /** + * This is a lookup table to find tracked beacons by the calculated beacon key + */ + @NonNull + private final HashMap> mBeaconsByKey = new HashMap<>(); + + private final boolean matchBeaconsByServiceUUID; + public ExtraDataBeaconTracker() { + this(true); } public ExtraDataBeaconTracker(boolean matchBeaconsByServiceUUID) { @@ -25,12 +34,10 @@ public ExtraDataBeaconTracker(boolean matchBeaconsByServiceUUID) { /** * Tracks a beacon. For Gatt-based beacons, returns a merged copy of fields from multiple - * frames. Returns null when passed a Gatt-based beacon that has is only extra beacon data. - * - * @param beacon - * @return + * frames. Returns null when passed a Gatt-based beacon that has is only extra beacon data. */ - public synchronized Beacon track(Beacon beacon) { + @Nullable + public synchronized Beacon track(@NonNull Beacon beacon) { Beacon trackedBeacon = null; if (beacon.isMultiFrameBeacon() || beacon.getServiceUuid() != -1) { trackedBeacon = trackGattBeacon(beacon); @@ -41,42 +48,42 @@ public synchronized Beacon track(Beacon beacon) { return trackedBeacon; } - // The following code is for dealing with merging data fields in beacons - private Beacon trackGattBeacon(Beacon beacon) { - Beacon trackedBeacon = null; - HashMap matchingTrackedBeacons = mBeaconsByKey.get(getBeaconKey(beacon)); - if (matchingTrackedBeacons != null) { - for (Beacon matchingTrackedBeacon: matchingTrackedBeacons.values()) { - if (beacon.isExtraBeaconData()) { - matchingTrackedBeacon.setRssi(beacon.getRssi()); - matchingTrackedBeacon.setExtraDataFields(beacon.getDataFields()); - } - else { - beacon.setExtraDataFields(matchingTrackedBeacon.getExtraDataFields()); - // replace the tracked beacon instance with this one so it has updated values - trackedBeacon = beacon; - } - } - } - if (!beacon.isExtraBeaconData()) { - updateTrackingHashes(beacon, matchingTrackedBeacons); + /** + * The following code is for dealing with merging data fields in beacons + */ + @Nullable + private Beacon trackGattBeacon(@NonNull Beacon beacon) { + if (beacon.isExtraBeaconData()) { + updateTrackedBeacons(beacon); + return null; } - if (trackedBeacon == null && !beacon.isExtraBeaconData()) { - trackedBeacon = beacon; + String key = getBeaconKey(beacon); + HashMap matchingTrackedBeacons = mBeaconsByKey.get(key); + if (null == matchingTrackedBeacons) { + matchingTrackedBeacons = new HashMap<>(); } - return trackedBeacon; + else { + Beacon trackedBeacon = matchingTrackedBeacons.values().iterator().next(); + beacon.setExtraDataFields(trackedBeacon.getExtraDataFields()); + } + matchingTrackedBeacons.put(beacon.hashCode(), beacon); + mBeaconsByKey.put(key, matchingTrackedBeacons); + + return beacon; } - private void updateTrackingHashes(Beacon trackedBeacon, HashMap matchingTrackedBeacons) { - if (matchingTrackedBeacons == null) { - matchingTrackedBeacons = new HashMap(); + private void updateTrackedBeacons(@NonNull Beacon beacon) { + HashMap matchingTrackedBeacons = mBeaconsByKey.get(getBeaconKey(beacon)); + if (null != matchingTrackedBeacons) { + for (Beacon matchingTrackedBeacon : matchingTrackedBeacons.values()) { + matchingTrackedBeacon.setRssi(beacon.getRssi()); + matchingTrackedBeacon.setExtraDataFields(beacon.getDataFields()); + } } - matchingTrackedBeacons.put(trackedBeacon.hashCode(), trackedBeacon); - mBeaconsByKey.put(getBeaconKey(trackedBeacon), matchingTrackedBeacons); } - private String getBeaconKey(Beacon beacon) { + private String getBeaconKey(@NonNull Beacon beacon) { if (matchBeaconsByServiceUUID) { return beacon.getBluetoothAddress() + beacon.getServiceUuid(); } else { diff --git a/src/main/java/org/altbeacon/beacon/service/ScanHelper.java b/src/main/java/org/altbeacon/beacon/service/ScanHelper.java index d44700ccd..8821006ef 100644 --- a/src/main/java/org/altbeacon/beacon/service/ScanHelper.java +++ b/src/main/java/org/altbeacon/beacon/service/ScanHelper.java @@ -65,7 +65,10 @@ class ScanHelper { private MonitoringStatus mMonitoringStatus; private final Map mRangedRegionState = new HashMap<>(); private DistinctPacketDetector mDistinctPacketDetector = new DistinctPacketDetector(); - private ExtraDataBeaconTracker mExtraDataBeaconTracker; + + @NonNull + private ExtraDataBeaconTracker mExtraDataBeaconTracker = new ExtraDataBeaconTracker(); + private Set mBeaconParsers = new HashSet<>(); private List mSimulatedScanData = null; private Context mContext; @@ -99,7 +102,7 @@ void setRangedRegionState(Map rangedRegionState) { } } - void setExtraDataBeaconTracker(ExtraDataBeaconTracker extraDataBeaconTracker) { + void setExtraDataBeaconTracker(@NonNull ExtraDataBeaconTracker extraDataBeaconTracker) { mExtraDataBeaconTracker = extraDataBeaconTracker; } diff --git a/src/test/java/org/altbeacon/beacon/service/ExtraDataBeaconTrackerTest.java b/src/test/java/org/altbeacon/beacon/service/ExtraDataBeaconTrackerTest.java index cc7dcd998..24927ac5c 100644 --- a/src/test/java/org/altbeacon/beacon/service/ExtraDataBeaconTrackerTest.java +++ b/src/test/java/org/altbeacon/beacon/service/ExtraDataBeaconTrackerTest.java @@ -10,11 +10,9 @@ import java.util.ArrayList; import java.util.List; -import dalvik.annotation.TestTargetClass; - import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNull; @RunWith(RobolectricTestRunner.class) @Config(sdk = 18) @@ -59,6 +57,7 @@ Beacon getGattBeaconExtraData() { return new Beacon.Builder() .setBluetoothAddress("01:02:03:04:05:06") .setServiceUuid(1234) + .setRssi(-25) .setDataFields(getDataFields()) .build(); } @@ -67,6 +66,7 @@ Beacon getGattBeaconExtraData2() { return new Beacon.Builder() .setBluetoothAddress("01:02:03:04:05:06") .setServiceUuid(1234) + .setRssi(-50) .setDataFields(getDataFields2()) .build(); } @@ -119,32 +119,44 @@ public void gattBeaconExtraDataGetUpdated() { ExtraDataBeaconTracker tracker = new ExtraDataBeaconTracker(); tracker.track(beacon); tracker.track(extraDataBeacon); - tracker.track(extraDataBeacon2); Beacon trackedBeacon = tracker.track(beacon); + assertEquals("rssi should be updated", extraDataBeacon.getRssi(), trackedBeacon.getRssi()); + assertEquals("extra data is updated", extraDataBeacon.getDataFields(), trackedBeacon.getExtraDataFields()); + + tracker.track(extraDataBeacon2); + trackedBeacon = tracker.track(beacon); + assertEquals("rssi should be updated", extraDataBeacon2.getRssi(), trackedBeacon.getRssi()); assertEquals("extra data is updated", extraDataBeacon2.getDataFields(), trackedBeacon.getExtraDataFields()); } @Test - public void gattBeaconExtraDataAreNotOverwritten() { + public void gattBeaconFieldsAreNotUpdated() { Beacon beacon = getGattBeacon(); - Beacon extraDataBeacon = getGattBeaconExtraData(); + final int originalRssi = beacon.getRssi(); + final List originalData = beacon.getDataFields(); + final List originalExtra = beacon.getExtraDataFields(); + Beacon beaconUpdate = getGattBeaconUpdate(); ExtraDataBeaconTracker tracker = new ExtraDataBeaconTracker(); tracker.track(beacon); - tracker.track(extraDataBeacon); + tracker.track(beaconUpdate); Beacon trackedBeacon = tracker.track(beacon); - assertEquals("extra data should not be overwritten", extraDataBeacon.getDataFields(), trackedBeacon.getExtraDataFields()); + assertEquals("rssi should NOT be updated", originalRssi, trackedBeacon.getRssi()); + assertEquals("data should NOT be updated", originalData, trackedBeacon.getDataFields()); + assertEquals("extra data should NOT be updated", originalExtra, trackedBeacon.getExtraDataFields()); } @Test public void gattBeaconFieldsGetUpdated() { Beacon beacon = getGattBeacon(); - Beacon beaconUpdate = getGattBeaconUpdate(); Beacon extraDataBeacon = getGattBeaconExtraData(); + Beacon repeatBeacon = getGattBeacon(); + repeatBeacon.setRssi(-100); ExtraDataBeaconTracker tracker = new ExtraDataBeaconTracker(); tracker.track(beacon); - Beacon trackedBeacon = tracker.track(beaconUpdate); - assertEquals("rssi should be updated", beaconUpdate.getRssi(), trackedBeacon.getRssi()); - assertEquals("data fields should be updated", beaconUpdate.getDataFields(), trackedBeacon.getDataFields()); + tracker.track(extraDataBeacon); + Beacon trackedBeacon = tracker.track(repeatBeacon); + assertEquals("rssi should NOT be updated", -100, trackedBeacon.getRssi()); + assertEquals("extra data fields should be updated", extraDataBeacon.getDataFields(), trackedBeacon.getExtraDataFields()); } @Test