diff --git a/src/main/java/org/altbeacon/beacon/BeaconManager.java b/src/main/java/org/altbeacon/beacon/BeaconManager.java index 59ce70cf7..987bf47f4 100644 --- a/src/main/java/org/altbeacon/beacon/BeaconManager.java +++ b/src/main/java/org/altbeacon/beacon/BeaconManager.java @@ -291,6 +291,13 @@ public boolean isBound(BeaconConsumer consumer) { } } + /** + * Tells you if the any beacon consumer is bound to the service + * @return + */ + public boolean isAnyConsumerBound() { + return consumers.size() > 0 && (serviceMessenger != null); + } /** * This method notifies the beacon service that the application is either moving to background * mode or foreground mode. When in background mode, BluetoothLE scans to look for beacons are @@ -375,7 +382,7 @@ public void setMonitorNotifier(MonitorNotifier notifier) { * @see BeaconManager#setRangeNotifier(RangeNotifier) * @see BeaconManager#stopRangingBeaconsInRegion(Region region) * @see RangeNotifier - * @see Region + * @see Region * @param region */ @TargetApi(18) @@ -388,7 +395,7 @@ public void startRangingBeaconsInRegion(Region region) throws RemoteException { throw new RemoteException("The BeaconManager is not bound to the service. Call beaconManager.bind(BeaconConsumer consumer) and wait for a callback to onBeaconServiceConnect()"); } Message msg = Message.obtain(null, BeaconService.MSG_START_RANGING, 0, 0); - StartRMData obj = new StartRMData(region, callbackPackageName(), this.getScanPeriod(), this.getBetweenScanPeriod() ); + StartRMData obj = new StartRMData(region, callbackPackageName(), this.getScanPeriod(), this.getBetweenScanPeriod(), this.mBackgroundMode ); msg.obj = obj; serviceMessenger.send(msg); synchronized (rangedRegions) { @@ -415,7 +422,7 @@ public void stopRangingBeaconsInRegion(Region region) throws RemoteException { throw new RemoteException("The BeaconManager is not bound to the service. Call beaconManager.bind(BeaconConsumer consumer) and wait for a callback to onBeaconServiceConnect()"); } Message msg = Message.obtain(null, BeaconService.MSG_STOP_RANGING, 0, 0); - StartRMData obj = new StartRMData(region, callbackPackageName(),this.getScanPeriod(), this.getBetweenScanPeriod() ); + StartRMData obj = new StartRMData(region, callbackPackageName(),this.getScanPeriod(), this.getBetweenScanPeriod(), this.mBackgroundMode ); msg.obj = obj; serviceMessenger.send(msg); synchronized (rangedRegions) { @@ -449,7 +456,7 @@ public void startMonitoringBeaconsInRegion(Region region) throws RemoteException throw new RemoteException("The BeaconManager is not bound to the service. Call beaconManager.bind(BeaconConsumer consumer) and wait for a callback to onBeaconServiceConnect()"); } Message msg = Message.obtain(null, BeaconService.MSG_START_MONITORING, 0, 0); - StartRMData obj = new StartRMData(region, callbackPackageName(),this.getScanPeriod(), this.getBetweenScanPeriod() ); + StartRMData obj = new StartRMData(region, callbackPackageName(),this.getScanPeriod(), this.getBetweenScanPeriod(), this.mBackgroundMode ); msg.obj = obj; serviceMessenger.send(msg); synchronized (monitoredRegions) { @@ -477,7 +484,7 @@ public void stopMonitoringBeaconsInRegion(Region region) throws RemoteException throw new RemoteException("The BeaconManager is not bound to the service. Call beaconManager.bind(BeaconConsumer consumer) and wait for a callback to onBeaconServiceConnect()"); } Message msg = Message.obtain(null, BeaconService.MSG_STOP_MONITORING, 0, 0); - StartRMData obj = new StartRMData(region, callbackPackageName(),this.getScanPeriod(), this.getBetweenScanPeriod() ); + StartRMData obj = new StartRMData(region, callbackPackageName(),this.getScanPeriod(), this.getBetweenScanPeriod(), this.mBackgroundMode ); msg.obj = obj; serviceMessenger.send(msg); synchronized (monitoredRegions) { @@ -688,4 +695,5 @@ public static void setAndroidLScanningDisabled(boolean disabled) { sAndroidLScanningDisabled = disabled; } + } diff --git a/src/main/java/org/altbeacon/beacon/service/BeaconService.java b/src/main/java/org/altbeacon/beacon/service/BeaconService.java index 59b865770..77324379a 100644 --- a/src/main/java/org/altbeacon/beacon/service/BeaconService.java +++ b/src/main/java/org/altbeacon/beacon/service/BeaconService.java @@ -144,27 +144,26 @@ public void handleMessage(Message msg) { case MSG_START_RANGING: Log.i(TAG, "start ranging received"); service.startRangingBeaconsInRegion(startRMData.getRegionData(), new org.altbeacon.beacon.service.Callback(startRMData.getCallbackPackageName())); - service.setScanPeriods(startRMData.getScanPeriod(), startRMData.getBetweenScanPeriod()); + service.setScanPeriods(startRMData.getScanPeriod(), startRMData.getBetweenScanPeriod(), startRMData.getBackgroundFlag()); break; case MSG_STOP_RANGING: Log.i(TAG, "stop ranging received"); service.stopRangingBeaconsInRegion(startRMData.getRegionData()); - service.setScanPeriods(startRMData.getScanPeriod(), startRMData.getBetweenScanPeriod()); + service.setScanPeriods(startRMData.getScanPeriod(), startRMData.getBetweenScanPeriod(), startRMData.getBackgroundFlag()); break; case MSG_START_MONITORING: Log.i(TAG, "start monitoring received"); service.startMonitoringBeaconsInRegion(startRMData.getRegionData(), new org.altbeacon.beacon.service.Callback(startRMData.getCallbackPackageName())); - service.setScanPeriods(startRMData.getScanPeriod(), startRMData.getBetweenScanPeriod()); + service.setScanPeriods(startRMData.getScanPeriod(), startRMData.getBetweenScanPeriod(), startRMData.getBackgroundFlag()); break; case MSG_STOP_MONITORING: Log.i(TAG, "stop monitoring received"); service.stopMonitoringBeaconsInRegion(startRMData.getRegionData()); - service.setScanPeriods(startRMData.getScanPeriod(), startRMData.getBetweenScanPeriod()); + service.setScanPeriods(startRMData.getScanPeriod(), startRMData.getBetweenScanPeriod(), startRMData.getBackgroundFlag()); break; case MSG_SET_SCAN_PERIODS: Log.i(TAG, "set scan intervals received"); - service.setScanPeriods(startRMData.getScanPeriod(), startRMData.getBetweenScanPeriod()); - service.setBackgroundFlag(startRMData.getBackgroundFlag()); + service.setScanPeriods(startRMData.getScanPeriod(), startRMData.getBetweenScanPeriod(), startRMData.getBackgroundFlag()); break; default: super.handleMessage(msg); @@ -293,13 +292,8 @@ public void stopMonitoringBeaconsInRegion(Region region) { } } - public void setBackgroundFlag(boolean flag) { - mBackgroundFlag = flag; - mCycledScanner.setBackgroundFlag(flag); - } - - public void setScanPeriods(long scanPeriod, long betweenScanPeriod) { - mCycledScanner.setScanPeriods(scanPeriod, betweenScanPeriod); + public void setScanPeriods(long scanPeriod, long betweenScanPeriod, boolean backgroundFlag) { + mCycledScanner.setScanPeriods(scanPeriod, betweenScanPeriod, backgroundFlag); } private CycledLeScanCallback mCycledLeScanCallback = new CycledLeScanCallback() { diff --git a/src/main/java/org/altbeacon/beacon/service/CycledLeScanner.java b/src/main/java/org/altbeacon/beacon/service/CycledLeScanner.java index 1cf7260f5..4a48300d4 100644 --- a/src/main/java/org/altbeacon/beacon/service/CycledLeScanner.java +++ b/src/main/java/org/altbeacon/beacon/service/CycledLeScanner.java @@ -18,6 +18,7 @@ import android.util.Log; import org.altbeacon.beacon.BeaconManager; +import org.altbeacon.beacon.startup.StartupBroadcastReceiver; import org.altbeacon.bluetooth.BluetoothCrashResolver; import java.util.ArrayList; @@ -76,22 +77,27 @@ public CycledLeScanner(Context context, long scanPeriod, long betweenScanPeriod, } /** - * Tells the cycler whether it is in operating in background mode. - * This is used only on Android 5.0 scanning implementations to go to - * LOW_POWER_MODE vs. LOW_LATENCY_MODE - * @param flag + * Tells the cycler the scan rate and whether it is in operating in background mode. + * Background mode flag is used only with the Android 5.0 scanning implementations to switch + * between LOW_POWER_MODE vs. LOW_LATENCY_MODE + * @param backgroundFlag */ - public void setBackgroundFlag(boolean flag) { - if (mBackgroundFlag != flag) { + public void setScanPeriods(long scanPeriod, long betweenScanPeriod, boolean backgroundFlag) { + Log.d(TAG, "Set scan periods called with "+scanPeriod+", "+betweenScanPeriod+" Background mode must have changed."); + if (mBackgroundFlag != backgroundFlag) { mRestartNeeded = true; } - mBackgroundFlag = flag; - } - - public void setScanPeriods(long scanPeriod, long betweenScanPeriod) { - Log.d(TAG, "Set scan periods called with "+scanPeriod+", "+betweenScanPeriod+" Background mode must have changed."); + mBackgroundFlag = backgroundFlag; mScanPeriod = scanPeriod; mBetweenScanPeriod = betweenScanPeriod; + if (mBackgroundFlag == true) { + BeaconManager.logDebug(TAG, "We are in the background. Setting wakeup alarm"); + setWakeUpAlarm(); + } + else { + BeaconManager.logDebug(TAG, "We are not in the background. Cancelling wakeup alarm"); + cancelWakeUpAlarm(); + } long now = new Date().getTime(); if (mNextScanCycleStartTime > now) { // We are waiting to start scanning. We may need to adjust the next start time @@ -159,6 +165,9 @@ private boolean deferScanIfNeeded() { // Don't actually wait until the next scan time -- only wait up to 1 second. this // allows us to start scanning sooner if a consumer enters the foreground and expects // results more quickly + if (mBackgroundFlag) { + setWakeUpAlarm(); + } mHandler.postDelayed(new Runnable() { @Override public void run() { @@ -274,6 +283,9 @@ private void scheduleScanCycleStop() { long millisecondsUntilStop = mScanCycleStopTime - (new Date().getTime()); if (millisecondsUntilStop > 0) { BeaconManager.logDebug(TAG, "Waiting to stop scan cycle for another " + millisecondsUntilStop + " milliseconds"); + if (mBackgroundFlag) { + setWakeUpAlarm(); + } mHandler.postDelayed(new Runnable() { @Override public void run() { @@ -334,6 +346,7 @@ private void finishScanCycle() { else { BeaconManager.logDebug(TAG, "Scanning disabled. No ranging or monitoring regions are active."); mScanCyclerStarted = false; + cancelWakeUpAlarm(); } } } @@ -406,16 +419,36 @@ private BluetoothAdapter getBluetoothAdapter() { return mBluetoothAdapter; } - // In case we go into deep sleep, we will set up a wakeup alarm when in the background to kick + + private PendingIntent mWakeUpOperation = null; + // In case we go into deep sleep, we will set up a wakeup alarm when in the background to kickofÆ’ // off the scan cycle again - private void setPeriodicWakeUpAlarm() { + @TargetApi(3) + private void setWakeUpAlarm() { // wake up time will be the maximum of 5 minutes, the scan period, the between scan period - mill + long milliseconds = 1000l*60*5; /* five minutes */ + if (milliseconds < mBetweenScanPeriod) { + milliseconds = mBetweenScanPeriod; + } + if (milliseconds < mScanPeriod) { + milliseconds = mScanPeriod; + } AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); Intent intent = new Intent(); - intent.setClassName(mContext, BeaconService.class.getName()); - PendingIntent operation = PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT); - alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, milliseconds, operation); + intent.setClassName(mContext, StartupBroadcastReceiver.class.getName()); + intent.putExtra("wakeup", true); + cancelWakeUpAlarm(); + mWakeUpOperation = PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT); + alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, System.currentTimeMillis()+milliseconds, mWakeUpOperation); + BeaconManager.logDebug(TAG, "Set a wakeup alarm to go off in "+milliseconds+" ms: "+mWakeUpOperation); } + @TargetApi(3) + private void cancelWakeUpAlarm() { + Log.d(TAG, "cancel wakeup alarm: "+mWakeUpOperation); + if (mWakeUpOperation != null) { + AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); + alarmManager.cancel(mWakeUpOperation); + } + } } diff --git a/src/main/java/org/altbeacon/beacon/service/StartRMData.java b/src/main/java/org/altbeacon/beacon/service/StartRMData.java index a5000e0ba..e981bdca3 100644 --- a/src/main/java/org/altbeacon/beacon/service/StartRMData.java +++ b/src/main/java/org/altbeacon/beacon/service/StartRMData.java @@ -42,13 +42,15 @@ public StartRMData(Region region, String callbackPackageName) { public StartRMData(long scanPeriod, long betweenScanPeriod, boolean backgroundFlag) { this.scanPeriod = scanPeriod; this.betweenScanPeriod = betweenScanPeriod; + this.backgroundFlag = backgroundFlag; } - public StartRMData(Region region, String callbackPackageName, long scanPeriod, long betweenScanPeriod) { + public StartRMData(Region region, String callbackPackageName, long scanPeriod, long betweenScanPeriod, boolean backgroundFlag) { this.scanPeriod = scanPeriod; this.betweenScanPeriod = betweenScanPeriod; this.region = region; this.callbackPackageName = callbackPackageName; + this.backgroundFlag = backgroundFlag; } diff --git a/src/main/java/org/altbeacon/beacon/startup/RegionBootstrap.java b/src/main/java/org/altbeacon/beacon/startup/RegionBootstrap.java index 81ef67611..d721b0b85 100644 --- a/src/main/java/org/altbeacon/beacon/startup/RegionBootstrap.java +++ b/src/main/java/org/altbeacon/beacon/startup/RegionBootstrap.java @@ -50,7 +50,6 @@ public RegionBootstrap(BootstrapNotifier application, Region region) { throw new NullPointerException("The BootstrapNotifier instance is returning null from its getApplicationContext() method. Have you implemented this method?"); } beaconManager = BeaconManager.getInstanceForApplication(application.getApplicationContext()); - this.application = application; regions = new ArrayList(); regions.add(region); diff --git a/src/main/java/org/altbeacon/beacon/startup/StartupBroadcastReceiver.java b/src/main/java/org/altbeacon/beacon/startup/StartupBroadcastReceiver.java index b4d203aca..621fa8533 100644 --- a/src/main/java/org/altbeacon/beacon/startup/StartupBroadcastReceiver.java +++ b/src/main/java/org/altbeacon/beacon/startup/StartupBroadcastReceiver.java @@ -14,7 +14,6 @@ public class StartupBroadcastReceiver extends BroadcastReceiver { private static final String TAG = "StartupBroadcastReceiver"; - private Context context; @Override public void onReceive(Context context, Intent intent) { @@ -24,11 +23,14 @@ public void onReceive(Context context, Intent intent) { return; } BeaconManager beaconManager = BeaconManager.getInstanceForApplication(context.getApplicationContext()); - - Intent startServiceIntent = new Intent(context, BeaconService.class); - context.startService(startServiceIntent); - startServiceIntent = new Intent(context, BeaconIntentProcessor.class); - context.startService(startServiceIntent); - + if (beaconManager.isAnyConsumerBound()) { + if (intent.getBooleanExtra("wakeup", false)) { + BeaconManager.logDebug(TAG, "got wake up intent"); + } + else { + BeaconManager.logDebug(TAG, "Already started. Ignoring intent: "+intent+" of type: "+intent.getStringExtra("wakeup")); + } + return; + } } }