Android Question BLE Android 9 - Scanning stops when screen turned off

Jmu5667

Well-Known Member
Licensed User
Longtime User
Hello

We are working with these devices, https://store.kontakt.io/next-generation/43-asset-tag-s18-3.html.

We have a service that runs and is scanning:

B4X:
#Region  Service Attributes
   #StartAtBoot: False
  
#End Region

Sub Process_Globals
   'These global variables will be declared once when the application starts.
   'These variables can be accessed from all modules.

   Private manager As BleManager2
   Private currentStateText As String = "UNKNOWN"
   Private currentState As Int
   Private scanning As Boolean
   Private bc As ByteConverter
      
   Private const TIME_TO_LIVE_SECONDS As Int = 30
  
   Private Timer1 As Timer
   Private rp As RuntimePermissions
   Private ManagerJO As JavaObject
   Private Adapter As JavaObject
   Private Scanner As JavaObject
   Private ScanCallback As JavaObject


End Sub

Sub Service_Create

   manager.Initialize("manager")
   ManagerJO = manager
   Adapter = ManagerJO.GetField("blueAdapter")
   Scanner = Adapter.RunMethod("getBluetoothLeScanner", Null)
   ScanCallback.InitializeNewInstance(Application.PackageName & ".svc_ble_sos_le$MyScanCallback", Null)
  
   Timer1.Initialize("hook",5000)

End Sub

Sub Service_Start (StartingIntent As Intent)
   Timer1.Enabled = True
  
   StartScan_le
  
  
End Sub

Sub Service_Destroy
  
   StopScanWithLeScanner
  
   Timer1.Enabled = False
   

End Sub

Sub hook_tick
  
   mod_functions.writelog("hook_tick() Still Running")
  
End Sub

Sub Manager_StateChanged (State As Int)
   Select State
       Case manager.STATE_POWERED_OFF
           currentStateText = "POWERED OFF"
       Case manager.STATE_POWERED_ON
           currentStateText = "POWERED ON"
       Case manager.STATE_UNSUPPORTED
           currentStateText = "UNSUPPORTED"
   End Select
   currentState = State

End Sub

Sub Manager_DeviceFound (Name As String, Id As String, AdvertisingData As Map, RSSI As Double)
  
   
  
End Sub

' // based on this answer: http://stackoverflow.com/questions/20416218/understanding-ibeacon-distancing
Private Sub CalculateDistance(tx As Int, rssi As Double) As Double
   If rssi = 0 Then Return -1
   Dim ratio As Double = rssi / tx
   If ratio < 1 Then
       Return Power(ratio, 10)
   Else
       Return 0.89976 * Power(ratio, 7.7095) + 0.111
   End If
End Sub

#region "LE Scanner"

Public Sub StartScan_le
   If manager.State <> manager.STATE_POWERED_ON Then
       Log("Not powered on.")
   Else If rp.Check(rp.PERMISSION_ACCESS_COARSE_LOCATION) = False Then
       Log("No location permission.")
   Else
       ScanWithLeScanner
   End If
End Sub


Private Sub Scan_Result (Result As Object)
  

   Dim ScanResult As JavaObject = Result
   Dim device As JavaObject = ScanResult.RunMethod("getDevice", Null)
   Dim address As String = device.RunMethod("getAddress", Null)
  
  
  
   ManagerJO.GetFieldJO("devices").RunMethod("put", Array(address, device))

   Dim name As String
   Dim o As Object = device.RunMethod("getName", Null)
   If o = Null Then name = "" Else name = o
   Dim ScanRecord As JavaObject = ScanResult.RunMethod("getScanRecord", Null)
   'Log(ScanRecord) 'you can extend the code to access the data.
   'https://developer.android.com/reference/android/bluetooth/le/ScanRecord
   Dim rssi As Double = ScanResult.RunMethod("getRssi", Null)
  
   log("svc_ble_sos_le() Scan_Result > name : " & name & ", rssi : " & rssi & ", " & address)
  
' // not called yet as screen off issue
'   Manager_DeviceFound(name, address, CreateMap(), rssi)
  
End Sub

