How psiphon-inc/psiphon-android works
1. Overall shape
- This is an Android VPN/proxy client for Psiphon.
- The repository is split into two Gradle modules:
- app: the actual Android client app.
- tray: an embedded preference-storage library used by the app.
- The app combines a Java/Kotlin-free Android UI layer with a substantial native networking layer built from badvpn/tun2socks.
2. User-facing app shell
- MainActivity is the main entry point and hosts the top-level tabbed UI.
- The primary screens are organized into fragments such as HomeTabFragment, OptionsTabFragment, LogsTabFragment, and StatisticsTabFragment.
- Supporting activities handle permissions, onboarding/help, feedback, and Psiphon-specific NFC “bump” flows.
3. Tunnel control plane
- The core orchestration lives under com.psiphon3.psiphonlibrary.
- TunnelManager, TunnelServiceInteractor, TunnelVpnService, and VpnManager coordinate starting, stopping, and monitoring the VPN tunnel.
- TunnelState represents the connection lifecycle, while MainActivityViewModel likely exposes that state to the UI.
4. VPN and routing boundary
- The app uses Android’s VPN service model as the boundary between device traffic and the Psiphon tunnel.
- VpnRulesHelper, VpnAppsUtils, and related preference activities manage which apps/routes are included or excluded.
- This is the point where user configuration becomes actual device routing behavior.
5. Native networking engine
- The app/src/main/jni/badvpn tree contains the low-level tunnel implementation, including tun2socks, udpgw, lwip, and networking utilities.
- Java loads native code through ca.psiphon.Tun2SocksJniLoader.
- The native layer is responsible for moving packets from the Android VPN interface into socket-based network traffic and back again.
6. Configuration and protocol handling
- The native code includes large generated and protocol-oriented subsystems (protocol, ncd, generated, server_connection, socksclient, udpgw_client).
- These pieces suggest a modular native control/configuration system, with packet framing, SOCKS/UDP gateway handling, and connection management separated from the Android UI.
- The Java app configures this engine rather than implementing packet forwarding itself.
7. State, preferences, and persistence
- Persistent settings are split across standard Android preferences and the bundled tray library.
- tray provides a content-provider-backed storage layer with SQLite helpers and typed preference accessors.
- In the app, helpers like SharedPreferenceUtils, SafeFileStorage, PackageHelper, and various preference activities manage stored state, migrations, and app-specific configuration.
8. Logging and diagnostics
- Logging is a distinct subsystem under com.psiphon3.log.
- LoggingContentProvider, LogEntryDao, LogsDataSourceFactory, LogsMaintenanceWorker, and LogsLastEntryHelper provide structured log persistence and cleanup.
- LogsTabFragment and LogsListAdapter expose those logs in the UI.
- This is an important boundary because tunnel/network events are persisted separately from the main app state.
9. Feedback and crash handling
- FeedbackActivity, FeedbackWorker, and the assets/feedback.html template support user feedback submission.
- PsiphonCrashService and the included jndcrash AAR indicate dedicated crash reporting/handling.
- These flows are operationally separate from tunnel management but fed by app state and logs.
10. Update and lifecycle hooks
- PsiphonUpdateReceiver handles app/package update events.
- UpgradeChecker and UpgradeManager encapsulate version/update logic.
- PsiphonApplication is the process-level initialization point for global app setup.
11. Localization and content assets
- The repo has extensive locale coverage in res/values-* and localized layout variants.
- The i18n directory contains translation tooling and feedback template generation.
- assets/startpage and other bundled assets provide built-in content shown by the app.
12. External dependencies that matter architecturally
- The app uses packaged third-party binaries/libraries: ca.psiphon.aar, achartengine, jackson-core, snakeyaml, and jndcrash.
- achartengine is likely used for statistics visualization.
- jackson/snakeyaml suggest structured configuration/data parsing in the client or its bundled libraries.
13. How the main data flow works
- User interaction starts in MainActivity and fragments.
- Preferences and actions are passed into the Psiphon library layer.
- That layer drives TunnelVpnService / VpnManager, which hands traffic to the native tun2socks/badvpn stack.
- Runtime events, logs, and statistics are written back to persistence and surfaced in the UI.
- Feedback/crash/update flows reuse the same persisted state but remain separate from the live tunnel path.
14. Diagram boundaries to draw
- Treat the UI, tunnel orchestration, persistence/logging, and native networking as four separate boxes.
- The most important boundary is the Java-to-native JNI bridge feeding the VPN/tunnel engine.
- A second key boundary is the app’s storage layer: user preferences and logs are stored via different mechanisms and consumed by different UI surfaces.
Main entry points: MainActivity.java MainActivity · tun2socks.c tun2socks · TunnelManager.java TunnelManager · VpnManager.java VpnManager
Components 20
App shell
Main activity, tabs, onboarding, permissions, and user-facing screens.
- MainActivity · Top-level activity hosting the client UI.
app/src/main/java/com/psiphon3/MainActivity.java - Home Tab · Primary connection/status screen.
app/src/main/java/com/psiphon3/HomeTabFragment.java - Options Tab · Settings and configuration surface.
app/src/main/java/com/psiphon3/OptionsTabFragment.java - Logs Tab · Log viewer surface.
app/src/main/java/com/psiphon3/LogsTabFragment.java - Stats Tab · Connection statistics and charts.
app/src/main/java/com/psiphon3/StatisticsTabFragment.java - ViewModel · UI-facing tunnel state and coordination state.
app/src/main/java/com/psiphon3/MainActivityViewModel.java
Tunnel control
Java-side orchestration of VPN lifecycle and routing decisions.
- TunnelManager · Starts, stops, and monitors the Psiphon tunnel.
app/src/main/java/com/psiphon3/psiphonlibrary/TunnelManager.java - Service Interactor · Binds app logic to the VPN service lifecycle.
app/src/main/java/com/psiphon3/psiphonlibrary/TunnelServiceInteractor.java - TunnelVpnService · Android VPNService boundary for tunnel traffic.
app/src/main/java/com/psiphon3/psiphonlibrary/TunnelVpnService.java - VpnManager · Applies VPN routing and tunnel configuration.
app/src/main/java/com/psiphon3/VpnManager.java - VpnRulesHelper · Resolves app/include-exclude routing policy.
app/src/main/java/com/psiphon3/VpnRulesHelper.java
Storage & logs
Preferences, file state, and persisted log/event data.
- Logging Provider · Structured log storage and maintenance.
app/src/main/java/com/psiphon3/log/LoggingContentProvider.java - Safe Storage · App-specific durable file storage helpers.
app/src/main/java/com/psiphon3/SafeFileStorage.java
Native engine
JNI bridge and the embedded badvpn/tun2socks networking stack.
- Jni Loader · Loads the native tun2socks library into the app process.
app/src/main/java/ca/psiphon/Tun2SocksJniLoader.java - tun2socks · Moves packets between the VPN interface and socket traffic.
app/src/main/jni/badvpn/tun2socks/tun2socks.c - udpgw · UDP gateway component used by the tunnel stack.
app/src/main/jni/badvpn/udpgw/udpgw.c - ServerConnection · Native connection/session management for remote server links.
app/src/main/jni/badvpn/server_connection/ServerConnection.c - Protocols · Generated and hand-written packet/protocol framing code.
app/src/main/jni/badvpn/protocol/spproto.h
Tray library
Bundled content-provider preference storage module.
- Tray · Bundled preference-storage library backed by a content provider.
tray/src/main/java/net/grandcentrix/tray/Tray.java - Tray Provider · SQLite/content-provider backend for tray preferences.
tray/src/main/java/net/grandcentrix/tray/provider/TrayContentProvider.java
Connections 21
- MainActivity → Home Tab: hosts · Main activity displays the primary connection screen.
- MainActivity → Options Tab: hosts · Main activity exposes settings and options.
- MainActivity → Logs Tab: hosts · Main activity exposes log viewing.
- MainActivity → Stats Tab: hosts · Main activity exposes statistics.
- MainActivity → ViewModel: binds · Activity consumes shared tunnel state.
- ViewModel → TunnelManager: controls · ViewModel reflects and drives tunnel lifecycle state.
- TunnelManager → Service Interactor: starts · Coordinates service binding and tunnel operations.
- Service Interactor → TunnelVpnService: binds · Manages the VPN service boundary.
- TunnelVpnService → VpnManager: delegates · Service hands routing/control decisions to VPN manager.
- VpnManager → VpnRulesHelper: applies · VPN manager turns user policy into routing behavior.
- VpnManager → Jni Loader: loads · VPN setup initializes the native tunnel engine.
- Jni Loader → tun2socks: bridges · Java loads the native packet-forwarding core.
- tun2socks → udpgw: uses · Native tunnel stack relies on UDP gateway support.
- tun2socks → ServerConnection: connects · Tunnel core establishes and manages remote sessions.
- tun2socks → Protocols: encodes · Packet flow uses shared protocol/framing definitions.
- TunnelManager → Logging Provider: writes · Tunnel events are persisted for diagnostics.
- Logs Tab → Logging Provider: reads · Log UI reads structured persisted logs.
- Stats Tab → TunnelManager: observes · Statistics derive from tunnel/runtime metrics.
- Options Tab → Tray: reads/writes · Settings use the tray preference layer.
- Tray → Tray Provider: stores · Tray persists preferences via its content provider backend.
- Safe Storage → Tray: adjacent · App-specific storage complements preference storage.