-
Notifications
You must be signed in to change notification settings - Fork 354
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
plugin crash after reconnect() on Android #705
Comments
Note that I can workaround the issue by calling close() after the pause event. But then the connect on resume takes a bit longer. |
Do you know what is calling queueAdd? Are you calling read or subscribe? I typically didn't use reconnect, so there may be some strange behavior. |
Sorry I know I should debug it further, but I'm not familiar with the Android source and debug tools. The workaround I mentioned above is working for me at the moment. @VinardoZzZ2000 would you please assist with this issue since you added this code recently? UPDATE: I noticed that it didn't fail in my initial minimal test case, so I progressively added more of my actual code until I see the crash.
Also, I tried on an earlier version of this plugin (6.2.1) and it works ok. My guess is this bug is introduced with the new queuing function added recently. Here's some minimal code to recreate the issue. To test, put the app in the background for >10s, then bring it back. // @ts-check
"use strict"
const BLE_MTU_REQ = 517; // request server to increase to as high as this.
let ble_mtu = 23; // default is 23 if not requested higher.
/**@type {BluetoothlePlugin.DeviceInfo} */
let deviceHandle = {
address: "3C:71:BF:5F:66:4E",
name: "",
status: "discovered"
};
/**@type {?*} */
let sleepTimer = null;
/**
*
* @param {?function()=} cb Callback function on done (pass or fail) (optional).
*/
function requestMtu(cb) {
if (cordova.platformId == "android") {
window.bluetoothle.mtu(
function (mtu) {
ble_mtu = mtu.mtu;
console.log('MTU has been set to ' + ble_mtu);
cb && cb();
},
function (error) {
console.error('MTU set fail' , error);
cb && cb();
},
{
address: deviceHandle.address,
mtu: BLE_MTU_REQ,
}
);
} else {
// set MTU not needed on iOS, it is done automatically by the OS
cb && cb();
}
}
/**
* subscribe to notifications
* @param {string} service
* @param {string} characteristic
* @param {?function(string)=} cb Callback function on recieved notification accepts argument of (message)=>{}
* @return the promise is only used for the setup of the notification
*/
function subscribe (service, characteristic, cb) {
return new Promise((resolve, reject) => {
console.log('enabling notify');
window.bluetoothle.subscribe((result) => {
// This callback is called repeatedly until disableNotification is called.
if (result.status == "subscribed") {
console.log('subscribed');
resolve(true);
} else {
cb && cb(result.value);
}
}, (error) => {
if (error.error = "isDisconnected") {
// this callback fires when device is disconnected (after setup)
//my_disconnectCallback();
} else {
// assume an error in setup subscribe
console.error(error);
resolve(false); // but continue anyway
}
},
{
address: deviceHandle.address,
service: service,
characteristic: characteristic,
}
);
});
};
/**
*
* @param {?BluetoothlePlugin.DeviceInfo=} result
*/
function connectCallback(result) {
if (!result) return;
console.log(result.status);
if (result.status === "connected") {
requestMtu(function () {
console.log("Getting device services.");
new Promise(function (resolve, reject) {
window.bluetoothle.discover(resolve, reject,
{
address: deviceHandle.address,
//clearCache: true, // clearCache = true / false (default) Force the device to re-discover services, instead of relying on cache from previous discovery (Android only)
});
}).then(() => {
console.log("connected!");
return subscribe('b0c0fff0-7c23-476c-9aa9-39dc3e58e0e9', 'b0c0fff4-7c23-476c-9aa9-39dc3e58e0e9', (val) => console.log(val));
});
});
}
else if (result.status === "disconnected") {
console.log("Disconnected from: " + result.address);
}
}
const ble_connect = () => new Promise((resolve, reject) => {
console.log('try connect');
window.bluetoothle.connect((result) => {
connectCallback(result);
if (result.status == "connected") {
resolve(result);
}
}, (error) => {
reject(error);
}, { address: deviceHandle.address, autoConnect: true });
});
// Disconnect b/c pause application for quick reconnect
function ble_pause() {
console.log('try disconnect');
return new Promise(function (resolve, reject) {
if (!(deviceHandle)) reject();
else {
window.bluetoothle.disconnect(resolve, resolve,
{ address: deviceHandle.address });
}
});
};
// Resume a paused connection
function ble_resume() {
return new Promise((resolve, reject) => {
console.log('try reconnect');
if (!(deviceHandle)) reject();
else window.bluetoothle.reconnect((result) => {
connectCallback(result);
if (result.status == "connected") {
resolve(result);
}
}, (error) => {
reject(error);
}, { address: deviceHandle.address });
});
};
/**
* The pause event fires when the native platform puts the application into the background, typically when the user switches to a different application.
* @param {Event} e
*/
function onPause(e) {
console.log("onPause() starting 10s timer");
sleepTimer = setTimeout(() => { // if app stays in background for 10s, then sleep
ble_pause();
sleepTimer = null;
}, 10000);
}
/**
* The resume event fires when the native platform pulls the application out from the background.
* @param {Event} e
*/
function onResume(e) {
console.log("onResume()");
if (sleepTimer != null) {
clearInterval(sleepTimer);
sleepTimer = null;
// sleepTimer was still in progress, so not asleep, no need to resume
} else {
// sleepTimer must have expired, so asleep, resume
ble_resume();
}
}
document.addEventListener('deviceready', function () {
// wrap in 0 timeout per https://cordova.apache.org/docs/en/latest/cordova/events/events.html#ios-quirks
document.addEventListener("resume", () => setTimeout(onResume, 0), false);
document.addEventListener("pause", () => setTimeout(onPause, 0), false);
new Promise(function (resolve) {
bluetoothle.initialize(resolve, { request: true, statusReceiver: false });
}).then(ble_connect);
}); |
Sorry for the late reply. I was busy preparing my thesis. Seems like I overlooked this, as I've never used disconnect() and reconnect(). I'll prepare a fix ASAP. Sorry for the inconvenience. |
I've made a PR: #707 But unfortunately, I cannot fully test your code. connect() with |
Waiting for merge. I'm testing using a real device, however, I haven't implemented Apart from that, the PR works well for me. Will respond if |
@max-scopp what did the PR #707 fix for you? It didn't help my case. |
It seems the autoconnect / reconnect feature was first discussed here: #333 And seems it was left in the state where it could only be used like this: That last call to connect with autoreconnect if disconnected is just to tell the plugin to keep trying to reconnect. It also re-registers the callback (since the callbacks were erased when the disconnect happened). I think the logic could be more useful. If my BLE peripheral device disappears (out of range, or reboots), this plugin should just try to reconnect to it. It does reconnect, but my JS code never gets a callback that it reconnected. I suspect it is due to the plugin clearing the callbacks when a disconnect happens.
which clears the connection object when a disconnect happens. Why does it get cleared it here? That means that a (automatic) reconnect will not trigger a callback to the JS code. Instead, the callbacks should only be cleared after the close(). @randdusing Do you agree? Should I bother trying to fix it? Or just use the workaround? |
Another observation: When I initially connect with (autoconnect:true), the Android (and iOS?) takes anywhere from 1~20 seconds to even begin the connection attempt. I verified that the delay is from the central device with a BLE sniffer. Setting (autoconnect:false) on initial connect fixes this issue. I was going to open a separate issue for this, but it seems it's not a problem if I use the pattern I outlined above, since the initial connection will always be with autoconnect:false. |
When app goes into the background, "pause" event fires and I call bluetoothle.disconnect().
When app is back in foreground, I call reconnect() and it seems to reconnect OK.
But after that when getting the services the plugin crashes:
The crash happened in queueAdd() function (Android java code) at queue.add(operation);
The text was updated successfully, but these errors were encountered: