Revert "fix: wifi aware socket binding (#533)" (#535)

This reverts commit 0c7505b588.
This commit is contained in:
callebtc
2026-01-03 23:37:49 +07:00
committed by GitHub
parent 0c7505b588
commit fa1978d587
18 changed files with 17 additions and 1722 deletions
-9
View File
@@ -5,7 +5,6 @@
<!-- Internet permissions for Nostr relay connections -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<!-- Bluetooth permissions -->
<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
@@ -20,12 +19,6 @@
<!-- Notification permissions -->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<!-- WiFi / WiFi Aware permissions -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<!-- Android 13+ runtime permission for WiFi operations (including Aware) -->
<uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES" />
<!-- Signature permission for internal UI shutdown broadcasts -->
<uses-permission android:name="com.bitchat.android.permission.FORCE_FINISH" />
<!-- Foreground service and boot permissions for long-running background mesh -->
@@ -54,8 +47,6 @@
<!-- Hardware features -->
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />
<uses-feature android:name="android.hardware.bluetooth" android:required="true" />
<!-- Device support hint for WiFi Aware (optional) -->
<uses-feature android:name="android.hardware.wifi.aware" android:required="false" />
<permission
android:name="com.bitchat.android.permission.FORCE_FINISH"
@@ -41,11 +41,6 @@ class BitchatApplication : Application() {
// Initialize debug preference manager (persists debug toggles)
try { com.bitchat.android.ui.debug.DebugPreferenceManager.init(this) } catch (_: Exception) { }
// Initialize WiFi Aware controller with persisted default
try {
val enabled = com.bitchat.android.ui.debug.DebugPreferenceManager.getWifiAwareEnabled(false)
com.bitchat.android.wifiaware.WifiAwareController.initialize(this, enabled)
} catch (_: Exception) { }
// Initialize mesh service preferences
try { com.bitchat.android.service.MeshServicePreferences.init(this) } catch (_: Exception) { }
@@ -112,8 +112,6 @@ class MainActivity : OrientationAwareActivity() {
// Ensure foreground service is running and get mesh instance from holder
try { com.bitchat.android.service.MeshForegroundService.start(applicationContext) } catch (_: Exception) { }
meshService = com.bitchat.android.service.MeshServiceHolder.getOrCreate(applicationContext)
// Expose BLE mesh to WiFi Aware controller for cross-transport relays
try { com.bitchat.android.wifiaware.WifiAwareController.setBleMeshService(meshService) } catch (_: Exception) { }
bluetoothStatusManager = BluetoothStatusManager(
activity = this,
context = this,
@@ -161,52 +159,6 @@ class MainActivity : OrientationAwareActivity() {
}
}
}
// Bridge WiFi Aware callbacks into ChatViewModel (reusing BLE delegate methods)
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
com.bitchat.android.wifiaware.WifiAwareController.running.collect { running ->
val svc = com.bitchat.android.wifiaware.WifiAwareController.getService()
if (running && svc != null) {
svc.delegate = object : com.bitchat.android.wifiaware.WifiAwareMeshDelegate {
override fun didReceiveMessage(message: com.bitchat.android.model.BitchatMessage) {
if (message.isPrivate) {
message.senderPeerID?.let { pid -> com.bitchat.android.services.AppStateStore.addPrivateMessage(pid, message) }
} else if (message.channel != null) {
com.bitchat.android.services.AppStateStore.addChannelMessage(message.channel, message)
} else {
com.bitchat.android.services.AppStateStore.addPublicMessage(message)
}
chatViewModel.didReceiveMessage(message)
}
override fun didUpdatePeerList(peers: List<String>) {
chatViewModel.didUpdatePeerList(peers)
}
override fun didReceiveChannelLeave(channel: String, fromPeer: String) {
chatViewModel.didReceiveChannelLeave(channel, fromPeer)
}
override fun didReceiveDeliveryAck(messageID: String, recipientPeerID: String) {
chatViewModel.didReceiveDeliveryAck(messageID, recipientPeerID)
}
override fun didReceiveReadReceipt(messageID: String, recipientPeerID: String) {
chatViewModel.didReceiveReadReceipt(messageID, recipientPeerID)
}
override fun decryptChannelMessage(encryptedContent: ByteArray, channel: String): String? {
return chatViewModel.decryptChannelMessage(encryptedContent, channel)
}
override fun getNickname(): String? {
return chatViewModel.getNickname()
}
override fun isFavorite(peerID: String): Boolean {
return try {
com.bitchat.android.favorites.FavoritesPersistenceService.shared.getFavoriteStatus(peerID)?.isMutual == true
} catch (_: Exception) { false }
}
}
}
}
}
}
// Only start onboarding process if we're in the initial CHECKING state
// This prevents restarting onboarding on configuration changes
@@ -395,12 +347,6 @@ class MainActivity : OrientationAwareActivity() {
bluetoothStatusManager.logBluetoothStatus()
mainViewModel.updateBluetoothStatus(bluetoothStatusManager.checkBluetoothStatus())
val bleRequired = try { com.bitchat.android.ui.debug.DebugPreferenceManager.getBleEnabled(true) } catch (_: Exception) { true }
if (!bleRequired) {
// Skip BLE checks entirely when BLE is disabled in debug settings
checkLocationAndProceed()
return
}
when (mainViewModel.bluetoothStatus.value) {
BluetoothStatus.ENABLED -> {
// Bluetooth is enabled, check location services next
@@ -567,9 +513,8 @@ class MainActivity : OrientationAwareActivity() {
else -> BatteryOptimizationStatus.ENABLED
}
val bleRequired2 = try { com.bitchat.android.ui.debug.DebugPreferenceManager.getBleEnabled(true) } catch (_: Exception) { true }
when {
bleRequired2 && currentBluetoothStatus != BluetoothStatus.ENABLED -> {
currentBluetoothStatus != BluetoothStatus.ENABLED -> {
// Bluetooth still disabled, but now we have permissions to enable it
Log.d("MainActivity", "Permissions granted, but Bluetooth still disabled. Showing Bluetooth enable screen.")
mainViewModel.updateBluetoothStatus(currentBluetoothStatus)
@@ -94,9 +94,6 @@ class BluetoothMeshService(private val context: Context) {
} catch (_: Exception) { 0.01 }
}
)
// Register as shared instance for Wi-Fi Aware transport
com.bitchat.android.service.MeshServiceHolder.setGossipManager(gossipSyncManager)
// Wire sync manager delegate
gossipSyncManager.delegate = object : GossipSyncManager.Delegate {
@@ -271,8 +268,7 @@ class BluetoothMeshService(private val context: Context) {
override fun sendPacket(packet: BitchatPacket) {
// Sign the packet before broadcasting
val signedPacket = signPacketBeforeBroadcast(packet)
val routed = RoutedPacket(signedPacket)
connectionManager.broadcastPacket(routed)
connectionManager.broadcastPacket(RoutedPacket(signedPacket))
}
override fun relayPacket(routed: RoutedPacket) {
@@ -209,7 +209,6 @@ class OnboardingCoordinator(
return when {
permission.contains("BLUETOOTH") -> "Bluetooth/Nearby Devices"
permission.contains("LOCATION") -> "Location (for Bluetooth scanning)"
permission.contains("NEARBY_WIFI") -> "Nearby WiFi Devices (for WiFi Aware)"
permission.contains("NOTIFICATION") -> "Notifications"
else -> permission.substringAfterLast(".")
}
@@ -11,7 +11,6 @@ import androidx.compose.material.icons.filled.Notifications
import androidx.compose.material.icons.filled.Power
import androidx.compose.material.icons.filled.Mic
import androidx.compose.material.icons.filled.Security
import androidx.compose.material.icons.filled.Wifi
import androidx.compose.material.icons.filled.Settings
import androidx.compose.material.icons.filled.Warning
import androidx.compose.material3.*
@@ -243,7 +242,6 @@ private fun getPermissionIcon(permissionType: PermissionType): ImageVector {
PermissionType.PRECISE_LOCATION -> Icons.Filled.LocationOn
PermissionType.MICROPHONE -> Icons.Filled.Mic
PermissionType.NOTIFICATIONS -> Icons.Filled.Notifications
PermissionType.WIFI_AWARE -> Icons.Filled.Wifi
PermissionType.BATTERY_OPTIMIZATION -> Icons.Filled.Power
PermissionType.OTHER -> Icons.Filled.Settings
}
@@ -67,11 +67,6 @@ class PermissionManager(private val context: Context) {
Manifest.permission.ACCESS_FINE_LOCATION
))
// WiFi Aware: Android 13+ requires NEARBY_WIFI_DEVICES runtime permission
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
permissions.add(Manifest.permission.NEARBY_WIFI_DEVICES)
}
// Notification permission intentionally excluded to keep it optional
return permissions
@@ -182,20 +177,6 @@ class PermissionManager(private val context: Context) {
)
)
// WiFi Aware category (Android 13+)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
val wifiAwarePermissions = listOf(Manifest.permission.NEARBY_WIFI_DEVICES)
categories.add(
PermissionCategory(
type = PermissionType.WIFI_AWARE,
description = "Enable WiFi Aware to discover and connect to nearby bitchat users over WiFi.",
permissions = wifiAwarePermissions,
isGranted = wifiAwarePermissions.all { isPermissionGranted(it) },
systemDescription = "Allow bitchat to discover nearby WiFi devices"
)
)
}
// Notifications category (if applicable)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
categories.add(
@@ -281,7 +262,6 @@ enum class PermissionType(val nameValue: String) {
PRECISE_LOCATION("Precise Location"),
MICROPHONE("Microphone"),
NOTIFICATIONS("Notifications"),
WIFI_AWARE("WiFi Aware"),
BATTERY_OPTIMIZATION("Battery Optimization"),
OTHER("Other")
}
@@ -9,12 +9,6 @@ import com.bitchat.android.mesh.BluetoothMeshService
*/
object MeshServiceHolder {
private const val TAG = "MeshServiceHolder"
@Volatile
var sharedGossipSyncManager: com.bitchat.android.sync.GossipSyncManager? = null
private set
fun setGossipManager(mgr: com.bitchat.android.sync.GossipSyncManager) { sharedGossipSyncManager = mgr }
@Volatile
var meshService: BluetoothMeshService? = null
private set
@@ -72,15 +72,9 @@ class MessageRouter private constructor(
val hasMesh = mesh.getPeerInfo(toPeerID)?.isConnected == true
val hasEstablished = mesh.hasEstablishedSession(toPeerID)
// Check WiFi Aware availability as a secondary transport
val aware = try { com.bitchat.android.wifiaware.WifiAwareController.getService() } catch (_: Exception) { null }
val hasAware = try { aware?.getPeerInfo(toPeerID)?.isConnected == true && aware.hasEstablishedSession(toPeerID) } catch (_: Exception) { false }
if (hasMesh && hasEstablished) {
Log.d(TAG, "Routing PM via mesh to ${toPeerID} msg_id=${messageID.take(8)}")
mesh.sendPrivateMessage(content, toPeerID, recipientNickname, messageID)
} else if (hasAware) {
Log.d(TAG, "Routing PM via WiFi Aware to ${toPeerID} msg_id=${messageID.take(8)}")
aware?.sendPrivateMessage(content, toPeerID, recipientNickname, messageID)
} else if (canSendViaNostr(toPeerID)) {
Log.d(TAG, "Routing PM via Nostr to ${toPeerID.take(32)}… msg_id=${messageID.take(8)}")
nostr.sendPrivateMessage(content, toPeerID, recipientNickname, messageID)
@@ -89,21 +83,14 @@ class MessageRouter private constructor(
val q = outbox.getOrPut(toPeerID) { mutableListOf() }
q.add(Triple(content, recipientNickname, messageID))
Log.d(TAG, "Initiating noise handshake after queueing PM for ${toPeerID.take(8)}")
if (hasMesh) mesh.initiateNoiseHandshake(toPeerID) else aware?.initiateNoiseHandshake(toPeerID)
mesh.initiateNoiseHandshake(toPeerID)
}
}
fun sendReadReceipt(receipt: ReadReceipt, toPeerID: String) {
val aware = try { com.bitchat.android.wifiaware.WifiAwareController.getService() } catch (_: Exception) { null }
val viaMesh = (mesh.getPeerInfo(toPeerID)?.isConnected == true) && mesh.hasEstablishedSession(toPeerID)
val viaAware = try { aware?.getPeerInfo(toPeerID)?.isConnected == true && aware.hasEstablishedSession(toPeerID) } catch (_: Exception) { false }
if (viaMesh) {
if ((mesh.getPeerInfo(toPeerID)?.isConnected == true) && mesh.hasEstablishedSession(toPeerID)) {
Log.d(TAG, "Routing READ via mesh to ${toPeerID.take(8)}… id=${receipt.originalMessageID.take(8)}")
mesh.sendReadReceipt(receipt.originalMessageID, toPeerID, mesh.getPeerNicknames()[toPeerID] ?: mesh.myPeerID)
} else if (viaAware) {
Log.d(TAG, "Routing READ via WiFi Aware to ${toPeerID.take(8)}… id=${receipt.originalMessageID.take(8)}")
val me = try { aware?.myPeerID } catch (_: Exception) { null }
aware?.sendReadReceipt(receipt.originalMessageID, toPeerID, me ?: "")
} else {
Log.d(TAG, "Routing READ via Nostr to ${toPeerID.take(8)}… id=${receipt.originalMessageID.take(8)}")
nostr.sendReadReceipt(receipt, toPeerID)
@@ -441,9 +441,8 @@ class ChatViewModel(
state.getNicknameValue()
)
} else {
// Default: route via mesh + WiFi Aware
// Default: route via mesh
meshService.sendMessage(messageContent, mentions, channel)
try { com.bitchat.android.wifiaware.WifiAwareController.getService()?.sendMessage(messageContent, mentions, channel) } catch (_: Exception) {}
}
})
return
@@ -513,23 +512,19 @@ class ChatViewModel(
state.getNicknameValue(),
meshService.myPeerID,
onEncryptedPayload = { encryptedData ->
// Send encrypted payload announcement over both transports for reachability
// This would need proper mesh service integration
meshService.sendMessage(content, mentions, currentChannelValue)
try { com.bitchat.android.wifiaware.WifiAwareController.getService()?.sendMessage(content, mentions, currentChannelValue) } catch (_: Exception) {}
},
onFallback = {
meshService.sendMessage(content, mentions, currentChannelValue)
try { com.bitchat.android.wifiaware.WifiAwareController.getService()?.sendMessage(content, mentions, currentChannelValue) } catch (_: Exception) {}
}
)
} else {
meshService.sendMessage(content, mentions, currentChannelValue)
try { com.bitchat.android.wifiaware.WifiAwareController.getService()?.sendMessage(content, mentions, currentChannelValue) } catch (_: Exception) {}
}
} else {
messageManager.addMessage(message)
meshService.sendMessage(content, mentions, null)
try { com.bitchat.android.wifiaware.WifiAwareController.getService()?.sendMessage(content, mentions, null) } catch (_: Exception) {}
}
}
}
@@ -652,23 +647,16 @@ class ChatViewModel(
val fingerprints = privateChatManager.getAllPeerFingerprints()
state.setPeerFingerprints(fingerprints)
// Merge nicknames from BLE and WiFi Aware to display names for all peers
val bleNick = meshService.getPeerNicknames()
val awareNickRaw = try { com.bitchat.android.wifiaware.WifiAwareController.getService()?.getPeerNicknamesMap() } catch (_: Exception) { null }
val mergedNick = if (awareNickRaw != null) bleNick + awareNickRaw.filter { it.value != null }.mapValues { it.value!! }.filterKeys { it !in bleNick || bleNick[it].isNullOrBlank() } else bleNick
state.setPeerNicknames(mergedNick)
val nicknames = meshService.getPeerNicknames()
state.setPeerNicknames(nicknames)
val rssiValues = meshService.getPeerRSSI()
val awareRssi = try { com.bitchat.android.wifiaware.WifiAwareController.getService()?.getPeerRSSI() } catch (_: Exception) { null }
val mergedRssi = if (awareRssi != null) rssiValues + awareRssi.filterKeys { it !in rssiValues } else rssiValues
state.setPeerRSSI(mergedRssi)
state.setPeerRSSI(rssiValues)
// Update directness per peer (driven by PeerManager state)
try {
val directMap = state.getConnectedPeersValue().associateWith { pid ->
val ble = meshService.getPeerInfo(pid)?.isDirectConnection == true
val aware = try { com.bitchat.android.wifiaware.WifiAwareController.getService()?.getPeerInfo(pid)?.isDirectConnection == true } catch (_: Exception) { false }
ble || aware
meshService.getPeerInfo(pid)?.isDirectConnection == true
}
state.setPeerDirect(directMap)
} catch (_: Exception) { }
@@ -210,7 +210,6 @@ class MediaSendingManager(
Log.d(TAG, "📤 Calling meshService.sendFilePrivate to $toPeerID")
meshService.sendFilePrivate(toPeerID, filePacket)
try { com.bitchat.android.wifiaware.WifiAwareController.getService()?.sendFilePrivate(toPeerID, filePacket) } catch (_: Exception) {}
Log.d(TAG, "✅ File send completed successfully")
}
@@ -265,7 +264,6 @@ class MediaSendingManager(
Log.d(TAG, "📤 Calling meshService.sendFileBroadcast")
meshService.sendFileBroadcast(filePacket)
try { com.bitchat.android.wifiaware.WifiAwareController.getService()?.sendFileBroadcast(filePacket) } catch (_: Exception) {}
Log.d(TAG, "✅ File broadcast completed successfully")
}
@@ -96,10 +96,7 @@ class MeshDelegateHandler(
override fun didUpdatePeerList(peers: List<String>) {
coroutineScope.launch {
// Merge peers from multiple transports to avoid flapping
val current = state.getConnectedPeersValue().toMutableSet()
current.addAll(peers)
state.setConnectedPeers(current.toList())
state.setConnectedPeers(peers)
state.setIsConnected(peers.isNotEmpty())
notificationManager.showActiveUserNotification(peers)
// Flush router outbox for any peers that just connected (and their noiseHex aliases)
@@ -576,26 +576,9 @@ private fun PeerItem(
tint = Color.Gray
)
} else {
val awareConnected by com.bitchat.android.wifiaware.WifiAwareController.connectedPeers.collectAsState()
val awareDiscovered by com.bitchat.android.wifiaware.WifiAwareController.discoveredPeers.collectAsState()
val isWifiDirect = awareConnected.containsKey(peerID)
val isBleDirect = isDirect
val icon = when {
isWifiDirect -> Icons.Filled.Wifi
isBleDirect -> Icons.Outlined.SettingsInputAntenna
// Routed: show Route icon; optionally prefer WiFi Aware if discovered there
awareDiscovered.contains(peerID) -> Icons.Filled.WifiTethering
else -> Icons.Filled.Route
}
val cd = when {
isWifiDirect -> "Direct WiFi Aware"
isBleDirect -> "Direct Bluetooth"
awareDiscovered.contains(peerID) -> "Routed over WiFi"
else -> "Routed"
}
Icon(
imageVector = icon,
contentDescription = cd,
imageVector = if (isDirect) Icons.Outlined.SettingsInputAntenna else Icons.Filled.Route,
contentDescription = if (isDirect) "Direct Bluetooth" else "Routed",
modifier = Modifier.size(16.dp),
tint = colorScheme.onSurface.copy(alpha = 0.8f)
)
@@ -20,10 +20,7 @@ object DebugPreferenceManager {
// GCS keys (no migration/back-compat)
private const val KEY_GCS_MAX_BYTES = "gcs_max_filter_bytes"
private const val KEY_GCS_FPR = "gcs_filter_fpr_percent"
// Transport master toggles
private const val KEY_BLE_ENABLED = "ble_enabled"
private const val KEY_WIFI_AWARE_ENABLED = "wifi_aware_enabled"
private const val KEY_WIFI_AWARE_VERBOSE = "wifi_aware_verbose"
// Removed: persistent notification toggle is now governed by MeshServicePreferences.isBackgroundEnabled
private lateinit var prefs: SharedPreferences
@@ -105,25 +102,5 @@ object DebugPreferenceManager {
if (ready()) prefs.edit().putLong(KEY_GCS_FPR, java.lang.Double.doubleToRawLongBits(value)).apply()
}
// Transport toggles
fun getBleEnabled(default: Boolean = true): Boolean =
if (ready()) prefs.getBoolean(KEY_BLE_ENABLED, default) else default
fun setBleEnabled(value: Boolean) {
if (ready()) prefs.edit().putBoolean(KEY_BLE_ENABLED, value).apply()
}
fun getWifiAwareEnabled(default: Boolean = false): Boolean =
if (ready()) prefs.getBoolean(KEY_WIFI_AWARE_ENABLED, default) else default
fun setWifiAwareEnabled(value: Boolean) {
if (ready()) prefs.edit().putBoolean(KEY_WIFI_AWARE_ENABLED, value).apply()
}
fun getWifiAwareVerbose(default: Boolean = false): Boolean =
if (ready()) prefs.getBoolean(KEY_WIFI_AWARE_VERBOSE, default) else default
fun setWifiAwareVerbose(value: Boolean) {
if (ready()) prefs.edit().putBoolean(KEY_WIFI_AWARE_VERBOSE, value).apply()
}
// No longer storing persistent notification in debug prefs.
}
@@ -36,17 +36,6 @@ class DebugSettingsManager private constructor() {
private val _packetRelayEnabled = MutableStateFlow(true)
val packetRelayEnabled: StateFlow<Boolean> = _packetRelayEnabled.asStateFlow()
// Master transport toggles
private val _bleEnabled = MutableStateFlow(true)
val bleEnabled: StateFlow<Boolean> = _bleEnabled.asStateFlow()
private val _wifiAwareEnabled = MutableStateFlow(false)
val wifiAwareEnabled: StateFlow<Boolean> = _wifiAwareEnabled.asStateFlow()
// Master transport toggles
private val _wifiAwareVerbose = MutableStateFlow(false)
val wifiAwareVerbose: StateFlow<Boolean> = _wifiAwareVerbose.asStateFlow()
// Visibility of the debug sheet; gates heavy work
private val _debugSheetVisible = MutableStateFlow(false)
val debugSheetVisible: StateFlow<Boolean> = _debugSheetVisible.asStateFlow()
@@ -70,10 +59,6 @@ class DebugSettingsManager private constructor() {
_maxConnectionsOverall.value = DebugPreferenceManager.getMaxConnectionsOverall(8)
_maxServerConnections.value = DebugPreferenceManager.getMaxConnectionsServer(8)
_maxClientConnections.value = DebugPreferenceManager.getMaxConnectionsClient(8)
// Transport toggles
_bleEnabled.value = DebugPreferenceManager.getBleEnabled(true)
_wifiAwareEnabled.value = DebugPreferenceManager.getWifiAwareEnabled(false)
_wifiAwareVerbose.value = DebugPreferenceManager.getWifiAwareVerbose(false)
} catch (_: Exception) {
// Preferences not ready yet; keep defaults. They will be applied on first change.
}
@@ -277,27 +262,6 @@ class DebugSettingsManager private constructor() {
))
}
fun setBleEnabled(enabled: Boolean) {
DebugPreferenceManager.setBleEnabled(enabled)
_bleEnabled.value = enabled
addDebugMessage(DebugMessage.SystemMessage(if (enabled) "🟢 BLE enabled" else "🔴 BLE disabled"))
}
fun setWifiAwareEnabled(enabled: Boolean) {
DebugPreferenceManager.setWifiAwareEnabled(enabled)
_wifiAwareEnabled.value = enabled
addDebugMessage(DebugMessage.SystemMessage(if (enabled) "🟢 WiFi Aware enabled" else "🔴 WiFi Aware disabled"))
try {
com.bitchat.android.wifiaware.WifiAwareController.setEnabled(enabled)
} catch (_: Exception) { }
}
fun setWifiAwareVerbose(enabled: Boolean) {
DebugPreferenceManager.setWifiAwareVerbose(enabled)
_wifiAwareVerbose.value = enabled
addDebugMessage(DebugMessage.SystemMessage(if (enabled) "🔊 WiFi Aware verbose logging enabled" else "🔇 WiFi Aware verbose logging disabled"))
}
fun setMaxConnectionsOverall(value: Int) {
val clamped = value.coerceIn(1, 32)
DebugPreferenceManager.setMaxConnectionsOverall(clamped)
@@ -355,16 +319,6 @@ class DebugSettingsManager private constructor() {
fun updateConnectedDevices(devices: List<ConnectedDevice>) {
_connectedDevices.value = devices
}
// WiFi Aware debug collections
private val _wifiAwareDiscovered = MutableStateFlow<Map<String, String>>(emptyMap()) // peerID->nickname
val wifiAwareDiscovered: StateFlow<Map<String, String>> = _wifiAwareDiscovered.asStateFlow()
private val _wifiAwareConnected = MutableStateFlow<Map<String, String>>(emptyMap()) // peerID->ip
val wifiAwareConnected: StateFlow<Map<String, String>> = _wifiAwareConnected.asStateFlow()
fun updateWifiAwareDiscovered(map: Map<String, String>) { _wifiAwareDiscovered.value = map }
fun updateWifiAwareConnected(map: Map<String, String>) { _wifiAwareConnected.value = map }
fun updateRelayStats(stats: PacketRelayStats) {
_relayStats.value = stats
@@ -10,8 +10,6 @@ import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Bluetooth
import androidx.compose.material.icons.filled.Wifi
import androidx.compose.material.icons.filled.WifiTethering
import androidx.compose.material.icons.filled.BugReport
import androidx.compose.material.icons.filled.Cancel
import androidx.compose.material.icons.filled.Devices
@@ -52,7 +50,7 @@ fun DebugSettingsSheet(
val verboseLogging by manager.verboseLoggingEnabled.collectAsState()
val gattServerEnabled by manager.gattServerEnabled.collectAsState()
val gattClientEnabled by manager.gattClientEnabled.collectAsState()
val packetRelayed by manager.packetRelayEnabled.collectAsState()
val packetRelayEnabled by manager.packetRelayEnabled.collectAsState()
val maxOverall by manager.maxConnectionsOverall.collectAsState()
val maxServer by manager.maxServerConnections.collectAsState()
val maxClient by manager.maxClientConnections.collectAsState()
@@ -64,12 +62,6 @@ fun DebugSettingsSheet(
val gcsMaxBytes by manager.gcsMaxBytes.collectAsState()
val gcsFpr by manager.gcsFprPercent.collectAsState()
val context = LocalContext.current
val bleEnabled by manager.bleEnabled.collectAsState()
val wifiAwareEnabled by manager.wifiAwareEnabled.collectAsState()
val wifiAwareVerbose by manager.wifiAwareVerbose.collectAsState()
val wifiAwareDiscovered by manager.wifiAwareDiscovered.collectAsState()
val wifiAwareConnected by manager.wifiAwareConnected.collectAsState()
// Persistent notification is now controlled solely by MeshServicePreferences.isBackgroundEnabled
// Push live connected devices from mesh service whenever sheet is visible
@@ -94,15 +86,6 @@ fun DebugSettingsSheet(
)
}
manager.updateConnectedDevices(devices)
// Also surface WiFi Aware status
try {
val ctrl = com.bitchat.android.wifiaware.WifiAwareController
val known = ctrl.knownPeers.value
val discovered = ctrl.discoveredPeers.value
val discoveredMap = discovered.associateWith { pid -> known[pid] ?: "" }
manager.updateWifiAwareDiscovered(discoveredMap)
manager.updateWifiAwareConnected(ctrl.connectedPeers.value)
} catch (_: Exception) { }
kotlinx.coroutines.delay(1000)
}
}
@@ -230,46 +213,6 @@ fun DebugSettingsSheet(
}
}
// Transport toggles (BLE + WiFi Aware)
item {
Surface(shape = RoundedCornerShape(12.dp), color = colorScheme.surfaceVariant.copy(alpha = 0.2f)) {
Column(Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(12.dp)) {
Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(8.dp)) {
Icon(Icons.Filled.Devices, contentDescription = null, tint = Color(0xFF4CAF50))
Text("Transports", fontFamily = FontFamily.Monospace, fontSize = 14.sp, fontWeight = FontWeight.Medium)
}
Row(verticalAlignment = Alignment.CenterVertically) {
Icon(Icons.Filled.Bluetooth, contentDescription = null, tint = Color(0xFF007AFF))
Spacer(Modifier.width(8.dp))
Text("BLE", fontFamily = FontFamily.Monospace, modifier = Modifier.weight(1f))
Switch(checked = bleEnabled, onCheckedChange = {
manager.setBleEnabled(it)
scope.launch {
if (it) {
if (gattServerEnabled) meshService.connectionManager.startServer()
if (gattClientEnabled) meshService.connectionManager.startClient()
} else {
meshService.connectionManager.stopServer()
meshService.connectionManager.stopClient()
}
}
})
}
Row(verticalAlignment = Alignment.CenterVertically) {
Icon(Icons.Filled.Wifi, contentDescription = null, tint = Color(0xFF9C27B0))
Spacer(Modifier.width(8.dp))
Text("WiFi Aware", fontFamily = FontFamily.Monospace, modifier = Modifier.weight(1f))
Switch(checked = wifiAwareEnabled, onCheckedChange = { manager.setWifiAwareEnabled(it) })
}
Row(verticalAlignment = Alignment.CenterVertically) {
Spacer(Modifier.width(24.dp))
Text("WiFi Aware verbose", fontFamily = FontFamily.Monospace, modifier = Modifier.weight(1f))
Switch(checked = wifiAwareVerbose, onCheckedChange = { manager.setWifiAwareVerbose(it) })
}
}
}
}
// Packet relay controls and stats
item {
Surface(shape = RoundedCornerShape(12.dp), color = colorScheme.surfaceVariant.copy(alpha = 0.2f)) {
@@ -280,7 +223,7 @@ fun DebugSettingsSheet(
Icon(Icons.Filled.PowerSettingsNew, contentDescription = null, tint = Color(0xFFFF9500))
Text(stringResource(R.string.debug_packet_relay), fontFamily = FontFamily.Monospace, fontSize = 14.sp, fontWeight = FontWeight.Medium)
Spacer(Modifier.weight(1f))
Switch(checked = packetRelayed, onCheckedChange = { manager.setPacketRelayEnabled(it) })
Switch(checked = packetRelayEnabled, onCheckedChange = { manager.setPacketRelayEnabled(it) })
}
// Removed aggregate labels; we will show per-direction compact labels below titles
// Toggle: overall vs per-connection vs per-peer
@@ -529,43 +472,6 @@ fun DebugSettingsSheet(
}
}
// WiFi Aware controls and status
item {
Surface(shape = RoundedCornerShape(12.dp), color = colorScheme.surfaceVariant.copy(alpha = 0.2f)) {
Column(Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(10.dp)) {
Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(8.dp)) {
Icon(Icons.Filled.WifiTethering, contentDescription = null, tint = Color(0xFF9C27B0))
Text("WiFi Aware", fontFamily = FontFamily.Monospace, fontSize = 14.sp, fontWeight = FontWeight.Medium)
Spacer(Modifier.weight(1f))
val running by com.bitchat.android.wifiaware.WifiAwareController.running.collectAsState()
Text(if (running) "running" else "stopped", fontFamily = FontFamily.Monospace, fontSize = 12.sp, color = colorScheme.onSurface.copy(alpha = 0.7f))
}
Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) {
AssistChip(onClick = { com.bitchat.android.wifiaware.WifiAwareController.startIfPossible() }, label = { Text("Start") })
AssistChip(onClick = { com.bitchat.android.wifiaware.WifiAwareController.stop() }, label = { Text("Stop") })
AssistChip(onClick = { com.bitchat.android.wifiaware.WifiAwareController.getService()?.sendBroadcastAnnounce() }, label = { Text("Announce") })
}
Text("Discovered: ${wifiAwareDiscovered.size}", fontFamily = FontFamily.Monospace, fontSize = 12.sp)
if (wifiAwareDiscovered.isEmpty()) {
Text("No discoveries yet", fontFamily = FontFamily.Monospace, fontSize = 11.sp, color = colorScheme.onSurface.copy(alpha = 0.6f))
} else {
wifiAwareDiscovered.entries.take(50).forEach { (peer, nick) ->
Text("${if (nick.isBlank()) peer.take(8) + "…" else nick} (${peer.take(8)}…) ", fontFamily = FontFamily.Monospace, fontSize = 12.sp)
}
}
Divider()
Text("Connected: ${wifiAwareConnected.size}", fontFamily = FontFamily.Monospace, fontSize = 12.sp)
if (wifiAwareConnected.isEmpty()) {
Text("No active sockets", fontFamily = FontFamily.Monospace, fontSize = 11.sp, color = colorScheme.onSurface.copy(alpha = 0.6f))
} else {
wifiAwareConnected.entries.take(50).forEach { (peer, ip) ->
Text("${peer.take(8)}… @ $ip", fontFamily = FontFamily.Monospace, fontSize = 12.sp)
}
}
}
}
}
// Connected devices
item {
Surface(shape = RoundedCornerShape(12.dp), color = colorScheme.surfaceVariant.copy(alpha = 0.2f)) {
@@ -1,116 +0,0 @@
package com.bitchat.android.wifiaware
import android.content.Context
import android.os.Build
import android.util.Log
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
/**
* WifiAwareController manages lifecycle and debug surfacing for the WifiAwareMeshService.
* It starts/stops the service based on debug preferences and exposes simple flows for UI.
*/
object WifiAwareController {
private const val TAG = "WifiAwareController"
private var service: WifiAwareMeshService? = null
private var appContext: Context? = null
private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
private val _enabled = MutableStateFlow(false)
val enabled: StateFlow<Boolean> = _enabled.asStateFlow()
private val _running = MutableStateFlow(false)
val running: StateFlow<Boolean> = _running.asStateFlow()
// Simple debug surfacing
private val _connectedPeers = MutableStateFlow<Map<String, String>>(emptyMap()) // peerID -> ip
val connectedPeers: StateFlow<Map<String, String>> = _connectedPeers.asStateFlow()
private val _knownPeers = MutableStateFlow<Map<String, String>>(emptyMap()) // peerID -> nickname
val knownPeers: StateFlow<Map<String, String>> = _knownPeers.asStateFlow()
private val _discoveredPeers = MutableStateFlow<Set<String>>(emptySet())
val discoveredPeers: StateFlow<Set<String>> = _discoveredPeers.asStateFlow()
fun initialize(context: Context, enabledByDefault: Boolean) {
appContext = context.applicationContext
setEnabled(enabledByDefault)
// Start background poller for debug surfacing
scope.launch {
while (isActive) {
try {
val s = service
if (s != null) {
_connectedPeers.value = s.getDeviceAddressToPeerMapping() // peerID -> ip
_knownPeers.value = s.getPeerNicknames()
_discoveredPeers.value = s.getDiscoveredPeerIds()
} else {
_connectedPeers.value = emptyMap()
_knownPeers.value = emptyMap()
_discoveredPeers.value = emptySet()
}
} catch (_: Exception) { }
delay(1000)
}
}
}
fun setEnabled(value: Boolean) {
_enabled.value = value
if (value) startIfPossible() else stop()
}
fun startIfPossible() {
if (_running.value) return
val ctx = appContext ?: return
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
Log.w(TAG, "WiFi Aware requires Android 10 (Q)+; disabled.")
try { com.bitchat.android.ui.debug.DebugSettingsManager.getInstance().addDebugMessage(com.bitchat.android.ui.debug.DebugMessage.SystemMessage("WiFi Aware not supported on this device (requires Android 10+)")) } catch (_: Exception) {}
return
}
// Android 13+: require NEARBY_WIFI_DEVICES runtime permission
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
val granted = androidx.core.content.ContextCompat.checkSelfPermission(ctx, android.Manifest.permission.NEARBY_WIFI_DEVICES) == android.content.pm.PackageManager.PERMISSION_GRANTED
if (!granted) {
Log.w(TAG, "Missing NEARBY_WIFI_DEVICES permission; not starting WiFi Aware")
try { com.bitchat.android.ui.debug.DebugSettingsManager.getInstance().addDebugMessage(com.bitchat.android.ui.debug.DebugMessage.SystemMessage("Grant Nearby WiFi Devices to start WiFi Aware")) } catch (_: Exception) {}
return
}
}
try {
service = WifiAwareMeshService(ctx).also {
it.startServices()
_running.value = true
try { com.bitchat.android.ui.debug.DebugSettingsManager.getInstance().addDebugMessage(com.bitchat.android.ui.debug.DebugMessage.SystemMessage("WiFi Aware started")) } catch (_: Exception) {}
}
} catch (e: Throwable) {
Log.e(TAG, "Failed to start WifiAwareMeshService", e)
_running.value = false
try { com.bitchat.android.ui.debug.DebugSettingsManager.getInstance().addDebugMessage(com.bitchat.android.ui.debug.DebugMessage.SystemMessage("WiFi Aware failed to start: ${e.message}")) } catch (_: Exception) {}
}
}
fun stop() {
try { service?.stopServices() } catch (_: Exception) { }
service = null
_running.value = false
try { com.bitchat.android.ui.debug.DebugSettingsManager.getInstance().addDebugMessage(com.bitchat.android.ui.debug.DebugMessage.SystemMessage("WiFi Aware stopped")) } catch (_: Exception) {}
}
fun getService(): WifiAwareMeshService? = service
// Optional bridge to BLE mesh for cross-transport relaying
@Volatile private var bleMesh: com.bitchat.android.mesh.BluetoothMeshService? = null
fun setBleMeshService(svc: com.bitchat.android.mesh.BluetoothMeshService) { bleMesh = svc }
fun getBleMeshService(): com.bitchat.android.mesh.BluetoothMeshService? = bleMesh
}
File diff suppressed because it is too large Load Diff