Merge pull request #79 from permissionlesstech/fix-startup

fix null pointer
This commit is contained in:
callebtc
2025-07-12 12:54:47 +02:00
committed by GitHub
5 changed files with 118 additions and 38 deletions
@@ -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")
@@ -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)
}
@@ -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)
}
@@ -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() {
@@ -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)
}
}