Private Sub ScanWithLeScanner
   Dim ScanSettingsStatic As JavaObject
   ScanSettingsStatic.InitializeStatic("android.bluetooth.le.ScanSettings")
   Dim ScanSettingsBuilder As JavaObject
   ScanSettingsBuilder.InitializeNewInstance("android.bluetooth.le.ScanSettings.Builder", Null)
   'https://developer.android.com/reference/android/bluetooth/le/ScanSettings.Builder
   ScanSettingsBuilder.RunMethod("setScanMode", Array(ScanSettingsStatic.GetField("SCAN_MODE_LOW_POWER")))
  
   Scanner.RunMethod("startScan", Array(Null, ScanSettingsBuilder.RunMethod("build", Null), ScanCallback))
End Sub

Private Sub StopScanWithLeScanner
   Scanner.RunMethod("stopScan", Array(ScanCallback))
End Sub


#End Region


#if Java
import android.bluetooth.le.*;
public static class MyScanCallback extends ScanCallback {
 public void onScanResult(int callbackType, ScanResult result) {
         processBA.raiseEvent(this, "scan_result", result);
    }

  

    /**
     * Callback when scan could not be started.
     *
     * @param errorCode Error code (one of SCAN_FAILED_*) for scan failure.
     */
    public void onScanFailed(int errorCode) {
       BA.Log("Error: " + errorCode);
    }
}
#End If

We have noticed that on Android 9 when the screen is turned off no more Scan_Result events are fired, and when the screen is turn back on the scan results resume.

We were using the standard Ble method that Erel provided, https://www.b4x.com/android/forum/threads/ble-2-bluetooth-low-energy.59937/#content

We switched to the above code example to see if it made a difference, the result was the same. It functioned fine up to Android 8.

Regards

John.
 

Jmu5667

Well-Known Member
Licensed User
Longtime User
Is the service a foreground service? Are you acquiring a partial wake lock?

Example: https://www.b4x.com/android/forum/threads/99873/#content
No it is not a foreground service and there is not partial wake lock. I have a timer in the service that is serviced when the screen is off. There is a main service that is a foreground service and has a partial wake lock.

Did I not read some where that if there was a foreground service that other service would inherit these properties ?
 
Upvote 0

Erel

B4X founder
Staff member
Licensed User
Longtime User
Did I not read some where that if there was a foreground service that other service would inherit these properties ?
That's true. You should also check the logs and make sure that the service is not destroyed.
Check the unfiltered logs. Might include more information.

Are you limiting the BLE scanning with a filter list? Seems that it is required for background scanning: https://github.com/AltBeacon/android-beacon-library/issues/633
 
Upvote 0

Jmu5667

Well-Known Member
Licensed User
Longtime User
That's true. You should also check the logs and make sure that the service is not destroyed.
Check the unfiltered logs. Might include more information.

Are you limiting the BLE scanning with a filter list? Seems that it is required for background scanning: https://github.com/AltBeacon/android-beacon-library/issues/633


Yes, we want to limit it to 2 particular UUID's

1. "F7826DA64FA24E988024BC5B71E0893E" - this is the device advertising itself
2. "F7826DA64FA24E988024BC5B71E0893F" - this is same device but the button has been activated on it.

In the app configuration the user selects the device they are using, and we use the MAC address to identify the correct one.

I tried setting up the list in manager.Scan2(uuidlist, true) but did not get any devices. I downloaded an app fromt he app store Beacon Scanner, and it shows the UUID in lcase with the seperators, the above show in 1,2 are from the Blemanager2 library.

Regards

John.
 
Upvote 0

Jmu5667

Well-Known Member
Licensed User
Longtime User
In the service start:


B4X:
Sub Service_Start (StartingIntent As Intent)
   
   Dim l As List
   
   l.Initialize
   l.Add("f7826da6-4fa2-4e98-8024-bc5b71e0893e")
   l.Add("f7826da6-4fa2-4e98-8024-bc5b71e0893f")
   
   manager.Scan2(l, True)

End Sub

When this filtered list is applied I do not receive any Manager_DeviceFound events.

Is the above code correct ?
 
Upvote 0

Jmu5667

