mirror of
https://github.com/permissionlesstech/bitchat-android.git
synced 2026-07-25 05:25:20 +00:00
Debug button (#377)
* feat(debug): add DebugSettingsManager + DebugSettingsSheet scaffold and AboutSheet entry; groundwork for verbose logging, GATT controls, relay stats, device/scan views * fix(debug): wire debug sheet launch via onShowDebug in AboutSheet from ChatScreen; add public start/stop server/client methods} * fix(about): remove misplaced debug item block inside PoW section; keep debug launcher managed by ChatScreen only * feat(debug): wire DebugSettingsSheet role switches to explicit startServer/stopServer and startClient/stopClient; expose role controls + disconnectAll on BluetoothConnectionManager; fix syntax error; build passes * feat(debug): add connect/disconnect helpers; wire DebugSettingsSheet connect/disconnect actions to BluetoothConnectionManager * feat(debug): add debug settings button at bottom of AboutSheet; wire onShowDebug callback to open DebugSettingsSheet from ChatScreen * feat(debug): wire verbose logging into chat view via DebugSettingsManager; add rolling relay stats; push connected devices and scan results; ensure GATT role stop closes connections; log incoming packets and relay events; add UI polling for devices; build passes * chore(debug-branch): remove unrelated files that were mistakenly added in first commit; keep only intended debug settings changes * fix(chat): prevent mesh→geohash leak Root cause: - DebugSettingsManager→chat bridge appended system logs to the global timeline regardless of active channel - MeshDelegateHandler added public mesh messages to UI unconditionally Fixes: - Only inject debug system messages when selected location channel is Mesh - Only add public mesh messages to UI when Mesh is selected (still send notifications) Build: ./gradlew assembleDebug } * fix(timeline): restore message persistence across channel switches Root cause: - NostrGeohashService.switchLocationChannel() called messageManager.clearMessages() which wiped ALL messages (mesh + debug + geohash) from main timeline - Geohash events were adding to main timeline, causing cross-contamination Fixes: - Remove messageManager.clearMessages() from channel switching - ChatScreen displayMessages now routes to separate storage: - Mesh: messages (main timeline, includes debug logs when Mesh selected) - Geohash: viewModel.getGeohashMessages(geohash) from separate history - Private: privateChats[peerID] - Channels: channelMessages[channel] - Geohash events no longer add to main timeline, only to geohash history - Mesh messages always stored to preserve history when switching away Result: Each chat type has persistent separate storage, no message loss * Debug/relay logs: use MessageType names; include device route in verbose packet logs. * Verbose device-peer assignment and connection/disconnection logs with peerId, deviceId, nickname; packet relay log uses MessageType names.} * Debug logs API: include nickname and deviceId for incoming/relay; update callers.} * Fix compile: import MessageType; correct debug manager API usage and remove stray insertion; build.} * Respect relay toggle; helper to log relay with deviceId.} * PacketRelayManager: add relay toggle and unified logging helper; clean file header comments; build fixes.} * changes * Geohash local echo: do not add to mesh timeline; rely on geohash history and ChatScreen display for location channels. * persisting * persist debug settings * gitignore * gatt server / client controls * debugger * max connection settings and graph * more graph * better logging * refactor logging
This commit is contained in:
@@ -6,6 +6,7 @@ import android.util.Log
|
||||
import com.bitchat.android.model.RoutedPacket
|
||||
import com.bitchat.android.protocol.BitchatPacket
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.collect
|
||||
|
||||
/**
|
||||
* Power-optimized Bluetooth connection manager with comprehensive memory management
|
||||
@@ -86,6 +87,44 @@ class BluetoothConnectionManager(
|
||||
|
||||
init {
|
||||
powerManager.delegate = this
|
||||
// Observe debug settings to enforce role state while active
|
||||
try {
|
||||
val dbg = com.bitchat.android.ui.debug.DebugSettingsManager.getInstance()
|
||||
// Role enable/disable
|
||||
connectionScope.launch {
|
||||
dbg.gattServerEnabled.collect { enabled ->
|
||||
if (!isActive) return@collect
|
||||
if (enabled) startServer() else stopServer()
|
||||
}
|
||||
}
|
||||
connectionScope.launch {
|
||||
dbg.gattClientEnabled.collect { enabled ->
|
||||
if (!isActive) return@collect
|
||||
if (enabled) startClient() else stopClient()
|
||||
}
|
||||
}
|
||||
// Connection caps: enforce on change
|
||||
connectionScope.launch {
|
||||
dbg.maxConnectionsOverall.collect {
|
||||
if (!isActive) return@collect
|
||||
connectionTracker.enforceConnectionLimits()
|
||||
// Also enforce server side best-effort
|
||||
serverManager.enforceServerLimit(dbg.maxServerConnections.value)
|
||||
}
|
||||
}
|
||||
connectionScope.launch {
|
||||
dbg.maxClientConnections.collect {
|
||||
if (!isActive) return@collect
|
||||
connectionTracker.enforceConnectionLimits()
|
||||
}
|
||||
}
|
||||
connectionScope.launch {
|
||||
dbg.maxServerConnections.collect {
|
||||
if (!isActive) return@collect
|
||||
serverManager.enforceServerLimit(dbg.maxServerConnections.value)
|
||||
}
|
||||
}
|
||||
} catch (_: Exception) { }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -125,18 +164,29 @@ class BluetoothConnectionManager(
|
||||
// Start power manager
|
||||
powerManager.start()
|
||||
|
||||
// Start server manager
|
||||
if (!serverManager.start()) {
|
||||
Log.e(TAG, "Failed to start server manager")
|
||||
this@BluetoothConnectionManager.isActive = false
|
||||
return@launch
|
||||
// Start server/client based on debug settings
|
||||
val dbg = try { com.bitchat.android.ui.debug.DebugSettingsManager.getInstance() } catch (_: Exception) { null }
|
||||
val startServer = dbg?.gattServerEnabled?.value != false
|
||||
val startClient = dbg?.gattClientEnabled?.value != false
|
||||
|
||||
if (startServer) {
|
||||
if (!serverManager.start()) {
|
||||
Log.e(TAG, "Failed to start server manager")
|
||||
this@BluetoothConnectionManager.isActive = false
|
||||
return@launch
|
||||
}
|
||||
} else {
|
||||
Log.i(TAG, "GATT Server disabled by debug settings; not starting")
|
||||
}
|
||||
|
||||
// Start client manager
|
||||
if (!clientManager.start()) {
|
||||
Log.e(TAG, "Failed to start client manager")
|
||||
this@BluetoothConnectionManager.isActive = false
|
||||
return@launch
|
||||
|
||||
if (startClient) {
|
||||
if (!clientManager.start()) {
|
||||
Log.e(TAG, "Failed to start client manager")
|
||||
this@BluetoothConnectionManager.isActive = false
|
||||
return@launch
|
||||
}
|
||||
} else {
|
||||
Log.i(TAG, "GATT Client disabled by debug settings; not starting")
|
||||
}
|
||||
|
||||
Log.i(TAG, "Bluetooth services started successfully")
|
||||
@@ -198,6 +248,58 @@ class BluetoothConnectionManager(
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
// Expose role controls for debug UI
|
||||
fun startServer() { connectionScope.launch { serverManager.start() } }
|
||||
fun stopServer() { connectionScope.launch { serverManager.stop() } }
|
||||
fun startClient() { connectionScope.launch { clientManager.start() } }
|
||||
fun stopClient() { connectionScope.launch { clientManager.stop() } }
|
||||
|
||||
// Inject nickname resolver for broadcaster logs
|
||||
fun setNicknameResolver(resolver: (String) -> String?) { packetBroadcaster.setNicknameResolver(resolver) }
|
||||
|
||||
// Debug snapshots for connected devices
|
||||
fun getConnectedDeviceEntries(): List<Triple<String, Boolean, Int?>> {
|
||||
return try {
|
||||
connectionTracker.getConnectedDevices().values.map { dc ->
|
||||
val rssi = if (dc.rssi != Int.MIN_VALUE) dc.rssi else null
|
||||
Triple(dc.device.address, dc.isClient, rssi)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
// Expose local adapter address for debug UI
|
||||
fun getLocalAdapterAddress(): String? = try { bluetoothAdapter?.address } catch (e: Exception) { null }
|
||||
|
||||
fun isClientConnection(address: String): Boolean? {
|
||||
return try { connectionTracker.getConnectedDevices()[address]?.isClient } catch (e: Exception) { null }
|
||||
}
|
||||
|
||||
/**
|
||||
* Public: connect/disconnect helpers for debug UI
|
||||
*/
|
||||
fun connectToAddress(address: String): Boolean = clientManager.connectToAddress(address)
|
||||
fun disconnectAddress(address: String) { connectionTracker.disconnectDevice(address) }
|
||||
|
||||
|
||||
// Optionally disconnect all connections (server and client)
|
||||
fun disconnectAll() {
|
||||
connectionScope.launch {
|
||||
// Stop and restart to force disconnects
|
||||
clientManager.stop()
|
||||
serverManager.stop()
|
||||
delay(200)
|
||||
if (isActive) {
|
||||
// Restart managers if service is active
|
||||
serverManager.start()
|
||||
clientManager.start()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get connected device count
|
||||
*/
|
||||
@@ -230,20 +332,35 @@ class BluetoothConnectionManager(
|
||||
// Avoid rapid scan restarts by checking if we need to change scan behavior
|
||||
val wasUsingDutyCycle = powerManager.shouldUseDutyCycle()
|
||||
|
||||
// Update advertising with new power settings
|
||||
serverManager.restartAdvertising()
|
||||
// Update advertising with new power settings if server enabled
|
||||
val serverEnabled = try { com.bitchat.android.ui.debug.DebugSettingsManager.getInstance().gattServerEnabled.value } catch (_: Exception) { true }
|
||||
if (serverEnabled) {
|
||||
serverManager.restartAdvertising()
|
||||
} else {
|
||||
serverManager.stop()
|
||||
}
|
||||
|
||||
// Only restart scanning if the duty cycle behavior changed
|
||||
val nowUsingDutyCycle = powerManager.shouldUseDutyCycle()
|
||||
if (wasUsingDutyCycle != nowUsingDutyCycle) {
|
||||
Log.d(TAG, "Duty cycle behavior changed (${wasUsingDutyCycle} -> ${nowUsingDutyCycle}), restarting scan")
|
||||
clientManager.restartScanning()
|
||||
val clientEnabled = try { com.bitchat.android.ui.debug.DebugSettingsManager.getInstance().gattClientEnabled.value } catch (_: Exception) { true }
|
||||
if (clientEnabled) {
|
||||
clientManager.restartScanning()
|
||||
} else {
|
||||
clientManager.stop()
|
||||
}
|
||||
} else {
|
||||
Log.d(TAG, "Duty cycle behavior unchanged, keeping existing scan state")
|
||||
}
|
||||
|
||||
// Enforce connection limits
|
||||
connectionTracker.enforceConnectionLimits()
|
||||
// Best-effort server cap
|
||||
try {
|
||||
val maxServer = com.bitchat.android.ui.debug.DebugSettingsManager.getInstance().maxServerConnections.value
|
||||
serverManager.enforceServerLimit(maxServer)
|
||||
} catch (_: Exception) { }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user