mirror of
https://github.com/permissionlesstech/bitchat-android.git
synced 2026-07-25 22:05:26 +00:00
Merge pull request #79 from permissionlesstech/fix-startup
fix null pointer
This commit is contained in:
@@ -128,11 +128,13 @@ class BluetoothConnectionManager(
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
isActive = true
|
isActive = true
|
||||||
setupGattServer()
|
|
||||||
|
|
||||||
// Start power manager and services
|
// Start power manager and services
|
||||||
connectionScope.launch {
|
connectionScope.launch {
|
||||||
powerManager.start()
|
powerManager.start()
|
||||||
|
|
||||||
|
// Setup GATT server after power manager is ready
|
||||||
|
setupGattServer()
|
||||||
delay(500) // Ensure GATT server is ready
|
delay(500) // Ensure GATT server is ready
|
||||||
|
|
||||||
startAdvertising()
|
startAdvertising()
|
||||||
@@ -146,13 +148,14 @@ class BluetoothConnectionManager(
|
|||||||
|
|
||||||
startPeriodicCleanup()
|
startPeriodicCleanup()
|
||||||
|
|
||||||
Log.i(TAG, "Power-optimized Bluetooth services started successfully (CLIENT ONLY)")
|
Log.i(TAG, "Power-optimized Bluetooth services started successfully")
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e(TAG, "Failed to start Bluetooth services: ${e.message}")
|
Log.e(TAG, "Failed to start Bluetooth services: ${e.message}")
|
||||||
|
isActive = false
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -355,6 +358,12 @@ class BluetoothConnectionManager(
|
|||||||
|
|
||||||
val serverCallback = object : BluetoothGattServerCallback() {
|
val serverCallback = object : BluetoothGattServerCallback() {
|
||||||
override fun onConnectionStateChange(device: BluetoothDevice, status: Int, newState: Int) {
|
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) {
|
when (newState) {
|
||||||
BluetoothProfile.STATE_CONNECTED -> {
|
BluetoothProfile.STATE_CONNECTED -> {
|
||||||
Log.d(TAG, "Server: Device connected ${device.address}")
|
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(
|
override fun onCharacteristicWriteRequest(
|
||||||
device: BluetoothDevice,
|
device: BluetoothDevice,
|
||||||
requestId: Int,
|
requestId: Int,
|
||||||
@@ -380,6 +403,12 @@ class BluetoothConnectionManager(
|
|||||||
offset: Int,
|
offset: Int,
|
||||||
value: ByteArray
|
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) {
|
if (characteristic.uuid == CHARACTERISTIC_UUID) {
|
||||||
val packet = BitchatPacket.fromBinaryData(value)
|
val packet = BitchatPacket.fromBinaryData(value)
|
||||||
if (packet != null) {
|
if (packet != null) {
|
||||||
@@ -402,13 +431,21 @@ class BluetoothConnectionManager(
|
|||||||
offset: Int,
|
offset: Int,
|
||||||
value: ByteArray
|
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)) {
|
if (BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE.contentEquals(value)) {
|
||||||
Log.d(TAG, "Device ${device.address} subscribed to notifications")
|
Log.d(TAG, "Device ${device.address} subscribed to notifications")
|
||||||
subscribedDevices.add(device)
|
subscribedDevices.add(device)
|
||||||
|
|
||||||
connectionScope.launch {
|
connectionScope.launch {
|
||||||
delay(100)
|
delay(100)
|
||||||
delegate?.onDeviceConnected(device)
|
if (isActive) { // Check if still active
|
||||||
|
delegate?.onDeviceConnected(device)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -418,34 +455,51 @@ class BluetoothConnectionManager(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean up existing server
|
// Proper cleanup sequencing to prevent race conditions
|
||||||
gattServer?.close()
|
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 new server after cleanup delay
|
||||||
|
connectionScope.launch {
|
||||||
|
delay(200) // Allow previous server to fully close
|
||||||
|
|
||||||
// Create characteristic with notification support
|
if (!isActive) {
|
||||||
characteristic = BluetoothGattCharacteristic(
|
Log.d(TAG, "Service inactive, skipping GATT server creation")
|
||||||
CHARACTERISTIC_UUID,
|
return@launch
|
||||||
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(
|
gattServer = bluetoothManager.openGattServer(context, serverCallback)
|
||||||
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)
|
// Create characteristic with notification support
|
||||||
service.addCharacteristic(characteristic)
|
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
|
||||||
|
)
|
||||||
|
|
||||||
gattServer?.addService(service)
|
val descriptor = BluetoothGattDescriptor(
|
||||||
|
UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"),
|
||||||
|
BluetoothGattDescriptor.PERMISSION_READ or BluetoothGattDescriptor.PERMISSION_WRITE
|
||||||
|
)
|
||||||
|
characteristic?.addDescriptor(descriptor)
|
||||||
|
|
||||||
Log.i(TAG, "GATT server setup complete")
|
val service = BluetoothGattService(SERVICE_UUID, BluetoothGattService.SERVICE_TYPE_PRIMARY)
|
||||||
|
service.addCharacteristic(characteristic)
|
||||||
|
|
||||||
|
gattServer?.addService(service)
|
||||||
|
|
||||||
|
Log.i(TAG, "GATT server setup complete")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
|
|||||||
@@ -47,6 +47,9 @@ class BluetoothMeshService(private val context: Context) {
|
|||||||
internal val connectionManager = BluetoothConnectionManager(context, myPeerID) // Made internal for access
|
internal val connectionManager = BluetoothConnectionManager(context, myPeerID) // Made internal for access
|
||||||
private val packetProcessor = PacketProcessor(myPeerID)
|
private val packetProcessor = PacketProcessor(myPeerID)
|
||||||
|
|
||||||
|
// Service state management
|
||||||
|
private var isActive = false
|
||||||
|
|
||||||
// Delegate for message callbacks (maintains same interface)
|
// Delegate for message callbacks (maintains same interface)
|
||||||
var delegate: BluetoothMeshDelegate? = null
|
var delegate: BluetoothMeshDelegate? = null
|
||||||
|
|
||||||
@@ -98,7 +101,10 @@ class BluetoothMeshService(private val context: Context) {
|
|||||||
|
|
||||||
// SecurityManager delegate for key exchange notifications
|
// SecurityManager delegate for key exchange notifications
|
||||||
securityManager.delegate = object : SecurityManagerDelegate {
|
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
|
// Send announcement and cached messages after key exchange
|
||||||
serviceScope.launch {
|
serviceScope.launch {
|
||||||
delay(100)
|
delay(100)
|
||||||
@@ -276,15 +282,24 @@ class BluetoothMeshService(private val context: Context) {
|
|||||||
* Start the mesh service
|
* Start the mesh service
|
||||||
*/
|
*/
|
||||||
fun startServices() {
|
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")
|
Log.i(TAG, "Starting Bluetooth mesh service with peer ID: $myPeerID")
|
||||||
|
|
||||||
if (connectionManager.startServices()) {
|
if (connectionManager.startServices()) {
|
||||||
|
isActive = true
|
||||||
Log.i(TAG, "Bluetooth services started successfully")
|
Log.i(TAG, "Bluetooth services started successfully")
|
||||||
|
|
||||||
// Send initial announcements after services are ready
|
// Send initial announcements after services are ready
|
||||||
serviceScope.launch {
|
serviceScope.launch {
|
||||||
delay(1000)
|
delay(1000)
|
||||||
sendBroadcastAnnounce()
|
if (isActive) { // Check if still active
|
||||||
|
sendBroadcastAnnounce()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Log.e(TAG, "Failed to start Bluetooth services")
|
Log.e(TAG, "Failed to start Bluetooth services")
|
||||||
@@ -295,7 +310,13 @@ class BluetoothMeshService(private val context: Context) {
|
|||||||
* Stop all mesh services
|
* Stop all mesh services
|
||||||
*/
|
*/
|
||||||
fun stopServices() {
|
fun stopServices() {
|
||||||
|
if (!isActive) {
|
||||||
|
Log.w(TAG, "Mesh service not active, ignoring stop request")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
Log.i(TAG, "Stopping Bluetooth mesh service")
|
Log.i(TAG, "Stopping Bluetooth mesh service")
|
||||||
|
isActive = false
|
||||||
|
|
||||||
// Send leave announcement
|
// Send leave announcement
|
||||||
sendLeaveAnnouncement()
|
sendLeaveAnnouncement()
|
||||||
@@ -547,4 +568,5 @@ interface BluetoothMeshDelegate {
|
|||||||
fun decryptChannelMessage(encryptedContent: ByteArray, channel: String): String?
|
fun decryptChannelMessage(encryptedContent: ByteArray, channel: String): String?
|
||||||
fun getNickname(): String?
|
fun getNickname(): String?
|
||||||
fun isFavorite(peerID: String): Boolean
|
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")
|
Log.d(TAG, "Successfully processed key exchange from $peerID")
|
||||||
|
|
||||||
// Notify delegate
|
// Notify delegate
|
||||||
delegate?.onKeyExchangeCompleted(peerID)
|
delegate?.onKeyExchangeCompleted(peerID, packet.payload)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
|
||||||
@@ -315,5 +315,5 @@ class SecurityManager(private val encryptionService: EncryptionService, private
|
|||||||
* Delegate interface for security manager callbacks
|
* Delegate interface for security manager callbacks
|
||||||
*/
|
*/
|
||||||
interface SecurityManagerDelegate {
|
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)
|
privateChatManager.toggleFavorite(peerID)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun registerPeerPublicKey(peerID: String, publicKeyData: ByteArray) {
|
|
||||||
privateChatManager.registerPeerPublicKey(peerID, publicKeyData)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Debug and Troubleshooting
|
// MARK: - Debug and Troubleshooting
|
||||||
|
|
||||||
fun getDebugStatus(): String {
|
fun getDebugStatus(): String {
|
||||||
@@ -341,6 +337,10 @@ class ChatViewModel(application: Application) : AndroidViewModel(application), B
|
|||||||
return meshDelegateHandler.isFavorite(peerID)
|
return meshDelegateHandler.isFavorite(peerID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun registerPeerPublicKey(peerID: String, publicKeyData: ByteArray) {
|
||||||
|
privateChatManager.registerPeerPublicKey(peerID, publicKeyData)
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Emergency Clear
|
// MARK: - Emergency Clear
|
||||||
|
|
||||||
fun panicClearAllData() {
|
fun panicClearAllData() {
|
||||||
|
|||||||
@@ -154,4 +154,8 @@ class MeshDelegateHandler(
|
|||||||
override fun isFavorite(peerID: String): Boolean {
|
override fun isFavorite(peerID: String): Boolean {
|
||||||
return privateChatManager.isFavorite(peerID)
|
return privateChatManager.isFavorite(peerID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun registerPeerPublicKey(peerID: String, publicKeyData: ByteArray) {
|
||||||
|
privateChatManager.registerPeerPublicKey(peerID, publicKeyData)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user