@Override
 public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
   if (bluetoothCrashResolver != null) {
     bluetoothCrashResolver.notifyScannedDevice(device, this);
   }
   callback.onLeScan(device, rssi, scanRecord);
 }
 @Override
 @TargetApi(18)
 public void onDestroy() {
   if (android.os.Build.VERSION.SDK_INT < 18) {
     Log.w(TAG, "Not supported prior to API 18.");
     return;
   }
   bluetoothCrashResolver.stop();
   Log.i(TAG, "onDestroy called.  stopping scanning");
   handler.removeCallbacksAndMessages(null);
   scanLeDevice(false);
   if (bluetoothAdapter != null) {
     bluetoothAdapter.stopLeScan((BluetoothAdapter.LeScanCallback) getLeScanCallback());
     lastScanEndTime = new Date().getTime();
   }
 }
  @Override
  public void onCreate() {
    Log.i(TAG, "iBeaconService version is starting up");
    getBluetoothAdapter();
    bluetoothCrashResolver = new BluetoothCrashResolver(this);
    bluetoothCrashResolver.start();

    // Look for simulated scan data
    try {
      Class klass = Class.forName("com.radiusnetworks.ibeacon.SimulatedScanData");
      java.lang.reflect.Field f = klass.getField("iBeacons");
      this.simulatedScanData = (List<IBeacon>) f.get(null);
    } catch (ClassNotFoundException e) {
      if (IBeaconManager.LOG_DEBUG)
        Log.d(TAG, "No com.radiusnetworks.ibeacon.SimulatedScanData class exists.");
    } catch (Exception e) {
      Log.e(
          TAG,
          "Cannot get simulated Scan data.  Make sure your com.radiusnetworks.ibeacon.SimulatedScanData class defines a field with the signature 'public static List<IBeacon> iBeacons'",
          e);
    }
  }
 /**
  * default constructor, internally setting up the {@link
  * com.radiusnetworks.bluetooth.BluetoothCrashResolver}
  *
  * @param application parameter, required for the initialization of the {@link
  *     com.radiusnetworks.bluetooth.BluetoothCrashResolver}
  */
 public CrashCallBackWrapper(Context application) {
   bluetoothCrashResolver = new BluetoothCrashResolver(application);
   bluetoothCrashResolver.start();
 }
  @TargetApi(18)
  private void scanLeDevice(final Boolean enable) {
    scanCyclerStarted = true;
    if (android.os.Build.VERSION.SDK_INT < 18) {
      Log.w(TAG, "Not supported prior to API 18.");
      return;
    }
    if (getBluetoothAdapter() == null) {
      Log.e(TAG, "No bluetooth adapter.  iBeaconService cannot scan.");
      if (simulatedScanData == null) {
        Log.w(TAG, "exiting");
        return;
      } else {
        Log.w(TAG, "proceeding with simulated scan data");
      }
    }
    if (enable) {
      long millisecondsUntilStart = nextScanStartTime - (new Date().getTime());
      if (millisecondsUntilStart > 0) {
        if (IBeaconManager.LOG_DEBUG)
          Log.d(
              TAG,
              "Waiting to start next bluetooth scan for another "
                  + millisecondsUntilStart
                  + " milliseconds");
        // 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
        handler.postDelayed(
            new Runnable() {
              @Override
              public void run() {
                scanLeDevice(true);
              }
            },
            millisecondsUntilStart > 1000 ? 1000 : millisecondsUntilStart);
        return;
      }

      trackedBeacons = new HashSet<IBeacon>();
      trackedBeaconsPacketCount = 0;
      if (scanning == false || scanningPaused == true) {
        scanning = true;
        scanningPaused = false;
        try {
          if (getBluetoothAdapter() != null) {
            if (getBluetoothAdapter().isEnabled()) {
              if (bluetoothCrashResolver.isRecoveryInProgress()) {
                Log.w(TAG, "Skipping scan because crash recovery is in progress.");
              } else {
                if (scanningEnabled) {
                  getBluetoothAdapter()
                      .startLeScan((BluetoothAdapter.LeScanCallback) getLeScanCallback());
                } else {
                  if (IBeaconManager.LOG_DEBUG)
                    Log.d(TAG, "Scanning unnecessary - no monitoring or ranging active.");
                }
              }
              lastScanStartTime = new Date().getTime();
            } else {
              Log.w(TAG, "Bluetooth is disabled.  Cannot scan for iBeacons.");
            }
          }
        } catch (Exception e) {
          Log.e(
              "TAG",
              "Exception starting bluetooth scan.  Perhaps bluetooth is disabled or unavailable?");
        }
      } else {
        if (IBeaconManager.LOG_DEBUG) Log.d(TAG, "We are already scanning");
      }
      scanStopTime = (new Date().getTime() + scanPeriod);
      scheduleScanStop();

      if (IBeaconManager.LOG_DEBUG) Log.d(TAG, "Scan started");
    } else {
      if (IBeaconManager.LOG_DEBUG) Log.d(TAG, "disabling scan");
      scanning = false;
      if (getBluetoothAdapter() != null) {
        getBluetoothAdapter().stopLeScan((BluetoothAdapter.LeScanCallback) getLeScanCallback());
        lastScanEndTime = new Date().getTime();
      }
    }
  }