Repository: altbeacon/android-beacon-library
Issue: CycledLeScanner crashes on setWakeUpAlarm

### Expected behavior
No crash
### Actual behavior
Fatal Exception: java.lang.SecurityException
get application info: Neither user 1010129 nor current process has android.permission.INTERACT_ACROSS_USERS.

Fatal Exception: java.lang.SecurityException
Permission Denial: getIntentForIntentSender() from pid=20646, uid=10159 requires android.permission.GET_INTENT_SENDER_INTENT

Fatal Exception: java.lang.NullPointerException
println needs a message
### Steps to reproduce this behavior
Not sure about the reproduction, we noticed these crashes on Firebase. Here's the stack trace for LENOVO VIBE X3 Lite, Android 5.1:
```
Fatal Exception: java.lang.SecurityException: get application info: Neither user 1010129 nor current process has android.permission.INTERACT_ACROSS_USERS.
       at android.os.Parcel.readException(Parcel.java:1546)
       at android.os.Parcel.readException(Parcel.java:1499)
       at android.app.IAlarmManager$Stub$Proxy.set(IAlarmManager.java:244)
       at android.app.AlarmManager.setImpl(AlarmManager.java:425)
       at android.app.AlarmManager.set(AlarmManager.java:224)
       at org.altbeacon.beacon.service.scanner.CycledLeScanner.setWakeUpAlarm(CycledLeScanner.java:489)
       at org.altbeacon.beacon.service.scanner.CycledLeScanner.scheduleScanCycleStop(CycledLeScanner.java:368)
       at org.altbeacon.beacon.service.scanner.CycledLeScanner$2.run(CycledLeScanner.java:374)
       at android.os.Handler.handleCallback(Handler.java:815)
       at android.os.Handler.dispatchMessage(Handler.java:104)
       at android.os.Looper.loop(Looper.java:194)
       at android.app.ActivityThread.main(ActivityThread.java:5763)
       at java.lang.reflect.Method.invoke(Method.java)
       at java.lang.reflect.Method.invoke(Method.java:372)
       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:960)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:755)
```

There was another similar crash on Galaxy S5 Neo (6.0.1):
```
Fatal Exception: java.lang.SecurityException: Permission Denial: getIntentForIntentSender() from pid=20646, uid=10159 requires android.permission.GET_INTENT_SENDER_INTENT
       at android.os.Parcel.readException(Parcel.java:1620)
       at android.os.Parcel.readException(Parcel.java:1573)
       at android.app.IAlarmManager$Stub$Proxy.set(IAlarmManager.java:217)
       at android.app.AlarmManager.setImpl(AlarmManager.java:484)
       at android.app.AlarmManager.set(AlarmManager.java:260)
       at org.altbeacon.beacon.service.scanner.CycledLeScanner.setWakeUpAlarm(CycledLeScanner.java:489)
       at org.altbeacon.beacon.service.scanner.CycledLeScanner.scheduleScanCycleStop(CycledLeScanner.java:368)
       at org.altbeacon.beacon.service.scanner.CycledLeScanner$2.run(CycledLeScanner.java:374)
       at android.os.Handler.handleCallback(Handler.java:739)
       at android.os.Handler.dispatchMessage(Handler.java:95)
       at android.os.Looper.loop(Looper.java:158)
       at android.app.ActivityThread.main(ActivityThread.java:7230)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120)
```

And another one on LENOVO A6010 (5.0.2):
```
Fatal Exception: java.lang.NullPointerException: println needs a message
       at android.util.Log.println_native(Log.java)
       at android.util.Slog.v(Slog.java:28)
       at android.app.AlarmManager.setImpl(AlarmManager.java:429)
       at android.app.AlarmManager.set(AlarmManager.java:228)
       at org.altbeacon.beacon.service.scanner.CycledLeScanner.setWakeUpAlarm(CycledLeScanner.java:489)
       at org.altbeacon.beacon.service.scanner.CycledLeScanner.scheduleScanCycleStop(CycledLeScanner.java:368)
       at org.altbeacon.beacon.service.scanner.CycledLeScanner.scanLeDevice(CycledLeScanner.java:338)
       at org.altbeacon.beacon.service.scanner.CycledLeScannerForLollipop$1.run(CycledLeScannerForLollipop.java:153)
       at android.os.Handler.handleCallback(Handler.java:739)
       at android.os.Handler.dispatchMessage(Handler.java:95)
       at android.os.Looper.loop(Looper.java:135)
       at android.app.ActivityThread.main(ActivityThread.java:5233)
       at java.lang.reflect.Method.invoke(Method.java)
       at java.lang.reflect.Method.invoke(Method.java:372)
       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:898)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:693)
```
### Mobile device model and OS version
LENOVO VIBE X3 Lite (5.1)
Galaxy S5 Neo (6.0.1)
LENOVO A6010 (5.0.2)
### Android Beacon Library version
2.15.4


