diff --git a/CHANGELOG.md b/CHANGELOG.md index 32be0f162..abf5443b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ ### Development -- Prevent NPE in BluetoothMedic#runScanTest. (#893 Van Hoang Tran) +- Fix thread leak with 0 regions and settings applied, (#888, David G. Young) +- Prevent NPE when ScanState restore hangs or ScanJob thread is slow to start (#890, David G. Young) +- Prevent crash from IAE when ending scan cycle (#891, David G. Young) +- Prevent NPE in BluetoothMedic#runScanTest. (#893 Van Hoang Tran) ### 2.16.2 / 2019-05-29 diff --git a/lib/src/main/java/org/altbeacon/beacon/service/ScanJob.java b/lib/src/main/java/org/altbeacon/beacon/service/ScanJob.java index b13e0e99d..86fbddff4 100644 --- a/lib/src/main/java/org/altbeacon/beacon/service/ScanJob.java +++ b/lib/src/main/java/org/altbeacon/beacon/service/ScanJob.java @@ -11,6 +11,7 @@ import android.content.pm.PackageManager; import android.os.Build; import android.os.Handler; +import android.support.annotation.Nullable; import org.altbeacon.beacon.Beacon; import org.altbeacon.beacon.BeaconManager; @@ -47,16 +48,20 @@ public class ScanJob extends JobService { private static int sOverrideImmediateScanJobId = -1; private static int sOverridePeriodicScanJobId = -1; - private ScanState mScanState; + @Nullable + private ScanState mScanState = null; private Handler mStopHandler = new Handler(); + @Nullable private ScanHelper mScanHelper; private boolean mInitialized = false; + private boolean mStopCalled = false; @Override public boolean onStartJob(final JobParameters jobParameters) { // We start off on the main UI thread here. // But the ScanState restore from storage sometimes hangs, so we start new thread here just // to kick that off. This way if the restore hangs, we don't hang the UI thread. + LogManager.d(TAG, "ScanJob Lifecycle START: "+ScanJob.this); new Thread(new Runnable() { public void run() { if (!initialzeScanHelper()) { @@ -65,10 +70,10 @@ public void run() { } ScanJobScheduler.getInstance().ensureNotificationProcessorSetup(getApplicationContext()); if (jobParameters.getJobId() == getImmediateScanJobId(ScanJob.this)) { - LogManager.i(TAG, "Running immediate scan job: instance is "+this); + LogManager.i(TAG, "Running immediate scan job: instance is "+ScanJob.this); } else { - LogManager.i(TAG, "Running periodic scan job: instance is "+this); + LogManager.i(TAG, "Running periodic scan job: instance is "+ScanJob.this); } List queuedScanResults = ScanJobScheduler.getInstance().dumpBackgroundScanResultQueue(); @@ -76,46 +81,65 @@ public void run() { for (ScanResult result : queuedScanResults) { ScanRecord scanRecord = result.getScanRecord(); if (scanRecord != null) { - mScanHelper.processScanResult(result.getDevice(), result.getRssi(), scanRecord.getBytes()); + if (mScanHelper != null) { + mScanHelper.processScanResult(result.getDevice(), result.getRssi(), scanRecord.getBytes()); + } } } LogManager.d(TAG, "Done processing queued scan resuilts"); - boolean startedScan; - if (mInitialized) { - LogManager.d(TAG, "Scanning already started. Resetting for current parameters"); - startedScan = restartScanning(); - } - else { - startedScan = startScanning(); - } - mStopHandler.removeCallbacksAndMessages(null); + // This syncronized block is around the scan start. + // Without it, it is possilbe that onStopJob is called in another thread and + // closing out the CycledScanner + synchronized(ScanJob.this) { + if (mStopCalled) { + LogManager.d(TAG, "Quitting scan job before we even start. Somebody told us to stop."); + ScanJob.this.jobFinished(jobParameters , false); + return; + } - if (startedScan) { - LogManager.i(TAG, "Scan job running for "+mScanState.getScanJobRuntimeMillis()+" millis"); - mStopHandler.postDelayed(new Runnable() { - @Override - public void run() { - LogManager.i(TAG, "Scan job runtime expired: " + ScanJob.this); - stopScanning(); - mScanState.save(); - ScanJob.this.jobFinished(jobParameters , false); + boolean startedScan; + if (mInitialized) { + LogManager.d(TAG, "Scanning already started. Resetting for current parameters"); + startedScan = restartScanning(); + } + else { + startedScan = startScanning(); + } + mStopHandler.removeCallbacksAndMessages(null); - // need to execute this after the current block or Android stops this job prematurely - mStopHandler.post(new Runnable() { + if (startedScan) { + if (mScanState != null) { + LogManager.i(TAG, "Scan job running for "+mScanState.getScanJobRuntimeMillis()+" millis"); + mStopHandler.postDelayed(new Runnable() { @Override public void run() { - scheduleNextScan(); - } - }); + LogManager.i(TAG, "Scan job runtime expired: " + ScanJob.this); + stopScanning(); + mScanState.save(); + ScanJob.this.jobFinished(jobParameters , false); + + // need to execute this after the current block or Android stops this job prematurely + mStopHandler.post(new Runnable() { + @Override + public void run() { + scheduleNextScan(); + } + }); + } + }, mScanState.getScanJobRuntimeMillis()); } - }, mScanState.getScanJobRuntimeMillis()); - } - else { - LogManager.i(TAG, "Scanning not started so Scan job is complete."); - ScanJob.this.jobFinished(jobParameters , false); + } + else { + LogManager.i(TAG, "Scanning not started so Scan job is complete."); + stopScanning(); + mScanState.save(); + LogManager.d(TAG, "ScanJob Lifecycle STOP (start fail): "+ScanJob.this); + ScanJob.this.jobFinished(jobParameters , false); + } } + } }).start(); @@ -123,121 +147,147 @@ public void run() { } private void scheduleNextScan(){ - if(!mScanState.getBackgroundMode()){ - // immediately reschedule scan if running in foreground - LogManager.d(TAG, "In foreground mode, schedule next scan"); - ScanJobScheduler.getInstance().forceScheduleNextScan(ScanJob.this); - } else { - startPassiveScanIfNeeded(); + if (mScanState != null) { + if(!mScanState.getBackgroundMode()){ + // immediately reschedule scan if running in foreground + LogManager.d(TAG, "In foreground mode, schedule next scan"); + ScanJobScheduler.getInstance().forceScheduleNextScan(ScanJob.this); + } else { + startPassiveScanIfNeeded(); + } } } private void startPassiveScanIfNeeded() { - LogManager.d(TAG, "Checking to see if we need to start a passive scan"); - boolean insideAnyRegion = false; - // Clone the collection before iterating to prevent ConcurrentModificationException per #577 - List regions = new ArrayList<>(mScanState.getMonitoringStatus().regions()); - for (Region region : regions) { - RegionMonitoringState state = mScanState.getMonitoringStatus().stateOf(region); - if (state != null && state.getInside()) { - insideAnyRegion = true; + if (mScanState != null) { + LogManager.d(TAG, "Checking to see if we need to start a passive scan"); + boolean insideAnyRegion = false; + // Clone the collection before iterating to prevent ConcurrentModificationException per #577 + List regions = new ArrayList<>(mScanState.getMonitoringStatus().regions()); + for (Region region : regions) { + RegionMonitoringState state = mScanState.getMonitoringStatus().stateOf(region); + if (state != null && state.getInside()) { + insideAnyRegion = true; + } } - } - if (insideAnyRegion) { - // TODO: Set up a scan filter for not detecting a beacon pattern - LogManager.i(TAG, "We are inside a beacon region. We will not scan between cycles."); - } - else { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - mScanHelper.startAndroidOBackgroundScan(mScanState.getBeaconParsers()); + if (insideAnyRegion) { + // TODO: Set up a scan filter for not detecting a beacon pattern + LogManager.i(TAG, "We are inside a beacon region. We will not scan between cycles."); } else { - LogManager.d(TAG, "This is not Android O. No scanning between cycles when using ScanJob"); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + if (mScanHelper != null) { + mScanHelper.startAndroidOBackgroundScan(mScanState.getBeaconParsers()); + } + } + else { + LogManager.d(TAG, "This is not Android O. No scanning between cycles when using ScanJob"); + } } } } @Override public boolean onStopJob(JobParameters params) { - if (params.getJobId() == getPeriodicScanJobId(this)) { - LogManager.i(TAG, "onStopJob called for periodic scan " + this); - } - else { - LogManager.i(TAG, "onStopJob called for immediate scan " + this); - } - // Cancel the stop timer. The OS is stopping prematurely - mStopHandler.removeCallbacksAndMessages(null); - stopScanning(); - startPassiveScanIfNeeded(); - mScanHelper.terminateThreads(); + // See corresponding synchronized block in onStartJob + synchronized(ScanJob.this) { + mStopCalled = true; + if (params.getJobId() == getPeriodicScanJobId(this)) { + LogManager.i(TAG, "onStopJob called for periodic scan " + this); + } + else { + LogManager.i(TAG, "onStopJob called for immediate scan " + this); + } + LogManager.d(TAG, "ScanJob Lifecycle STOP: "+ScanJob.this); + // Cancel the stop timer. The OS is stopping prematurely + mStopHandler.removeCallbacksAndMessages(null); + stopScanning(); + startPassiveScanIfNeeded(); + if (mScanHelper != null) { + mScanHelper.terminateThreads(); + } + } return false; } private void stopScanning() { mInitialized = false; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - mScanHelper.stopAndroidOBackgroundScan(); - } - if (mScanHelper.getCycledScanner() != null) { - mScanHelper.getCycledScanner().stop(); - mScanHelper.getCycledScanner().destroy(); + if (mScanHelper != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + mScanHelper.stopAndroidOBackgroundScan(); + } + if (mScanHelper.getCycledScanner() != null) { + mScanHelper.getCycledScanner().stop(); + mScanHelper.getCycledScanner().destroy(); + } } LogManager.d(TAG, "Scanning stopped"); } - // Returns false if cycle thread cannot be allocated + // Returns false if ScanHelper cannot be initialized private boolean initialzeScanHelper() { - mScanHelper = new ScanHelper(this); mScanState = ScanState.restore(ScanJob.this); - mScanState.setLastScanStartTimeMillis(System.currentTimeMillis()); - mScanHelper.setMonitoringStatus(mScanState.getMonitoringStatus()); - mScanHelper.setRangedRegionState(mScanState.getRangedRegionState()); - mScanHelper.setBeaconParsers(mScanState.getBeaconParsers()); - mScanHelper.setExtraDataBeaconTracker(mScanState.getExtraBeaconDataTracker()); - if (mScanHelper.getCycledScanner() == null) { - try { - mScanHelper.createCycledLeScanner(mScanState.getBackgroundMode(), null); - } - catch (OutOfMemoryError e) { - LogManager.w(TAG, "Failed to create CycledLeScanner thread."); - return false; + if (mScanState != null) { + ScanHelper scanHelper = new ScanHelper(this); + mScanState.setLastScanStartTimeMillis(System.currentTimeMillis()); + scanHelper.setMonitoringStatus(mScanState.getMonitoringStatus()); + scanHelper.setRangedRegionState(mScanState.getRangedRegionState()); + scanHelper.setBeaconParsers(mScanState.getBeaconParsers()); + scanHelper.setExtraDataBeaconTracker(mScanState.getExtraBeaconDataTracker()); + if (scanHelper.getCycledScanner() == null) { + try { + scanHelper.createCycledLeScanner(mScanState.getBackgroundMode(), null); + } + catch (OutOfMemoryError e) { + LogManager.w(TAG, "Failed to create CycledLeScanner thread."); + return false; + } } + mScanHelper = scanHelper; + } + else { + return false; } return true; } // Returns true of scanning actually was started, false if it did not need to be private boolean restartScanning() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - mScanHelper.stopAndroidOBackgroundScan(); - } - long scanPeriod = mScanState.getBackgroundMode() ? mScanState.getBackgroundScanPeriod() : mScanState.getForegroundScanPeriod(); - long betweenScanPeriod = mScanState.getBackgroundMode() ? mScanState.getBackgroundBetweenScanPeriod() : mScanState.getForegroundBetweenScanPeriod(); - if (mScanHelper.getCycledScanner() != null) { - mScanHelper.getCycledScanner().setScanPeriods(scanPeriod, - betweenScanPeriod, - mScanState.getBackgroundMode()); - } - mInitialized = true; - if (scanPeriod <= 0) { - LogManager.w(TAG, "Starting scan with scan period of zero. Exiting ScanJob."); + if (mScanState != null && mScanHelper != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + mScanHelper.stopAndroidOBackgroundScan(); + } + long scanPeriod = mScanState.getBackgroundMode() ? mScanState.getBackgroundScanPeriod() : mScanState.getForegroundScanPeriod(); + long betweenScanPeriod = mScanState.getBackgroundMode() ? mScanState.getBackgroundBetweenScanPeriod() : mScanState.getForegroundBetweenScanPeriod(); if (mScanHelper.getCycledScanner() != null) { - mScanHelper.getCycledScanner().stop(); + mScanHelper.getCycledScanner().setScanPeriods(scanPeriod, + betweenScanPeriod, + mScanState.getBackgroundMode()); + } + mInitialized = true; + if (scanPeriod <= 0) { + LogManager.w(TAG, "Starting scan with scan period of zero. Exiting ScanJob."); + if (mScanHelper.getCycledScanner() != null) { + mScanHelper.getCycledScanner().stop(); + } + return false; } - return false; - } - if (mScanHelper.getRangedRegionState().size() > 0 || mScanHelper.getMonitoringStatus().regions().size() > 0) { - if (mScanHelper.getCycledScanner() != null) { - mScanHelper.getCycledScanner().start(); + if (mScanHelper.getRangedRegionState().size() > 0 || mScanHelper.getMonitoringStatus().regions().size() > 0) { + if (mScanHelper.getCycledScanner() != null) { + mScanHelper.getCycledScanner().start(); + } + return true; + } + else { + if (mScanHelper.getCycledScanner() != null) { + mScanHelper.getCycledScanner().stop(); + } + return false; } - return true; } else { - if (mScanHelper.getCycledScanner() != null) { - mScanHelper.getCycledScanner().stop(); - } return false; } } diff --git a/lib/src/main/java/org/altbeacon/beacon/service/ScanJobScheduler.java b/lib/src/main/java/org/altbeacon/beacon/service/ScanJobScheduler.java index 9f56baa3b..1fd100ac2 100644 --- a/lib/src/main/java/org/altbeacon/beacon/service/ScanJobScheduler.java +++ b/lib/src/main/java/org/altbeacon/beacon/service/ScanJobScheduler.java @@ -102,8 +102,6 @@ public void cancelSchedule(Context context) { } } - // This method appears to be never used, because it is only used by Android O APIs, which - // must exist on another branch until the SDKs are released. public void scheduleAfterBackgroundWakeup(Context context, List scanResults) { if (scanResults != null) { mBackgroundScanResultQueue.addAll(scanResults); @@ -156,99 +154,57 @@ private void schedule(Context context, ScanState scanState, boolean backgroundWa JobScheduler jobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE); - if (backgroundWakeup || !scanState.getBackgroundMode()) { - // If we are in the foreground, and we want to start a scan soon, we will schedule an - // immediate job - if (millisToNextJobStart < scanState.getScanJobIntervalMillis() - 50) { - // If the next time we want to scan is less than 50ms from the periodic scan cycle, then] - // we schedule it for that specific time. - LogManager.d(TAG, "Scheduling immediate ScanJob to run in "+millisToNextJobStart+" millis"); - JobInfo immediateJob = new JobInfo.Builder(ScanJob.getImmediateScanJobId(context), new ComponentName(context, ScanJob.class)) - .setPersisted(true) // This makes it restart after reboot - .setExtras(new PersistableBundle()) - .setMinimumLatency(millisToNextJobStart) - .setOverrideDeadline(millisToNextJobStart).build(); - int error = jobScheduler.schedule(immediateJob); - if (error < 0) { - LogManager.e(TAG, "Failed to schedule scan job. Beacons will not be detected. Error: "+error); + int monitoredAndRangedRegionCount = scanState.getMonitoringStatus().regions().size() + scanState.getRangedRegionState().size(); + if (monitoredAndRangedRegionCount > 0) { + if (backgroundWakeup || !scanState.getBackgroundMode()) { + // If we are in the foreground, and we want to start a scan soon, we will schedule an + // immediate job + if (millisToNextJobStart < scanState.getScanJobIntervalMillis() - 50) { + // If the next time we want to scan is less than 50ms from the periodic scan cycle, then] + // we schedule it for that specific time. + LogManager.d(TAG, "Scheduling immediate ScanJob to run in "+millisToNextJobStart+" millis"); + JobInfo immediateJob = new JobInfo.Builder(ScanJob.getImmediateScanJobId(context), new ComponentName(context, ScanJob.class)) + .setPersisted(true) // This makes it restart after reboot + .setExtras(new PersistableBundle()) + .setMinimumLatency(millisToNextJobStart) + .setOverrideDeadline(millisToNextJobStart).build(); + int error = jobScheduler.schedule(immediateJob); + if (error < 0) { + LogManager.e(TAG, "Failed to schedule scan job. Beacons will not be detected. Error: "+error); + } + } else { + LogManager.d(TAG, "Not scheduling immediate scan, assuming periodic is about to run"); } - } else { - LogManager.d(TAG, "Not scheduling immediate scan, assuming periodic is about to run"); } - } - else { - LogManager.d(TAG, "Not scheduling an immediate scan because we are in background mode. Cancelling existing immediate scan."); - jobScheduler.cancel(ScanJob.getImmediateScanJobId(context)); - } + else { + LogManager.d(TAG, "Not scheduling an immediate scan because we are in background mode. Cancelling existing immediate ScanJob."); + jobScheduler.cancel(ScanJob.getImmediateScanJobId(context)); + } - JobInfo.Builder periodicJobBuilder = new JobInfo.Builder(ScanJob.getPeriodicScanJobId(context), new ComponentName(context, ScanJob.class)) - .setPersisted(true) // This makes it restart after reboot - .setExtras(new PersistableBundle()); + JobInfo.Builder periodicJobBuilder = new JobInfo.Builder(ScanJob.getPeriodicScanJobId(context), new ComponentName(context, ScanJob.class)) + .setPersisted(true) // This makes it restart after reboot + .setExtras(new PersistableBundle()); + + if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + // ON Android N+ we specify a tolerance of 0ms (capped at 5% by the OS) to ensure + // our scans happen within 5% of the schduled time. + periodicJobBuilder.setPeriodic(scanState.getScanJobIntervalMillis(), 0L).build(); + } + else { + periodicJobBuilder.setPeriodic(scanState.getScanJobIntervalMillis()).build(); + } + final JobInfo jobInfo = periodicJobBuilder.build(); + LogManager.d(TAG, "Scheduling ScanJob " + jobInfo + " to run every "+scanState.getScanJobIntervalMillis()+" millis"); + int error = jobScheduler.schedule(jobInfo); + if (error < 0) { + LogManager.e(TAG, "Failed to schedule scan job. Beacons will not be detected. Error: "+error); + } - if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - // ON Android N+ we specify a tolerance of 0ms (capped at 5% by the OS) to ensure - // our scans happen within 5% of the schduled time. - periodicJobBuilder.setPeriodic(scanState.getScanJobIntervalMillis(), 0L).build(); } else { - periodicJobBuilder.setPeriodic(scanState.getScanJobIntervalMillis()).build(); - } - // On Android O I see this: - // - // 06-07 22:15:51.361 6455-6455/org.altbeacon.beaconreference W/JobInfo: Specified interval for 1 is +5m10s0ms. Clamped to +15m0s0ms - // 06-07 22:15:51.361 6455-6455/org.altbeacon.beaconreference W/JobInfo: Specified flex for 1 is 0. Clamped to +5m0s0ms - // - // This suggests logs are being clamped at a max of every 15 minutes +/- 5 minutes in the background - // This is the same way it worked on Android N per this post: https://stackoverflow.com/questions/38344220/job-scheduler-not-running-on-android-n - // - // In practice, I see the following runtimes on the Nexus Player with Android O - // This shows that the 15 minutes has some slop. - // - /* -06-07 22:25:51.380 6455-6455/org.altbeacon.beaconreference I/ScanJob: Running periodic scan job: instance is org.altbeacon.beacon.service.ScanJob@7188bc6 -06-07 22:41:01.227 6455-6455/org.altbeacon.beaconreference I/ScanJob: Running periodic scan job: instance is org.altbeacon.beacon.service.ScanJob@382ed7b -06-07 22:55:51.373 6455-6455/org.altbeacon.beaconreference I/ScanJob: Running periodic scan job: instance is org.altbeacon.beacon.service.ScanJob@203c928 -06-07 23:10:59.083 6455-6455/org.altbeacon.beaconreference I/ScanJob: Running periodic scan job: instance is org.altbeacon.beacon.service.ScanJob@dc96415 -06-07 23:25:51.371 6455-6455/org.altbeacon.beaconreference I/ScanJob: Running periodic scan job: instance is org.altbeacon.beacon.service.ScanJob@68bed2e -06-07 23:40:59.142 6455-6455/org.altbeacon.beaconreference I/ScanJob: Running periodic scan job: instance is org.altbeacon.beacon.service.ScanJob@c295843 -06-07 23:55:51.369 6455-6455/org.altbeacon.beaconreference I/ScanJob: Running periodic scan job: instance is org.altbeacon.beacon.service.ScanJob@cd047e4 -06-08 00:10:59.082 6455-6455/org.altbeacon.beaconreference I/ScanJob: Running periodic scan job: instance is org.altbeacon.beacon.service.ScanJob@8009a61 -06-08 00:25:51.368 6455-6455/org.altbeacon.beaconreference I/ScanJob: Running periodic scan job: instance is org.altbeacon.beacon.service.ScanJob@f1fa2ca -06-08 00:40:59.085 6455-6455/org.altbeacon.beaconreference I/ScanJob: Running periodic scan job: instance is org.altbeacon.beacon.service.ScanJob@88dddef -06-08 00:55:51.374 6455-6455/org.altbeacon.beaconreference I/ScanJob: Running periodic scan job: instance is org.altbeacon.beacon.service.ScanJob@eb2b360 -06-08 01:10:51.670 6455-6455/org.altbeacon.beaconreference I/ScanJob: Running periodic scan job: instance is org.altbeacon.beacon.service.ScanJob@9bca225 -06-08 01:25:51.383 6455-6455/org.altbeacon.beaconreference I/ScanJob: Running periodic scan job: instance is org.altbeacon.beacon.service.ScanJob@871c8fe -06-08 01:45:51.404 6455-6455/org.altbeacon.beaconreference I/ScanJob: Running periodic scan job: instance is org.altbeacon.beacon.service.ScanJob@3bf42d3 -06-08 01:56:12.354 6455-6455/org.altbeacon.beaconreference I/ScanJob: Running periodic scan job: instance is org.altbeacon.beacon.service.ScanJob@c3d4e34 -06-08 02:21:51.771 6455-6455/org.altbeacon.beaconreference I/ScanJob: Running periodic scan job: instance is org.altbeacon.beacon.service.ScanJob@1557571 -06-08 02:37:01.861 6455-6455/org.altbeacon.beaconreference I/ScanJob: Running periodic scan job: instance is org.altbeacon.beacon.service.ScanJob@e2c879a -06-08 02:52:11.943 6455-6455/org.altbeacon.beaconreference I/ScanJob: Running periodic scan job: instance is org.altbeacon.beacon.service.ScanJob@c9f0d7f -06-08 03:07:22.041 6455-6455/org.altbeacon.beaconreference I/ScanJob: Running periodic scan job: instance is org.altbeacon.beacon.service.ScanJob@4e0cab0 -06-08 03:23:12.696 6455-6455/org.altbeacon.beaconreference I/ScanJob: Running periodic scan job: instance is org.altbeacon.beacon.service.ScanJob@1139a7d -06-08 03:38:22.776 6455-6455/org.altbeacon.beaconreference I/ScanJob: Running periodic scan job: instance is org.altbeacon.beacon.service.ScanJob@e06b8f6 -06-08 03:52:12.792 6455-6455/org.altbeacon.beaconreference I/ScanJob: Running periodic scan job: instance is org.altbeacon.beacon.service.ScanJob@74147eb -06-08 04:08:32.872 6455-6455/org.altbeacon.beaconreference I/ScanJob: Running periodic scan job: instance is org.altbeacon.beacon.service.ScanJob@90d9fec -06-08 04:21:12.856 6455-6455/org.altbeacon.beaconreference I/ScanJob: Running periodic scan job: instance is org.altbeacon.beacon.service.ScanJob@a4abd49 -06-08 04:38:42.959 6455-6455/org.altbeacon.beaconreference I/ScanJob: Running periodic scan job: instance is org.altbeacon.beacon.service.ScanJob@741d912 -06-08 04:50:12.923 6455-6455/org.altbeacon.beaconreference I/ScanJob: Running periodic scan job: instance is org.altbeacon.beacon.service.ScanJob@15bfe17 -06-08 05:08:53.047 6455-6455/org.altbeacon.beaconreference I/ScanJob: Running periodic scan job: instance is org.altbeacon.beacon.service.ScanJob@fa229e8 -06-08 05:19:13.050 6455-6455/org.altbeacon.beaconreference I/ScanJob: Running periodic scan job: instance is org.altbeacon.beacon.service.ScanJob@b0e49d5 -06-08 05:39:03.142 6455-6455/org.altbeacon.beaconreference I/ScanJob: Running periodic scan job: instance is org.altbeacon.beacon.service.ScanJob@18823ee -06-08 05:54:13.212 6455-6455/org.altbeacon.beaconreference I/ScanJob: Running periodic scan job: instance is org.altbeacon.beacon.service.ScanJob@a72fc03 -06-08 06:10:51.850 6455-6455/org.altbeacon.beaconreference I/ScanJob: Running periodic scan job: instance is org.altbeacon.beacon.service.ScanJob@3fb84a4 -06-08 06:26:01.917 6455-6455/org.altbeacon.beaconreference I/ScanJob: Running periodic scan job: instance is org.altbeacon.beacon.service.ScanJob@53d6c21 -06-08 06:41:11.994 6455-6455/org.altbeacon.beaconreference I/ScanJob: Running periodic scan job: instance is org.altbeacon.beacon.service.ScanJob@848958a -06-08 06:56:22.053 6455-6455/org.altbeacon.beaconreference I/ScanJob: Running periodic scan job: instance is org.altbeacon.beacon.service.ScanJob@43cdaf -06-08 07:06:32.119 6455-6455/org.altbeacon.beaconreference I/ScanJob: Running periodic scan job: instance is org.altbeacon.beacon.service.ScanJob@5318c20 -06-08 07:29:12.356 6455-6455/org.altbeacon.beaconreference I/ScanJob: Running periodic scan job: instance is org.altbeacon.beacon.service.ScanJob@34f102d -06-08 07:44:22.431 6455-6455/org.altbeacon.beaconreference I/ScanJob: Running periodic scan job: instance is org.altbeacon.beacon.service.ScanJob@4d2e9e6 - */ - - final JobInfo jobInfo = periodicJobBuilder.build(); - LogManager.d(TAG, "Scheduling ScanJob " + jobInfo + " to run every "+scanState.getScanJobIntervalMillis()+" millis"); - int error = jobScheduler.schedule(jobInfo); - if (error < 0) { - LogManager.e(TAG, "Failed to schedule scan job. Beacons will not be detected. Error: "+error); + LogManager.d(TAG, "We are not monitoring or ranging any regions. We are going to cancel all scan jobs."); + jobScheduler.cancel(ScanJob.getImmediateScanJobId(context)); + jobScheduler.cancel(ScanJob.getPeriodicScanJobId(context)); } } } diff --git a/lib/src/main/java/org/altbeacon/beacon/service/scanner/CycledLeScanner.java b/lib/src/main/java/org/altbeacon/beacon/service/scanner/CycledLeScanner.java index 399f10716..e9d5d28c1 100644 --- a/lib/src/main/java/org/altbeacon/beacon/service/scanner/CycledLeScanner.java +++ b/lib/src/main/java/org/altbeacon/beacon/service/scanner/CycledLeScanner.java @@ -513,7 +513,10 @@ public void onReceive(Context context, Intent intent) { } protected void cleanupCancelAlarmOnUserSwitch() { if (mCancelAlarmOnUserSwitchBroadcastReceiver != null) { - mContext.unregisterReceiver(mCancelAlarmOnUserSwitchBroadcastReceiver); + try { + mContext.unregisterReceiver(mCancelAlarmOnUserSwitchBroadcastReceiver); + } + catch (IllegalArgumentException e) {} // thrown if OS does not think it was registered mCancelAlarmOnUserSwitchBroadcastReceiver = null; } }