rsproperties
A pure Rust implementation of Android's property system, providing cross-platform access to Android system properties on both Linux and Android platforms.
Supported Android Versions
This library supports Android versions from Android 9 (API level 28) to Android 16 (API level 36).
Features
- Complete Android Properties Implementation: Full Rust implementation of Android's property system - read, write, and monitor properties exactly like Android native code
- Cross-Platform Compatibility: Works seamlessly on both Android devices and Linux systems without modification
- Pure Rust Solution: No dependencies on Android's C libraries or JNI - everything implemented in safe Rust
- Real-time Property Monitoring: Watch for property changes in real-time, enabling reactive applications
- High Performance: Optimized for speed with direct memory access and zero-copy operations
- Drop-in Replacement: Compatible with Android's property naming conventions and value constraints
- Linux Emulation: Full Android property system emulation on Linux for development and testing
- Thread-Safe Design: Safe to use across multiple threads without external synchronization
Quick Start
Add rsproperties to your Cargo.toml:
[]
= "0.4"
# Optional features
[]
= ["rsproperties/builder"] # Enable property database building
Basic Usage
use rsproperties;
// Get property with default value (no initialization needed for default configuration)
let sdk_version: String = get_or;
println!;
// Get property with type parsing and default fallback
let sdk_version: i32 = get_or;
let is_debuggable: bool = get_or;
// Get property with error handling
match
// Set property (requires property service to be running)
if let Err = set
Panic-free Initialization
init() and system_properties() panic when initialization fails (e.g.
missing properties directory, corrupt mmap). The try_* variants surface
those errors as Result instead — preferable for embedded use, tests,
and any code that must not unwind through OnceLock initialization.
use ;
try_init is first-write-wins: subsequent calls return
Error::FileValidation without poisoning the global state.
try_system_properties caches both the success and the failure in a
OnceLock, so every later call observes the same outcome.
Zero-Allocation Reads (read_with)
get<T> and get_or<T> already route through a zero-allocation path,
but if you want raw access to the validated value without parsing,
SystemProperties::read_with hands you a &str borrowed from the
seqlock-protected mmap buffer:
let props = system_properties;
// Compute over the value without ever allocating a String.
let prefix_len: Result =
props.read_with;
// Borrow into a stack buffer; the callback runs while the bytes are
// still seqlock-validated, so keep it cheap and non-blocking.
let mut buf = Stringnew;
props.read_with?;
This mirrors bionic's __system_property_read_callback pattern.
Property Monitoring
use rsproperties;
let system_properties = system_properties;
// Wait for any property change
spawn;
// Wait for specific property change
spawn;
// Monitor multiple properties
let monitored_props = vec!;
for prop_name in monitored_props
Setting Properties
Setting properties requires a running property service (like rsproperties-service):
use rsproperties;
// Basic property setting
if let Err = set
// Set application configuration
set?;
set?;
// Set system properties (may require elevated permissions)
set?;
// Set persistent properties (survive reboots on Android)
set?;
Property Setting Requirements
On Android:
- Properties are set through the property service
- Some properties require specific SELinux permissions
ro.*properties are read-only and cannot be modified- System properties may require root or system privileges
On Linux:
- Requires
rsproperties-serviceto be running - Properties are stored in memory-mapped files
- All properties are writable unless explicitly restricted
Property Setting Examples by Type
// Debug properties - usually writable by applications
set?;
set?;
// Vendor properties - device-specific configuration
set?;
// Custom application properties
set?;
set?;
// System state properties
set?;
set?;
Error Handling
Error is a thiserror-derived enum with #[from] conversions for
std::io::Error, rustix::io::Errno, Utf8Error, and ParseIntError.
The Error::Context variant carries a panic::Location so the failing
call site is preserved across error boundaries, and ContextWithLocation
attaches that context to any Result whose Err implements
Into<Error>.
use ;
// Batch property operations with error handling
Custom Configuration
Warning: Do not use custom configuration on Android devices. Custom configuration is only intended for Linux environments or development/testing purposes.
use PropertyConfig;
// Configure custom directories
let config = PropertyConfig ;
init;
// Using the builder pattern
let config = builder
.properties_dir
.socket_dir
.build;
init;
// Convenience methods
init;
Platform Support
Android
- Native Integration: Direct access to
/dev/__properties__ - Property Contexts: Full SELinux property context support
- Bionic Compatibility: Compatible with Android's property implementation
- Standard Properties: Access to all standard Android properties
Linux
- Full Emulation: Complete Android property system emulation
- Socket Communication: Unix domain socket property setting
- Memory Mapping: Efficient memory-mapped property storage
- Property Service: Use with
rsproperties-servicefor full daemon functionality
API Reference
Configuration
PropertyConfig— public-fields struct describing the properties & socket directoriesPropertyConfig::builder()/with_properties_dir()/with_socket_dir()/with_both_dirs()— construction helpersinit(config)— initialize globals; logs and swallows failurestry_init(config)— initialize globals, returningResultproperties_dir()— currently-configured properties directorysocket_dir()— currently-configured socket directory. Priority:PropertyConfig.socket_dir(viainit/try_init) >PROPERTY_SERVICE_SOCKET_DIRenv var >/dev/socket
Property Operations
get<T>(name)— parse-typed read;Erron missing / parse failureget_or<T>(name, default)— infallible read with fallbackset<T>(name, value)—Display-format and send to the property service over the socketsystem_properties()—&'static SystemPropertiesor panictry_system_properties()—&'static SystemPropertiesorErr
SystemProperties (read side)
read_with(name, |&str| -> R)— zero-alloc callback readerget_with_result(name)—String-allocating convenience wrapperfind(name)—Result<Option<PropertyIndex>>;Ok(None)for a missing property,Erronly for I/O / mmap problemsserial(index)/context_serial()— current generation counterswait_any()— futex-wait for any property changewait(index, timeout)— futex-wait for a specific property
Wire-protocol constants & validators (rsproperties::wire)
PROP_VALUE_MAX,PROP_NAME_MAX— AOSP wire-format size capsPROP_MSG_SETPROP,PROP_MSG_SETPROP2— command IDsPROP_SUCCESS,PROP_ERROR— V2 response codesvalidate_property_name(name)— name charset / leading-char checkvalidate_value_len(name, value)— value-length policy with the long-ro.*exception
Two socket-name constants are re-exported at the crate root for clients that want to point at a custom socket directory:
PROPERTY_SERVICE_SOCKET_NAME—"property_service"PROPERTY_SERVICE_FOR_SYSTEM_SOCKET_NAME—"property_service_for_system"
Errors
Error— non-exhaustive enum (Io,Errno,NotFound,Encoding,Parse,FileValidation,Conversion,PermissionDenied,FileSize,FileOwnership,LockError,Context)Result<T>=Result<T, Error>ContextWithLocation::context_with_location(msg)— attachpanic::Locationto anyResult<_, impl Into<Error>>
SystemProperties write side (builder feature)
SystemProperties::new_area(dir)— create/serialize an on-disk property areaSystemProperties::add(name, value)— append a new entrySystemProperties::update(index, value)— overwrite an existing entry (seqlock-guarded)SystemProperties::set(name, value)— add-or-updateload_properties_from_file(path, filter, context, &mut HashMap)— parse build.prop entriesPropertyInfoEntry::parse_from_file(path, require_prefix_or_exact)— parse aproperty_contexts-format file into(Vec<PropertyInfoEntry>, Vec<Error>)(the entries that survived and the per-line errors)build_trie(entries, default_context, default_type)— serialize a property-info trie
Thread Safety
All operations are thread-safe and can be used concurrently:
use thread;
// Multiple threads can safely access properties
let handles: = .map.collect;
for handle in handles
Building Property Databases (with builder feature)
use ;
use HashMap;
use Path;
// Load properties from Android build.prop files
let mut properties = new;
load_properties_from_file?;
// Create a system properties area for testing or service
let mut system_properties = new_area?;
// Add loaded properties to the area
for in properties
// Now properties can be read normally
let sdk_version: i32 = get_or;
Constants
The library exposes Android-compatible constants. PROP_VALUE_MAX is
re-exported at the crate root for source-compatibility; the canonical
home (along with the wire-protocol opcodes and validators) is the
wire module:
use ;
use ;
assert_eq!; // buffer size including NUL; content cap = 91 bytes
assert_eq!; // V1 wire only; V2 is length-prefixed
assert_eq!;
assert_eq!;
validate_property_name.unwrap;
validate_value_len.unwrap; // ro.* may exceed PROP_VALUE_MAX
Performance
- Memory-mapped access: Direct memory access for optimal performance
- Zero-copy reads: Efficient property value retrieval
- Atomic operations: Thread-safe property updates
- Futex-based waiting: Efficient property change notifications
Examples
The crate includes Android-compatible command-line tools:
getprop.rs- Android-compatible property getter with support for custom directoriessetprop.rs- Android-compatible property setter with validation and error handling
Run examples with:
# Get a property with default value
# Set a property
Related Crates
rsproperties-service- Full async property service daemon for Linux environments
Building
# Build the library
# Build with all features
# Run tests
# Build documentation
License
Licensed under the Apache License, Version 2.0. See LICENSE for details.
Contributing
Contributions are welcome! Please ensure:
- All tests pass:
cargo test - Code is formatted:
cargo fmt - No clippy warnings:
cargo clippy --all-targets --all-features
This implementation is based on Android's property system and maintains compatibility with Android's property semantics and behavior.