Well-Known Member
Licensed User
Longtime User
Are these the services uuids?
These are the only UUID's I can see. They are set here in the Kontakt dash board when you purchase them:

upload_2019-3-1_8-39-26.png



Here is the log data from the Manager_DeviceFound event:

B4X:
03/01/2019 08:42:38.163 - Atlas SOS - Manager_DeviceFound() ID =E7:FA:C1:5D:B5:04 
03/01/2019 08:42:38.169 - Atlas SOS - UUID F7826DA64FA24E988024BC5B71E0893E
03/01/2019 08:42:38.174 - Atlas SOS - Unique ID F7826DA64FA24E988024BC5B71E0893EAE68A9FC

Manager_DeviceFound function:

B4X:
Sub Manager_DeviceFound (Name As String, Id As String, AdvertisingData As Map, RSSI As Double)
   
   Dim key As Int = -1 'type is important. Must be Int.
   
   
   ' // NOTE: we will not process this is we do not have a selected MAC address
   mod_functions.writelog("Manager_DeviceFound() ID =" & Id)
   If AdvertisingData.ContainsKey(key) Then
       Dim b() As Byte = AdvertisingData.Get(key)
       If b.Length > 4 And b(0) = 0x4c And b(1) = 0 And b(2) = 0x02 And b(3) = 0x15 Then
           
           
           
           Dim beacon As Beacon
           beacon.Initialize
           Dim raf As RandomAccessFile
           raf.Initialize3(b, False)
           Dim hex As String = bc.HexFromBytes(b)
           beacon.uuid = hex.SubString2(8, 40) 'bytes 4 - 19
           beacon.uniqueid = hex.SubString2(8, 48) 'this also includes the major and minor parts
           beacon.major = raf.ReadShort(20)
           beacon.minor = raf.ReadShort(22)
           Dim tx As Byte = raf.ReadSignedByte(24)
           beacon.distance = CalculateDistance(tx, RSSI)
           beacon.time = DateTime.Now
           If Name = "" Then
               Name = "UNKNOWN "
           End If
           beacon.name = Name
           beacon.id = Id
           
           
           
           ' // trap a button press
           If (beacon.uuid  = u1 Or beacon.uuid  = u2) And beacon.id = Main.APPSET.bleSOS.MacAddress  Then               
               mod_functions.writelog("UUID " & beacon.uuid)
               mod_functions.writelog("Unique ID " & beacon.uniqueid)
           
               
               ' // we have found a button press already
               If Main.APPSET.bleSOS.Foundtime <> 0 Then
                   ' // this is not press, which is what we want
                   If beacon.uuid  <> u2 Then
                       ' // get the time period between now and the last time we got an 3F UUID
                       Dim dP As Period =  DateUtils.PeriodBetween(Main.APPSET.bleSOS.Foundtime, DateTime.Now)
                       ' // reset the u2 button press, if it was more then 2 seconds ago
                       If dP.Seconds > 2 Then
                           Main.APPSET.bleSOS.Foundtime = 0
                           Main.APPSET.bleSOS.PressTime = 0
                           Main.APPSET.bleSOS.StartNow = False
                           mod_functions.writelog("Button Pressed Reset")
                           Dim pv As PhoneVibrate
                           pv.Vibrate(250)
                           ToastMessageShow("BLE SOS BUTTON READY", True)
                       End If
                   End If
               End If
               
               
               ' // this a putton press UUID - 3F UUID
               If beacon.uuid  = u2 Then
                   ' // record the last time we got a button press
                   Main.APPSET.bleSOS.Foundtime = DateTime.now
                   ' // have we caught this already
                   If (Main.APPSET.bleSOS.PressTime = 0) And (Main.APPSET.alert_mode = 0) Then
                       mod_functions.writelog("Button Pressed")
                       Main.APPSET.bleSOS.PressTime = DateTime.now
                       Main.APPSET.bleSOS.StartNow = True
                       'Dim pw As PhoneWakeState
                       ' // turn the screen on
                       'pw.KeepAlive(True)
                       StartActivity(Main)                       
                   End If
                   
               End If
           End If

       End If
   End If
   
End Sub

Screen shot from beacon scanner to verity that i am using the correct UUID.

