简体   繁体   中英

Android Bluetooth LE scanner on foreground service stops immediately when screen turns off

I have the following BLE foreground service implementation -

public class BluetoothForegroundService extends Service {

    private PowerManager.WakeLock wakeLock;

    @Override
    public void onCreate() {
        super.onCreate();
        PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
        wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK , this.getClass().getSimpleName());
        wakeLock.acquire();
    }


    @Override
    public void onDestroy() {
        super.onDestroy();
        wakeLock.release();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        super.onStartCommand(intent, flags, startId);
        Intent notificationIntent = new Intent(this, MainActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);

        Notification notification = new NotificationCompat.Builder(this, "BLE test")
                .setContentTitle("Bluetooth Service")
                .setContentText("Fetching Bluetooth")
                .setSmallIcon(R.drawable.ic_launcher)
                .setContentIntent(pendingIntent)
                .build();

        startForeground(1, notification);

        BluetoothScanner bluetoothScanner = BluetoothScanner.getInstance(new BluetoothScanner.OnBlePacketBuilderListener() {
            @Override
            public void onBleObjectsCreated(BleBeaconPacketData beaconPacketData, BleHardwarePacketData bleBroadcastPacketData) {
                if (bleBroadcastPacketData == null && beaconPacketData == null) return;
                Log.d("onBleObjectsCreated", "onBleObjectsCreated");
                Intent intent = new Intent("action");
                if (beaconPacketData != null)
                    intent.putExtra("BleBeaconPacketData", beaconPacketData);
                if (bleBroadcastPacketData != null)
                    intent.putExtra("BleHardwarePacketData", bleBroadcastPacketData);
                sendBroadcast(intent);
            }
        });
        bluetoothScanner.startBluetooth();

        return START_STICKY;
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}
public class BluetoothScanner implements BluetoothAdapter.LeScanCallback, BlePacketBuilder.BlePacketBuilderListener {

    interface OnBlePacketBuilderListener{
        void onBleObjectsCreated(BleBeaconPacketData beaconPacketData, BleHardwarePacketData bleBroadcastPacketData);
    }

    private final OnBlePacketBuilderListener listener;
    private BluetoothAdapter bluetoothAdapter;
    public static BluetoothScanner instance;

    private BluetoothScanner(OnBlePacketBuilderListener listener) {
        this.listener = listener;
    }

    public static BluetoothScanner getInstance(OnBlePacketBuilderListener listener) {
        if (instance == null)
            instance = new BluetoothScanner(listener);
        return instance;
    }

    public void startBluetooth() {
        BluetoothManager bluetoothManager = MainApplication.getPureTrackApplicationContext().getSystemService(BluetoothManager.class);
        if ((bluetoothManager != null ? bluetoothManager.getAdapter() : null) == null) return;
        bluetoothAdapter = bluetoothManager.getAdapter();
        bluetoothAdapter.startLeScan(this);
    }

    public void stopBluetooth() {
        if (bluetoothAdapter == null) return;
        bluetoothAdapter.stopLeScan(this);
    }

    @Override
    public void onLeScan(BluetoothDevice bluetoothDevice, int rssi, byte[] scanRecord) {
        Log.d("BluetoothScanner", "New BLE - " + Arrays.toString(scanRecord));
        BlePacketBuilder blePacketBuilder = new BlePacketBuilder(this);
        blePacketBuilder.parseResult(bluetoothDevice, rssi, scanRecord);
    }


    @Override
    public void onfinishedToParseResults(BleBeaconPacketData beaconPacketData, BleHardwarePacketData bleBroadcastPacketData) {
        listener.onBleObjectsCreated(beaconPacketData, bleBroadcastPacketData);
    }
}

And on my MainActivity I am receiving the broadcast -

private void getBluetoothDataFromForegroundService() {
        registerReceiver(new BluetoothScannerReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                if (powerManager == null) return;
                Log.d("onReceiveBle", "onReceiveBle");

                //At this point data is handled both for UI and for network calls 
            }
        }, new IntentFilter("action"));
    }

The thing is that I need the BLE to keep sampling data even when the screen is off forever. This is an internal product so I do not care about battery consumption or anything that normal applications users would be worried about. I just need to sample the BLE forever and handle the data accordingly with network calls.

I tried to use WakeLock and it did not help. I am not gettings logs when the screen shuts down and gettings the logs back when the screen turns on back. What is it that I am missing for the BLE to keep sampling?

I think you are lacking some things.

Android 10 permissions
On Android 10, you generally must have obtained ACCESS_BACKGROUND_LOCATION for beacon detection to work when your app is not visible. However, it is possible to scan with only foreground location permission granted and a foreground service if you add android:foregroundServiceType="location" to your foreground service declaration in the manifest. See here for details.

So you need ACCESS_BACKGROUND_LOCATION in order to get scanning results when the screen is off in A10+.

Also, has introduced in A8, (you can see it here ), there is a limit of 10 minutes before scan function stops. So I recommend you to use a JobScheduler or a Runnable to make a 9 minutes timer (or 10) and reset the scan. That way you get a long scanning service. This is an example:

 mHandlerUpdate?.removeCallbacksAndMessages(null)

        mHandlerUpdate?.postDelayed(object : Runnable {
            override fun run() {
                Log.d(BleService::class.java.simpleName, "run: in runnable handler")
                scanner.stopScan(mScanCallback)

                scanner.startScan(
                    filters,
                    settings,
                    mScanCallback
                )
                mHandlerUpdate?.postDelayed(this, 60000)
            }
        }, 60000)

The handler above resets the scanning every 60 seconds. Is just a demo code. You can modify accoriding to your needs. Remember to removeCallbacksAndMessages when you are done with it.

Also, it is necessary to have Location Services on in order to get results. You can see the why here .

Some other links of interests..

Chet talk

Develpers Android Bt Page

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM