package com.bitchat.android.mesh import android.bluetooth.* import android.content.Context import android.util.Log import com.bitchat.android.model.RoutedPacket import com.bitchat.android.protocol.BitchatPacket import kotlinx.coroutines.* /** * Power-optimized Bluetooth connection manager with comprehensive memory management * Integrates with PowerManager for adaptive power consumption * Coordinates smaller, focused components for better maintainability */ class BluetoothConnectionManager( private val context: Context, private val myPeerID: String ) : PowerManagerDelegate { companion object { private const val TAG = "BluetoothConnectionManager" } // Core Bluetooth components private val bluetoothManager: BluetoothManager = context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager private val bluetoothAdapter: BluetoothAdapter? = bluetoothManager.adapter // Power management private val powerManager = PowerManager(context) // Coroutines private val connectionScope = CoroutineScope(Dispatchers.IO + SupervisorJob()) // Component managers private val permissionManager = BluetoothPermissionManager(context) private val connectionTracker = BluetoothConnectionTracker(connectionScope, powerManager) private val packetBroadcaster = BluetoothPacketBroadcaster(connectionScope, connectionTracker) private val serverManager = BluetoothGattServerManager( context, connectionScope, connectionTracker, permissionManager, powerManager, null ) private val clientManager = BluetoothGattClientManager( context, connectionScope, connectionTracker, permissionManager, powerManager, null ) // Service state private var isActive = false // Delegate for callbacks var delegate: BluetoothConnectionManagerDelegate? = null // Public property for address-peer mapping val addressPeerMap get() = connectionTracker.addressPeerMap init { powerManager.delegate = this } /** * Start all Bluetooth services with power optimization */ fun startServices(): Boolean { Log.i(TAG, "Starting power-optimized Bluetooth services...") if (!permissionManager.hasBluetoothPermissions()) { Log.e(TAG, "Missing Bluetooth permissions") return false } if (bluetoothAdapter?.isEnabled != true) { Log.e(TAG, "Bluetooth is not enabled") return false } try { isActive = true // Start all component managers connectionScope.launch { // Start connection tracker first connectionTracker.start() // 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 client manager if (!clientManager.start()) { Log.e(TAG, "Failed to start client manager") this@BluetoothConnectionManager.isActive = false return@launch } Log.i(TAG, "Bluetooth services started successfully") } return true } catch (e: Exception) { Log.e(TAG, "Failed to start Bluetooth services: ${e.message}") isActive = false return false } } /** * Stop all Bluetooth services with proper cleanup */ fun stopServices() { Log.i(TAG, "Stopping power-optimized Bluetooth services") isActive = false connectionScope.launch { // Stop component managers clientManager.stop() serverManager.stop() // Stop power manager powerManager.stop() // Stop connection tracker connectionTracker.stop() // Cancel the coroutine scope connectionScope.cancel() Log.i(TAG, "All Bluetooth services stopped") } } /** * Set app background state for power optimization */ fun setAppBackgroundState(inBackground: Boolean) { powerManager.setAppBackgroundState(inBackground) } /** * Broadcast packet to connected devices with connection limit enforcement */ fun broadcastPacket(routed: RoutedPacket) { if (!isActive) return packetBroadcaster.broadcastPacket( routed, serverManager.getGattServer(), serverManager.getCharacteristic() ) } /** * Get connected device count */ fun getConnectedDeviceCount(): Int = connectionTracker.getConnectedDeviceCount() /** * Get debug information including power management */ fun getDebugInfo(): String { return buildString { appendLine("=== Bluetooth Connection Manager ===") appendLine("Bluetooth MAC Address: ${bluetoothAdapter?.address}") appendLine("Active: $isActive") appendLine("Bluetooth Enabled: ${bluetoothAdapter?.isEnabled}") appendLine("Has Permissions: ${permissionManager.hasBluetoothPermissions()}") appendLine("GATT Server Active: ${serverManager.getGattServer() != null}") appendLine() appendLine(powerManager.getPowerInfo()) appendLine() appendLine(connectionTracker.getDebugInfo()) } } // MARK: - PowerManagerDelegate Implementation override fun onPowerModeChanged(newMode: PowerManager.PowerMode) { Log.i(TAG, "Power mode changed to: $newMode") connectionScope.launch { // CRITICAL FIX: 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() // 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() } else { Log.d(TAG, "Duty cycle behavior unchanged, keeping existing scan state") } // Enforce connection limits connectionTracker.enforceConnectionLimits() } } override fun onScanStateChanged(shouldScan: Boolean) { clientManager.onScanStateChanged(shouldScan) } // MARK: - Private Implementation - All moved to component managers } /** * Delegate interface for Bluetooth connection manager callbacks */ interface BluetoothConnectionManagerDelegate { fun onPacketReceived(packet: BitchatPacket, peerID: String, device: BluetoothDevice?) fun onDeviceConnected(device: BluetoothDevice) }