upload_2019-3-1_8-50-47.png


Am I sending in the correct UUID's ?

Regards

John.
 
Upvote 0

Jmu5667

Well-Known Member
Licensed User
Longtime User
I have tried using the following code :

B4X:
rivate Sub ScanWithLeScanner
   
   Dim ScanSettingsStatic As JavaObject
   ScanSettingsStatic.InitializeStatic("android.bluetooth.le.ScanSettings")
   Dim ScanSettingsBuilder As JavaObject
   ScanSettingsBuilder.InitializeNewInstance("android.bluetooth.le.ScanSettings.Builder", Null)
   'https://developer.android.com/reference/android/bluetooth/le/ScanSettings.Builder
   ScanSettingsBuilder.RunMethod("setScanMode", Array(ScanSettingsStatic.GetField("SCAN_MODE_LOW_POWER")))
   
   ' // https://www.b4x.com/android/forum/threads/ble-custom-scanfilter-device-settings.99300/#post-625276
   Dim ScanFilterStatic As JavaObject
   ScanFilterStatic.InitializeStatic("android.bluetooth.le.ScanFilter")
   Dim ScanFilterBuilder As JavaObject
   ScanFilterBuilder.InitializeNewInstance("android.bluetooth.le.ScanFilter.Builder", Null)


   ' // https://developer.android.com/reference/android/os/ParcelUuid.html
   Dim ParcelUuidStatic As JavaObject
   ParcelUuidStatic.InitializeStatic("android.os.ParcelUuid")
   Dim ParcelUuid As JavaObject
   ParcelUuid = ParcelUuidStatic.RunMethod("fromString",Array("f7826da6-4fa2-4e98-8024-bc5b71e0893e"))
   Log("LOOK HERE => " & ParcelUuid.RunMethod("toString",Null))
   
   'https://developer.android.com/reference/android/bluetooth/le/ScanFilter.Builder
   ScanFilterBuilder.RunMethod("setServiceUuid", Array(ParcelUuid))
   
   ' // MAC Address
   'ScanFilterBuilder.RunMethod("setDeviceAddress", Array(Main.APPSET.bleSOS.MacAddress))
   
   
   Dim Filters As List = Array(ScanFilterBuilder.RunMethod("build", Null))
   ' // start the scanning   
   Scanner.RunMethod("startScan", Array(Filters, ScanSettingsBuilder.RunMethod("build", Null), ScanCallback))
   
End Sub

It works using the Mac Address, but while I dump the scan record I see this :

B4X:
(ScanRecord) ScanRecord [mAdvertiseFlags=6, mServiceUuids=null, mManufacturerSpecificData={76=[2, 21, -9, -126, 109, -90, 79, -94, 78, -104, -128, 36, -68, 91, 113, -32, -119, 62, -82, 104, -87, -4, -77]}, mServiceData={}, mTxPowerLevel=-2147483648, mDeviceName=null]
(ScanRecord) ScanRecord [mAdvertiseFlags=6, mServiceUuids=null, mManufacturerSpecificData={76=[2, 21, -9, -126, 109, -90, 79, -94, 78, -104, -128, 36, -68, 91, 113, -32, -119, 62, -82, 104, -87, -4, -77]}, mServiceData={}, mTxPowerLevel=-2147483648, mDeviceName=null]
(ScanRecord) ScanRecord [mAdvertiseFlags=6, mServiceUuids=null, mManufacturerSpecificData={76=[2, 21, -9, -126, 109, -90, 79, -94, 78, -104, -128, 36, -68, 91, 113, -32, -119, 62, -82, 104, -87, -4, -77]}, mServiceData={}, mTxPowerLevel=-2147483648, mDeviceName=null]

I am not sure if I have set the ParcelUuidStatic correctly.

Regards

John.
 
Upvote 0

Erel

B4X founder
Staff member
Licensed User
Longtime User
A good explanation here: https://stackoverflow.com/a/50841523/971547

You need to connect to the beacon. You can use the BLE example project for this. See which services are listed. The next step is to check whether one or more of these services are advertised. If so then you will be able to use the service as a filter.
 
Upvote 0

Jmu5667

