From 1f02531c4a4019ff21efd1ccabdbcb2aa5eda6d7 Mon Sep 17 00:00:00 2001 From: Aaron Kromer Date: Tue, 12 Dec 2017 16:59:54 -0500 Subject: [PATCH] Include running average in Parcel data This fixes a minor issue where the running average is not set after restoring a beacon from a parcel. Looking at the history it seems that the running average was an internal detail until #479 where support was added for running the service in a separate process. Part of the reason this needs to be included is that on Android 8.0 the running average _is_ restored and available for access. The reason the behavior is different across Android versions is in the way a service intent versus local notification handles the data `Bundle` (i.e. which [`Callback`](https://github.com/AltBeacon/android-beacon-library/blob/2.12.3/src/main/java/org/altbeacon/beacon/service/Callback.java#L58-L86) branch runs). Explicitly setting the `BeaconManager` to use scheduled scan jobs, forcing the first conditional code path using local notifications, allows the running average to be available on older versions. --- CHANGELOG.md | 6 +- .../java/org/altbeacon/beacon/Beacon.java | 3 + .../java/org/altbeacon/beacon/BeaconTest.java | 63 +++++++++++++++++++ .../beacon/service/RangingDataTest.java | 13 +++- 4 files changed, 81 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e3cdcf00..200b50e91 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ Bug Fixes: Identifier#toHexString(). (#615, David G. Young) - Fix regression with `RunningAverageRssiFilter.setSampleExpirationMilliseconds` being overwritten when committing ranged beacon measurements. (#629, Aaron Kromer) + - Fix missing running average RSSI in callbacks when apps do not use the + scheduled scan job feature. (#630, Aaron Kromer) ### 2.12.3 / 2017-10-14 @@ -13,7 +15,7 @@ Bug Fixes: Bug Fixes: - Fix NullPointerException in ProcessUtils. (#598, David G. Young) - Fix ConcurrentModificationException crashing app on Android 8 when monitored regions are - changed at the same time the app shifts from active scanning to passive scanning. + changed at the same time the app shifts from active scanning to passive scanning. (#578, David G. Young) - Fix ConcurrentModifictionExceptions starting ScanJobs. (#584, #588, David G. Young) - Fix NullPointerException when BluetoothLeScanner cannot be obtained. @@ -50,7 +52,7 @@ Bug Fixes: [Full Changelog](https://github.com/AltBeacon/android-beacon-library/compare/2.11...2.12) Enhancements: - - Add Android O support with ScanJob using JobScheduler to do scans instead of BeaconService, + - Add Android O support with ScanJob using JobScheduler to do scans instead of BeaconService, set as default for Android O. (#484, David G. Young) Bug Fixes: diff --git a/src/main/java/org/altbeacon/beacon/Beacon.java b/src/main/java/org/altbeacon/beacon/Beacon.java index 8e1f183fd..4a3161297 100644 --- a/src/main/java/org/altbeacon/beacon/Beacon.java +++ b/src/main/java/org/altbeacon/beacon/Beacon.java @@ -244,6 +244,8 @@ protected Beacon(Parcel in) { mBluetoothName = in.readString(); mParserIdentifier = in.readString(); mMultiFrameBeacon = in.readByte() != 0; + double tmpAverageRssi = in.readDouble(); + mRunningAverageRssi = tmpAverageRssi < Double.MAX_VALUE ? tmpAverageRssi : null; } /** @@ -584,6 +586,7 @@ public void writeToParcel(Parcel out, int flags) { out.writeString(mBluetoothName); out.writeString(mParserIdentifier); out.writeByte((byte) (mMultiFrameBeacon ? 1: 0)); + out.writeDouble(null == mRunningAverageRssi ? Double.MAX_VALUE : mRunningAverageRssi); } /** diff --git a/src/test/java/org/altbeacon/beacon/BeaconTest.java b/src/test/java/org/altbeacon/beacon/BeaconTest.java index d95fb0e72..4456543c5 100644 --- a/src/test/java/org/altbeacon/beacon/BeaconTest.java +++ b/src/test/java/org/altbeacon/beacon/BeaconTest.java @@ -1,11 +1,18 @@ package org.altbeacon.beacon; +import android.os.Parcel; + import org.altbeacon.beacon.distance.ModelSpecificDistanceCalculator; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasProperty; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -35,11 +42,21 @@ 4. Expand the System.err section */ public class BeaconTest { + private Parcel aParcel = null; @Before public void before() { Beacon.setHardwareEqualityEnforced(false); } + + @After + public void after() { + // Clean up any obtained parcel + if (null != aParcel) { + aParcel.recycle(); + } + } + @Test public void testAccessBeaconIdentifiers() { Beacon beacon = new AltBeacon.Builder().setMfgReserved(7).setId1("1").setId2("2").setId3("3").setRssi(4) @@ -206,6 +223,52 @@ public void testHashCodeWithNullIdentifier() { .build(); assertTrue("hashCode() should not throw exception", beacon.hashCode() >= Integer.MIN_VALUE); } + + @Test + public void parcelingBeaconContainsAllFields() { + final Beacon original = new Beacon.Builder().setBluetoothAddress("aa:bb:cc:dd:ee:ff") + .setBluetoothName("Any Bluetooth") + .setBeaconTypeCode(1) + .setDataFields(Arrays.asList(2L, 3L)) + .setExtraDataFields(Arrays.asList(4L, 5L)) + .setId1("6") + .setId2("7") + .setId3("8") + .setManufacturer(10) + .setMultiFrameBeacon(true) + .setParserIdentifier("Any Parser ID") + .setRssi(-11) + .setRunningAverageRssi(-12.3) + .setServiceUuid(13) + .setTxPower(14) + .build(); + + aParcel = Parcel.obtain(); + original.writeToParcel(aParcel, 0); + aParcel.setDataPosition(0); + final Beacon parceled = Beacon.CREATOR.createFromParcel(aParcel); + assertThat( + parceled, + allOf( + hasProperty("bluetoothAddress", equalTo("aa:bb:cc:dd:ee:ff")), + hasProperty("bluetoothName", equalTo("Any Bluetooth")), + hasProperty("beaconTypeCode", equalTo(1)), + hasProperty("dataFields", equalTo(Arrays.asList(2L, 3L))), + hasProperty("extraDataFields", equalTo(Arrays.asList(4L, 5L))), + hasProperty("id1", equalTo(Identifier.fromInt(6))), + hasProperty("id2", equalTo(Identifier.fromInt(7))), + hasProperty("id3", equalTo(Identifier.fromInt(8))), + hasProperty("manufacturer", equalTo(10)), + hasProperty("multiFrameBeacon", equalTo(true)), + hasProperty("parserIdentifier", equalTo("Any Parser ID")), + hasProperty("rssi", equalTo(-11)), + hasProperty("runningAverageRssi", equalTo(-12.3)), + hasProperty("serviceUuid", equalTo(13)), + hasProperty("txPower", equalTo(14)) + ) + ); + } + // utilty methods for testing serialization private byte[] convertToBytes(Object object) throws IOException { diff --git a/src/test/java/org/altbeacon/beacon/service/RangingDataTest.java b/src/test/java/org/altbeacon/beacon/service/RangingDataTest.java index 403cd9553..7a7264d2c 100644 --- a/src/test/java/org/altbeacon/beacon/service/RangingDataTest.java +++ b/src/test/java/org/altbeacon/beacon/service/RangingDataTest.java @@ -41,7 +41,12 @@ public void testSerialization() throws Exception { identifiers.add(Identifier.parse("2")); Region region = new Region("testRegion", identifiers); ArrayList beacons = new ArrayList(); - Beacon beacon = new Beacon.Builder().setIdentifiers(identifiers).setRssi(-1).setRunningAverageRssi(-2).setTxPower(-50).setBluetoothAddress("01:02:03:04:05:06").build(); + Beacon beacon = new Beacon.Builder().setIdentifiers(identifiers) + .setRssi(-1) + .setRunningAverageRssi(-2) + .setTxPower(-50) + .setBluetoothAddress("01:02:03:04:05:06") + .build(); for (int i=0; i < 10; i++) { beacons.add(beacon); } @@ -49,8 +54,12 @@ public void testSerialization() throws Exception { Bundle bundle = data.toBundle(); RangingData data2 = RangingData.fromBundle(bundle); assertEquals("beacon count shouild be restored", 10, data2.getBeacons().size()); - assertEquals("beacon identifier 1 shouild be restored", "2f234454-cf6d-4a0f-adf2-f4911ba9ffa6", data2.getBeacons().iterator().next().getId1().toString()); assertEquals("region identifier 1 shouild be restored", "2f234454-cf6d-4a0f-adf2-f4911ba9ffa6", data2.getRegion().getId1().toString()); + Beacon restoredBeacon = data2.getBeacons().iterator().next(); + assertEquals("beacon identifier 1 shouild be restored", "2f234454-cf6d-4a0f-adf2-f4911ba9ffa6", restoredBeacon.getId1().toString()); + assertEquals("RSSI is restored", -1, restoredBeacon.getRssi()); + assertEquals("Average RSSI is restored", -2.0, restoredBeacon.getRunningAverageRssi(), 0.0); + assertEquals("TXPower is restored", -50, restoredBeacon.getTxPower()); } @Test