Skip to content

Commit

Permalink
Attempt to syncronize beacon parsers
Browse files Browse the repository at this point in the history
  • Loading branch information
davidgyoung committed Mar 11, 2017
1 parent 866aed8 commit 7c1f76e
Show file tree
Hide file tree
Showing 5 changed files with 175 additions and 64 deletions.
54 changes: 43 additions & 11 deletions src/main/java/org/altbeacon/beacon/BeaconManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@
public class BeaconManager {
private static final String TAG = "BeaconManager";
private Context mContext;
protected static BeaconManager client = null;
protected static BeaconManager sInstance = null;
private final ConcurrentMap<BeaconConsumer, ConsumerInfo> consumers = new ConcurrentHashMap<BeaconConsumer,ConsumerInfo>();
private Messenger serviceMessenger = null;
protected final Set<RangeNotifier> rangeNotifiers = new CopyOnWriteArraySet<>();
Expand All @@ -123,7 +123,7 @@ public class BeaconManager {
private boolean mBackgroundMode = false;
private boolean mBackgroundModeUninitialized = true;
private boolean mMainProcess = false;
private Boolean mScannerProcess = null;
private Boolean mScannerInSameProcess = null;

private static boolean sAndroidLScanningDisabled = false;
private static boolean sManifestCheckingDisabled = false;
Expand Down Expand Up @@ -244,11 +244,11 @@ public static long getRegionExitPeriod(){
* or non-Service class, you can attach it to another singleton or a subclass of the Android Application class.
*/
public static BeaconManager getInstanceForApplication(Context context) {
if (client == null) {
if (sInstance == null) {
LogManager.d(TAG, "BeaconManager instance creation");
client = new BeaconManager(context);
sInstance = new BeaconManager(context);
}
return client;
return sInstance;
}

protected BeaconManager(Context context) {
Expand All @@ -273,19 +273,19 @@ public boolean isMainProcess() {
/**
* Determines if this BeaconManager instance is part of the process hosting the beacon scanning
* service. This is normally true, except when scanning is hosted in a different service.
* This may return null until the first consumer is bound, because the value cannot be determined.
* This will return null until the scanning service starts up, at which time it will be known.
* @return
*/
public Boolean isScannerProcess() {
return mScannerProcess;
public Boolean isScannerInSameProcess() {
return mScannerInSameProcess;
}

/**
* Reserved for internal by the library.
* @hide
*/
public void setScannerProcess(boolean isScanner) {
mScannerProcess = isScanner;
public void setScannerInSameProcess(boolean isScanner) {
mScannerInSameProcess = isScanner;
}

protected void checkIfMainProcess() {
Expand Down Expand Up @@ -707,13 +707,34 @@ public void stopRangingBeaconsInRegion(Region region) throws RemoteException {
}
}

/**
* Call this method if you are running the scanner service in a different process in order to
* synchronize any configuration settings, including BeaconParsers to the scanner
* @see #isScannerInSameProcess()
*/
public void applySettings() {
if (isScannerInSameProcess() != null && isScannerInSameProcess() == false) {
LogManager.d(TAG, "Synchronizing settings to service");
syncSettingsToService();
}
else {
if (isScannerInSameProcess() == null) {
LogManager.d(TAG, "Not synchronizing settings to service, as it has not started up yet");

}
else {
LogManager.d(TAG, "Not synchronizing settings to service, as it is in the same process");
}
}
}

protected void syncSettingsToService() {
if (serviceMessenger == null) {
LogManager.e(TAG, "The BeaconManager is not bound to the service. Settings synchronization will fail.");
return;
}
try {
Message msg = Message.obtain(null, BeaconService.MSG_STOP_RANGING, 0, 0);
Message msg = Message.obtain(null, BeaconService.MSG_SYNC_SETTINGS, 0, 0);
msg.setData(new SettingsData().collect(mContext).toBundle());
serviceMessenger.send(msg);
}
Expand Down Expand Up @@ -920,6 +941,9 @@ public static Class getRssiFilterImplClass() {
*/
public static void setUseTrackingCache(boolean useTrackingCache) {
RangeState.setUseTrackingCache(useTrackingCache);
if (sInstance != null) {
sInstance.applySettings();
}
}

/**
Expand Down Expand Up @@ -1000,7 +1024,12 @@ private BeaconServiceConnection() {
// Called when the connection with the service is established
public void onServiceConnected(ComponentName className, IBinder service) {
LogManager.d(TAG, "we have a connection to the service now");
if (mScannerInSameProcess == null) {
mScannerInSameProcess = false;
}
serviceMessenger = new Messenger(service);
// This will sync settings to the scanning service if it is in a different process
applySettings();
synchronized(consumers) {
Iterator<Map.Entry<BeaconConsumer, ConsumerInfo>> iter = consumers.entrySet().iterator();
while (iter.hasNext()) {
Expand Down Expand Up @@ -1046,6 +1075,9 @@ public static boolean isAndroidLScanningDisabled() {
*/
public static void setAndroidLScanningDisabled(boolean disabled) {
sAndroidLScanningDisabled = disabled;
if (sInstance != null) {
sInstance.applySettings();
}
}

/**
Expand Down
26 changes: 25 additions & 1 deletion src/main/java/org/altbeacon/beacon/BeaconParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ public class BeaconParser implements Serializable {
private static final String LITTLE_ENDIAN_SUFFIX = "l";
private static final String VARIABLE_LENGTH_SUFFIX = "v";

protected String mBeaconLayout;
private Long mMatchingBeaconTypeCode;
protected final List<Integer> mIdentifierStartOffsets = new ArrayList<Integer>();
protected final List<Integer> mIdentifierEndOffsets = new ArrayList<Integer>();
Expand Down Expand Up @@ -166,7 +167,7 @@ public BeaconParser(String identifier) {
* @return the BeaconParser instance
*/
public BeaconParser setBeaconLayout(String beaconLayout) {

mBeaconLayout = beaconLayout;
Log.d(TAG, "Parsing beacon layout: "+beaconLayout);

String[] terms = beaconLayout.split(",");
Expand Down Expand Up @@ -759,6 +760,13 @@ public int getDataFieldCount() {
return mDataStartOffsets.size();
}

/**
* @return the layout string for the parser
*/
public String getLayout() {
return mBeaconLayout;
}

/**
* @return the correction value in dBm to apply to the calibrated txPower to get a 1m calibrated value.
* Some formats like Eddystone use a 0m calibrated value, which requires this correction
Expand Down Expand Up @@ -926,4 +934,20 @@ public int hashCode() {
}
);
}

@Override
public boolean equals(Object o) {
BeaconParser that = null;
try {
that = (BeaconParser) o;
if (that.mBeaconLayout != null && that.mBeaconLayout.equals(this.mBeaconLayout)) {
if (that.mIdentifier != null && that.mIdentifier.equals(this.mIdentifier)) {
return true;
}
}
}
catch (ClassCastException e ) { }
return false;
}

}
110 changes: 65 additions & 45 deletions src/main/java/org/altbeacon/beacon/service/BeaconService.java
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ public BeaconService getService() {
public static final int MSG_START_MONITORING = 4;
public static final int MSG_STOP_MONITORING = 5;
public static final int MSG_SET_SCAN_PERIODS = 6;
public static final int MSG_SYNC_SETTINGS = 7;

static class IncomingHandler extends Handler {
private final WeakReference<BeaconService> mService;
Expand All @@ -148,37 +149,52 @@ static class IncomingHandler extends Handler {
@Override
public void handleMessage(Message msg) {
BeaconService service = mService.get();
StartRMData startRMData = StartRMData.fromBundle(msg.getData());

if (service != null) {
switch (msg.what) {
case MSG_START_RANGING:
LogManager.i(TAG, "start ranging received");
service.startRangingBeaconsInRegion(startRMData.getRegionData(), new org.altbeacon.beacon.service.Callback(startRMData.getCallbackPackageName()));
service.setScanPeriods(startRMData.getScanPeriod(), startRMData.getBetweenScanPeriod(), startRMData.getBackgroundFlag());
break;
case MSG_STOP_RANGING:
LogManager.i(TAG, "stop ranging received");
service.stopRangingBeaconsInRegion(startRMData.getRegionData());
service.setScanPeriods(startRMData.getScanPeriod(), startRMData.getBetweenScanPeriod(), startRMData.getBackgroundFlag());
break;
case MSG_START_MONITORING:
LogManager.i(TAG, "start monitoring received");
service.startMonitoringBeaconsInRegion(startRMData.getRegionData(), new org.altbeacon.beacon.service.Callback(startRMData.getCallbackPackageName()));
service.setScanPeriods(startRMData.getScanPeriod(), startRMData.getBetweenScanPeriod(), startRMData.getBackgroundFlag());
break;
case MSG_STOP_MONITORING:
LogManager.i(TAG, "stop monitoring received");
service.stopMonitoringBeaconsInRegion(startRMData.getRegionData());
service.setScanPeriods(startRMData.getScanPeriod(), startRMData.getBetweenScanPeriod(), startRMData.getBackgroundFlag());
break;
case MSG_SET_SCAN_PERIODS:
LogManager.i(TAG, "set scan intervals received");
service.setScanPeriods(startRMData.getScanPeriod(), startRMData.getBetweenScanPeriod(), startRMData.getBackgroundFlag());
break;
default:
super.handleMessage(msg);
StartRMData startRMData = StartRMData.fromBundle(msg.getData());
if (startRMData != null) {
switch (msg.what) {
case MSG_START_RANGING:
LogManager.i(TAG, "start ranging received");
service.startRangingBeaconsInRegion(startRMData.getRegionData(), new org.altbeacon.beacon.service.Callback(startRMData.getCallbackPackageName()));
service.setScanPeriods(startRMData.getScanPeriod(), startRMData.getBetweenScanPeriod(), startRMData.getBackgroundFlag());
break;
case MSG_STOP_RANGING:
LogManager.i(TAG, "stop ranging received");
service.stopRangingBeaconsInRegion(startRMData.getRegionData());
service.setScanPeriods(startRMData.getScanPeriod(), startRMData.getBetweenScanPeriod(), startRMData.getBackgroundFlag());
break;
case MSG_START_MONITORING:
LogManager.i(TAG, "start monitoring received");
service.startMonitoringBeaconsInRegion(startRMData.getRegionData(), new org.altbeacon.beacon.service.Callback(startRMData.getCallbackPackageName()));
service.setScanPeriods(startRMData.getScanPeriod(), startRMData.getBetweenScanPeriod(), startRMData.getBackgroundFlag());
break;
case MSG_STOP_MONITORING:
LogManager.i(TAG, "stop monitoring received");
service.stopMonitoringBeaconsInRegion(startRMData.getRegionData());
service.setScanPeriods(startRMData.getScanPeriod(), startRMData.getBetweenScanPeriod(), startRMData.getBackgroundFlag());
break;
case MSG_SET_SCAN_PERIODS:
LogManager.i(TAG, "set scan intervals received");
service.setScanPeriods(startRMData.getScanPeriod(), startRMData.getBetweenScanPeriod(), startRMData.getBackgroundFlag());
break;
default:
super.handleMessage(msg);
}
}
else if (msg.what == MSG_SYNC_SETTINGS) {
LogManager.i(TAG, "Received settings update from other process");
SettingsData settingsData = SettingsData.fromBundle(msg.getData());
if (settingsData != null) {
settingsData.apply(service);
}
else {
LogManager.w(TAG, "Settings data missing");
}
}
else {
LogManager.i(TAG, "Received unknown message from other process : "+msg.what);
}

}
}
}
Expand All @@ -202,7 +218,7 @@ public void onCreate() {
BeaconManager.DEFAULT_FOREGROUND_BETWEEN_SCAN_PERIOD, mBackgroundFlag, mCycledLeScanCallback, bluetoothCrashResolver);

beaconManager = BeaconManager.getInstanceForApplication(getApplicationContext());
beaconManager.setScannerProcess(true);
beaconManager.setScannerInSameProcess(true);
if (beaconManager.isMainProcess()) {
LogManager.i(TAG, "beaconService version %s is starting up on the main process", BuildConfig.VERSION_NAME);
}
Expand All @@ -212,21 +228,7 @@ public void onCreate() {
LogManager.i(TAG, "beaconService PID is "+processUtils.getPid()+" with process name "+processUtils.getProcessName());
}


//flatMap all beacon parsers
boolean matchBeaconsByServiceUUID = true;
if (beaconManager.getBeaconParsers() != null) {
beaconParsers.addAll(beaconManager.getBeaconParsers());
for (BeaconParser beaconParser : beaconManager.getBeaconParsers()) {
if (beaconParser.getExtraDataParsers().size() > 0) {
matchBeaconsByServiceUUID = false;
beaconParsers.addAll(beaconParser.getExtraDataParsers());
}
}
}

//initialize the extra data beacon tracker
mExtraDataBeaconTracker = new ExtraDataBeaconTracker(matchBeaconsByServiceUUID);
reloadParsers();

defaultDistanceCalculator = new ModelSpecificDistanceCalculator(this, BeaconManager.getDistanceModelUpdateUrl());
Beacon.setDistanceCalculator(defaultDistanceCalculator);
Expand All @@ -244,6 +246,24 @@ public void onCreate() {
}
}

protected void reloadParsers() {
HashSet<BeaconParser> newBeaconParsers = new HashSet<BeaconParser>();
//flatMap all beacon parsers
boolean matchBeaconsByServiceUUID = true;
if (beaconManager.getBeaconParsers() != null) {
newBeaconParsers.addAll(beaconManager.getBeaconParsers());
for (BeaconParser beaconParser : beaconManager.getBeaconParsers()) {
if (beaconParser.getExtraDataParsers().size() > 0) {
matchBeaconsByServiceUUID = false;
newBeaconParsers.addAll(beaconParser.getExtraDataParsers());
}
}
}
beaconParsers = newBeaconParsers;
//initialize the extra data beacon tracker
mExtraDataBeaconTracker = new ExtraDataBeaconTracker(matchBeaconsByServiceUUID);
}


@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Expand Down
35 changes: 31 additions & 4 deletions src/main/java/org/altbeacon/beacon/service/SettingsData.java
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
package org.altbeacon.beacon.service;

import android.app.Service;
import android.content.Context;
import android.os.Bundle;

import org.altbeacon.beacon.Beacon;
import org.altbeacon.beacon.BeaconManager;
import org.altbeacon.beacon.BeaconParser;
import org.altbeacon.beacon.Region;
import org.altbeacon.beacon.logging.LogManager;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

/**
* Created by dyoung on 3/10/17.
*/

public class SettingsData implements Serializable {
private static final String TAG = SettingsData.class.getSimpleName();
private static final String SETTINGS_DATA_KEY = "SettingsData";
ArrayList<BeaconParser> mBeaconParsers;
Boolean mRegionStatePersistenceEnabled;
Expand Down Expand Up @@ -48,10 +52,33 @@ public static SettingsData fromBundle(Bundle bundle) {
return settingsData;
}

public void apply(Context context) {
BeaconManager beaconManager = BeaconManager.getInstanceForApplication(context);
beaconManager.getBeaconParsers().clear();
beaconManager.getBeaconParsers().addAll(mBeaconParsers);
public void apply(BeaconService scanService) {
LogManager.d(TAG, "Applying settings changes to scanner in other process");
BeaconManager beaconManager = BeaconManager.getInstanceForApplication(scanService);
List<BeaconParser> beaconParsers = beaconManager.getBeaconParsers();
boolean beaconParsersChanged = false;
if (beaconParsers.size() == mBeaconParsers.size()) {
for (int i = 0; i < beaconParsers.size(); i++) {
if (!beaconParsers.get(i).equals(mBeaconParsers.get(i))) {
LogManager.d(TAG, "Beacon parsers have changed to: "+mBeaconParsers.get(i).getLayout());
beaconParsersChanged = true;
break;
}
}
}
else {
beaconParsersChanged = true;
LogManager.d(TAG, "Beacon parsers have been added or removed.");
}
if (beaconParsersChanged) {
LogManager.d(TAG, "Updating beacon parsers");
beaconManager.getBeaconParsers().clear();
beaconManager.getBeaconParsers().addAll(mBeaconParsers);
scanService.reloadParsers();
}
else {
LogManager.d(TAG, "Beacon parsers unchanged.");
}
beaconManager.setRegionStatePersistenceEnabled(mRegionStatePersistenceEnabled);
beaconManager.setAndroidLScanningDisabled(mAndroidLScanningDisabled);
BeaconManager.setRegionExitPeriod(mRegionExitPeriod);
Expand Down
Loading

0 comments on commit 7c1f76e

Please sign in to comment.