Well-Known Member
Licensed User
Longtime User
A good explanation here: https://stackoverflow.com/a/50841523/971547

You need to connect to the beacon. You can use the BLE example project for this. See which services are listed. The next step is to check whether one or more of these services are advertised. If so then you will be able to use the service as a filter.
Thanks, I will check it out
 
Upvote 0

Jmu5667

Well-Known Member
Licensed User
Longtime User
A good explanation here: https://stackoverflow.com/a/50841523/971547

You need to connect to the beacon. You can use the BLE example project for this. See which services are listed. The next step is to check whether one or more of these services are advertised. If so then you will be able to use the service as a filter.


Hi

I got a list of services from a different beacon, (same manufacturer, as I could not connect to the one we want to use) using the BLE Example, put that as filtered list in the our app that wants to background scan and did not yield any results in the foreground no the background.

I can see in the BLE example that the characteristics are listed, how are these interpreted ? Contact the Manufacturer ?


B4X:
Sub Service_Start (StartingIntent As Intent)
   
   Dim l As List
   
   l.Initialize
   l.Add("00001800-0000-1000-8000-00805f9b34fb")
   l.Add("00001801-0000-1000-8000-00805f9b34fb")
   l.Add("a1ea8130-0e1b-d4a1-b840-63f88c8da1ea")
   l.Add("00001804-0000-1000-8000-00805f9b34fb")
   l.Add("a1ea8110-0e1b-d4a1-b840-63f88c8da1ea")
   l.Add("a1ea8120-0e1b-d4a1-b840-63f88c8da1ea")
   l.Add("0000180a-0000-1000-8000-00805f9b34fb")
   l.Add("0000180f-0000-1000-8000-00805f9b34fb")
   
   manager.Scan2(l, True)

End Sub

Is is possible that none of these services are broadcasting ?

Regards

John.
 
Upvote 0

emexes

Expert
Licensed User
I can see in the BLE example that the characteristics are listed, how are these interpreted ? Contact the Manufacturer ?

The UUIDs that end in 0000-1000-8000-00805f9b34fb are Bluetooth SIG (Special Interest Group aka bluetooth.org now bluetooth.com) assigned standard (common) services per https://www.bluetooth.com/specifications/gatt/services

eg re: your list above are:

