bluetooth-le icon indicating copy to clipboard operation
bluetooth-le copied to clipboard

Android AutoConnect functionality

Open juanaza opened this issue 6 months ago • 13 comments

Is your feature request related to a problem? Please describe. The problem is that on a device disconnection, you can't really scan a BLE device when the phone is on background. Therefore could we get the autoConnect functionality that Android is offering?

Describe alternatives you've considered This is included in capawesome-team/capacitor-bluetooth-low-energy but the plugin is giving me issues on old Andoid devices.

juanaza avatar May 14 '25 10:05 juanaza

Surprisingly, the existing plugin does quite happily remain scanning and operational in the background. What is likely to be causing you problems however is the pause that occurs 5 minutes after the application is put into the background, due to Android's web view optimizations.

To keep your javascript/app code executing beyond this pause, you need to use a plugin such as capacitor-background-mode, specifically the disableWebViewOptimizations() feature.

DISCLAIMER: Web view optimisations are important for most applications to ensure they don't exceed Android's power budgets and end up black-listed. If you disable them, you need to take great care to ensure any animations and unnecessary global timers are paused when the app is put in the background, otherwise you will find your app continues to use CPU and eventually will be terminated by Android.

This is not the complete solution though for robust, background scanning and connectivity. I've outlined the depth of those challenges in https://github.com/capacitor-community/bluetooth-le/issues/643#issuecomment-2446345584

In short, I don't believe there's a gap in the current plugin here. Happy to hear alternative thoughts however...

peitschie avatar May 14 '25 23:05 peitschie

Thanks for the reply, the thing is that the scanning in background mode is not even working just after the screen is locked. 5 minutes are not passing at all.

To summarize the workflow is the following.

  • Foreground State
  • Phone scans and connects to BLE device
  • Phone subscribes to BLE device and receives data
  • Background State (after phone is locked)
  • Communication continues correctly through BLE subscriptions
  • A sudden disconnection takes place (not even 1 minute since the phone got locked and running on background)
  • Every 15 seconds after the phone disconnected from the BLE device i try to manually connect to the BLE device again but performing a scan to make sure the BLE device is available to connect. This last step is never executing judging by the Android Studio logs

And just to confirm, this reconnect logic works 100% when the APP is running on the foreground.

Do you know what could be going on?

juanaza avatar May 15 '25 09:05 juanaza

@juanaza interesting. Thanks for the extra detail there.

A quick question from your workflow here, does the MAC address of the device change between connections? Or does the device ID you use for connect remain consistent?

If the device ID remains constant, you shouldn't need a scan here as you can simply call connect to try again. If the device is not available, you'd eventually get a connection timeout, and can simply retry calling connect again and again.

On a separate note, when you are calling scan, what filters are you passing into the call here? Is it a fully unfiltered scan, or are you scanning for specific services or device names?

peitschie avatar May 16 '25 05:05 peitschie

@peitschie

No the MAC address remains constant and the same device ID can be used to directly call .connect() for reconnection. There are two issues here I have experienced:

  • Some time ago (we have being using this plugin for our production clients for more than 2 years now) we were doing this direct .connect() on reconnect and experienced some buggy behaviors that left both the phone and device on an unknown status. Thats why I eventually decided to limit the reconnect procedure to intervals of 15 seconds and .connect should only take place if a scan could locate the device (confirming its available)
  • When calling scan for a normal initial connection we are simply filtering by a name prefix, but when doing a reconnect (which it usually takes place during what we define as a session recording and the phone is basically locked) we are filtering both by name prefix and service id. The service ID part used to work until ios 18 was introduced, as reconnection was working flawlessly on my ios 17 iphone until i updated.
private async scanSpbDevices(isReconnect: boolean = false) {
    try {
      this.scannedSpbDevices = [];
      await BleClient.requestLEScan(
        {
          namePrefix: "PadelBand",
          services: isReconnect ? [this.spbCustomServiceUUID] : null
        },
        (result: ScanResult) => {
          this.scannedSpbDevices.push(result.device);
        }
      );
    } catch { }
  }

Thanks for your comments

juanaza avatar May 16 '25 08:05 juanaza

@peitschie Do you have an update on my latest message?

juanaza avatar May 19 '25 18:05 juanaza

Hi @juanaza

Does the scan keep functioning if you start it before with the services filter before the screen is turned off? I.e., start the scan, turn the screen off, and then see if the device successfully connects?

I'm trying to figure out if the scan is being prevented from starting, or if even an ongoing scan stops immediately.

Also, how long are you leaving the scan run for each time in the background?

peitschie avatar May 19 '25 23:05 peitschie

The other point of consideration is whether you potentially need Android's ACCESS_BACKGROUND_LOCATION permission given your use-case: https://developer.android.com/develop/sensors-and-location/location/background

peitschie avatar May 20 '25 06:05 peitschie

Hi @peitschie yes exactly I have tested that and if the scan starts before the screen is turned off, the reconnect functionality will take place correctly. As the scan gets called in the foreground and finalizes in the background.

I think the issue is the scan is being prevented from starting when it is called from the background.

I always scan for only 1500ms which is enough to get find the device I need

Regarding the ACCESS_BACKGROUND_LOCATION, i already have this on my latest version as I have a DFU plugin that needed this.

juanaza avatar May 20 '25 09:05 juanaza

This sounds very much like we need that ACCESS_BACKGROUND_LOCATION permission for the scan.

For the interim, I'd suggest adding some code in the Android portion of your capacitor app to request this runtime permission (ChatGPT and friends should be able to help you out with the exact code snippet), and see if this allows the scan to be started while the device screen is off.

peitschie avatar May 20 '25 10:05 peitschie

I always scan for only 1500ms which is enough to get find the device I need

What do you do if no device is found within that scan? Do you sleep for a while before attempting another scan?

peitschie avatar May 20 '25 10:05 peitschie

I always scan for only 1500ms which is enough to get find the device I need

What do you do if no device is found within that scan? Do you sleep for a while before attempting another scan?

Scan can be triggered in two ways:

  1. Manually, if no devices are found an error message is shown to allow the user scanning again. If devices are found a picker is available to select one
  2. Automatically, as part of the reconnection process, once the device disconnected every 15 seconds I will scan for 1500ms if the previously connected device is not found, the app will not try to reconnect and we won't scan until those 15 seconds have passed.

juanaza avatar May 20 '25 11:05 juanaza

This sounds very much like we need that ACCESS_BACKGROUND_LOCATION permission for the scan.

For the interim, I'd suggest adding some code in the Android portion of your capacitor app to request this runtime permission (ChatGPT and friends should be able to help you out with the exact code snippet), and see if this allows the scan to be started while the device screen is off.

will this eventually be fixed in the community plugin?

juanaza avatar May 20 '25 11:05 juanaza

I'd definitely consider it if we can prove it works.

peitschie avatar May 20 '25 12:05 peitschie