Warm tip: This article is reproduced from serverfault.com, please click

Delphi android : Scanning Bluetooth LE devices does not work while screen is off

发布于 2020-12-08 12:41:42

I have an application scanning BluetoothLE devices (temperature). The application is whitelisted in the list for battery optimizations and uses a PARTIAL_WAKE_LOCK.

It works like a charm with a Zebra TC25 Android 7.1.2 using a thread scanning BLE devices every 10 minutes independently of the android device state.

However the same application does not work at all with a samsung galaxy S10+ Android 10 when the screen is off (it works when screen is on). I know that the simple thread method will not work because of doze mode. So I also tried these ways :

setExactAndAllowWhileIdle(TJAlarmManager.JavaClass.RTC_WAKEUP, time,PendingIntent) and firebase cloud messaging sending high priority message. Alarm method has limitations but fcm has not as far as I know.

These 2 ways also failed. The fcm message is received and the alarm triggers, both run the BLEDiscover procedure below to run a bluetooth scan, but no bluetooth device are discovered.

Here is what happens in two situations. In the fisrt one, the screen is on, it always works :

12-08 12:39:58.994: D/BluetoothAdapter(30351): STATE_ON
12-08 12:39:58.994: D/BluetoothLeScanner(30351): could not find callback wrapper
12-08 12:39:58.995: D/BluetoothAdapter(30351): STATE_ON
12-08 12:39:58.997: D/BluetoothAdapter(30351): STATE_ON
12-08 12:39:58.997: D/BluetoothLeScanner(30351): Start Scan with callback
12-08 12:39:59.001: D/BluetoothLeScanner(30351): onScannerRegistered() - status=0 scannerId=14 mScannerId=0
12-08 12:39:59.965: I/info(30351): FMX: tags (Nil): 4154326484 - BLEDiscoverLEDevice - device touvé : C_T_801362
12-08 12:39:59.977: I/info(30351): FMX: tags (Nil): 4154326484 - BLEDiscoverLEDevice - TAG touvé : C_T_801362
12-08 12:39:59.977: I/info(30351): FMX: tags (Nil): 4154326484 - Trame 6E2A7207 - T° 19,06
12-08 12:39:59.983: I/info(30351): FMX: alerte (Nil): 4154326484 - BLEAlerte
12-08 12:40:04.928: I/info(30351): FMX: tags (Nil): 4154326484 - BLEDiscoverLEDevice - device touvé : P_T_8038DB
12-08 12:40:04.948: I/info(30351): FMX: tags (Nil): 4154326484 - BLEDiscoverLEDevice - TAG touvé : P_T_8038DB
12-08 12:40:04.948: I/info(30351): FMX: tags (Nil): 4154326484 - Trame 6E2A6C07 - T° 19,00
12-08 12:40:04.955: I/info(30351): FMX: alerte (Nil): 4154326484 - BLEAlerte
12-08 12:40:19.038: D/BluetoothAdapter(30351): STATE_ON
12-08 12:40:19.038: D/BluetoothLeScanner(30351): Stop Scan with callback

As soon as the screen is off, it never works:

12-08 12:44:02.094: D/BluetoothAdapter(30351): STATE_ON
12-08 12:44:02.094: D/BluetoothLeScanner(30351): could not find callback wrapper
12-08 12:44:02.096: D/BluetoothAdapter(30351): STATE_ON
12-08 12:44:02.098: D/BluetoothAdapter(30351): STATE_ON
12-08 12:44:02.098: D/BluetoothLeScanner(30351): Start Scan with callback
12-08 12:44:02.101: D/BluetoothLeScanner(30351): onScannerRegistered() - status=0 scannerId=10 mScannerId=0
12-08 12:44:23.110: D/BluetoothAdapter(30351): STATE_ON
12-08 12:44:23.110: D/BluetoothLeScanner(30351): Stop Scan with callback

So thread, alarm and fcm lead to the same result, that is when screen is off, no BLE devices are discoverd.

Here is the code :

// Discovering BLE devices
procedure TfData.BLEDiscover();
begin
    FBluetoothManagerLE := TBluetoothLEManager.Current;
    FBluetoothManagerLE.OnDiscoverLEDevice := BLEDiscoverLEDevice;
    FBluetoothManagerLE.StartDiscovery(10000);
end;
 

procedure TfData.BLEDiscoverLEDevice(const Sender: TObject; const ADevice: TBluetoothLEDevice; Rssi: Integer; const ScanResponse: TScanResponse);
var tag, i: Integer;
    vals : TArray<Byte>;
    trame : string;
    temperature : single;
begin
  log('tags', 'BLEDiscoverLEDevice - device touvé : ' + ADevice.DeviceName);
  lockTags.Acquire;
  if ADevice.DeviceName <> '' then begin
    for tag := Low(Tags) to High(Tags) do begin
      if (ADevice.DeviceName = Tags[tag].K_TAG) then begin
          log('tags', 'BLEDiscoverLEDevice - TAG touvé : ' + ADevice.DeviceName);
          trame := '';
          vals := ADevice.AdvertisedData.ExtractPair(TScanResponseKey.ServiceData).Value;
          for i  := 0 to length(vals) - 1 do begin
            trame := trame + IntToHex(vals[i]);
          end;
          temperature:=BLEReadTrame(trame);
          log('tags', 'Trame '+trame + ' - T° '+ formatFloat('0.00',temperature));
          if (temperature <> tagNullTemp) then begin
            Tags[tag].T_LAST := temperature;
            Tags[tag].DtLastTemp := now;
          end;
        end;
        break;
      end;
    end;
  end;
  lockTags.Release;
end;

Edit : Please note I use fcm method to get real time gps position, so the device gets GPS and sends it to server over Internet, it always works , even screen off.

Questioner
user2244705
Viewed
0
user2244705 2020-12-12 16:51:30

I tried using a foreground service, I get exactly the same result : Screen on, service background, the discover works. Screen off, service foreground, discover does not work at all cause the call back function is never called. I guess the problem is Samsung hardware, not Android. Maybe Samsung disables Bluetooth scan when screen is off. That's stupid, particularly for bluetooth "LE". The tags (for temperature measure) I use are as big as a coin but have 5 to 15 years autonomy sending bluetooth trames every 3 seconds. So even if bluetooth scan consumes a bit more energy (not sure), it can not be a battery drainer assuming you scan everys 5 or 10 minutes. Not more than a location service. Note that in France we have an application "Anticovid" based on bluetooth... probably it does not work at all with Samsung phones as long as theses phones screens stay off ?

Edit : I found a workaround using this post Android: How to turn screen on and off programmatically?

WakeLock := PowerManager.newWakeLock(TJPowerManager.JavaClass.SCREEN_BRIGHT_WAKE_LOCK or TJPowerManager.JavaClass.ACQUIRE_CAUSES_WAKEUP, StringToJString('myapp'));

Before calling the bluetooth scan, I call this code, it turns on the screen for some seconds, enough to scan BLE devices, it works fine. So one solution is to check whether the device is able to scan with screen off and to activate this code only for devices unable to scan.

Not already sure it's a good way, but it's the only one in my case.