title: How to do background scan for BLE devices?
body: <p>I am currently doing a project which requires the android phone app to do background scanning for a particular SensorTag and trigger a notification to remind the user to sanitise their hands. </p>
  
  <p>I have managed to create the app and can receive a notification message when the app is running. My app can scan for ble devices and display them using a listadapter, and can scan in intervals of 10 seconds.</p>
  
  <p>However, I cannot receive any notification message when the phone is sleeping or when I am using another app, which sort of defeat the purpose of my project.</p>
  
  <p>I will appreciate if someone can enlighten me.</p>
  
  <p>Thanks</p>
  
  <p>Chrysline</p>
  
  <p>*P.S. I am using the SensorTag in the broadcaster mode and bluetooth smart mode only. I am targeting API 18 phones only.</p>
  
  <pre><code>public class MainActivity extends ListActivity{
  
  //Bluetooth Management
  private BluetoothAdapter btAdapter;
  private LeDeviceListAdapter mLeDeviceListAdapter;
  private boolean mScanning;
  
  // Set the enable bluetooth code
  private final static int REQUEST_ENABLE_BT = 0;
  
  
  //Handler
  private Handler myHandler;
  private static final int SCAN_INTERVAL_MS_1 = 10000;
  
  // Notification
  private static final int NOTIFY_1 = 0x1001;
  private static int mNotificationCount = 1;
  
  @Override
  protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
  
      myHandler = new Handler();
      //Check whether BLE is supported on the device(can selectively disable BLE-related features)
      if(!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)){
          Toast.makeText(this, "ble_not_supported", Toast.LENGTH_SHORT).show();
          finish();
      }
  
      final BluetoothManager btManager =
              (BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE);
      btAdapter = btManager.getAdapter();
  
      //Check if Bluetooth Adapter is present in the device
      if(btAdapter == null)
      {
          Toast.makeText(this, "Error : Bluetooth not supported", Toast.LENGTH_SHORT).show();
          Log.i("DEBUG_TAG", "No bluetooth available");
          finish();
          return;
      }
  
  }
  
  @Override
  public void onStart() {
      super.onStart();
  }
  
  @Override
  public void onResume() {
      super.onResume();
  
      // check for Bluetooth enabled on each resume
      if (btAdapter != null &amp;&amp; !btAdapter.isEnabled()) {
          Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
          startActivityForResult(enableIntent, REQUEST_ENABLE_BT);
      }
  
      //Initialise list view adapter
      mLeDeviceListAdapter = new LeDeviceListAdapter();
      setListAdapter(mLeDeviceListAdapter);
      scanLeDevice(true);
  }
  
  
  @Override
  public void onPause() {
      super.onPause();
  
      mLeDeviceListAdapter.clear();
      scanLeDevice(true);
      Log.i("DEBUG_TOAST", "Scanning in onPause method!!!");
  
  }
  
  @Override
  public void onStop() {
      super.onStop();
      scanLeDevice(false);
      mLeDeviceListAdapter.clear();
  
  }
  
  @Override
  public void onRestart() {
      super.onRestart();
      scanLeDevice(true);
  }
  
  @Override
  public void onDestroy() {
      super.onDestroy();
      btAdapter = null;
  }
  
  @Override
  protected void onActivityResult(int request_enable_bt, int result_enable_bt, Intent data) {
      if (result_enable_bt == RESULT_OK) {
          //Display "Turn On" message
          Toast.makeText(this, "Turned On", Toast.LENGTH_SHORT).show();
          Log.i("DEBUG_TOAST", "Turn On Toast Success!");
  
          // Display device name and MAC Address
          BluetoothManager btManager =
                  (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
          btAdapter = btManager.getAdapter();
          String address = btAdapter.getAddress();
          String name = btAdapter.getName();
          String toastText = name + " : " + address;
          Toast.makeText(this, toastText, Toast.LENGTH_LONG).show();
  
          Log.i("DEBUG_TOAST", "Device Name and Address Toast Success!");
      } else if (result_enable_bt == RESULT_CANCELED) {
          Toast.makeText(this, "Didn't Turn On", Toast.LENGTH_SHORT).show();
          Log.i("DEBUG_TOAST", "Turn Off Toast Success!");
  
          finish();
      }
      super.onActivityResult(request_enable_bt, result_enable_bt, data);
  }
  
  @Override
  public boolean onCreateOptionsMenu(Menu menu){
      getMenuInflater().inflate(R.menu.main_menu, menu);
      if(!mScanning){
          menu.findItem(R.id.scan).setVisible(true);
          menu.findItem(R.id.stop).setVisible(false);
          menu.findItem(R.id.exit).setVisible(true);
          menu.findItem(R.id.info).setVisible(true);
          menu.findItem(R.id.refresh).setActionView(null);
          Log.i("DEBUG_TOAST", "Menu_mScanning = 0");
      }
  
      else{
          menu.findItem(R.id.scan).setVisible(false);
          menu.findItem(R.id.stop).setVisible(true);
          menu.findItem(R.id.exit).setVisible(true);
          menu.findItem(R.id.info).setVisible(true);
          menu.findItem(R.id.refresh).setActionView(
                  R.layout.actionbar_indeterminate_progress);
          Log.i("DEBUG_TOAST", "Menu_mScanning = 1");
      }
      return true;
  }
  
  @Override
  public boolean onOptionsItemSelected(MenuItem item){
      switch (item.getItemId()){
          case R.id.scan:
  
              if(mLeDeviceListAdapter.getCount() &gt;0)
                  mLeDeviceListAdapter.clear();
              scanLeDevice(true);
              Log.i("DEBUG_TOAST", "Scan button pressed!");
              break;
  
          case R.id.stop:
              scanLeDevice(false);
              break;
  
          case R.id.info:
              Intent information_Intent = new Intent(MainActivity.this, Information.class);
              startActivity(information_Intent);
              break;
  
          case R.id.reminder:
              Intent reminder_Intent = new Intent(MainActivity.this, Reminder.class);
              startActivity(reminder_Intent);
              break;
  
          case  R.id.exit:
              finish();
              break;
      }
      return true;
  }
  
  protected void onListItemClick(ListView l, View v, int position, long id) {
      final BluetoothDevice device = mLeDeviceListAdapter.getDevice(position);
      if (device == null) return;
      final Intent intent = new Intent(this, DeviceControlActivity.class);
      intent.putExtra(DeviceControlActivity.EXTRAS_DEVICE_NAME, device.getName());
      intent.putExtra(DeviceControlActivity.EXTRAS_DEVICE_ADDRESS, device.getAddress());
      if (mScanning) {
          btAdapter.stopLeScan(leScanCallback);
          mScanning = false;
      }
      startActivity(intent);
  }
  
  
  private BluetoothAdapter.LeScanCallback leScanCallback = new BluetoothAdapter.LeScanCallback() {
      @Override
      public void onLeScan(final BluetoothDevice device, final int rssi, byte[] scanRecord) {
          runOnUiThread(new Runnable() {
              @Override
              public void run() {
                  mLeDeviceListAdapter.addDevice(device, rssi);
                  Log.i("DEBUG-ScanCallBack", "add device");
                  mLeDeviceListAdapter.notifyDataSetChanged();
                  Log.i("DEBUG-ScanCallBack", "notify data set changed");
  
              }
          });
      }
  };
  
  
  private void scanLeDevice(final boolean enable) {
      if (enable) {
          Log.i("DEBUG_TOAST", "Scanning enabled!");
  
          // Stops scanning after a pre-defined scan period.
          myHandler.postDelayed(new Runnable() {
              @Override
              public void run() {
                  mScanning = false;
                  Log.i("DEBUG_TOAST", "scanLeEnable_mScanning = 0");
                  btAdapter.stopLeScan(leScanCallback);
                  invalidateOptionsMenu();
              }
          }, SCAN_INTERVAL_MS_1);
  
          mScanning = true;
          Log.i("DEBUG_TOAST", "scanLeEnable_mScanning = 1");
  
          btAdapter.startLeScan(leScanCallback);
  
          new CountDownTimer(5000, 1000) {
              public void onFinish() {
                  // When timer is finished
                  // Execute your code here
  
                  myHandler.postDelayed(new Runnable() {
                      @Override
                      public void run() {
  
                          mLeDeviceListAdapter.clear();
                          Log.i("DEBUG_TOAST", "List Adapter cleared scan handler");
  
                          mScanning = true;
                          Log.i("DEBUG_TOAST", "scanLeEnable_mScanning = 1 (Timer)");
                          btAdapter.startLeScan(leScanCallback);
                          invalidateOptionsMenu();
                          scanLeDevice(true);
                      }
                  }, SCAN_INTERVAL_MS_1);  }
  
              public void onTick(long millisUntilFinished) {
                  // millisUntilFinished    The amount of time until finished.
              }
          }.start();
  
      } else {
  
          mScanning = false;
          Log.i("DEBUG_TOAST", "scanLeNotEnable_mScanning = 0");
  
          btAdapter.stopLeScan(leScanCallback);
      }
      invalidateOptionsMenu();
  }
  
  //Adapter for holding devices found through scanning
  private class LeDeviceListAdapter extends BaseAdapter {
      private ArrayList&lt;BluetoothDevice&gt; mLeDevices;
      private LayoutInflater mInflator;
      private final HashMap&lt;BluetoothDevice, Integer&gt; rssiMap = new HashMap&lt;BluetoothDevice, Integer&gt;();
  
      public LeDeviceListAdapter() {
          super();
          mLeDevices = new ArrayList&lt;BluetoothDevice&gt;();
          Log.i("LISTVIEW_1", "z" );
          mInflator = MainActivity.this.getLayoutInflater();
          Log.i("LISTVIEW_1", "a" );
      }
  
      public void addDevice(BluetoothDevice device, int rssi) {
          if (!mLeDevices.contains(device)) {
              mLeDevices.add(device);
              Log.i("LISTVIEW_1", "b");
              rssiMap.put(device, rssi);
              Log.i("LISTVIEW_1", "b1");
          }
          else {
              rssiMap.put(device, rssi);
              Log.i("LISTVIEW_1", "b2");
          }
      }
  
      public BluetoothDevice getDevice(int position) {
          Log.i("LISTVIEW_1", "c");
          return mLeDevices.get(position);
      }
  
      public void clear() {
          Log.i("LISTVIEW_1", "d" );
          mLeDevices.clear();
      }
  
      @Override
      public int getCount() {
          Log.i("LISTVIEW_1", "e" + mLeDevices.size());
          return mLeDevices.size();
      }
  
      @Override
      public Object getItem(int i) {
          Log.i("LISTVIEW_1", "f" );
          return mLeDevices.get(i);
      }
  
      @Override
      public long getItemId(int i) {
          Log.i("LISTVIEW_1", "g" );
          return i;
      }
  
      public View getView(int position, View convertView, ViewGroup parent){
          ViewHolder viewHolder;
  
          if (convertView == null){
              mInflator =
                      (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
              convertView = mInflator.inflate(R.layout.listitem_device, null);
              Log.i("LISTVIEW_1", "0" );
              viewHolder = new ViewHolder();
              viewHolder.deviceAddress = (TextView) convertView.findViewById(R.id.device_address);
              Log.i("LISTVIEW_1", "1" );
              viewHolder.deviceName = (TextView) convertView.findViewById(R.id.device_name);
              Log.i("LISTVIEW_1", "2" );
              viewHolder.deviceRssi = (TextView) convertView.findViewById(R.id.device_rssi);
              Log.i("LISTVIEW_1", "2.5" );
              convertView.setTag(viewHolder);
              Log.i("LISTVIEW_1", "3");
          }
          else{
              viewHolder = (ViewHolder) convertView.getTag();
              Log.i("LISTVIEW_1", "4" );
          }
          BluetoothDevice device = mLeDevices.get(position);
          Log.i("LISTVIEW_1", "5" );
          final String deviceName = device.getName();
          Log.i("LISTVIEW_1", "6");
          final int deviceRSSI = rssiMap.get(device);
          Log.i("LISTVIEW_1", "6.1");
  
          if(deviceName != null &amp;&amp; deviceName.length() &gt; 0 ) {
              if (deviceRSSI &gt;= -50 &amp;&amp; deviceName.equals("CC2650 SensorTag") ||
                      deviceName.equals("SimpleBLEBroadcaster")) {
  
                  viewHolder.deviceName.setText(deviceName);
  
                  NotificationManager notifier = (NotificationManager)
                          getSystemService(Context.NOTIFICATION_SERVICE);
  
                  NotificationCompat.Builder notifyBuilder = new
                          NotificationCompat.Builder(getApplicationContext());
  
                  notifyBuilder.setSmallIcon(R.drawable.hand_sanitise_image);
                  notifyBuilder.setTicker("Attention!");
                  notifyBuilder.setWhen(System.currentTimeMillis());
  
                  Intent toLaunch = new Intent(MainActivity.this,
                          Reminder.class);
                  notifyBuilder.setContentIntent(PendingIntent.getActivity(
                          MainActivity.this, 0, toLaunch, 0));
                  notifyBuilder.setContentTitle("Take a look!");
                  notifyBuilder.setContentText("Will benefit everyone!!! (" + mNotificationCount++
                          + ")");
  
                  notifyBuilder.setAutoCancel(true);
                  notifyBuilder.setVibrate(new long[]{0, 200, 200, 600, 600});
                  notifier.notify(NOTIFY_1, notifyBuilder.build());
  
                  Log.i("LISTVIEW_1", "Notification Vibrate Success");
  
  
  
                  mScanning = false;
                  Log.i("DEBUG_TOAST", "scanLeEnable_mScanning = 0 (Notification)");
                  btAdapter.stopLeScan(leScanCallback);
                  invalidateOptionsMenu();
  
                  new CountDownTimer(10000, 1000) {
                      public void onFinish() {
                          // When timer is finished
                          // Execute your code here
  
                          mLeDeviceListAdapter.clear();
                          Log.i("LISTVIEW_1", "List Adapter cleared due to notification");
                          scanLeDevice(true);
                          Log.i("LISTVIEW_1", "CountDownTimer Success!");
  
                      }
  
                      public void onTick(long millisUntilFinished) {
                          // millisUntilFinished    The amount of time until finished.
                      }
                  }.start();
              }
  
  
              viewHolder.deviceName.setText(deviceName);
              Log.i("LISTVIEW_1", "8.1");
              viewHolder.deviceAddress.setText(device.getAddress());
              Log.i("LISTVIEW_1", "9.1");
              viewHolder.deviceRssi.setText("" + rssiMap.get(device) + " dBm");
              Log.i("LISTVIEW_1", "10.1");
          }
          else
              viewHolder.deviceName.setText(R.string.unknown_device);
          Log.i("LISTVIEW_1", "8");
          viewHolder.deviceAddress.setText(device.getAddress());
          Log.i("LISTVIEW_1", "9");
          viewHolder.deviceRssi.setText(""+rssiMap.get(device)+" dBm");
          Log.i("LISTVIEW_1", "10");
          return convertView;
      }
  
  }
  static class ViewHolder {
      TextView deviceRssi, deviceAddress, deviceName;
  }
  </code></pre>
  
  <p>}</p>