Files
wehub-resource-sync 2114b14ee0
Sync main into demo / sync (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:35:26 +08:00

6.9 KiB
Raw Permalink Blame History

OS State

OS state is the Android-like device model owned by OsStateStore, managers, providers, and volatile services.

Buckets

Bucket Persisted? Examples
os.settings.global yes (os_state) WiFi, Bluetooth, mobile data, airplane mode, language.
os.settings.system yes (os_state) Brightness, volume, font/display size, eye comfort.
os.hardware yes (os_state) Battery, connectivity, sensors, storage, device flags.
os.permissions yes (os_state) Per-app permission states. Persistent like real Android — runtime permissions survive app restart and "device reboot" (simulator refresh) until the user revokes them. PermissionService is a thin facade over OsStateStore.permissions, not a separate store.
os.preferences yes (os_state) Generic preference bucket reachable through routeSetPreference() when no Manager owns the key.
os.build / os.telephony yes, but separately under __os_scenario_overrides__ Factory-immutable data; managed by os/managers/registry.ts override mechanism so bench scenarios can inject device model / IMEI / carrier without touching the regular state path. Not part of OsStateStore.
os.providers yes, per-provider keys (provider_sms, provider_contacts, provider_media) Contacts, SMS, media and other shared content providers.
os.clipboard yes (os_clipboard_v1) Clipboard contents — persisted so paste survives across sessions.
os.notifications no (volatile) Active notifications. Cleared on reload.
os.services runtime no (volatile) Shade, keyboard, text-selection menu. Cleared on reload.
Location no (volatile, seeded from SIMULATOR_CONFIG.location) The only volatile service that isn't a blank reset — it re-initializes from the simulator-config preset on each reload.

Writes should flow through Managers or service APIs rather than direct store mutation so constraints and side effects stay centralized.

Managers

Managers are write facades under os/managers/. They register the preference keys they own at module load via registerManager(keys, manager). When a write comes in through routeSetPreference(key, value), the lookup table picks the right one:

routeSetPreference(key, value)
  → normalizePreferenceKey(key)          # canonical alias resolution
  → state.preferences[normalized] = value  # raw write into the preferences bucket
  → keyToManager.get(normalized)?
      → manager.setPreference(key, value)   # Manager-owned: enforce constraints + side effects
      → genericSetPreference(key, value)    # No owner: just settle the value into OsStateStore

The same Managers receive direct calls from applyOsStatePatch() when a bench inject targets os.*, so the constraints below are enforced regardless of entry point (UI, preference API, or __SIM__.setState).

Manager Owns Enforced constraints
AudioManager Volume, ringer mode, DND, silent mode. Volume clamp 0100. DND ⇄ silentMode sync: enabling DND forces silentMode true (and records the pre-DND value); disabling DND restores it.
DisplayManager Brightness, font size, display size, dark mode, eye comfort. Brightness clamp 0100.
BatteryManager Battery percent, charging state. Percent clamp 0100.
ConnectivityManager WiFi, mobile data, Bluetooth, airplane mode. Airplane-mode cascade: enabling airplane forces wifi / bluetooth / cellular enabled=false, zeroes signal level fields. Disabling it does not automatically re-enable them; that's a user action.

Providers vs. services

os/createOsStore.ts exposes two factories with a registerToServiceRegistry flag (default true):

  • createOsStore(...) — persistent store.
  • createVolatileOsStore(...) — in-memory store, reset on reload.

Stores registered with the flag set to true are tracked in _registry and walked by snapshotOsStores() / resetAllOsStores(). The two exceptions opt out:

  • OsStateStore sets registerToServiceRegistry: false — it has its own snapshot and reset paths because it's the canonical settings bucket.
  • Provider stores (SmsProvider, ContactsProvider, MediaProvider) also opt out of _registry, but opt into _providerRegistry instead. They surface in the snapshot under os.providers.* rather than os.services.*.

The snapshot under os.services.* deliberately excludes notifications and clipboard even though they're service-registered stores, because they're already surfaced at the top level under os.notifications and os.clipboard.

Apps must go through ContentResolver

Apps must not import provider stores directly (os/providers/SmsProvider.ts etc.). The only public API is ContentResolver.query / insert / update / delete, which mirrors Android's URI-addressed content access and lets the OS enforce permissions and observability. Direct imports break the abstraction and the bench can't intercept them.

localStorage layout

Keys actually used by the current code:

Key Owner Notes
os_state OsStateStore Settings, hardware, permissions, preferences.
__os_scenario_overrides__ os/managers/registry.ts Build + telephony overrides for bench scenarios. Separate from os_state so scenario injects don't pollute user-facing state.
os_clipboard_v1 ClipboardService Persisted clipboard contents.
provider_sms / provider_contacts / provider_media Provider stores Per-provider persistence.
launcher Launcher Pinned apps, layout.
<manifest.id> App stores One key per installed app, named exactly manifest.id (no suffix).

There is currently no boot-time cleanup of historical keys — if you find os_quick_settings_v2 etc. in a localStorage dump, it's stale browser data; the simulator no longer creates those keys but doesn't actively purge them either.

SIMULATOR_CONFIG

os/data/simulatorConfig.ts exports SIMULATOR_CONFIG, the simulator-only configuration layer that has no Android counterpart. It is intentionally kept separate from OS_DEFAULTS (which mirrors defaults.json) so the Android-aligned state stays "pure".

Section Examples
framework Chooser enabled, intent dispatch flags.
time Default simulated time, time-zone preset.
location mode, simulatedLocation preset, PRESET_LOCATIONS lookup. Consumed by LocationService at boot.
ai AI feature toggles.
display scale, theme color overrides.
intent Intent filter behaviors specific to the simulator.

Scenario inject paths that target SIMULATOR_CONFIG go through the same __os_scenario_overrides__ mechanism as build / telephony.