max / goingson
| 1 | //! Handing the Android runtime to rustls-platform-verifier. |
| 2 | //! |
| 3 | //! Every TLS client in the app -- reqwest for HTTP, async-imap for mail fetch, |
| 4 | //! the updater -- validates against the OS trust store through |
| 5 | //! `rustls-platform-verifier`. On every other platform that crate reads the |
| 6 | //! trust store directly and needs no setup. On Android the trust store is only |
| 7 | //! reachable through the JVM, so the crate has to be handed a `JavaVM`, an |
| 8 | //! application `Context` and a class loader before the first handshake, and it |
| 9 | //! `expect()`s on that state rather than falling back. Skip this and the app |
| 10 | //! launches, works offline, and panics the moment anything reaches the network. |
| 11 | //! |
| 12 | //! The handles arrive from Kotlin: `MainActivity.onCreate` calls the native |
| 13 | //! method below *before* `super.onCreate()`, because Tauri starts the Rust app |
| 14 | //! from `WryLifecycleObserver.onCreate` inside that super call and sync can be |
| 15 | //! running by the time it returns. |
| 16 | //! |
| 17 | //! Note this is jni 0.22, which `rustls-platform-verifier` 0.7 depends on -- |
| 18 | //! not the 0.21 that wry and tauri use. Both are in the graph on purpose and |
| 19 | //! must not be "unified": the types here have to match the crate being |
| 20 | //! initialised, so this module follows rustls-platform-verifier's jni version |
| 21 | //! and nothing else. |
| 22 | |
| 23 | use OnceLock; |
| 24 | |
| 25 | use EnvUnowned; |
| 26 | use JObject; |
| 27 | |
| 28 | /// Set when initialisation fails, drained by [`report_init_failure`]. |
| 29 | /// |
| 30 | /// This exists because of an ordering trap. The native method below runs from |
| 31 | /// `MainActivity.onCreate` *before* `super.onCreate()`, which is what makes it |
| 32 | /// early enough to be useful -- but `tracing_subscriber::fmt::init()` runs |
| 33 | /// inside that same super call, from the mobile entry point in `lib.rs`. Logging |
| 34 | /// the failure where it happens would therefore write to a subscriber that does |
| 35 | /// not exist yet and be dropped, leaving the one diagnostic for "no TLS at all" |
| 36 | /// invisible. So the error is parked here and logged once there is somewhere for |
| 37 | /// it to go. |
| 38 | static INIT_FAILURE: = new; |
| 39 | |
| 40 | /// `MainActivity.initRustlsPlatformVerifier(Context)`. |
| 41 | /// |
| 42 | /// Takes the *application* context rather than the activity: the crate holds a |
| 43 | /// global reference to whatever it is given for the life of the process, and |
| 44 | /// pinning an Activity there would leak it across every rotation and fold. |
| 45 | /// |
| 46 | /// A failure is recorded rather than thrown. GoingsOn is local-first and a |
| 47 | /// tasks-and-calendar session is entirely usable with no network, so taking the |
| 48 | /// whole app down at startup would cost more than it explains. The tradeoff is |
| 49 | /// that a failure surfaces later as a panic on first network use, which is why |
| 50 | /// [`report_init_failure`] exists to name the real cause before that happens. |
| 51 | |
| 52 | pub extern "system" |
| 53 | mut env: , |
| 54 | _this: , |
| 55 | context: , |
| 56 | |
| 57 | env.with_env |
| 58 | if let Err = init_with_env |
| 59 | let _ = INIT_FAILURE.set; |
| 60 | |
| 61 | Ok:: |
| 62 | |
| 63 | .; |
| 64 | |
| 65 | |
| 66 | /// Logs an initialisation failure, if there was one. |
| 67 | /// |
| 68 | /// Call once from the mobile entry point, after the tracing subscriber is |
| 69 | /// installed and before anything can reach the network. |
| 70 | |
| 71 | if let Some = INIT_FAILURE.get |
| 72 | error! |
| 73 | error = %e, |
| 74 | "rustls-platform-verifier did not initialise: no TLS connection can be \ |
| 75 | verified, so sync and mail will panic rather than fall back. Restarting \ |
| 76 | the app is the only recovery." |
| 77 | ; |
| 78 | |
| 79 | |
| 80 |