Skip to content

Commit

Permalink
Merge branch 'rlatapy-luna-feature/getDetectionTime'
Browse files Browse the repository at this point in the history
  • Loading branch information
davidgyoung committed Apr 3, 2020
2 parents 713575a + 2a596f1 commit 3d4a050
Show file tree
Hide file tree
Showing 19 changed files with 130 additions and 57 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
### Development

- Add timestamps of precsely when first and last packet was detected for beacon (#956, Rémi Latapy)

### 2.16.4 / 2020-01-26

- Fix Bluetooth Medic notifications on apps targeting API 26+ (#943 Anu Vakkachen)
Expand Down
5 changes: 3 additions & 2 deletions lib/src/main/java/org/altbeacon/beacon/AltBeaconParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,12 @@ public AltBeaconParser() {
* @param scanData The actual packet bytes
* @param rssi The measured signal strength of the packet
* @param device The Bluetooth device that was detected
* @param timestampMs The timestamp in milliseconds of the scan execution
* @return An instance of an <code>Beacon</code>
*/
@Override
public Beacon fromScanData(byte[] scanData, int rssi, BluetoothDevice device) {
return fromScanData(scanData, rssi, device, new AltBeacon());
public Beacon fromScanData(byte[] scanData, int rssi, BluetoothDevice device, long timestampMs) {
return fromScanData(scanData, rssi, device, timestampMs, new AltBeacon());
}

}
52 changes: 50 additions & 2 deletions lib/src/main/java/org/altbeacon/beacon/Beacon.java
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,16 @@ public class Beacon implements Parcelable, Serializable {
*/
protected boolean mMultiFrameBeacon = false;

/**
* The timestamp of the first packet detected in milliseconds.
*/
protected long mFirstCycleDetectionTimestamp = 0L;

/**
* The timestamp of the last packet detected in milliseconds.
*/
protected long mLastCycleDetectionTimestamp = 0L;

/**
* Required for making object Parcelable. If you override this class, you must provide an
* equivalent version of this method.
Expand Down Expand Up @@ -257,6 +267,8 @@ protected Beacon(Parcel in) {
mRunningAverageRssi = (Double) in.readValue(null);
mRssiMeasurementCount = in.readInt();
mPacketCount = in.readInt();
mFirstCycleDetectionTimestamp = in.readLong();
mLastCycleDetectionTimestamp = in.readLong();
}

/**
Expand All @@ -281,6 +293,8 @@ protected Beacon(Beacon otherBeacon) {
this.mParserIdentifier = otherBeacon.mParserIdentifier;
this.mMultiFrameBeacon = otherBeacon.mMultiFrameBeacon;
this.mManufacturer = otherBeacon.mManufacturer;
this.mFirstCycleDetectionTimestamp = otherBeacon.mFirstCycleDetectionTimestamp;
this.mLastCycleDetectionTimestamp = otherBeacon.mLastCycleDetectionTimestamp;
}

/**
Expand Down Expand Up @@ -316,6 +330,38 @@ public void setPacketCount(int packetCount) {
mPacketCount = packetCount;
}

/**
* Returns the timestamp of the first packet detected
*/
public long getFirstCycleDetectionTimestamp() {
return mFirstCycleDetectionTimestamp;
}

/**
* Sets the timestamp of the first packet detected
*
* @param firstCycleDetectionTimestamp
*/
public void setFirstCycleDetectionTimestamp(long firstCycleDetectionTimestamp) {
mFirstCycleDetectionTimestamp = firstCycleDetectionTimestamp;
}

/**
* Returns the timestamp of the last packet detected
*/
public long getLastCycleDetectionTimestamp() {
return mLastCycleDetectionTimestamp;
}

/**
* Sets the timestamp of the last packet detected
*
* @param lastCycleDetectionTimestamp
*/
public void setLastCycleDetectionTimestamp(long lastCycleDetectionTimestamp) {
mLastCycleDetectionTimestamp = lastCycleDetectionTimestamp;
}

/**
* Returns the number of packet detections that went in to the runningAverageRssi, if known.
* If not known or inapplicable for the rssi filter used, this is zero.
Expand Down Expand Up @@ -636,6 +682,8 @@ public void writeToParcel(Parcel out, int flags) {
out.writeValue(mRunningAverageRssi);
out.writeInt(mRssiMeasurementCount);
out.writeInt(mPacketCount);
out.writeLong(mFirstCycleDetectionTimestamp);
out.writeLong(mLastCycleDetectionTimestamp);
}

/**
Expand Down Expand Up @@ -886,13 +934,13 @@ public Builder setParserIdentifier(String id) {

/**
* @see Beacon#mMultiFrameBeacon
* @return multiFrameBeacon
* @param multiFrameBeacon
* @return builder
*/
public Builder setMultiFrameBeacon(boolean multiFrameBeacon) {
mBeacon.mMultiFrameBeacon = multiFrameBeacon;
return this;
}

}

}
9 changes: 6 additions & 3 deletions lib/src/main/java/org/altbeacon/beacon/BeaconParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -412,13 +412,14 @@ public int getServiceUuidEndOffset() {
* @param scanData The actual packet bytes
* @param rssi The measured signal strength of the packet
* @param device The Bluetooth device that was detected
* @param timestampMs The timestamp in milliseconds of the scan execution
* @return An instance of a <code>Beacon</code>
*/
public Beacon fromScanData(byte[] scanData, int rssi, BluetoothDevice device) {
return fromScanData(scanData, rssi, device, new Beacon());
public Beacon fromScanData(byte[] scanData, int rssi, BluetoothDevice device, long timestampMs) {
return fromScanData(scanData, rssi, device, timestampMs, new Beacon());
}

protected Beacon fromScanData(byte[] bytesToProcess, int rssi, BluetoothDevice device, Beacon beacon) {
protected Beacon fromScanData(byte[] bytesToProcess, int rssi, BluetoothDevice device, long timestampMs, Beacon beacon) {
BleAdvertisement advert = new BleAdvertisement(bytesToProcess);
boolean parseFailed = false;
Pdu pduToParse = null;
Expand Down Expand Up @@ -616,6 +617,8 @@ else if (endIndex > pduToParse.getEndIndex() && !mAllowPduOverflow) {
beacon.mManufacturer = manufacturer;
beacon.mParserIdentifier = mIdentifier;
beacon.mMultiFrameBeacon = extraParsers.size() > 0 || mExtraFrame;
beacon.mFirstCycleDetectionTimestamp = timestampMs;
beacon.mLastCycleDetectionTimestamp = timestampMs;
}
return beacon;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
public class Callback implements Serializable {
private static final String TAG = "Callback";

//TODO: Remove this constructor in favor of an empty one, as the packae name is no longer needed
//TODO: Remove this constructor in favor of an empty one, as the package name is no longer needed
public Callback(String intentPackageName) {
}

Expand Down
10 changes: 10 additions & 0 deletions lib/src/main/java/org/altbeacon/beacon/service/RangedBeacon.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ public class RangedBeacon implements Serializable {
Beacon mBeacon;
protected transient RssiFilter mFilter = null;
private int packetCount = 0;
private long firstCycleDetectionTimestamp = 0;
private long lastCycleDetectionTimestamp = 0;

public RangedBeacon(Beacon beacon) {
updateBeacon(beacon);
Expand All @@ -30,6 +32,10 @@ public RangedBeacon(Beacon beacon) {
public void updateBeacon(Beacon beacon) {
packetCount += 1;
mBeacon = beacon;
if(firstCycleDetectionTimestamp == 0) {
firstCycleDetectionTimestamp = beacon.getFirstCycleDetectionTimestamp();
}
lastCycleDetectionTimestamp = beacon.getLastCycleDetectionTimestamp();
addMeasurement(mBeacon.getRssi());
}

Expand Down Expand Up @@ -57,7 +63,11 @@ public void commitMeasurements() {
LogManager.d(TAG, "No measurements available to calculate running average");
}
mBeacon.setPacketCount(packetCount);
mBeacon.setFirstCycleDetectionTimestamp(firstCycleDetectionTimestamp);
mBeacon.setLastCycleDetectionTimestamp(lastCycleDetectionTimestamp);
packetCount = 0;
firstCycleDetectionTimestamp = 0L;
lastCycleDetectionTimestamp = 0L;
}

public void addMeasurement(Integer rssi) {
Expand Down
19 changes: 11 additions & 8 deletions lib/src/main/java/org/altbeacon/beacon/service/ScanHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -153,12 +153,12 @@ void createCycledLeScanner(boolean backgroundMode, BluetoothCrashResolver crashR
}

@TargetApi(Build.VERSION_CODES.HONEYCOMB)
void processScanResult(BluetoothDevice device, int rssi, byte[] scanRecord) {
void processScanResult(BluetoothDevice device, int rssi, byte[] scanRecord, long timestampMs) {
NonBeaconLeScanCallback nonBeaconLeScanCallback = mBeaconManager.getNonBeaconLeScanCallback();

try {
new ScanHelper.ScanProcessor(nonBeaconLeScanCallback).executeOnExecutor(getExecutor(),
new ScanHelper.ScanData(device, rssi, scanRecord));
new ScanHelper.ScanData(device, rssi, scanRecord, timestampMs));
} catch (RejectedExecutionException e) {
LogManager.w(TAG, "Ignoring scan result because we cannot keep up.");
} catch (OutOfMemoryError e) {
Expand Down Expand Up @@ -250,15 +250,15 @@ void stopAndroidOBackgroundScan() {
PendingIntent getScanCallbackIntent() {
Intent intent = new Intent(mContext, StartupBroadcastReceiver.class);
intent.putExtra("o-scan", true);
return PendingIntent.getBroadcast(mContext,0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
return PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}

private final CycledLeScanCallback mCycledLeScanCallback = new CycledLeScanCallback() {
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
@Override
@MainThread
public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
processScanResult(device, rssi, scanRecord);
public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord, long timestampMs) {
processScanResult(device, rssi, scanRecord, timestampMs);
}

@Override
Expand Down Expand Up @@ -362,10 +362,11 @@ private void processBeaconFromScan(@NonNull Beacon beacon) {
* <strong>This class is not thread safe.</strong>
*/
private class ScanData {
ScanData(@NonNull BluetoothDevice device, int rssi, @NonNull byte[] scanRecord) {
ScanData(@NonNull BluetoothDevice device, int rssi, @NonNull byte[] scanRecord, long timestampMs) {
this.device = device;
this.rssi = rssi;
this.scanRecord = scanRecord;
this.timestampMs = timestampMs;
}

final int rssi;
Expand All @@ -375,6 +376,9 @@ private class ScanData {

@NonNull
byte[] scanRecord;

@NonNull
long timestampMs;
}

private class ScanProcessor extends AsyncTask<ScanHelper.ScanData, Void, Void> {
Expand All @@ -393,8 +397,7 @@ protected Void doInBackground(ScanHelper.ScanData... params) {
Beacon beacon = null;

for (BeaconParser parser : ScanHelper.this.mBeaconParsers) {
beacon = parser.fromScanData(scanData.scanRecord,
scanData.rssi, scanData.device);
beacon = parser.fromScanData(scanData.scanRecord, scanData.rssi, scanData.device, scanData.timestampMs);

if (beacon != null) {
break;
Expand Down
8 changes: 5 additions & 3 deletions lib/src/main/java/org/altbeacon/beacon/service/ScanJob.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Handler;
import android.os.SystemClock;

import androidx.annotation.Nullable;

Expand Down Expand Up @@ -78,16 +79,17 @@ public void run() {
}

List<ScanResult> queuedScanResults = new ArrayList<>(ScanJobScheduler.getInstance().dumpBackgroundScanResultQueue());
LogManager.d(TAG, "Processing %d queued scan resuilts", queuedScanResults.size());
LogManager.d(TAG, "Processing %d queued scan results", queuedScanResults.size());
for (ScanResult result : queuedScanResults) {
ScanRecord scanRecord = result.getScanRecord();
if (scanRecord != null) {
if (mScanHelper != null) {
mScanHelper.processScanResult(result.getDevice(), result.getRssi(), scanRecord.getBytes());
mScanHelper.processScanResult(result.getDevice(), result.getRssi(), scanRecord.getBytes(),
System.currentTimeMillis() - SystemClock.elapsedRealtime() + result.getTimestampNanos() / 1000000);
}
}
}
LogManager.d(TAG, "Done processing queued scan resuilts");
LogManager.d(TAG, "Done processing queued scan results");

// This syncronized block is around the scan start.
// Without it, it is possilbe that onStopJob is called in another thread and
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ public void scheduleAfterBackgroundWakeup(Context context, List<ScanResult> scan
synchronized (this) {
// We typically get a bunch of calls in a row here, separated by a few millis. Only do this once.
if (System.currentTimeMillis() - mScanJobScheduleTime > MIN_MILLIS_BETWEEN_SCAN_JOB_SCHEDULING) {
LogManager.d(TAG, "scheduling an immediate scan job because last did "+(System.currentTimeMillis() - mScanJobScheduleTime)+"seconds ago.");
LogManager.d(TAG, "scheduling an immediate scan job because last did "+(System.currentTimeMillis() - mScanJobScheduleTime)+"millis ago.");
mScanJobScheduleTime = System.currentTimeMillis();
}
else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@
*/
@MainThread
public interface CycledLeScanCallback {
void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord);
void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord, long timestampMs);
void onCycleEnd();
}
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ private BluetoothAdapter.LeScanCallback getLeScanCallback() {
public void onLeScan(final BluetoothDevice device, final int rssi,
final byte[] scanRecord) {
LogManager.d(TAG, "got record");
mCycledLeScanCallback.onLeScan(device, rssi, scanRecord);
mCycledLeScanCallback.onLeScan(device, rssi, scanRecord, System.currentTimeMillis());
if (mBluetoothCrashResolver != null) {
mBluetoothCrashResolver.notifyScannedDevice(device, getLeScanCallback());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,8 @@ public void onScanResult(int callbackType, ScanResult scanResult) {
}
}
mCycledLeScanCallback.onLeScan(scanResult.getDevice(),
scanResult.getRssi(), scanResult.getScanRecord().getBytes());
scanResult.getRssi(), scanResult.getScanRecord().getBytes(),
System.currentTimeMillis() - SystemClock.elapsedRealtime() + scanResult.getTimestampNanos() / 1000000);
if (mBackgroundLScanStartTime > 0) {
LogManager.d(TAG, "got a filtered scan result in the background.");
}
Expand All @@ -360,7 +361,8 @@ public void onBatchScanResults(List<ScanResult> results) {
LogManager.d(TAG, "got batch records");
for (ScanResult scanResult : results) {
mCycledLeScanCallback.onLeScan(scanResult.getDevice(),
scanResult.getRssi(), scanResult.getScanRecord().getBytes());
scanResult.getRssi(), scanResult.getScanRecord().getBytes(),
System.currentTimeMillis() - SystemClock.elapsedRealtime() + scanResult.getTimestampNanos() / 1000000);
}
if (mBackgroundLScanStartTime > 0) {
LogManager.d(TAG, "got a filtered batch scan result in the background.");
Expand Down
10 changes: 5 additions & 5 deletions lib/src/test/java/org/altbeacon/beacon/AltBeaconParserTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public void testRecognizeBeacon() {
BeaconManager.setDebug(true);
byte[] bytes = hexStringToByteArray("02011a1bff1801beac2f234454cf6d4a0fadf2f4911ba9ffa600010002c50900");
AltBeaconParser parser = new AltBeaconParser();
Beacon beacon = parser.fromScanData(bytes, -55, null);
Beacon beacon = parser.fromScanData(bytes, -55, null, 123456L);
assertEquals ("Beacon should have one data field", 1, beacon.getDataFields().size());
assertEquals("manData should be parsed", 9, ((AltBeacon) beacon).getMfgReserved());
}
Expand All @@ -49,7 +49,7 @@ public void testDetectsDaveMHardwareBeacon() {
org.robolectric.shadows.ShadowLog.stream = System.err;
byte[] bytes = hexStringToByteArray("02011a1bff1801beac2f234454cf6d4a0fadf2f4911ba9ffa600050003be020e09526164426561636f6e20555342020a0300000000000000000000000000");
AltBeaconParser parser = new AltBeaconParser();
Beacon beacon = parser.fromScanData(bytes, -55, null);
Beacon beacon = parser.fromScanData(bytes, -55, null, 123456L);
assertNotNull("Beacon should be not null if parsed successfully", beacon);
}
@Test
Expand All @@ -58,7 +58,7 @@ public void testDetectsAlternateBeconType() {
byte[] bytes = hexStringToByteArray("02011a1bff1801aabb2f234454cf6d4a0fadf2f4911ba9ffa600010002c50900");
AltBeaconParser parser = new AltBeaconParser();
parser.setMatchingBeaconTypeCode(0xaabbl);
Beacon beacon = parser.fromScanData(bytes, -55, null);
Beacon beacon = parser.fromScanData(bytes, -55, null, 123456L);
assertNotNull("Beacon should be not null if parsed successfully", beacon);
}
@Test
Expand All @@ -68,7 +68,7 @@ public void testParseWrongFormatReturnsNothing() {
LogManager.d("XXX", "testParseWrongFormatReturnsNothing start");
byte[] bytes = hexStringToByteArray("02011a1aff1801ffff2f234454cf6d4a0fadf2f4911ba9ffa600010002c509");
AltBeaconParser parser = new AltBeaconParser();
Beacon beacon = parser.fromScanData(bytes, -55, null);
Beacon beacon = parser.fromScanData(bytes, -55, null, 123456L);
LogManager.d("XXX", "testParseWrongFormatReturnsNothing end");
assertNull("Beacon should be null if not parsed successfully", beacon);
}
Expand All @@ -79,7 +79,7 @@ public void testParsesBeaconMissingDataField() {
org.robolectric.shadows.ShadowLog.stream = System.err;
byte[] bytes = hexStringToByteArray("02011a1aff1801beac2f234454cf6d4a0fadf2f4911ba9ffa600010002c5000000");
AltBeaconParser parser = new AltBeaconParser();
Beacon beacon = parser.fromScanData(bytes, -55, null);
Beacon beacon = parser.fromScanData(bytes, -55, null, 123456L);
assertEquals("mRssi should be as passed in", -55, beacon.getRssi());
assertEquals("uuid should be parsed", "2f234454-cf6d-4a0f-adf2-f4911ba9ffa6", beacon.getIdentifier(0).toString());
assertEquals("id2 should be parsed", "1", beacon.getIdentifier(1).toString());
Expand Down
2 changes: 1 addition & 1 deletion lib/src/test/java/org/altbeacon/beacon/AltBeaconTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public static byte[] hexStringToByteArray(String s) {
public void testRecognizeBeacon() {
byte[] bytes = hexStringToByteArray("02011a1bff1801beac2f234454cf6d4a0fadf2f4911ba9ffa600010002c509");
AltBeaconParser parser = new AltBeaconParser();
Beacon beacon = parser.fromScanData(bytes, -55, null);
Beacon beacon = parser.fromScanData(bytes, -55, null, 123456L);
assertEquals("manData should be parsed", 9, ((AltBeacon) beacon).getMfgReserved() );
}

Expand Down
Loading

0 comments on commit 3d4a050

Please sign in to comment.