From e58cf4fd0d3fbb9f48b756724749302e53a2e0a7 Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Sat, 12 Jul 2025 12:53:01 +0200 Subject: [PATCH] fix null pointer --- .../mesh/BluetoothConnectionManager.kt | 114 +++++++++++++----- .../android/mesh/BluetoothMeshService.kt | 26 +++- .../bitchat/android/mesh/SecurityManager.kt | 4 +- .../com/bitchat/android/ui/ChatViewModel.kt | 8 +- .../bitchat/android/ui/MeshDelegateHandler.kt | 4 + 5 files changed, 118 insertions(+), 38 deletions(-) diff --git a/app/src/main/java/com/bitchat/android/mesh/BluetoothConnectionManager.kt b/app/src/main/java/com/bitchat/android/mesh/BluetoothConnectionManager.kt index faaa3a45..83673d1f 100644 --- a/app/src/main/java/com/bitchat/android/mesh/BluetoothConnectionManager.kt +++ b/app/src/main/java/com/bitchat/android/mesh/BluetoothConnectionManager.kt @@ -128,11 +128,13 @@ class BluetoothConnectionManager( try { isActive = true - setupGattServer() // Start power manager and services connectionScope.launch { powerManager.start() + + // Setup GATT server after power manager is ready + setupGattServer() delay(500) // Ensure GATT server is ready startAdvertising() @@ -146,13 +148,14 @@ class BluetoothConnectionManager( startPeriodicCleanup() - Log.i(TAG, "Power-optimized Bluetooth services started successfully (CLIENT ONLY)") + Log.i(TAG, "Power-optimized Bluetooth services started successfully") } return true } catch (e: Exception) { Log.e(TAG, "Failed to start Bluetooth services: ${e.message}") + isActive = false return false } } @@ -355,6 +358,12 @@ class BluetoothConnectionManager( val serverCallback = object : BluetoothGattServerCallback() { override fun onConnectionStateChange(device: BluetoothDevice, status: Int, newState: Int) { + // Guard against callbacks after service shutdown + if (!isActive) { + Log.d(TAG, "Server: Ignoring connection state change after shutdown") + return + } + when (newState) { BluetoothProfile.STATE_CONNECTED -> { Log.d(TAG, "Server: Device connected ${device.address}") @@ -371,6 +380,20 @@ class BluetoothConnectionManager( } } + override fun onServiceAdded(status: Int, service: BluetoothGattService) { + // Guard against callbacks after service shutdown + if (!isActive) { + Log.d(TAG, "Server: Ignoring service added callback after shutdown") + return + } + + if (status == BluetoothGatt.GATT_SUCCESS) { + Log.d(TAG, "Server: Service added successfully: ${service.uuid}") + } else { + Log.e(TAG, "Server: Failed to add service: ${service.uuid}, status: $status") + } + } + override fun onCharacteristicWriteRequest( device: BluetoothDevice, requestId: Int, @@ -380,6 +403,12 @@ class BluetoothConnectionManager( offset: Int, value: ByteArray ) { + // Guard against callbacks after service shutdown + if (!isActive) { + Log.d(TAG, "Server: Ignoring characteristic write after shutdown") + return + } + if (characteristic.uuid == CHARACTERISTIC_UUID) { val packet = BitchatPacket.fromBinaryData(value) if (packet != null) { @@ -402,13 +431,21 @@ class BluetoothConnectionManager( offset: Int, value: ByteArray ) { + // Guard against callbacks after service shutdown + if (!isActive) { + Log.d(TAG, "Server: Ignoring descriptor write after shutdown") + return + } + if (BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE.contentEquals(value)) { Log.d(TAG, "Device ${device.address} subscribed to notifications") subscribedDevices.add(device) connectionScope.launch { delay(100) - delegate?.onDeviceConnected(device) + if (isActive) { // Check if still active + delegate?.onDeviceConnected(device) + } } } @@ -418,34 +455,51 @@ class BluetoothConnectionManager( } } - // Clean up existing server - gattServer?.close() + // Proper cleanup sequencing to prevent race conditions + gattServer?.let { server -> + Log.d(TAG, "Cleaning up existing GATT server") + connectionScope.launch { + // Give time for pending callbacks to complete + delay(100) + server.close() + } + } - gattServer = bluetoothManager.openGattServer(context, serverCallback) - - // Create characteristic with notification support - characteristic = BluetoothGattCharacteristic( - CHARACTERISTIC_UUID, - BluetoothGattCharacteristic.PROPERTY_READ or - BluetoothGattCharacteristic.PROPERTY_WRITE or - BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE or - BluetoothGattCharacteristic.PROPERTY_NOTIFY, - BluetoothGattCharacteristic.PERMISSION_READ or - BluetoothGattCharacteristic.PERMISSION_WRITE - ) - - val descriptor = BluetoothGattDescriptor( - UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"), - BluetoothGattDescriptor.PERMISSION_READ or BluetoothGattDescriptor.PERMISSION_WRITE - ) - characteristic?.addDescriptor(descriptor) - - val service = BluetoothGattService(SERVICE_UUID, BluetoothGattService.SERVICE_TYPE_PRIMARY) - service.addCharacteristic(characteristic) - - gattServer?.addService(service) - - Log.i(TAG, "GATT server setup complete") + // Create new server after cleanup delay + connectionScope.launch { + delay(200) // Allow previous server to fully close + + if (!isActive) { + Log.d(TAG, "Service inactive, skipping GATT server creation") + return@launch + } + + gattServer = bluetoothManager.openGattServer(context, serverCallback) + + // Create characteristic with notification support + characteristic = BluetoothGattCharacteristic( + CHARACTERISTIC_UUID, + BluetoothGattCharacteristic.PROPERTY_READ or + BluetoothGattCharacteristic.PROPERTY_WRITE or + BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE or + BluetoothGattCharacteristic.PROPERTY_NOTIFY, + BluetoothGattCharacteristic.PERMISSION_READ or + BluetoothGattCharacteristic.PERMISSION_WRITE + ) + + val descriptor = BluetoothGattDescriptor( + UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"), + BluetoothGattDescriptor.PERMISSION_READ or BluetoothGattDescriptor.PERMISSION_WRITE + ) + characteristic?.addDescriptor(descriptor) + + val service = BluetoothGattService(SERVICE_UUID, BluetoothGattService.SERVICE_TYPE_PRIMARY) + service.addCharacteristic(characteristic) + + gattServer?.addService(service) + + Log.i(TAG, "GATT server setup complete") + } } @Suppress("DEPRECATION") diff --git a/app/src/main/java/com/bitchat/android/mesh/BluetoothMeshService.kt b/app/src/main/java/com/bitchat/android/mesh/BluetoothMeshService.kt index 0a66bcf8..e92a6dfc 100644 --- a/app/src/main/java/com/bitchat/android/mesh/BluetoothMeshService.kt +++ b/app/src/main/java/com/bitchat/android/mesh/BluetoothMeshService.kt @@ -47,6 +47,9 @@ class BluetoothMeshService(private val context: Context) { internal val connectionManager = BluetoothConnectionManager(context, myPeerID) // Made internal for access private val packetProcessor = PacketProcessor(myPeerID) + // Service state management + private var isActive = false + // Delegate for message callbacks (maintains same interface) var delegate: BluetoothMeshDelegate? = null @@ -98,7 +101,10 @@ class BluetoothMeshService(private val context: Context) { // SecurityManager delegate for key exchange notifications securityManager.delegate = object : SecurityManagerDelegate { - override fun onKeyExchangeCompleted(peerID: String) { + override fun onKeyExchangeCompleted(peerID: String, peerPublicKeyData: ByteArray) { + // Notify delegate about key exchange completion so it can register peer fingerprint + delegate?.registerPeerPublicKey(peerID, peerPublicKeyData) + // Send announcement and cached messages after key exchange serviceScope.launch { delay(100) @@ -276,15 +282,24 @@ class BluetoothMeshService(private val context: Context) { * Start the mesh service */ fun startServices() { + // Prevent double starts + if (isActive) { + Log.w(TAG, "Mesh service already active, ignoring duplicate start request") + return + } + Log.i(TAG, "Starting Bluetooth mesh service with peer ID: $myPeerID") if (connectionManager.startServices()) { + isActive = true Log.i(TAG, "Bluetooth services started successfully") // Send initial announcements after services are ready serviceScope.launch { delay(1000) - sendBroadcastAnnounce() + if (isActive) { // Check if still active + sendBroadcastAnnounce() + } } } else { Log.e(TAG, "Failed to start Bluetooth services") @@ -295,7 +310,13 @@ class BluetoothMeshService(private val context: Context) { * Stop all mesh services */ fun stopServices() { + if (!isActive) { + Log.w(TAG, "Mesh service not active, ignoring stop request") + return + } + Log.i(TAG, "Stopping Bluetooth mesh service") + isActive = false // Send leave announcement sendLeaveAnnouncement() @@ -547,4 +568,5 @@ interface BluetoothMeshDelegate { fun decryptChannelMessage(encryptedContent: ByteArray, channel: String): String? fun getNickname(): String? fun isFavorite(peerID: String): Boolean + fun registerPeerPublicKey(peerID: String, publicKeyData: ByteArray) } diff --git a/app/src/main/java/com/bitchat/android/mesh/SecurityManager.kt b/app/src/main/java/com/bitchat/android/mesh/SecurityManager.kt index 3b655d08..65b74e42 100644 --- a/app/src/main/java/com/bitchat/android/mesh/SecurityManager.kt +++ b/app/src/main/java/com/bitchat/android/mesh/SecurityManager.kt @@ -113,7 +113,7 @@ class SecurityManager(private val encryptionService: EncryptionService, private Log.d(TAG, "Successfully processed key exchange from $peerID") // Notify delegate - delegate?.onKeyExchangeCompleted(peerID) + delegate?.onKeyExchangeCompleted(peerID, packet.payload) return true @@ -315,5 +315,5 @@ class SecurityManager(private val encryptionService: EncryptionService, private * Delegate interface for security manager callbacks */ interface SecurityManagerDelegate { - fun onKeyExchangeCompleted(peerID: String) + fun onKeyExchangeCompleted(peerID: String, peerPublicKeyData: ByteArray) } diff --git a/app/src/main/java/com/bitchat/android/ui/ChatViewModel.kt b/app/src/main/java/com/bitchat/android/ui/ChatViewModel.kt index 7d9a5055..4240c1cf 100644 --- a/app/src/main/java/com/bitchat/android/ui/ChatViewModel.kt +++ b/app/src/main/java/com/bitchat/android/ui/ChatViewModel.kt @@ -253,10 +253,6 @@ class ChatViewModel(application: Application) : AndroidViewModel(application), B privateChatManager.toggleFavorite(peerID) } - fun registerPeerPublicKey(peerID: String, publicKeyData: ByteArray) { - privateChatManager.registerPeerPublicKey(peerID, publicKeyData) - } - // MARK: - Debug and Troubleshooting fun getDebugStatus(): String { @@ -341,6 +337,10 @@ class ChatViewModel(application: Application) : AndroidViewModel(application), B return meshDelegateHandler.isFavorite(peerID) } + override fun registerPeerPublicKey(peerID: String, publicKeyData: ByteArray) { + privateChatManager.registerPeerPublicKey(peerID, publicKeyData) + } + // MARK: - Emergency Clear fun panicClearAllData() { diff --git a/app/src/main/java/com/bitchat/android/ui/MeshDelegateHandler.kt b/app/src/main/java/com/bitchat/android/ui/MeshDelegateHandler.kt index 873d9c56..d8e8503a 100644 --- a/app/src/main/java/com/bitchat/android/ui/MeshDelegateHandler.kt +++ b/app/src/main/java/com/bitchat/android/ui/MeshDelegateHandler.kt @@ -154,4 +154,8 @@ class MeshDelegateHandler( override fun isFavorite(peerID: String): Boolean { return privateChatManager.isFavorite(peerID) } + + override fun registerPeerPublicKey(peerID: String, publicKeyData: ByteArray) { + privateChatManager.registerPeerPublicKey(peerID, publicKeyData) + } }