Skip to content

Commit 5e5fcff

Browse files
author
Jack Levin
committed
Add Bluetooth power cycle to help reset stale BLE connections
- Import BleManager from react-native-ble-plx - Add bleManagerRef to store BLE Manager instance - Create powerCycleBluetooth() function to reset Bluetooth adapter - Automatically power cycle on second retry attempt (retryCount === 1) - Works on Android <13 (direct disable/enable) - On Android 13+: Shows Toast instructing user to manually toggle BT - Helps clear stale connections when comprehensive cleanup isn't enough Smart retry strategy: 1st attempt: Normal connection 1st retry: Cleanup + retry 2nd retry: Power cycle + cleanup + retry 3rd retry: Final attempt with full cleanup
1 parent 79911ba commit 5e5fcff

1 file changed

Lines changed: 89 additions & 1 deletion

File tree

android/App.js

Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ import TokenIcon, { clearImageCache } from "./src/components/TokenIcon";
7171
import QRCode from "react-native-qrcode-svg";
7272
import { CameraView, useCameraPermissions } from "expo-camera";
7373
import TransportBLE from "@ledgerhq/react-native-hw-transport-ble";
74+
import { BleManager } from "react-native-ble-plx";
7475
import AppSolana from "@ledgerhq/hw-app-solana";
7576
import AsyncStorage from "@react-native-async-storage/async-storage";
7677
import * as SecureStore from "expo-secure-store";
@@ -392,6 +393,7 @@ function AppContent() {
392393
const ledgerScanSubscriptionRef = useRef(null); // Store scan subscription for cleanup
393394
const ledgerCleaningRef = useRef(false); // Prevent concurrent cleanup
394395
const ledgerCleanedUpRef = useRef(false); // Track if cleanup has already been completed
396+
const bleManagerRef = useRef(null); // BLE Manager instance for Bluetooth control
395397
const sendAddressInputRef = useRef(null); // Ref for send address TextInput
396398

397399
// Send and Receive states
@@ -3188,6 +3190,78 @@ function AppContent() {
31883190
scanForLedger();
31893191
};
31903192

3193+
// Bluetooth Power Cycle - Reset Bluetooth adapter to clear stale connections
3194+
const powerCycleBluetooth = async () => {
3195+
console.log("🔄 Attempting to power cycle Bluetooth adapter...");
3196+
3197+
try {
3198+
// Initialize BLE Manager if not already done
3199+
if (!bleManagerRef.current) {
3200+
bleManagerRef.current = new BleManager();
3201+
console.log(" ✓ BLE Manager initialized");
3202+
}
3203+
3204+
const manager = bleManagerRef.current;
3205+
3206+
// Check current Bluetooth state
3207+
const state = await manager.state();
3208+
console.log(` 📡 Current Bluetooth state: ${state}`);
3209+
3210+
if (state === "PoweredOn") {
3211+
// Try to disable Bluetooth (only works on Android <13)
3212+
try {
3213+
console.log(" ⏸ Disabling Bluetooth...");
3214+
await manager.disable();
3215+
console.log(" ✓ Bluetooth disabled");
3216+
3217+
// Wait for it to fully power down
3218+
await new Promise((resolve) => setTimeout(resolve, 1000));
3219+
3220+
// Re-enable Bluetooth
3221+
console.log(" ▶ Re-enabling Bluetooth...");
3222+
await manager.enable();
3223+
console.log(" ✓ Bluetooth re-enabled");
3224+
3225+
// Wait for it to fully power up
3226+
await new Promise((resolve) => setTimeout(resolve, 1500));
3227+
3228+
console.log("✅ Bluetooth power cycle complete!");
3229+
return true;
3230+
} catch (error) {
3231+
// Disable/enable not supported (Android 13+)
3232+
if (error.message && error.message.includes("not supported")) {
3233+
console.log(
3234+
" ⚠ Bluetooth disable/enable not supported on this Android version"
3235+
);
3236+
console.log(
3237+
" 💡 User must manually toggle Bluetooth in system settings"
3238+
);
3239+
3240+
Toast.show({
3241+
type: "info",
3242+
text1: "Bluetooth Reset Required",
3243+
text2:
3244+
"Please toggle Bluetooth OFF then ON in your phone settings to clear stale connections",
3245+
position: "bottom",
3246+
visibilityTime: 5000,
3247+
});
3248+
return false;
3249+
}
3250+
throw error;
3251+
}
3252+
} else {
3253+
console.log(
3254+
` ⚠ Bluetooth is not powered on (state: ${state}), skipping power cycle`
3255+
);
3256+
return false;
3257+
}
3258+
} catch (error) {
3259+
console.error("❌ Error during Bluetooth power cycle:", error);
3260+
console.error(" Error message:", error.message);
3261+
return false;
3262+
}
3263+
};
3264+
31913265
// Proper BLE cleanup function following best practices
31923266
const cleanupLedgerBLE = async () => {
31933267
// Prevent double cleanup - check if already cleaned up OR currently cleaning
@@ -3536,7 +3610,21 @@ function AppContent() {
35363610
ledgerCleaningRef.current = false;
35373611
ledgerCleanedUpRef.current = false;
35383612

3539-
// 4. Attempt to disconnect using device ID if supported
3613+
// 4. Power cycle Bluetooth on second retry to clear stale connections
3614+
// (Skip on first attempt to avoid unnecessary delay)
3615+
if (retryCount === 1) {
3616+
console.log(" 🔄 Second retry attempt - power cycling Bluetooth...");
3617+
const cycled = await powerCycleBluetooth();
3618+
if (cycled) {
3619+
console.log(" ✓ Bluetooth power cycle succeeded");
3620+
} else {
3621+
console.log(
3622+
" ⚠ Bluetooth power cycle not available (Android 13+)"
3623+
);
3624+
}
3625+
}
3626+
3627+
// 5. Attempt to disconnect using device ID if supported
35403628
if (TransportBLE.disconnectDevice) {
35413629
await TransportBLE.disconnectDevice(deviceId);
35423630
console.log(" ✓ TransportBLE.disconnectDevice succeeded");

0 commit comments

Comments
 (0)