1use parking_lot::RwLock;
8use std::sync::Arc;
9use std::time::{Duration, Instant};
10use thiserror::Error;
11use tracing::{debug, info};
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum BackgroundState {
16 Active,
18 Paused,
20 Pausing,
22 Resuming,
24}
25
26#[derive(Debug, Clone)]
28pub struct BackgroundModeConfig {
29 pub pause_dht_queries: bool,
31 pub pause_provider_announcements: bool,
33 pub close_idle_connections: bool,
35 pub idle_connection_threshold: Duration,
37 pub keep_minimal_connections: bool,
39 pub minimal_connection_count: usize,
41 pub reduce_dht_frequency: bool,
43 pub background_dht_interval: Duration,
45}
46
47impl Default for BackgroundModeConfig {
48 fn default() -> Self {
49 Self {
50 pause_dht_queries: false,
51 pause_provider_announcements: true,
52 close_idle_connections: true,
53 idle_connection_threshold: Duration::from_secs(300), keep_minimal_connections: true,
55 minimal_connection_count: 3,
56 reduce_dht_frequency: true,
57 background_dht_interval: Duration::from_secs(300), }
59 }
60}
61
62impl BackgroundModeConfig {
63 pub fn mobile() -> Self {
65 Self {
66 pause_dht_queries: true, pause_provider_announcements: true,
68 close_idle_connections: true,
69 idle_connection_threshold: Duration::from_secs(60), keep_minimal_connections: true,
71 minimal_connection_count: 2, reduce_dht_frequency: true,
73 background_dht_interval: Duration::from_secs(600), }
75 }
76
77 pub fn server() -> Self {
79 Self {
80 pause_dht_queries: false,
81 pause_provider_announcements: false,
82 close_idle_connections: false,
83 idle_connection_threshold: Duration::from_secs(3600), keep_minimal_connections: false,
85 minimal_connection_count: 0,
86 reduce_dht_frequency: false,
87 background_dht_interval: Duration::from_secs(60),
88 }
89 }
90
91 pub fn balanced() -> Self {
93 Self::default()
94 }
95}
96
97pub struct BackgroundModeManager {
99 config: BackgroundModeConfig,
100 state: Arc<RwLock<BackgroundState>>,
101 stats: Arc<RwLock<BackgroundModeStats>>,
102 last_transition: Arc<RwLock<Option<Instant>>>,
104 time_in_active: Arc<RwLock<Duration>>,
106 time_in_paused: Arc<RwLock<Duration>>,
107}
108
109#[derive(Debug, Clone, Default)]
111pub struct BackgroundModeStats {
112 pub pause_count: usize,
114 pub resume_count: usize,
116 pub total_background_time: Duration,
118 pub total_foreground_time: Duration,
120 pub connections_closed_on_pause: usize,
122 pub dht_queries_skipped: usize,
124}
125
126#[derive(Debug, Error)]
128pub enum BackgroundModeError {
129 #[error("Invalid state transition from {from:?} to {to:?}")]
130 InvalidStateTransition {
131 from: BackgroundState,
132 to: BackgroundState,
133 },
134
135 #[error("Operation not allowed in current state: {0:?}")]
136 OperationNotAllowed(BackgroundState),
137}
138
139impl BackgroundModeManager {
140 pub fn new(config: BackgroundModeConfig) -> Self {
142 Self {
143 config,
144 state: Arc::new(RwLock::new(BackgroundState::Active)),
145 stats: Arc::new(RwLock::new(BackgroundModeStats::default())),
146 last_transition: Arc::new(RwLock::new(Some(Instant::now()))),
147 time_in_active: Arc::new(RwLock::new(Duration::ZERO)),
148 time_in_paused: Arc::new(RwLock::new(Duration::ZERO)),
149 }
150 }
151
152 pub fn state(&self) -> BackgroundState {
154 *self.state.read()
155 }
156
157 pub fn is_paused(&self) -> bool {
159 matches!(self.state(), BackgroundState::Paused)
160 }
161
162 pub fn is_active(&self) -> bool {
164 matches!(self.state(), BackgroundState::Active)
165 }
166
167 pub fn pause(&self) -> Result<(), BackgroundModeError> {
169 let current_state = *self.state.read();
170
171 match current_state {
172 BackgroundState::Active => {
173 info!("Pausing network for background mode");
174 *self.state.write() = BackgroundState::Pausing;
175
176 self.update_time_tracking(current_state);
178
179 self.perform_pause_operations();
181
182 *self.state.write() = BackgroundState::Paused;
183 *self.last_transition.write() = Some(Instant::now());
184
185 let mut stats = self.stats.write();
186 stats.pause_count += 1;
187
188 debug!("Network paused successfully");
189 Ok(())
190 }
191 BackgroundState::Paused => {
192 Ok(())
194 }
195 state => Err(BackgroundModeError::InvalidStateTransition {
196 from: state,
197 to: BackgroundState::Paused,
198 }),
199 }
200 }
201
202 pub fn resume(&self) -> Result<(), BackgroundModeError> {
204 let current_state = *self.state.read();
205
206 match current_state {
207 BackgroundState::Paused => {
208 info!("Resuming network from background mode");
209 *self.state.write() = BackgroundState::Resuming;
210
211 self.update_time_tracking(current_state);
213
214 self.perform_resume_operations();
216
217 *self.state.write() = BackgroundState::Active;
218 *self.last_transition.write() = Some(Instant::now());
219
220 let mut stats = self.stats.write();
221 stats.resume_count += 1;
222
223 debug!("Network resumed successfully");
224 Ok(())
225 }
226 BackgroundState::Active => {
227 Ok(())
229 }
230 state => Err(BackgroundModeError::InvalidStateTransition {
231 from: state,
232 to: BackgroundState::Active,
233 }),
234 }
235 }
236
237 fn perform_pause_operations(&self) {
239 debug!(
241 "Background mode config: pause_dht={}, pause_announcements={}, close_idle={}",
242 self.config.pause_dht_queries,
243 self.config.pause_provider_announcements,
244 self.config.close_idle_connections
245 );
246
247 }
250
251 fn perform_resume_operations(&self) {
253 debug!("Resuming network operations");
254
255 }
258
259 fn update_time_tracking(&self, old_state: BackgroundState) {
261 if let Some(last_transition) = *self.last_transition.read() {
262 let elapsed = last_transition.elapsed();
263
264 match old_state {
265 BackgroundState::Active => {
266 *self.time_in_active.write() += elapsed;
267 let mut stats = self.stats.write();
268 stats.total_foreground_time += elapsed;
269 }
270 BackgroundState::Paused => {
271 *self.time_in_paused.write() += elapsed;
272 let mut stats = self.stats.write();
273 stats.total_background_time += elapsed;
274 }
275 _ => {}
276 }
277 }
278 }
279
280 pub fn should_allow_dht_query(&self) -> bool {
282 match self.state() {
283 BackgroundState::Active | BackgroundState::Resuming => true,
284 BackgroundState::Paused | BackgroundState::Pausing => !self.config.pause_dht_queries,
285 }
286 }
287
288 pub fn should_allow_provider_announcements(&self) -> bool {
290 match self.state() {
291 BackgroundState::Active | BackgroundState::Resuming => true,
292 BackgroundState::Paused | BackgroundState::Pausing => {
293 !self.config.pause_provider_announcements
294 }
295 }
296 }
297
298 pub fn config(&self) -> &BackgroundModeConfig {
300 &self.config
301 }
302
303 pub fn stats(&self) -> BackgroundModeStats {
305 let current_state = *self.state.read();
307 self.update_time_tracking(current_state);
308
309 self.stats.read().clone()
310 }
311
312 pub fn reset_stats(&self) {
314 *self.stats.write() = BackgroundModeStats::default();
315 *self.time_in_active.write() = Duration::ZERO;
316 *self.time_in_paused.write() = Duration::ZERO;
317 *self.last_transition.write() = Some(Instant::now());
318 }
319
320 pub fn record_dht_query_skipped(&self) {
322 self.stats.write().dht_queries_skipped += 1;
323 }
324
325 pub fn record_connections_closed(&self, count: usize) {
327 self.stats.write().connections_closed_on_pause += count;
328 }
329}
330
331#[cfg(test)]
332mod tests {
333 use super::*;
334
335 #[test]
336 fn test_background_mode_creation() {
337 let manager = BackgroundModeManager::new(BackgroundModeConfig::default());
338 assert_eq!(manager.state(), BackgroundState::Active);
339 assert!(manager.is_active());
340 assert!(!manager.is_paused());
341 }
342
343 #[test]
344 fn test_pause_resume() {
345 let manager = BackgroundModeManager::new(BackgroundModeConfig::default());
346
347 assert!(manager.pause().is_ok());
349 assert_eq!(manager.state(), BackgroundState::Paused);
350 assert!(manager.is_paused());
351 assert!(!manager.is_active());
352
353 assert!(manager.resume().is_ok());
355 assert_eq!(manager.state(), BackgroundState::Active);
356 assert!(manager.is_active());
357 assert!(!manager.is_paused());
358 }
359
360 #[test]
361 fn test_pause_when_already_paused() {
362 let manager = BackgroundModeManager::new(BackgroundModeConfig::default());
363
364 assert!(manager.pause().is_ok());
365 assert!(manager.pause().is_ok()); assert_eq!(manager.state(), BackgroundState::Paused);
367 }
368
369 #[test]
370 fn test_resume_when_already_active() {
371 let manager = BackgroundModeManager::new(BackgroundModeConfig::default());
372
373 assert!(manager.resume().is_ok()); assert_eq!(manager.state(), BackgroundState::Active);
375 }
376
377 #[test]
378 fn test_dht_query_allowed_in_active_state() {
379 let manager = BackgroundModeManager::new(BackgroundModeConfig::default());
380 assert!(manager.should_allow_dht_query());
381 }
382
383 #[test]
384 fn test_dht_query_behavior_in_paused_state() {
385 let config = BackgroundModeConfig {
386 pause_dht_queries: true,
387 ..Default::default()
388 };
389 let manager = BackgroundModeManager::new(config);
390
391 manager.pause().unwrap();
392 assert!(!manager.should_allow_dht_query());
393 }
394
395 #[test]
396 fn test_dht_query_allowed_when_not_paused_in_background() {
397 let config = BackgroundModeConfig {
398 pause_dht_queries: false,
399 ..Default::default()
400 };
401 let manager = BackgroundModeManager::new(config);
402
403 manager.pause().unwrap();
404 assert!(manager.should_allow_dht_query());
405 }
406
407 #[test]
408 fn test_provider_announcements_in_active_state() {
409 let manager = BackgroundModeManager::new(BackgroundModeConfig::default());
410 assert!(manager.should_allow_provider_announcements());
411 }
412
413 #[test]
414 fn test_provider_announcements_in_paused_state() {
415 let manager = BackgroundModeManager::new(BackgroundModeConfig::default());
416 manager.pause().unwrap();
417 assert!(!manager.should_allow_provider_announcements());
419 }
420
421 #[test]
422 fn test_statistics_tracking() {
423 let manager = BackgroundModeManager::new(BackgroundModeConfig::default());
424
425 manager.pause().unwrap();
426 manager.resume().unwrap();
427 manager.pause().unwrap();
428
429 let stats = manager.stats();
430 assert_eq!(stats.pause_count, 2);
431 assert_eq!(stats.resume_count, 1);
432 }
433
434 #[test]
435 fn test_mobile_config() {
436 let config = BackgroundModeConfig::mobile();
437 assert!(config.pause_dht_queries);
438 assert!(config.pause_provider_announcements);
439 assert!(config.close_idle_connections);
440 assert_eq!(config.minimal_connection_count, 2);
441 }
442
443 #[test]
444 fn test_server_config() {
445 let config = BackgroundModeConfig::server();
446 assert!(!config.pause_dht_queries);
447 assert!(!config.pause_provider_announcements);
448 assert!(!config.close_idle_connections);
449 }
450
451 #[test]
452 fn test_record_dht_query_skipped() {
453 let manager = BackgroundModeManager::new(BackgroundModeConfig::default());
454 manager.record_dht_query_skipped();
455 manager.record_dht_query_skipped();
456
457 let stats = manager.stats();
458 assert_eq!(stats.dht_queries_skipped, 2);
459 }
460
461 #[test]
462 fn test_record_connections_closed() {
463 let manager = BackgroundModeManager::new(BackgroundModeConfig::default());
464 manager.record_connections_closed(5);
465
466 let stats = manager.stats();
467 assert_eq!(stats.connections_closed_on_pause, 5);
468 }
469
470 #[test]
471 fn test_reset_stats() {
472 let manager = BackgroundModeManager::new(BackgroundModeConfig::default());
473
474 manager.pause().unwrap();
475 manager.resume().unwrap();
476 manager.record_dht_query_skipped();
477
478 manager.reset_stats();
479
480 let stats = manager.stats();
481 assert_eq!(stats.pause_count, 0);
482 assert_eq!(stats.resume_count, 0);
483 assert_eq!(stats.dht_queries_skipped, 0);
484 }
485}