fix(#699): apply pre-PR review, never block the main thread in the backstop

Gemini review (gemini-2.5-pro, docs/llm-consultations) flagged that the
5 s backstop acquired mMethodCallMutex blockingly on the main looper, an
ANR class. It now tryAcquires and re-posts itself after 250 ms when a
callback holds the mutex. The other two findings were verified and
rejected with reasons recorded in the PR body: the disconnectAllDevices
race is pre-existing upstream (and naive mutex acquisition there would
deadlock the flutterRestart path, which already holds the semaphore),
and the untranslated new strings follow this repo's standing l10n
process.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
feat/638-top-toast
Strycher 4 days ago committed by Benjamin Wiechel
parent 3f05e67773
commit 74e461ba48

@ -816,33 +816,43 @@ public class FlutterBluePlusPlugin implements
if (mConnectedDevices.get(remoteId) == gatt) {
final BluetoothGatt disconnectingGatt = gatt;
final String backstopRemoteId = remoteId;
new Handler(Looper.getMainLooper()).postDelayed(() -> {
boolean stillTracked;
try {
acquireMutex(mMethodCallMutex);
stillTracked = mConnectedDevices.get(backstopRemoteId) == disconnectingGatt;
final Handler backstopHandler = new Handler(Looper.getMainLooper());
backstopHandler.postDelayed(new Runnable() {
@Override
public void run() {
// Never block the main thread on the mutex (ANR
// risk, pre-PR review): if a callback holds it,
// re-check shortly instead of waiting.
if (!mMethodCallMutex.tryAcquire()) {
backstopHandler.postDelayed(this, 250);
return;
}
boolean stillTracked;
try {
stillTracked = mConnectedDevices.get(backstopRemoteId) == disconnectingGatt;
if (stillTracked) {
log(LogLevel.WARNING, "disconnect not confirmed after 5s, force-closing: " + backstopRemoteId);
mConnectedDevices.remove(backstopRemoteId);
mCurrentlyConnectingDevices.remove(backstopRemoteId);
try { disconnectingGatt.close(); } catch (Exception e) { /* best effort */ }
}
} finally {
mMethodCallMutex.release();
}
if (stillTracked) {
log(LogLevel.WARNING, "disconnect not confirmed after 5s, force-closing: " + backstopRemoteId);
mConnectedDevices.remove(backstopRemoteId);
mCurrentlyConnectingDevices.remove(backstopRemoteId);
try { disconnectingGatt.close(); } catch (Exception e) { /* best effort */ }
// random number defined by this fork, alongside
// flutter blue plus's own 23789258 cancel code.
int bmForceClosedErrorCode = 23789259;
// see: BmConnectionStateResponse
HashMap<String, Object> backstopResponse = new HashMap<>();
backstopResponse.put("remote_id", backstopRemoteId);
backstopResponse.put("connection_state", bmConnectionStateEnum(BluetoothProfile.STATE_DISCONNECTED));
backstopResponse.put("disconnect_reason_code", bmForceClosedErrorCode);
backstopResponse.put("disconnect_reason_string", "disconnect not confirmed, force-closed");
invokeMethodUIThread("OnConnectionStateChanged", backstopResponse);
}
} finally {
mMethodCallMutex.release();
}
if (stillTracked) {
// random number defined by this fork, alongside
// flutter blue plus's own 23789258 cancel code.
int bmForceClosedErrorCode = 23789259;
// see: BmConnectionStateResponse
HashMap<String, Object> backstopResponse = new HashMap<>();
backstopResponse.put("remote_id", backstopRemoteId);
backstopResponse.put("connection_state", bmConnectionStateEnum(BluetoothProfile.STATE_DISCONNECTED));
backstopResponse.put("disconnect_reason_code", bmForceClosedErrorCode);
backstopResponse.put("disconnect_reason_string", "disconnect not confirmed, force-closed");
invokeMethodUIThread("OnConnectionStateChanged", backstopResponse);
}
}, 5000);
}

Loading…
Cancel
Save

Powered by TurnKey Linux.