0x1800 = "Generic Access" information, device name and type/class, plus some connection stuff that I've never used
0x1801 = "Generic Attribute" changed services, also something I've never used
0x1804 = "Tx Power" -100 to +20 dB (tempered by the specific device's hardware, no doubt)
0x180a = "Device Information" manufacturer, model, serial number, revisions, etc.
0x180f = "Battery Service" battery level 0..100% (but watch out for steps > 1%)

Is is possible that none of these services are broadcasting ?

What's more likely is that none of the nearby devices has *ALL* of those services, and thus none of them make it past the 8-UUID filter you listed. Perhaps just limit the filter to one service that you *know* your device has (I think every device has 0x180a) to get it working first, and then fine-tune it to let through fewer false matches (ie, devices that aren't the ones you're looking for).

Having said that, I'm just off now to check whether the scan filter is must-have-at-least-one or must-have-all.
 
Last edited:
Upvote 0

Jmu5667

Well-Known Member
Licensed User
Longtime User
The UUIDs that end in 0000-1000-8000-00805f9b34fb are Bluetooth SIG (Special Interest Group aka bluetooth.org now bluetooth.com) assigned standard (common) services per https://www.bluetooth.com/specifications/gatt/services

eg re: your list above are:

0x1800 = "Generic Access" information, device name and type/class, plus some connection stuff that I've never used
0x1801 = "Generic Attribute" changed services, also something I've never used
0x1804 = "Tx Power" -100 to +20 dB (tempered by the specific device's hardware, no doubt)
0x180a = "Device Information" manufacturer, model, serial number, revisions, etc.
0x180f = "Battery Service" battery level 0..100% (but watch out for steps > 1%)



What's more likely is that none of the nearby devices has *ALL* of those services, and thus none of them make it past the 8-UUID filter you listed. Perhaps just limit the filter to one service that you *know* your device has (I think every device has 0x180a) to get it working first, and then fine-tune it to let through fewer false matches (ie, devices that aren't the ones you're looking for).

Having said that, I'm just off now to check whether the scan filter is must-have-at-least-one or must-have-all.
The list is just from 1 device
 
Upvote 0

emexes

Expert
Licensed User
I'm not having any success even just scanning for one service that I *know* exists on the Bluetooth sensor device sitting next to me.

The BLE2 help says of Scan2:
ServiceUUIDs - A list (or array) with service uuids. Devices that don't advertise these uuids will not be discovered.

and now I'm wondering if perhaps devices don't necessarily advertise all their services... which would seem a reasonable thing to allow, because otherwise a device with many characteristics would soon have a flat battery from repeatedly sending out ginormous advertising packets.
 
Upvote 0

Jmu5667

Well-Known Member
Licensed User
Longtime User
I'm not having any success even just scanning for one service that I *know* exists on the Bluetooth sensor device sitting next to me.

The BLE2 help says of Scan2:


and now I'm wondering if perhaps devices don't necessarily advertise all their services... which would seem a reasonable thing to allow, because otherwise a device with many characteristics would soon have a flat battery from repeatedly sending out ginormous advertising packets.

This is the same problem I have, the device that I want to use, only sends proximity UUID's not service UUID's as Erel pointed out, and the stack overflow article he pointed me to clearly state that you cannot background scan unless it is a service ID. The background scan works fine with no filter, just kill the battery.

Seems like a dead end at the moment.
 
Upvote 0

emexes

Expert
Licensed User
I don't have a beacon, but I'm trying it with a iTag key finder thingo. When it gets disconnected, it beeps, and so I have just set it off. Hunted around the room trying to locate where the beeping was coming from, couldn't find it, gave up, sat back down, then realised it was in my pocket.

Some of us take a little longer than others...

:)
 
Upvote 0

Jmu5667

Well-Known Member
Licensed User
Longtime User
I don't have a beacon, but I'm trying it with a iTag key finder thingo. When it gets disconnected, it beeps, and so I have just set it off. Hunted around the room trying to locate where the beeping was coming from, couldn't find it, gave up, sat back down, then realised it was in my pocket.

Some of us take a little longer than others...

:)
;)
 
Upvote 0

emexes

Expert
Licensed User
Ok, I've got that Scan2 thing going with a Service UUID filter. What I did was look through the advertising data of that device for one of its service UUIDs, and I was (momentarily) disappointed, but then I spotted a 16-bit byte-reversed match for one of the service UUIDs so I gave that a go and now we're in like Flynn.

This is the list of four services available when connecting to the device:

1000.000 Connect FF:FF:C0:20:9A:E5
Discovering services.
1003.579 Manager_Connected

00001800-0000-1000-8000-00805f9b34fb
0000180f-0000-1000-8000-00805f9b34fb
00001802-0000-1000-8000-00805f9b34fb
0000ffe0-0000-1000-8000-00805f9b34fb


and this is the advertising data received when scanning:

1002.841 Manager_DeviceFound FF:FF:C0:20:9A:E5 iTAG
Key: 1 = 05
Key: 10 = 00
Key: 25 = C103
Key: 2 = E0FF
Key: 9 = 69544147202020202020202020202020
Key: 0 = 020105020A000319C1030302E0FF110969544147202020202020202020202020000000000000000000000000000000000000000000000000000000000000


and if your eyes are better than mine, then you will immediately spot that the EOFF in advertising data for Key: 0 is a possible match to the ffeo of the last listed service, and so wot I did's:

B4X:
Dim ScanFilter As List
ScanFilter.Initialize
ScanFilter.Add("0000ffe0-0000-1000-8000-00805f9b34fb")
manager.Scan2(ScanFilter, True)

and I think we might be on to something here.

Whilst it's reasonable that a device would not have to advertise *all* of its services, to keep within button-cell energy budgets, it is also reasonable that it should have to advertise at least one of its services. So perhaps go through those 8 service UUIDs of your device, one by one, and try each separately/alone as a filter.

Seems like a dead end at the moment.

upload_2019-3-5_0-48-49.png
 
Upvote 0
Top