mirror of
https://github.com/permissionlesstech/bitchat-android.git
synced 2026-07-24 22:45:20 +00:00
@@ -20,10 +20,10 @@ class BluetoothConnectionTracker(
|
||||
|
||||
companion object {
|
||||
private const val TAG = "BluetoothConnectionTracker"
|
||||
private const val CONNECTION_RETRY_DELAY = 5000L
|
||||
private const val MAX_CONNECTION_ATTEMPTS = 3
|
||||
private const val CLEANUP_DELAY = 500L
|
||||
private const val CLEANUP_INTERVAL = 30000L // 30 seconds
|
||||
private const val CONNECTION_RETRY_DELAY = com.bitchat.android.util.AppConstants.Mesh.CONNECTION_RETRY_DELAY_MS
|
||||
private const val MAX_CONNECTION_ATTEMPTS = com.bitchat.android.util.AppConstants.Mesh.MAX_CONNECTION_ATTEMPTS
|
||||
private const val CLEANUP_DELAY = com.bitchat.android.util.AppConstants.Mesh.CONNECTION_CLEANUP_DELAY_MS
|
||||
private const val CLEANUP_INTERVAL = com.bitchat.android.util.AppConstants.Mesh.CONNECTION_CLEANUP_INTERVAL_MS // 30 seconds
|
||||
}
|
||||
|
||||
// Connection tracking - reduced memory footprint
|
||||
|
||||
@@ -37,7 +37,7 @@ class BluetoothGattClientManager(
|
||||
private val DESCRIPTOR_UUID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")
|
||||
|
||||
// RSSI monitoring constants
|
||||
private const val RSSI_UPDATE_INTERVAL = 5000L // 5 seconds
|
||||
private const val RSSI_UPDATE_INTERVAL = com.bitchat.android.util.AppConstants.Mesh.RSSI_UPDATE_INTERVAL_MS // 5 seconds
|
||||
}
|
||||
|
||||
// Core Bluetooth components
|
||||
|
||||
@@ -36,7 +36,7 @@ class BluetoothMeshService(private val context: Context) {
|
||||
|
||||
companion object {
|
||||
private const val TAG = "BluetoothMeshService"
|
||||
private const val MAX_TTL: UByte = 7u
|
||||
private val MAX_TTL: UByte = com.bitchat.android.util.AppConstants.MESSAGE_TTL_HOPS
|
||||
}
|
||||
|
||||
// Core components - each handling specific responsibilities
|
||||
@@ -694,7 +694,7 @@ class BluetoothMeshService(private val context: Context) {
|
||||
timestamp = System.currentTimeMillis().toULong(),
|
||||
payload = encrypted,
|
||||
signature = null,
|
||||
ttl = 7u
|
||||
ttl = com.bitchat.android.util.AppConstants.MESSAGE_TTL_HOPS
|
||||
)
|
||||
|
||||
// Sign and send the encrypted packet
|
||||
@@ -840,7 +840,7 @@ class BluetoothMeshService(private val context: Context) {
|
||||
timestamp = System.currentTimeMillis().toULong(),
|
||||
payload = encrypted,
|
||||
signature = null,
|
||||
ttl = 7u // Same TTL as iOS messageTTL
|
||||
ttl = com.bitchat.android.util.AppConstants.MESSAGE_TTL_HOPS // Same TTL as iOS messageTTL
|
||||
)
|
||||
|
||||
// Sign the packet before broadcasting
|
||||
|
||||
@@ -47,7 +47,7 @@ class BluetoothPacketBroadcaster(
|
||||
|
||||
companion object {
|
||||
private const val TAG = "BluetoothPacketBroadcaster"
|
||||
private const val CLEANUP_DELAY = 500L
|
||||
private const val CLEANUP_DELAY = com.bitchat.android.util.AppConstants.Mesh.BROADCAST_CLEANUP_DELAY_MS
|
||||
}
|
||||
|
||||
// Optional nickname resolver injected by higher layer (peerID -> nickname?)
|
||||
|
||||
@@ -22,10 +22,10 @@ class FragmentManager {
|
||||
companion object {
|
||||
private const val TAG = "FragmentManager"
|
||||
// iOS values: 512 MTU threshold, 469 max fragment size (512 MTU - headers)
|
||||
private const val FRAGMENT_SIZE_THRESHOLD = 512 // Matches iOS: if data.count > 512
|
||||
private const val MAX_FRAGMENT_SIZE = 469 // Matches iOS: maxFragmentSize = 469
|
||||
private const val FRAGMENT_TIMEOUT = 30000L // Matches iOS: 30 seconds cleanup
|
||||
private const val CLEANUP_INTERVAL = 10000L // 10 seconds cleanup check
|
||||
private const val FRAGMENT_SIZE_THRESHOLD = com.bitchat.android.util.AppConstants.Fragmentation.FRAGMENT_SIZE_THRESHOLD // Matches iOS: if data.count > 512
|
||||
private const val MAX_FRAGMENT_SIZE = com.bitchat.android.util.AppConstants.Fragmentation.MAX_FRAGMENT_SIZE // Matches iOS: maxFragmentSize = 469
|
||||
private const val FRAGMENT_TIMEOUT = com.bitchat.android.util.AppConstants.Fragmentation.FRAGMENT_TIMEOUT_MS // Matches iOS: 30 seconds cleanup
|
||||
private const val CLEANUP_INTERVAL = com.bitchat.android.util.AppConstants.Fragmentation.CLEANUP_INTERVAL_MS // 10 seconds cleanup check
|
||||
}
|
||||
|
||||
// Fragment storage - iOS equivalent: incomingFragments: [String: [Int: Data]]
|
||||
|
||||
@@ -183,16 +183,16 @@ class MessageHandler(private val myPeerID: String, private val appContext: andro
|
||||
}
|
||||
|
||||
// Create NOISE_ENCRYPTED packet exactly like iOS
|
||||
val packet = BitchatPacket(
|
||||
version = 1u,
|
||||
type = MessageType.NOISE_ENCRYPTED.value,
|
||||
senderID = hexStringToByteArray(myPeerID),
|
||||
recipientID = hexStringToByteArray(senderPeerID),
|
||||
timestamp = System.currentTimeMillis().toULong(),
|
||||
payload = encryptedPayload,
|
||||
signature = null,
|
||||
ttl = 7u // Same TTL as iOS messageTTL
|
||||
)
|
||||
val packet = BitchatPacket(
|
||||
version = 1u,
|
||||
type = MessageType.NOISE_ENCRYPTED.value,
|
||||
senderID = hexStringToByteArray(myPeerID),
|
||||
recipientID = hexStringToByteArray(senderPeerID),
|
||||
timestamp = System.currentTimeMillis().toULong(),
|
||||
payload = encryptedPayload,
|
||||
signature = null,
|
||||
ttl = com.bitchat.android.util.AppConstants.MESSAGE_TTL_HOPS // Same TTL as iOS messageTTL
|
||||
)
|
||||
|
||||
delegate?.sendPacket(packet)
|
||||
Log.d(TAG, "📤 Sent delivery ACK to $senderPeerID for message $messageID")
|
||||
@@ -214,8 +214,8 @@ class MessageHandler(private val myPeerID: String, private val appContext: andro
|
||||
// Ignore stale announcements older than STALE_PEER_TIMEOUT
|
||||
val now = System.currentTimeMillis()
|
||||
val age = now - packet.timestamp.toLong()
|
||||
if (age > com.bitchat.android.util.AppConstants.STALE_PEER_TIMEOUT_MS) {
|
||||
Log.w(TAG, "Ignoring stale ANNOUNCE from ${peerID.take(8)} (age=${age}ms > ${com.bitchat.android.util.AppConstants.STALE_PEER_TIMEOUT_MS}ms)")
|
||||
if (age > com.bitchat.android.util.AppConstants.Mesh.STALE_PEER_TIMEOUT_MS) {
|
||||
Log.w(TAG, "Ignoring stale ANNOUNCE from ${peerID.take(8)} (age=${age}ms > ${com.bitchat.android.util.AppConstants.Mesh.STALE_PEER_TIMEOUT_MS}ms)")
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -318,7 +318,7 @@ class MessageHandler(private val myPeerID: String, private val appContext: andro
|
||||
timestamp = System.currentTimeMillis().toULong(),
|
||||
payload = response,
|
||||
signature = null,
|
||||
ttl = 7u // Same TTL as iOS
|
||||
ttl = com.bitchat.android.util.AppConstants.MESSAGE_TTL_HOPS // Same TTL as iOS
|
||||
)
|
||||
|
||||
delegate?.sendPacket(responsePacket)
|
||||
|
||||
@@ -67,11 +67,10 @@ class PeerManager {
|
||||
|
||||
companion object {
|
||||
private const val TAG = "PeerManager"
|
||||
private const val CLEANUP_INTERVAL = 60000L // 1 minute
|
||||
}
|
||||
|
||||
// Centralized timeout from AppConstants
|
||||
private val stalePeerTimeoutMs: Long = com.bitchat.android.util.AppConstants.STALE_PEER_TIMEOUT_MS
|
||||
private val stalePeerTimeoutMs: Long = com.bitchat.android.util.AppConstants.Mesh.STALE_PEER_TIMEOUT_MS
|
||||
|
||||
// Peer tracking data - enhanced with verification status
|
||||
private val peers = ConcurrentHashMap<String, PeerInfo>() // peerID -> PeerInfo
|
||||
@@ -416,7 +415,7 @@ class PeerManager {
|
||||
private fun startPeriodicCleanup() {
|
||||
managerScope.launch {
|
||||
while (isActive) {
|
||||
delay(CLEANUP_INTERVAL)
|
||||
delay(com.bitchat.android.util.AppConstants.Mesh.PEER_CLEANUP_INTERVAL_MS)
|
||||
cleanupStalePeers()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,22 +21,22 @@ class PowerManager(private val context: Context) {
|
||||
private const val TAG = "PowerManager"
|
||||
|
||||
// Battery thresholds
|
||||
private const val CRITICAL_BATTERY = 10
|
||||
private const val LOW_BATTERY = 20
|
||||
private const val MEDIUM_BATTERY = 50
|
||||
private const val CRITICAL_BATTERY = com.bitchat.android.util.AppConstants.Power.CRITICAL_BATTERY_PERCENT
|
||||
private const val LOW_BATTERY = com.bitchat.android.util.AppConstants.Power.LOW_BATTERY_PERCENT
|
||||
private const val MEDIUM_BATTERY = com.bitchat.android.util.AppConstants.Power.MEDIUM_BATTERY_PERCENT
|
||||
|
||||
// Scan duty cycle periods (ms)
|
||||
private const val SCAN_ON_DURATION_NORMAL = 8000L // 8 seconds on
|
||||
private const val SCAN_OFF_DURATION_NORMAL = 2000L // 2 seconds off
|
||||
private const val SCAN_ON_DURATION_POWER_SAVE = 2000L // 2 seconds on
|
||||
private const val SCAN_OFF_DURATION_POWER_SAVE = 8000L // 8 seconds off
|
||||
private const val SCAN_ON_DURATION_ULTRA_LOW = 1000L // 1 second on
|
||||
private const val SCAN_OFF_DURATION_ULTRA_LOW = 10000L // 10 seconds off
|
||||
private const val SCAN_ON_DURATION_NORMAL = com.bitchat.android.util.AppConstants.Power.SCAN_ON_DURATION_NORMAL_MS // 8 seconds on
|
||||
private const val SCAN_OFF_DURATION_NORMAL = com.bitchat.android.util.AppConstants.Power.SCAN_OFF_DURATION_NORMAL_MS // 2 seconds off
|
||||
private const val SCAN_ON_DURATION_POWER_SAVE = com.bitchat.android.util.AppConstants.Power.SCAN_ON_DURATION_POWER_SAVE_MS // 2 seconds on
|
||||
private const val SCAN_OFF_DURATION_POWER_SAVE = com.bitchat.android.util.AppConstants.Power.SCAN_OFF_DURATION_POWER_SAVE_MS // 8 seconds off
|
||||
private const val SCAN_ON_DURATION_ULTRA_LOW = com.bitchat.android.util.AppConstants.Power.SCAN_ON_DURATION_ULTRA_LOW_MS // 1 second on
|
||||
private const val SCAN_OFF_DURATION_ULTRA_LOW = com.bitchat.android.util.AppConstants.Power.SCAN_OFF_DURATION_ULTRA_LOW_MS // 10 seconds off
|
||||
|
||||
// Connection limits
|
||||
private const val MAX_CONNECTIONS_NORMAL = 8
|
||||
private const val MAX_CONNECTIONS_POWER_SAVE = 4
|
||||
private const val MAX_CONNECTIONS_ULTRA_LOW = 2
|
||||
private const val MAX_CONNECTIONS_NORMAL = com.bitchat.android.util.AppConstants.Power.MAX_CONNECTIONS_NORMAL
|
||||
private const val MAX_CONNECTIONS_POWER_SAVE = com.bitchat.android.util.AppConstants.Power.MAX_CONNECTIONS_POWER_SAVE
|
||||
private const val MAX_CONNECTIONS_ULTRA_LOW = com.bitchat.android.util.AppConstants.Power.MAX_CONNECTIONS_ULTRA_LOW
|
||||
}
|
||||
|
||||
enum class PowerMode {
|
||||
|
||||
@@ -19,10 +19,10 @@ class SecurityManager(private val encryptionService: EncryptionService, private
|
||||
|
||||
companion object {
|
||||
private const val TAG = "SecurityManager"
|
||||
private const val MESSAGE_TIMEOUT = 300000L // 5 minutes (same as iOS)
|
||||
private const val CLEANUP_INTERVAL = 300000L // 5 minutes
|
||||
private const val MAX_PROCESSED_MESSAGES = 10000
|
||||
private const val MAX_PROCESSED_KEY_EXCHANGES = 1000
|
||||
private const val MESSAGE_TIMEOUT = com.bitchat.android.util.AppConstants.Security.MESSAGE_TIMEOUT_MS // 5 minutes (same as iOS)
|
||||
private const val CLEANUP_INTERVAL = com.bitchat.android.util.AppConstants.Security.CLEANUP_INTERVAL_MS // 5 minutes
|
||||
private const val MAX_PROCESSED_MESSAGES = com.bitchat.android.util.AppConstants.Security.MAX_PROCESSED_MESSAGES
|
||||
private const val MAX_PROCESSED_KEY_EXCHANGES = com.bitchat.android.util.AppConstants.Security.MAX_PROCESSED_KEY_EXCHANGES
|
||||
}
|
||||
|
||||
// Security tracking
|
||||
|
||||
@@ -16,10 +16,10 @@ class StoreForwardManager {
|
||||
|
||||
companion object {
|
||||
private const val TAG = "StoreForwardManager"
|
||||
private const val MESSAGE_CACHE_TIMEOUT = 43200000L // 12 hours for regular peers
|
||||
private const val MAX_CACHED_MESSAGES = 100 // For regular peers
|
||||
private const val MAX_CACHED_MESSAGES_FAVORITES = 1000 // For favorites
|
||||
private const val CLEANUP_INTERVAL = 600000L // 10 minutes
|
||||
private const val MESSAGE_CACHE_TIMEOUT = com.bitchat.android.util.AppConstants.StoreForward.MESSAGE_CACHE_TIMEOUT_MS // 12 hours for regular peers
|
||||
private const val MAX_CACHED_MESSAGES = com.bitchat.android.util.AppConstants.StoreForward.MAX_CACHED_MESSAGES // For regular peers
|
||||
private const val MAX_CACHED_MESSAGES_FAVORITES = com.bitchat.android.util.AppConstants.StoreForward.MAX_CACHED_MESSAGES_FAVORITES // For favorites
|
||||
private const val CLEANUP_INTERVAL = com.bitchat.android.util.AppConstants.StoreForward.CLEANUP_INTERVAL_MS // 10 minutes
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -29,11 +29,11 @@ import java.util.concurrent.atomic.AtomicLong
|
||||
*/
|
||||
object TorManager {
|
||||
private const val TAG = "TorManager"
|
||||
private const val DEFAULT_SOCKS_PORT = 9060
|
||||
private const val RESTART_DELAY_MS = 2000L // 2 seconds between stop/start
|
||||
private const val INACTIVITY_TIMEOUT_MS = 5000L // 5 seconds of no activity before restart
|
||||
private const val MAX_RETRY_ATTEMPTS = 5
|
||||
private const val STOP_TIMEOUT_MS = 7000L
|
||||
private const val DEFAULT_SOCKS_PORT = com.bitchat.android.util.AppConstants.Tor.DEFAULT_SOCKS_PORT
|
||||
private const val RESTART_DELAY_MS = com.bitchat.android.util.AppConstants.Tor.RESTART_DELAY_MS // 2 seconds between stop/start
|
||||
private const val INACTIVITY_TIMEOUT_MS = com.bitchat.android.util.AppConstants.Tor.INACTIVITY_TIMEOUT_MS // 5 seconds of no activity before restart
|
||||
private const val MAX_RETRY_ATTEMPTS = com.bitchat.android.util.AppConstants.Tor.MAX_RETRY_ATTEMPTS
|
||||
private const val STOP_TIMEOUT_MS = com.bitchat.android.util.AppConstants.Tor.STOP_TIMEOUT_MS
|
||||
|
||||
private val appScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
|
||||
|
||||
|
||||
@@ -24,8 +24,8 @@ class NoiseEncryptionService(private val context: Context) {
|
||||
private const val TAG = "NoiseEncryptionService"
|
||||
|
||||
// Session limits for performance and security
|
||||
private const val REKEY_TIME_LIMIT = 3600000L // 1 hour (same as iOS)
|
||||
private const val REKEY_MESSAGE_LIMIT = 1000L // 1k messages (matches iOS) (same as iOS)
|
||||
private const val REKEY_TIME_LIMIT = com.bitchat.android.util.AppConstants.Noise.REKEY_TIME_LIMIT_MS // 1 hour (same as iOS)
|
||||
private const val REKEY_MESSAGE_LIMIT = com.bitchat.android.util.AppConstants.Noise.REKEY_MESSAGE_LIMIT_ENCRYPTION // 1k messages (matches iOS) (same as iOS)
|
||||
}
|
||||
|
||||
// Static identity key (persistent across app restarts) - loaded from secure storage
|
||||
|
||||
@@ -25,8 +25,8 @@ class NoiseSession(
|
||||
private const val PROTOCOL_NAME = "Noise_XX_25519_ChaChaPoly_SHA256"
|
||||
|
||||
// Rekey thresholds (same as iOS)
|
||||
private const val REKEY_TIME_LIMIT = 3600000L // 1 hour
|
||||
private const val REKEY_MESSAGE_LIMIT = 10000L // 10k messages
|
||||
private const val REKEY_TIME_LIMIT = com.bitchat.android.util.AppConstants.Noise.REKEY_TIME_LIMIT_MS // 1 hour
|
||||
private const val REKEY_MESSAGE_LIMIT = com.bitchat.android.util.AppConstants.Noise.REKEY_MESSAGE_LIMIT_SESSION // 10k messages
|
||||
|
||||
// XX Pattern Message Sizes (exactly matching iOS implementation)
|
||||
private const val XX_MESSAGE_1_SIZE = 32 // -> e (ephemeral key only)
|
||||
@@ -34,13 +34,13 @@ class NoiseSession(
|
||||
private const val XX_MESSAGE_3_SIZE = 48 // -> s, se (encrypted static key)
|
||||
|
||||
// Maximum payload size for safety
|
||||
private const val MAX_PAYLOAD_SIZE = 256
|
||||
private const val MAX_PAYLOAD_SIZE = com.bitchat.android.util.AppConstants.Noise.MAX_PAYLOAD_SIZE_BYTES
|
||||
|
||||
// Constants for replay protection (matching iOS implementation)
|
||||
private const val NONCE_SIZE_BYTES = 4
|
||||
private const val REPLAY_WINDOW_SIZE = 1024
|
||||
private const val REPLAY_WINDOW_BYTES = REPLAY_WINDOW_SIZE / 8 // 128 bytes
|
||||
private const val HIGH_NONCE_WARNING_THRESHOLD = 1_000_000_000L
|
||||
private const val HIGH_NONCE_WARNING_THRESHOLD = com.bitchat.android.util.AppConstants.Noise.HIGH_NONCE_WARNING_THRESHOLD
|
||||
|
||||
// MARK: - Sliding Window Replay Protection
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ object NostrEmbeddedBitChat {
|
||||
timestamp = System.currentTimeMillis().toULong(),
|
||||
payload = payload,
|
||||
signature = null,
|
||||
ttl = 7u
|
||||
ttl = com.bitchat.android.util.AppConstants.MESSAGE_TTL_HOPS
|
||||
)
|
||||
|
||||
val data = packet.toBinaryData() ?: return null
|
||||
@@ -86,7 +86,7 @@ object NostrEmbeddedBitChat {
|
||||
timestamp = System.currentTimeMillis().toULong(),
|
||||
payload = payload,
|
||||
signature = null,
|
||||
ttl = 7u
|
||||
ttl = com.bitchat.android.util.AppConstants.MESSAGE_TTL_HOPS
|
||||
)
|
||||
|
||||
val data = packet.toBinaryData() ?: return null
|
||||
@@ -123,7 +123,7 @@ object NostrEmbeddedBitChat {
|
||||
timestamp = System.currentTimeMillis().toULong(),
|
||||
payload = payload,
|
||||
signature = null,
|
||||
ttl = 7u
|
||||
ttl = com.bitchat.android.util.AppConstants.MESSAGE_TTL_HOPS
|
||||
)
|
||||
|
||||
val data = packet.toBinaryData() ?: return null
|
||||
@@ -158,7 +158,7 @@ object NostrEmbeddedBitChat {
|
||||
timestamp = System.currentTimeMillis().toULong(),
|
||||
payload = payload,
|
||||
signature = null,
|
||||
ttl = 7u
|
||||
ttl = com.bitchat.android.util.AppConstants.MESSAGE_TTL_HOPS
|
||||
)
|
||||
|
||||
val data = packet.toBinaryData() ?: return null
|
||||
|
||||
@@ -22,7 +22,7 @@ class NostrEventDeduplicator(
|
||||
) {
|
||||
companion object {
|
||||
private const val TAG = "NostrDeduplicator"
|
||||
private const val DEFAULT_CAPACITY = 10000
|
||||
private const val DEFAULT_CAPACITY = com.bitchat.android.util.AppConstants.Nostr.DEFAULT_DEDUP_CAPACITY
|
||||
|
||||
@Volatile
|
||||
private var INSTANCE: NostrEventDeduplicator? = null
|
||||
|
||||
@@ -41,10 +41,10 @@ class NostrRelayManager private constructor() {
|
||||
)
|
||||
|
||||
// Exponential backoff configuration (same as iOS)
|
||||
private const val INITIAL_BACKOFF_INTERVAL = 1000L // 1 second
|
||||
private const val MAX_BACKOFF_INTERVAL = 300000L // 5 minutes
|
||||
private const val BACKOFF_MULTIPLIER = 2.0
|
||||
private const val MAX_RECONNECT_ATTEMPTS = 10
|
||||
private const val INITIAL_BACKOFF_INTERVAL = com.bitchat.android.util.AppConstants.Nostr.INITIAL_BACKOFF_INTERVAL_MS // 1 second
|
||||
private const val MAX_BACKOFF_INTERVAL = com.bitchat.android.util.AppConstants.Nostr.MAX_BACKOFF_INTERVAL_MS // 5 minutes
|
||||
private const val BACKOFF_MULTIPLIER = com.bitchat.android.util.AppConstants.Nostr.BACKOFF_MULTIPLIER
|
||||
private const val MAX_RECONNECT_ATTEMPTS = com.bitchat.android.util.AppConstants.Nostr.MAX_RECONNECT_ATTEMPTS
|
||||
|
||||
// Track gift-wraps we initiated for logging
|
||||
private val pendingGiftWrapIDs = ConcurrentHashMap.newKeySet<String>()
|
||||
@@ -111,7 +111,7 @@ class NostrRelayManager private constructor() {
|
||||
|
||||
// Subscription validation timer
|
||||
private var subscriptionValidationJob: Job? = null
|
||||
private val SUBSCRIPTION_VALIDATION_INTERVAL = 30000L // 30 seconds
|
||||
private val SUBSCRIPTION_VALIDATION_INTERVAL = com.bitchat.android.util.AppConstants.Nostr.SUBSCRIPTION_VALIDATION_INTERVAL_MS // 30 seconds
|
||||
|
||||
// OkHttp client for WebSocket connections (via provider to honor Tor)
|
||||
private val httpClient: OkHttpClient
|
||||
|
||||
@@ -19,7 +19,7 @@ class NostrTransport(
|
||||
|
||||
companion object {
|
||||
private const val TAG = "NostrTransport"
|
||||
private const val READ_ACK_INTERVAL = 350L // ~3 per second (0.35s interval like iOS)
|
||||
private const val READ_ACK_INTERVAL = com.bitchat.android.util.AppConstants.Nostr.READ_ACK_INTERVAL_MS // ~3 per second (0.35s interval like iOS)
|
||||
|
||||
@Volatile
|
||||
private var INSTANCE: NostrTransport? = null
|
||||
|
||||
@@ -97,7 +97,7 @@ data class BitchatPacket(
|
||||
timestamp = timestamp,
|
||||
payload = payload,
|
||||
signature = null, // Remove signature for signing
|
||||
ttl = 0u // Use fixed TTL=0 for signing to ensure relay compatibility
|
||||
ttl = com.bitchat.android.util.AppConstants.SYNC_TTL_HOPS // Use fixed TTL=0 for signing to ensure relay compatibility
|
||||
)
|
||||
return BinaryProtocol.encode(unsignedPacket)
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import java.util.zip.Inflater
|
||||
* Uses the same zlib algorithm as iOS CompressionUtil.swift
|
||||
*/
|
||||
object CompressionUtil {
|
||||
private const val COMPRESSION_THRESHOLD = 100 // bytes - same as iOS
|
||||
private const val COMPRESSION_THRESHOLD = com.bitchat.android.util.AppConstants.Protocol.COMPRESSION_THRESHOLD_BYTES // bytes - same as iOS
|
||||
|
||||
/**
|
||||
* Helper to check if compression is worth it - exact same logic as iOS
|
||||
|
||||
@@ -13,7 +13,7 @@ class SeenMessageStore private constructor(private val context: Context) {
|
||||
companion object {
|
||||
private const val TAG = "SeenMessageStore"
|
||||
private const val STORAGE_KEY = "seen_message_store_v1"
|
||||
private const val MAX_IDS = 10_000
|
||||
private const val MAX_IDS = com.bitchat.android.util.AppConstants.Services.SEEN_MESSAGE_MAX_IDS
|
||||
|
||||
@Volatile private var INSTANCE: SeenMessageStore? = null
|
||||
fun getInstance(appContext: Context): SeenMessageStore {
|
||||
@@ -86,4 +86,3 @@ class SeenMessageStore private constructor(private val context: Context) {
|
||||
val read: List<String> = emptyList()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,6 @@ class GossipSyncManager(
|
||||
|
||||
companion object {
|
||||
private const val TAG = "GossipSyncManager"
|
||||
private const val CLEANUP_INTERVAL = 60_000L // 1 minute
|
||||
}
|
||||
|
||||
var delegate: Delegate? = null
|
||||
@@ -67,7 +66,7 @@ class GossipSyncManager(
|
||||
cleanupJob = scope.launch(Dispatchers.IO) {
|
||||
while (isActive) {
|
||||
try {
|
||||
delay(CLEANUP_INTERVAL)
|
||||
delay(com.bitchat.android.util.AppConstants.Sync.CLEANUP_INTERVAL_MS)
|
||||
pruneStaleAnnouncements()
|
||||
} catch (e: CancellationException) { throw e }
|
||||
catch (e: Exception) { Log.e(TAG, "Periodic cleanup error: ${e.message}") }
|
||||
@@ -118,8 +117,8 @@ class GossipSyncManager(
|
||||
// Ignore stale announcements older than STALE_PEER_TIMEOUT
|
||||
val now = System.currentTimeMillis()
|
||||
val age = now - packet.timestamp.toLong()
|
||||
if (age > com.bitchat.android.util.AppConstants.STALE_PEER_TIMEOUT_MS) {
|
||||
Log.d(TAG, "Ignoring stale ANNOUNCE (age=${age}ms > ${com.bitchat.android.util.AppConstants.STALE_PEER_TIMEOUT_MS}ms)")
|
||||
if (age > com.bitchat.android.util.AppConstants.Mesh.STALE_PEER_TIMEOUT_MS) {
|
||||
Log.d(TAG, "Ignoring stale ANNOUNCE (age=${age}ms > ${com.bitchat.android.util.AppConstants.Mesh.STALE_PEER_TIMEOUT_MS}ms)")
|
||||
return
|
||||
}
|
||||
// senderID is fixed-size 8 bytes; map to hex string for key
|
||||
@@ -142,7 +141,7 @@ class GossipSyncManager(
|
||||
senderID = hexStringToByteArray(myPeerID),
|
||||
timestamp = System.currentTimeMillis().toULong(),
|
||||
payload = payload,
|
||||
ttl = 0u // neighbors only
|
||||
ttl = com.bitchat.android.util.AppConstants.SYNC_TTL_HOPS // neighbors only
|
||||
)
|
||||
// Sign and broadcast
|
||||
val signed = delegate?.signPacketForBroadcast(packet) ?: packet
|
||||
@@ -158,7 +157,7 @@ class GossipSyncManager(
|
||||
recipientID = hexStringToByteArray(peerID),
|
||||
timestamp = System.currentTimeMillis().toULong(),
|
||||
payload = payload,
|
||||
ttl = 0u // neighbor only
|
||||
ttl = com.bitchat.android.util.AppConstants.SYNC_TTL_HOPS // neighbor only
|
||||
)
|
||||
Log.d(TAG, "Sending sync request to $peerID (${payload.size} bytes)")
|
||||
// Sign and send directly to peer
|
||||
@@ -186,7 +185,7 @@ class GossipSyncManager(
|
||||
val idBytes = hexToBytes(id)
|
||||
if (!mightContain(idBytes)) {
|
||||
// Send original packet unchanged to requester only (keep local TTL)
|
||||
val toSend = pkt.copy(ttl = 0u)
|
||||
val toSend = pkt.copy(ttl = com.bitchat.android.util.AppConstants.SYNC_TTL_HOPS)
|
||||
delegate?.sendPacketToPeer(fromPeerID, toSend)
|
||||
Log.d(TAG, "Sent sync announce: Type ${toSend.type} from ${toSend.senderID.toHexString()} to $fromPeerID packet id ${idBytes.toHexString()}")
|
||||
}
|
||||
@@ -197,7 +196,7 @@ class GossipSyncManager(
|
||||
for (pkt in toSendMsgs) {
|
||||
val idBytes = PacketIdUtil.computeIdBytes(pkt)
|
||||
if (!mightContain(idBytes)) {
|
||||
val toSend = pkt.copy(ttl = 0u)
|
||||
val toSend = pkt.copy(ttl = com.bitchat.android.util.AppConstants.SYNC_TTL_HOPS)
|
||||
delegate?.sendPacketToPeer(fromPeerID, toSend)
|
||||
Log.d(TAG, "Sent sync message: Type ${toSend.type} to $fromPeerID packet id ${idBytes.toHexString()}")
|
||||
}
|
||||
@@ -268,7 +267,7 @@ class GossipSyncManager(
|
||||
for ((peerID, pair) in latestAnnouncementByPeer.entries) {
|
||||
val pkt = pair.second
|
||||
val age = now - pkt.timestamp.toLong()
|
||||
if (age > com.bitchat.android.util.AppConstants.STALE_PEER_TIMEOUT_MS) {
|
||||
if (age > com.bitchat.android.util.AppConstants.Mesh.STALE_PEER_TIMEOUT_MS) {
|
||||
stalePeers.add(peerID)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,10 +3,7 @@ package com.bitchat.android.ui
|
||||
/**
|
||||
* UI constants/utilities for nickname rendering.
|
||||
*/
|
||||
const val MAX_NICKNAME_LENGTH: Int = 15
|
||||
|
||||
fun truncateNickname(name: String, maxLen: Int = MAX_NICKNAME_LENGTH): String {
|
||||
fun truncateNickname(name: String, maxLen: Int = com.bitchat.android.util.AppConstants.UI.MAX_NICKNAME_LENGTH): String {
|
||||
return if (name.length <= maxLen) name else name.take(maxLen)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ class MediaSendingManager(
|
||||
) {
|
||||
companion object {
|
||||
private const val TAG = "MediaSendingManager"
|
||||
private const val MAX_FILE_SIZE = 50 * 1024 * 1024 // 50MB limit
|
||||
private const val MAX_FILE_SIZE = com.bitchat.android.util.AppConstants.Media.MAX_FILE_SIZE_BYTES // 50MB limit
|
||||
}
|
||||
|
||||
// Track in-flight transfer progress: transferId -> messageId and reverse
|
||||
|
||||
@@ -13,8 +13,8 @@ class MessageManager(private val state: ChatState) {
|
||||
// Message deduplication - FIXED: Prevent duplicate messages from dual connection paths
|
||||
private val processedUIMessages = Collections.synchronizedSet(mutableSetOf<String>())
|
||||
private val recentSystemEvents = Collections.synchronizedMap(mutableMapOf<String, Long>())
|
||||
private val MESSAGE_DEDUP_TIMEOUT = 30000L // 30 seconds
|
||||
private val SYSTEM_EVENT_DEDUP_TIMEOUT = 5000L // 5 seconds
|
||||
private val MESSAGE_DEDUP_TIMEOUT = com.bitchat.android.util.AppConstants.UI.MESSAGE_DEDUP_TIMEOUT_MS // 30 seconds
|
||||
private val SYSTEM_EVENT_DEDUP_TIMEOUT = com.bitchat.android.util.AppConstants.UI.SYSTEM_EVENT_DEDUP_TIMEOUT_MS // 5 seconds
|
||||
|
||||
// MARK: - Public Message Management
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ class NotificationManager(
|
||||
private const val SUMMARY_NOTIFICATION_ID = 999
|
||||
private const val GEOHASH_SUMMARY_NOTIFICATION_ID = 998
|
||||
private const val ACTIVE_PEERS_NOTIFICATION_ID = 997
|
||||
private const val ACTIVE_PEERS_NOTIFICATION_TIME_INTERVAL = 300_000L
|
||||
private const val ACTIVE_PEERS_NOTIFICATION_TIME_INTERVAL = com.bitchat.android.util.AppConstants.UI.ACTIVE_PEERS_NOTIFICATION_INTERVAL_MS
|
||||
|
||||
// Intent extras for notification handling
|
||||
const val EXTRA_OPEN_PRIVATE_CHAT = "open_private_chat"
|
||||
|
||||
@@ -7,7 +7,7 @@ import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.sp
|
||||
|
||||
// Base font size for consistent scaling across the app
|
||||
const val BASE_FONT_SIZE = 15 // sp - increased from 14sp for better readability
|
||||
internal const val BASE_FONT_SIZE = com.bitchat.android.util.AppConstants.UI.BASE_FONT_SIZE_SP // sp - increased from 14sp for better readability
|
||||
|
||||
// Typography matching the iOS monospace design - using BASE_FONT_SIZE for consistency
|
||||
val Typography = Typography(
|
||||
|
||||
@@ -4,7 +4,116 @@ package com.bitchat.android.util
|
||||
* Centralized application-wide constants.
|
||||
*/
|
||||
object AppConstants {
|
||||
// Peer staleness threshold (ms). Keep consistent across mesh and sync layers.
|
||||
const val STALE_PEER_TIMEOUT_MS: Long = 180_000L // 3 minutes
|
||||
}
|
||||
// Packet time-to-live (hops)
|
||||
val MESSAGE_TTL_HOPS: UByte = 7u // Default TTL for regular packets
|
||||
val SYNC_TTL_HOPS: UByte = 0u // TTL for neighbor-only sync packets
|
||||
|
||||
object Mesh {
|
||||
// Peer lifecycle
|
||||
const val STALE_PEER_TIMEOUT_MS: Long = 180_000L // 3 minutes
|
||||
const val PEER_CLEANUP_INTERVAL_MS: Long = 60_000L
|
||||
|
||||
// BLE connection tracking
|
||||
const val CONNECTION_RETRY_DELAY_MS: Long = 5_000L
|
||||
const val MAX_CONNECTION_ATTEMPTS: Int = 3
|
||||
const val CONNECTION_CLEANUP_DELAY_MS: Long = 500L
|
||||
const val CONNECTION_CLEANUP_INTERVAL_MS: Long = 30_000L
|
||||
const val BROADCAST_CLEANUP_DELAY_MS: Long = 500L
|
||||
|
||||
// GATT client RSSI updates
|
||||
const val RSSI_UPDATE_INTERVAL_MS: Long = 5_000L
|
||||
}
|
||||
|
||||
object Sync {
|
||||
const val CLEANUP_INTERVAL_MS: Long = 60_000L
|
||||
}
|
||||
|
||||
object Fragmentation {
|
||||
const val FRAGMENT_SIZE_THRESHOLD: Int = 512
|
||||
const val MAX_FRAGMENT_SIZE: Int = 469
|
||||
const val FRAGMENT_TIMEOUT_MS: Long = 30_000L
|
||||
const val CLEANUP_INTERVAL_MS: Long = 10_000L
|
||||
}
|
||||
|
||||
object Security {
|
||||
const val MESSAGE_TIMEOUT_MS: Long = 300_000L
|
||||
const val CLEANUP_INTERVAL_MS: Long = 300_000L
|
||||
const val MAX_PROCESSED_MESSAGES: Int = 10_000
|
||||
const val MAX_PROCESSED_KEY_EXCHANGES: Int = 1_000
|
||||
}
|
||||
|
||||
object Noise {
|
||||
const val REKEY_TIME_LIMIT_MS: Long = 3_600_000L // 1 hour
|
||||
const val REKEY_MESSAGE_LIMIT_ENCRYPTION: Long = 1_000L // per session, encryption service policy
|
||||
const val REKEY_MESSAGE_LIMIT_SESSION: Long = 10_000L // session-level ceiling
|
||||
const val MAX_PAYLOAD_SIZE_BYTES: Int = 256
|
||||
const val HIGH_NONCE_WARNING_THRESHOLD: Long = 1_000_000_000L
|
||||
}
|
||||
|
||||
object Protocol {
|
||||
const val COMPRESSION_THRESHOLD_BYTES: Int = 100
|
||||
}
|
||||
|
||||
object StoreForward {
|
||||
const val MESSAGE_CACHE_TIMEOUT_MS: Long = 43_200_000L // 12h
|
||||
const val MAX_CACHED_MESSAGES: Int = 100
|
||||
const val MAX_CACHED_MESSAGES_FAVORITES: Int = 1_000
|
||||
const val CLEANUP_INTERVAL_MS: Long = 600_000L
|
||||
}
|
||||
|
||||
object Power {
|
||||
const val CRITICAL_BATTERY_PERCENT: Int = 10
|
||||
const val LOW_BATTERY_PERCENT: Int = 20
|
||||
const val MEDIUM_BATTERY_PERCENT: Int = 50
|
||||
const val SCAN_ON_DURATION_NORMAL_MS: Long = 8_000L
|
||||
const val SCAN_OFF_DURATION_NORMAL_MS: Long = 2_000L
|
||||
const val SCAN_ON_DURATION_POWER_SAVE_MS: Long = 2_000L
|
||||
const val SCAN_OFF_DURATION_POWER_SAVE_MS: Long = 8_000L
|
||||
const val SCAN_ON_DURATION_ULTRA_LOW_MS: Long = 1_000L
|
||||
const val SCAN_OFF_DURATION_ULTRA_LOW_MS: Long = 10_000L
|
||||
const val MAX_CONNECTIONS_NORMAL: Int = 8
|
||||
const val MAX_CONNECTIONS_POWER_SAVE: Int = 4
|
||||
const val MAX_CONNECTIONS_ULTRA_LOW: Int = 2
|
||||
}
|
||||
|
||||
object Nostr {
|
||||
// Relay backoff
|
||||
const val INITIAL_BACKOFF_INTERVAL_MS: Long = 1_000L
|
||||
const val MAX_BACKOFF_INTERVAL_MS: Long = 300_000L
|
||||
const val BACKOFF_MULTIPLIER: Double = 2.0
|
||||
const val MAX_RECONNECT_ATTEMPTS: Int = 10
|
||||
|
||||
// Transport
|
||||
const val READ_ACK_INTERVAL_MS: Long = 350L
|
||||
|
||||
// Deduplicator
|
||||
const val DEFAULT_DEDUP_CAPACITY: Int = 10_000
|
||||
|
||||
// Relay subscription validation
|
||||
const val SUBSCRIPTION_VALIDATION_INTERVAL_MS: Long = 30_000L
|
||||
}
|
||||
|
||||
object Tor {
|
||||
const val DEFAULT_SOCKS_PORT: Int = 9060
|
||||
const val RESTART_DELAY_MS: Long = 2_000L
|
||||
const val INACTIVITY_TIMEOUT_MS: Long = 5_000L
|
||||
const val MAX_RETRY_ATTEMPTS: Int = 5
|
||||
const val STOP_TIMEOUT_MS: Long = 7_000L
|
||||
}
|
||||
|
||||
object UI {
|
||||
const val MAX_NICKNAME_LENGTH: Int = 15
|
||||
const val BASE_FONT_SIZE_SP: Int = 15
|
||||
const val MESSAGE_DEDUP_TIMEOUT_MS: Long = 30_000L
|
||||
const val SYSTEM_EVENT_DEDUP_TIMEOUT_MS: Long = 5_000L
|
||||
const val ACTIVE_PEERS_NOTIFICATION_INTERVAL_MS: Long = 300_000L
|
||||
}
|
||||
|
||||
object Media {
|
||||
const val MAX_FILE_SIZE_BYTES: Long = 50L * 1024 * 1024
|
||||
}
|
||||
|
||||
object Services {
|
||||
const val SEEN_MESSAGE_MAX_IDS: Int = 10_000
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user