Changed files:
lib/src/main/java/org/altbeacon/beacon/service/scanner/CycledLeScanner.java

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 0089b18..399f107 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
@@ -6,13 +6,16 @@ import android.app.AlarmManager;
 import android.app.PendingIntent;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothManager;
+import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.os.Build;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Looper;
+import android.os.PowerManager;
 import android.os.SystemClock;
 import android.support.annotation.AnyThread;
 import android.support.annotation.MainThread;
@@ -52,6 +55,7 @@ public abstract class CycledLeScanner {
     // avoid doing too many scans in a limited time on Android 7.0 or because we are capable of
     // multiple detections.  If true, it indicates scanning needs to be stopped when we finish.
     private boolean mScanningLeftOn = false;
+    private BroadcastReceiver mCancelAlarmOnUserSwitchBroadcastReceiver = null;
 
     protected long mBetweenScanPeriod;
 
@@ -260,7 +264,6 @@ public abstract class CycledLeScanner {
 
         // Remove any postDelayed Runnables queued for the next scan cycle
         mHandler.removeCallbacksAndMessages(null);
-
         // We cannot quit the thread used by the handler until queued Runnables have been processed,
         // because the handler is what stops scanning, and we do not want scanning left on.
         // So we stop the thread using the handler, so we make sure it happens after all other
@@ -273,6 +276,7 @@ public abstract class CycledLeScanner {
                 mScanThread.quit();
             }
         });
+        cleanupCancelAlarmOnUserSwitch();
     }
 
     protected abstract void stopScan();
@@ -484,12 +488,37 @@ public abstract class CycledLeScanner {
         if (milliseconds < mScanPeriod) {
             milliseconds = mScanPeriod;
         }
-
         AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
         alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + milliseconds, getWakeUpOperation());
         LogManager.d(TAG, "Set a wakeup alarm to go off in %s ms: %s", milliseconds, getWakeUpOperation());
+        cancelAlarmOnUserSwitch();
     }
 
+    // Added to prevent crash on switching users.  See #876
+    protected void cancelAlarmOnUserSwitch() {
+        if (mCancelAlarmOnUserSwitchBroadcastReceiver == null) {
+            IntentFilter filter = new IntentFilter();
+            filter.addAction( Intent.ACTION_USER_BACKGROUND );
+            filter.addAction( Intent.ACTION_USER_FOREGROUND );
+
+            mCancelAlarmOnUserSwitchBroadcastReceiver = new BroadcastReceiver() {
+                @Override
+                public void onReceive(Context context, Intent intent) {
+                    LogManager.w(TAG, "User switch detected.  Cancelling alarm to prevent potential crash.");
+                    cancelWakeUpAlarm();
+                }
+            };
+            mContext.registerReceiver(mCancelAlarmOnUserSwitchBroadcastReceiver, filter);
+        }
+    }
+    protected void cleanupCancelAlarmOnUserSwitch() {
+        if (mCancelAlarmOnUserSwitchBroadcastReceiver != null) {
+            mContext.unregisterReceiver(mCancelAlarmOnUserSwitchBroadcastReceiver);
+            mCancelAlarmOnUserSwitchBroadcastReceiver = null;
+        }
+    }
+
+
     protected PendingIntent getWakeUpOperation() {
         if (mWakeUpOperation == null) {
             Intent wakeupIntent = new Intent(mContext, StartupBroadcastReceiver.class);