8649c75206
* perf: mmap libapp.so out of the APK instead of buffering in RAM `open_base_lib` previously read the entire decompressed libapp.so into a Vec<u8> via `read_to_end` and handed bipatch a Cursor over that buffer. For large apps libapp.so can be tens of megabytes, and that allocation happens immediately after the patch download is buffered to disk — a plausible OOM trigger on memory-constrained devices (we have at least one customer report on OnePlus where the patch install appears to halt silently right after the download completes). Modern AGP (3.6+) defaults to extractNativeLibs=false, which keeps libapp.so STORED uncompressed inside the APK so the dynamic linker can mmap it directly. When that's the case, do the same: find the entry's data offset via the zip crate, drop the archive, reopen the APK, and mmap the entry's slice. Cursor<Mmap> implements Read + Seek, which is what bipatch's `Reader::new(patch, base)` requires. When the entry isn't stored uncompressed (older builds, or builds that explicitly compress native libs), fall back to the previous buffered read so we always succeed. Mmap doesn't change the peak working set when bipatch traverses the whole base linearly, but file-backed mappings are clean and reclaimable under memory pressure where an anonymous Vec is not, and we avoid the ~2x transient allocation peak from `read_to_end` growing the buffer. Tests cover both paths (stored → mmap, deflated → buffered) on the host. * ci: add mmap, memmap, SIGBUS to cspell dictionary
87 lines
3.1 KiB
TOML
87 lines
3.1 KiB
TOML
[package]
|
|
name = "updater"
|
|
version = "0.1.0"
|
|
edition = "2021"
|
|
|
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
|
|
[lib]
|
|
# "lib" is used by the "cli" target for testing from Rust
|
|
# "cdylib" is used by the "dart_cli" target for testing from Dart
|
|
# "staticlib" is used by the engine build for linking into libflutter.so
|
|
crate-type = ["lib", "cdylib", "staticlib"]
|
|
|
|
[features]
|
|
# Exposes a small set of internal Rust items (e.g. `testing_reset_config`)
|
|
# to sibling crates that need to drive the updater from a test harness.
|
|
# Production builds (the cdylib/staticlib that ships in the engine) do not
|
|
# enable this feature; only `library_test_hooks` does.
|
|
test-hooks = []
|
|
|
|
[dependencies]
|
|
# Used for error handling for now.
|
|
anyhow = "1.0.69"
|
|
base64 = "0.22.0"
|
|
# For inflating compressed patch files.
|
|
bipatch = "1.0.0"
|
|
# Used for exposing C API
|
|
# comde is a wrapper around several compression libraries.
|
|
# We only use zstd and could depend on it directly instead.
|
|
comde = { version = "0.3.1", default-features = false, features = [
|
|
"zstandard",
|
|
] }
|
|
dyn-clone = "1.0.16"
|
|
# For decoding the hex-encoded hashes in Patch network responses.
|
|
hex = "0.4.3"
|
|
libc = "0.2.98"
|
|
# For error!(), info!(), etc macros. `print` will not show up on Android.
|
|
log = "0.4.14"
|
|
# For mmap'ing libapp.so out of the APK without buffering it in RAM. This
|
|
# matters on memory-constrained devices: reading the whole library into a
|
|
# Vec<u8> can cost tens of MB and trigger OOM kills mid-patch.
|
|
memmap2 = "0.9.10"
|
|
# For implementing thread-local-storage of ResolvedConfig object.
|
|
once_cell = "1.17.1"
|
|
# Json serialization/de-serialization.
|
|
# Pipe is a simple in-memory pipe implementation, there might be a std way too?
|
|
pipe = "0.4.0"
|
|
# Used for networking. ureq is a synchronous-only HTTP client that avoids
|
|
# pulling in tokio/hyper/async machinery, significantly reducing binary size.
|
|
ureq = { version = "3", features = ["json", "rustls"] }
|
|
ring = "0.17.8"
|
|
serde = { version = "1.0", features = ["derive"] }
|
|
serde_json = "1.0.93"
|
|
# For computing hashes of patch files for validation.
|
|
sha2 = "0.11.0"
|
|
uuid = { version = "1.18.1", features = ["v4"] }
|
|
# For decompressing .apk files.
|
|
zip = { version = "8.5.1", default-features = false, features = ["deflate"] }
|
|
|
|
[target.'cfg(target_os = "android")'.dependencies]
|
|
# For logging to Android logcat.
|
|
android_logger = "0.15.0"
|
|
# Send panics to log (instead of stderr), thus logcat on Android.
|
|
log-panics = "2"
|
|
|
|
[target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies]
|
|
# Send panics to syslog (instead of stderr).
|
|
log-panics = "2"
|
|
# Support for logging on iOS (https://developer.apple.com/documentation/oslog)
|
|
oslog = "0.2.0"
|
|
|
|
[target.'cfg(any(target_os = "linux", target_os = "windows"))'.dependencies]
|
|
simple_logger = "5.0.0"
|
|
|
|
[dev-dependencies]
|
|
mockall = "0.14.0"
|
|
mockito = "1.2.0"
|
|
mock_instant = "0.6.0"
|
|
# Gives #[serial] attribute for locking all of our shorebird_init
|
|
# tests to a single thread so they don't conflict with each other.
|
|
serial_test = "3.2.0"
|
|
tempfile = "3"
|
|
|
|
# <https://github.com/eqrion/cbindgen/blob/master/docs.md#buildrs>
|
|
[build-dependencies]
|
|
cbindgen = "0.29.2"
|