Android BLE Scan in the background

I am trying to make an android app that scans a specific bluetooth device as a background service. After the phone is in a certain range of the Bluetooth device, measured by reading RSSI, the background service will start the action displayed to the user. If the phone is moved outside the range of the Bluetooth device (after the RSSI value exceeds a certain threshold), activity should be killed.

Here is the code for the help desk:

public class BeaconScanService extends Service implements BluetoothAdapter.LeScanCallback { private Service self = this; public static final String UNLOCK = "unlock"; public static final String STATUS = "status"; public static final String SIGNAL = "signal"; public static final String GET_SIGNAL = "get_signal"; //desired device to find private static final String targetMAC = "E1:BE:A8:1A:8B:A0"; //class to handle saving RSSI values and detecting Bluetooth proximity private proxDetector proxy; //boolean to determine if phone is in BT range static boolean inProx = false; private BluetoothAdapter mBluetoothAdapter; @Override public IBinder onBind(Intent intent) { // TODO Auto-generated method stub return null; } @Override public void onCreate() { super.onCreate(); //Proximity Detector proxy = new proxDetector(); final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); mBluetoothAdapter = bluetoothManager.getAdapter(); } @Override public int onStartCommand(Intent intent, int flags, int startId){ /* * We need to enforce that Bluetooth is first enabled, and take the * user to settings to enable it if they have not done so. */ if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) { //Bluetooth is disabled Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); enableBtIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(enableBtIntent); } /* * Check for Bluetooth LE Support. In production, our manifest entry will keep this * from installing on these devices, but this will allow test devices or other * sideloads to report whether or not the feature exists. */ if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { Toast.makeText(this, "No LE Support.", Toast.LENGTH_SHORT).show(); return START_STICKY; } //Begin scanning for LE devices startScan(); return START_STICKY; } @Override public void onDestroy() { super.onDestroy(); //Cancel any scans in progress mHandler.removeCallbacks(mStopRunnable); mHandler.removeCallbacks(mStartRunnable); mBluetoothAdapter.stopLeScan(this); } private Runnable mStopRunnable = new Runnable() { @Override public void run() { stopScan(); } }; private Runnable mStartRunnable = new Runnable() { @Override public void run() { startScan(); } }; private void startScan() { Toast.makeText(this, "Scanning", Toast.LENGTH_SHORT).show(); //Scan for Bluetooth device with specified MAC mBluetoothAdapter.startLeScan(this); mHandler.postDelayed(mStopRunnable, 5000); } private void stopScan() { Toast.makeText(this, "Not Scanning", Toast.LENGTH_SHORT).show(); mBluetoothAdapter.stopLeScan(this); mHandler.postDelayed(mStartRunnable, 2500); } /* BluetoothAdapter.LeScanCallback */ @Override public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) { /* * Create a new beacon and pass it up to the main thread */ Toast.makeText(self, "OnLeScan", Toast.LENGTH_SHORT).show(); BT_Beacon beacon = new BT_Beacon(device.getAddress(), rssi); mHandler.sendMessage(Message.obtain(null, 0, beacon)); } /* * We have a Handler to process scan results on the main thread */ private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { Toast.makeText(self, "Handlering", Toast.LENGTH_SHORT).show(); BT_Beacon beacon = (BT_Beacon) msg.obj; if(beacon.getAddress().equals(targetMAC)){//Only look for target device Toast.makeText(self, String.format("%ddBm", beacon.getSignal()), Toast.LENGTH_SHORT).show(); //HANDLE PROXIMITY DETECTION Intent i1 = new Intent(UNLOCK); proxy.processProx(beacon.getSignal()); Intent i2 = new Intent(SIGNAL); i2.putExtra(GET_SIGNAL, proxy.prox); sendBroadcast(i2); if(proxy.crossedLine()){ i1.putExtra(STATUS, 1); sendBroadcast(i1); inProx = true; Intent i = new Intent(self,MyActivity.class); i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(i); }else{ i1.putExtra(STATUS, 0); sendBroadcast(i1); inProx = false; } } } }; } 

Ultimately, I want this background service to start at boot, but for testing purposes, I can create or destroy this service from Activity, which should also display an RSSI value:

 public class MainActivity extends Activity implements OnClickListener { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button btnScan = (Button) findViewById(R.id.btnScan); Button btnUnscan = (Button) findViewById(R.id.btnUnscan); btnScan.setOnClickListener(this); btnUnscan.setOnClickListener(this); } @Override protected void onResume() { super.onResume(); registerReceiver(mReceiver, new IntentFilter(BeaconScanService.SIGNAL)); } @Override protected void onPause() { super.onPause(); unregisterReceiver(mReceiver); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.btnScan: Intent i = new Intent(this,BeaconScanService.class); startService(i); break; case R.id.btnUnscan: Intent i = new Intent(this,BeaconScanService.class); stopService(i); break; } } private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { Bundle bundle = intent.getExtras(); if(bundle != null) { int sig = bundle.getInt(BeaconScanService.GET_SIGNAL); TextView rssiView = (TextView) findViewById(R.id.text_rssi); rssiView.setText(String.format("%ddBm", sig)); } } }; } 

This is the layout for MainActivity:

 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity"> <Button android:id="@+id/btnScan" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Scan"/> <Button android:id="@+id/btnUnscan" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Stop Scan" android:layout_below="@+id/btnScan"/> <TextView android:id="@+id/text_rssi" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:textAppearance="?android:attr/textAppearanceListItem" android:text="dBm" android:layout_below="@+id/btnUnscan"/> </RelativeLayout> 

In an activity that starts and ends with BeaconScanService, the BroadcastReceiver is logged as follows:

 @Override protected void onResume() { super.onResume(); registerReceiver(beaconScanReceiver, new IntentFilter(BeaconScanService.UNLOCK)); } 

and BroadcastReceiver looks like this:

 private final BroadcastReceiver beaconScanReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { Bundle bundle = intent.getExtras(); if(bundle != null) { int status = bundle.getInt(BeaconScanService.STATUS); if(status == 0) { finish(); } } } }; 

When I run this code, I can start and stop the BeaconScanService, and I know that the service scans the Bluetooth device due to debugging toasts. As far as I can tell, the code breaks somewhere in the service handler. RSSI is not displayed in the TextView, and MyActivity does not start when the phone approaches the Bluetooth device.

I'm not too good at aspects of the interaction between processes in Android programming, so I'm probably doing something wrong. Any thoughts?

+6
source share

Source: https://habr.com/ru/post/971400/


All Articles