Skip to content

Implement lazy Tor startup and idle shutdown for battery efficiency#10

Merged
greenart7c3 merged 4 commits into
masterfrom
claude/battery-drain-sources-BcWhf
Jun 10, 2026
Merged

Implement lazy Tor startup and idle shutdown for battery efficiency#10
greenart7c3 merged 4 commits into
masterfrom
claude/battery-drain-sources-BcWhf

Conversation

@greenart7c3

Copy link
Copy Markdown
Owner

Summary

This PR optimizes battery consumption by implementing lazy Tor startup and idle shutdown, along with improved resource cleanup for HTTP clients and Nostr relay connections.

Key Changes

Tor Lifecycle Management

  • Lazy startup: Tor is no longer started eagerly when the setting is enabled. Instead, it's brought up on-demand via a new ensureTorReady() method when a tor-routed request is needed
  • Idle shutdown: Tor is automatically stopped after 5 minutes of inactivity (no tor-routed operations in flight) via scheduleTorIdleStop()
  • Concurrent safety: Added torStartMutex to ensure only one caller starts Tor while others wait for the same bootstrap
  • Bootstrap timeout: Requests proceed even if Tor doesn't bootstrap within 60 seconds, allowing graceful degradation

Resource Cleanup

  • HTTP client cleanup: Old OkHttpClient instances are properly released when settings/Tor status changes by calling evictAll() on connection pools and shutdown() on dispatcher executors, preventing idle socket leaks
  • Nostr relay disconnection: The relay socket is now explicitly disconnected once all author lookups complete, preventing background reconnection loops
  • Tracking active operations: Added activeAuthorLookups and torUsers counters to track in-flight operations and coordinate resource cleanup

Logcat Filtering

  • Improved logcat command execution to use proper array-based arguments
  • Added filtering ("$TAG:V *:S") at the logcat level to reduce CPU wake-ups by only receiving Morganite-tagged log lines instead of all device logs

Implementation Details

  • ensureTorReady(): Suspends until Tor reaches STATUS_ON or timeout occurs, with mutex-protected startup
  • scheduleTorIdleStop(): Cancels previous idle job and arms a new timer that checks if Tor is still unused before stopping
  • tryFetchAndSave() and streamFromServer(): Now suspend functions that manage Tor user counts and ensure Tor is ready before executing blocking operations
  • Settings changes only trigger Tor shutdown (not startup), with startup deferred to actual usage

https://claude.ai/code/session_01V3TCgA2EBDCaDGAkY6Lzyy

claude added 3 commits June 8, 2026 17:28
The in-app diagnostic log reader ran `logcat -v time` with no filter, so it
received every log line from every app on the device and scanned each one for
the Morganite tag. Started from MainActivity.onStart(), this woke the CPU
continuously the whole time the UI was visible. Filter at the logcat level with
"Morganite:V *:S" so only relevant lines are ever delivered; the on-screen log
panel is unchanged.

updateClients() rebuilt OkHttpClient instances on every settings/Tor-status
change without releasing the old ones, leaving idle connection pools and
dispatcher threads alive (idle sockets keep the radio warm until OkHttp's
eviction). Release each distinct replaced client with evictAll() + graceful
shutdown(), guarding against the cases where the two clients alias the same
instance.

https://claude.ai/code/session_01V3TCgA2EBDCaDGAkY6Lzyy
The real background battery drain: CustomHttpServer creates a NostrClient whose
RelayPool keeps WebSocket connections alive with an auto-reconnect loop. The
downloadFirstEvent accessory used by fetchAuthorServers only closes the REQ
subscription, never the socket, so once any cache-miss request carrying an "as="
author hint triggers a lookup, the WebSocket to wss://nostr.land stays open and
keeps reconnecting in the background, holding the radio awake indefinitely.

Disconnect the client once no author lookup is still in flight (tracked with an
AtomicInteger so concurrent lookups share the socket and only the last one tears
it down), and also disconnect when the server stops. This keeps the relay
connection scoped to actual use instead of leaking it for the process lifetime.

https://claude.ai/code/session_01V3TCgA2EBDCaDGAkY6Lzyy
When useTor was enabled, Tor was started at server startup and kept running for
the entire lifetime of the foreground service, even with no traffic. The tor
daemon keeps the radio awake with circuit and connection padding, periodic
directory/consensus fetches and circuit maintenance, so this was the dominant
background battery drain whenever the setting was on. Tor here is only used as an
outbound SOCKS proxy for fetches (the HTTP server is not exposed as an onion
service), so it does not need to run while idle.

Bring Tor up lazily via ensureTorReady() only when a request actually routes
through it (an .onion URL, useTorForAllUrls, or a nostr author-server lookup),
suspending until it bootstraps (STATUS_ON) or a timeout elapses. Track in-flight
tor-routed operations with a counter and, once none remain, stop the daemon after
a 5 minute idle period. Tor is no longer started eagerly on enable or at server
start; it is still torn down immediately when the setting is turned off or the
server stops. The only user-visible cost is added latency on the first tor-routed
fetch after an idle period while Tor re-bootstraps.

https://claude.ai/code/session_01V3TCgA2EBDCaDGAkY6Lzyy
@greenart7c3
greenart7c3 force-pushed the claude/battery-drain-sources-BcWhf branch from d1f2bff to f2f29e8 Compare June 8, 2026 17:31
updateClients() is invoked from torStatusReceiver.onReceive, which runs on the
main thread. connectionPool.evictAll() closes idle sockets, and closing an SSL
socket performs network I/O, so it crashed with NetworkOnMainThreadException —
now reliably, because on-demand Tor emits several status broadcasts while it
bootstraps. Move the evictAll()/executor shutdown of replaced clients onto the
IO scope so no network work happens on the main thread.

https://claude.ai/code/session_01V3TCgA2EBDCaDGAkY6Lzyy
@greenart7c3
greenart7c3 merged commit f1bd70f into master Jun 10, 2026
1 check passed
@greenart7c3
greenart7c3 deleted the claude/battery-drain-sources-BcWhf branch June 10, 2026 16:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants