don / cordova-plugin-ble-central

Bluetooth Low Energy (BLE) Central plugin for Apache Cordova (aka PhoneGap)
Apache License 2.0
941 stars 601 forks source link

WebView might crash when repeatedly calling .connect before device has been scanned for #975

Closed graphefruit closed 6 months ago

graphefruit commented 11 months ago

Hey @peitschie,

Sorry for another ticket here!

ios 6.3.0
cordova-plugin-ble-central 1.7.0 "BLE"
ionic 6
angular 15.2.3

I've just encountered an issue on iOS:

When I run .connect and this triggers the .disconnect function and I reconnect again, the log goes crazy, and the app reloads.

That said, the fix was to add a timeout for 1-2 seconds, before triggering .connect again. (.autoConnect cant be used, after all the discussions we had on #963)

ble.connect(
        deviceId,
        (data: PeripheralData) => {
          this.logger.log('AutoConnectScale - Scale device connected.');

        },
        async () => {
          this.logger.log('AutoConnectScale - Scale device disconnected.');

            // If this line is removed, the app cycles and reloads.
            await new Promise((resolve) => {
              setTimeout(async () => {
                resolve(undefined);
              }, 2000);
            });

            this.connectScale(
               ...
            );
          }
        }

The log which is thrown on iOS:

   2023-06-04 19:26:17.066651+0200 Beanconqueror[1807:73106] connect
2023-06-04 19:26:17.067462+0200 Beanconqueror[1807:73106] Could not find peripheral F8B30B87-B6D4-53C3-92EC-EBFEB3D6788C.
2023-06-04 19:26:17.068752+0200 Beanconqueror[1807:73106] connect
2023-06-04 19:26:17.069386+0200 Beanconqueror[1807:73106] Could not find peripheral F8B30B87-B6D4-53C3-92EC-EBFEB3D6788C.
2023-06-04 19:26:17.071328+0200 Beanconqueror[1807:73106] connect
2023-06-04 19:26:17.072699+0200 Beanconqueror[1807:73106] Could not find peripheral F8B30B87-B6D4-53C3-92EC-EBFEB3D6788C.
2023-06-04 19:26:17.079072+0200 Beanconqueror[1807:73106] connect
2023-06-04 19:26:17.082581+0200 Beanconqueror[1807:73106] Could not find peripheral F8B30B87-B6D4-53C3-92EC-EBFEB3D6788C.
2023-06-04 19:26:17.087481+0200 Beanconqueror[1807:73106] connect
2023-06-04 19:26:17.091023+0200 Beanconqueror[1807:73106] Could not find peripheral F8B30B87-B6D4-53C3-92EC-EBFEB3D6788C.
2023-06-04 19:26:17.099299+0200 Beanconqueror[1807:73106] connect
2023-06-04 19:26:17.101547+0200 Beanconqueror[1807:73106] Could not find peripheral F8B30B87-B6D4-53C3-92EC-EBFEB3D6788C.
2023-06-04 19:26:17.121533+0200 Beanconqueror[1807:73106] connect
2023-06-04 19:26:17.122448+0200 Beanconqueror[1807:73106] Could not find peripheral F8B30B87-B6D4-53C3-92EC-EBFEB3D6788C.
2023-06-04 19:26:17.129561+0200 Beanconqueror[1807:73106] connect
2023-06-04 19:26:17.130399+0200 Beanconqueror[1807:73106] Could not find peripheral F8B30B87-B6D4-53C3-92EC-EBFEB3D6788C.
2023-06-04 19:26:17.134071+0200 Beanconqueror[1807:73106] connect
2023-06-04 19:26:17.134773+0200 Beanconqueror[1807:73106] Could not find peripheral F8B30B87-B6D4-53C3-92EC-EBFEB3D6788C.
2023-06-04 19:26:17.136218+0200 Beanconqueror[1807:73106] connect
2023-06-04 19:26:17.137164+0200 Beanconqueror[1807:73106] Could not find peripheral F8B30B87-B6D4-53C3-92EC-EBFEB3D6788C.

.... about 1000 times more

and ends with

 2023-06-04 19:26:17.771791+0200 Beanconqueror[1807:73106] [Process] 0x110000de0 - [PID=1810] WebProcessProxy::didClose: (web process 0 crash)
2023-06-04 19:26:17.781537+0200 Beanconqueror[1807:73106] [Process] 0x110000de0 - [PID=1810] WebProcessProxy::processDidTerminateOrFailedToLaunch: reason=Crash
2023-06-04 19:26:17.791474+0200 Beanconqueror[1807:73106] [ProcessSuspension] 0x10f0964c0 - ProcessAssertion: Failed to acquire RBS Background assertion 'XPCConnectionTerminationWatchdog' for process because PID 0 is invalid
2023-06-04 19:26:17.792634+0200 Beanconqueror[1807:73370] [ProcessSuspension] 0x10f0964c0 - ProcessAssertion::acquireSync Failed to acquire RBS assertion 'XPCConnectionTerminationWatchdog' for process with PID=0, error: (null)
2023-06-04 19:26:17.915547+0200 Beanconqueror[1807:73106] [Process] 0x12a83b618 - [pageProxyID=7, webPageID=8, PID=1810] WebPageProxy::processDidTerminate: (pid 1810), reason=Crash
2023-06-04 19:26:18.124643+0200 Beanconqueror[1807:73106] [Loading] 0x12a83b618 - [pageProxyID=7, webPageID=8, PID=1810] WebPageProxy::dispatchProcessDidTerminate: reason=Crash
2023-06-04 19:26:18.301610+0200 Beanconqueror[1807:73106] [Process] 0x10f06c640 - GPUProcessProxy::gpuProcessExited: reason=IdleExit
2023-06-04 19:26:18.301887+0200 Beanconqueror[1807:73106] [Process] 0x110001dc0 - [PID=1825] WebProcessProxy::gpuProcessExited: reason=IdleExit
2023-06-04 19:26:18.311604+0200 Beanconqueror[1807:73370] [assertion] Error acquiring assertion: <Error Domain=RBSServiceErrorDomain Code=1 "target is not running or doesn't have entitlement com.apple.runningboard.assertions.webkit" UserInfo={NSLocalizedFailureReason=target is not running or doesn't have entitlement com.apple.runningboard.assertions.webkit}>
2023-06-04 19:26:18.311755+0200 Beanconqueror[1807:73370] [ProcessSuspension] 0x10f096700 - ProcessAssertion::acquireSync Failed to acquire RBS assertion 'GPUProcess Foreground Assertion' for process with PID=1812, error: Error Domain=RBSServiceErrorDomain Code=1 "target is not running or doesn't have entitlement com.apple.runningboard.assertions.webkit" UserInfo={NSLocalizedFailureReason=target is not running or doesn't have entitlement com.apple.runningboard.assertions.webkit}
2023-06-04 19:26:18.316774+0200 Beanconqueror[1807:73370] [assertion] Error acquiring assertion: <Error Domain=RBSAssertionErrorDomain Code=2 "Specified target process does not exist" UserInfo={NSLocalizedFailureReason=Specified target process does not exist}>
2023-06-04 19:26:18.316949+0200 Beanconqueror[1807:73370] [ProcessSuspension] 0x10f0967c0 - ProcessAssertion::acquireSync Failed to acquire RBS assertion 'GPUProcess Background Assertion' for process with PID=1812, error: Error Domain=RBSAssertionErrorDomain Code=2 "Specified target process does not exist" UserInfo={NSLocalizedFailureReason=Specified target process does not exist}

Maybe! This issue is aswell on Android, but after I do some other magic here, I didn't test it right now tbh.

Hope this information helps you to track down :) Best regards Lars

peitschie commented 11 months ago

Howdy @graphefruit

No problems at all 🙂 ... always happy to have feedback/issues raised.

Based on what you're seeing there, I can't really think of anything obvious the plugin could do differently here. The webview is crashing, which means it's the javascript side running into problems. My assumption is some kind of memory exhaustion or call-stack limit caused by the repeated hammering of the connect method.

This issue only happens if you connect straight after app launch without any prior scans. Based on https://github.com/don/cordova-plugin-ble-central/issues/963#issuecomment-1569272637, most of my applications tend to use option 2, so they scan THEN connect. This avoids the tight-loop you're seeing here, because I won't try to connect until the peripheral has been seen, avoiding the Could not find peripheral error.

Android will not have this quirk as the Android stack allows us to create the proxy without a scan packet (though that leads to another issue where the first connect often fails...).

For what you're doing above, I'd suggest throttling the reconnect attempts if no prior connect has been made since application launch. E.g.,

function connect() {
  let hasConnectedOnce = false
  ble.connect(
    (data) => (hasConnectedOnce = true),
    (err) => {
      const reconnectPauseMs = hasConnectedOnce ? 1000 : 30000;
      setTimeout(connect, reconnectPauseMs);
    }
  );
}

But, you'll still have the problem that you must scan before a connect will work on iOS.

Happy for other ideas on how this problem could be mitigated thought.

graphefruit commented 11 months ago

Hey @peitschie , thanks for your feedback. If I remember it rightly this issue was not given with Version 1.6.3 where I already did the .connect thing and reconnecting when disconnect is triggered, but now with 1.7.0. I'll do tomorrow some more tests.

Any how, the good thing is that the "quickfix" is a timeout added, but I'm very confused, when the .disconnect is triggered and you .connect again, its going into a loop which is triggered in milliseconds

peitschie commented 11 months ago

Interesting.

There are no changes between 1.6.3 and 1.7.0 that should impact this, so I don't really have a good explanation for why it worked before but not now. Is this plugin the only thing that's changed recently?

but I'm very confused, when the .disconnect is triggered and you .connect again, its going into a loop which is triggered in milliseconds

The tight loop here is because the peripheral has never been seen by the plugin, so it returns immediately. On iOS, it's impossible to connect without first scanning for the peripheral (unless you have BLUETOOTH_RESTORE_STATE set to true).

peitschie commented 6 months ago

I'm closing this issue out as the key issue here is that connect is always being called within the disconnected callback despite the reported error not being recoverable.

The suggested workaround has been detailed in https://github.com/don/cordova-plugin-ble-central/issues/975#issuecomment-1575773986, but there's no generic fix that seems useful here.