Repository navigation
Disable background packages on slim Android emulators #164
Description
Part of #159.
Scope
After this PR a slim Android device also has a known set of background packages disabled. The set ships with Simlock in named categories. android.slim.categories narrows it; omitted means every default category. Play Store, Chrome, and the setup wizard form the one category that is off unless named. Google Play Services is never disabled and a slim device always finishes booting. The pass runs once, before the clean baseline is captured, so a reclaimed device comes back slim with no extra boot. simlock events shows device.slimmed for the device. A package the image lacks is skipped and reported; a pass that cannot run leaves the device usable and not yet captured.
{
"android": {
"slim": {
"categories": ["search", "media"] // optional; omitted means every default category
}
}
}Technical spec
Written before #172. #172 has shipped (#204): featureProfile is now device.mode ("slim" or "full", always present), slim.enabled gave way to android.defaultMode, and --full is now --mode full. Revise this section against that before approval.
Modules touched
src/drivers/android/slim-packages.ts(new): versioned data, no driver logic.SLIM_PACKAGE_CATEGORIES(name, description,default, packages),NEVER_DISABLED,resolveSlimCategories(names?)(omitted means every category withdefault: true; an unknown name is returned separately, not thrown),packagesFor(categories), andslimSignature(categories), a hash over the resolved names and their packages, mirroringsrc/drivers/ios/slim-labels.ts. The categories and packages:
search default com.google.android.googlequicksearchbox com.google.android.as com.google.android.as.oss
media default com.google.android.youtube com.google.android.apps.youtube.music com.google.android.videos
com.google.android.music com.android.music com.google.android.apps.photos com.android.gallery3d
com.android.camera2 com.android.cameraextensions com.android.DeviceAsWebcam
apps default com.google.android.apps.maps com.google.android.gm com.google.android.apps.docs
com.google.android.calendar com.android.calendar com.google.android.deskclock com.android.deskclock
com.google.android.calculator com.android.calculator2 com.google.android.apps.wellbeing
com.google.android.apps.bard com.google.android.markup com.google.android.apps.safetyhub
telephony default com.google.android.apps.messaging com.android.mms com.google.android.dialer com.android.dialer
com.google.android.contacts com.android.contacts com.google.android.cellbroadcastservice
com.android.cellbroadcastreceiver com.google.android.telephony.satellite com.android.stk
com.android.imsserviceentitlement com.android.carrierdefaultapp com.android.emergency
accessibility default com.google.android.tts com.google.android.marvin.talkback
com.google.android.apps.accessibility.voiceaccess
printing default com.android.printspooler com.android.bips com.google.android.printservice.recommendation
telemetry default com.google.android.feedback com.android.traceur com.google.android.adservices.api
com.google.mainline.adservices com.google.android.ondevicepersonalization.services
com.google.android.federatedcompute com.google.android.configupdater
com.google.android.settings.intelligence com.google.android.apps.restore
peripherals default com.android.bluetoothmidiservice com.google.android.projection.gearhead
com.android.emulator.multidisplay com.google.android.healthconnect.controller
com.android.dreams.basic com.android.dreams.phototable com.android.wallpaper.livepicker
com.google.android.apps.wallpaper
store opt-in com.android.vending com.android.chrome com.google.android.setupwizard com.android.setupwizard
com.google.android.partnersetup
NEVER_DISABLED: com.google.android.gms com.google.android.gsf com.google.android.bluetooth com.android.bluetooth
com.google.android.webview com.android.webview com.android.systemui android com.android.settings
com.android.shell, and any name starting with com.android.providers. or com.google.android.trichromelibrary
src/core/config.tsandsrc/contract/schemas.ts:android.slim.categories, optional, an array of non-empty strings, validated by shape only; the names are the driver's business.src/daemon/main.ts: threadcategories; bridge the Android driver'sonSlimmedtodevice.slimmedwithplatform: "android"throughemitSlimDiagnostic, which takes the platform from the fact; logonSlimSkippedas for iOS.src/bus/index.ts:device.slimmed'splatformbecomes"ios" | "android". No other payload change.src/drivers/android/index.ts:AndroidSlimOptions.categories. At construction, resolve once; an unknown name is logged through a diagnostic and dropped.#currentConfigHashadds the signature when slim applies (ADR 0006 §3).- The pass runs in
makeReadyon the!state.baselineCapturedpath only, after the first boot's readiness and before#captureBaseline:pm list packages, disable each resolved package that is present withpm disable-user --user 0 <package>, read backpm list packages -d, fireonSlimmed, then capture. Every command runs under the driver's command timeout and the whole pass underSLIM_PASS_TIMEOUT_MSmeasured from its start (ADR 0006 §8). - A package absent from the image, or one
pmrefuses, goes tounknownLabelsand does not block the capture. A command that fails to run, or a pass over budget, skips the capture, reportsonSlimSkipped, and returns the device withfeatureProfile: "reduced"(ADR 0006 §6). The baseline stays uncaptured so the nextprepareboot retries. - No pass on a boot that restores the baseline, on a
recoverboot, or on afulldevice. estimate({ operation: "boot" })adds the pass budget when slim applies to the spec.- Package names are validated against
^[A-Za-z0-9_.]+$at the one place they reach a shell argument (safety rule 10); the data test enforces the same shape.
docs/CONFIGURATION.md: thecategorieskey, the category names and what each disables, the opt-instorecategory, what stops working.docs/EVENTS.mdanddocs/internal/EVENTS.md: thedevice.slimmedrow covers both platforms; on Android a label is a package name and the fact is read back from the guest's package state before the baseline is captured.docs/internal/KNOWN-PITFALLS.md: the Android slim section gains the feature-loss list and the list-drift note.e2e/slow-android-slim.test.ts: extended, see Tests.
Contract and event changes
device.slimmed:platformgains"android". Additive. Both EVENTS.md files updated in this change (events rule 8, documentation rule 4).- Config schema:
android.slim.categories. No new operation or lease-request field.
Rules in play
docs/internal/adr/0006-opt-in-slim-android-emulators.md§2, §3, §6, §7, §8, §10.docs/internal/agent-rules/events.md: rule 3 (the fact is read back before it is reported), rule 6 (additive payload), rule 8.docs/internal/agent-rules/safety.md: rule 2 (no pass on a recovery boot), rule 10 (package names validated before the shell).docs/internal/agent-rules/architecture.md: rule 2 (the core validates shape only, never a category name), rule 11 (the pass budget is measured from its start and does not reset per command), rule 12 (every exit of the pass leaves the baseline either captured or uncaptured, never half).docs/internal/agent-rules/testing.md.
Tests
- Every package in every category matches
^[A-Za-z0-9_.]+$and none matchesNEVER_DISABLED. resolveSlimCategories(undefined)yields every category withdefault: trueand notstore; namingstoreincludes it; an unknown name is reported and dropped without throwing.slimSignaturechanges when a category's packages change and when the chosen categories change, and is stable across the order names are given in.- On a baseline-capturing boot with slim enabled, the driver lists installed packages, disables each resolved package the image carries, reads the disabled list back, and only then saves the baseline snapshot (scripted
ProcessRunner, order asserted). - A package absent from the installed list is skipped, appears in
unknownLabels, and the baseline is captured. - A
pm disable-userthat reports failure for a present package appears inunknownLabelsand the baseline is captured. - An adb command that fails to run during the pass leaves the baseline uncaptured, reports
onSlimSkipped, andmakeReadystill returns the device withfeatureProfile: "reduced"; the nextprepareboot runs the pass again. - A pass whose commands never answer fails within
SLIM_PASS_TIMEOUT_MSof its start, measured on the fake clock, and the boot still succeeds. - A boot that restores the clean baseline, a
recoverboot, and afulldevice's boot each run nopmcommand. - Changing
android.slim.categorieschanges the baseline hash of a non-full device and not of afulldevice. onSlimmedfires once per pass with the resolved categories, the disabled count aslabelCount, the signature,unknownLabels, and a duration; the daemon bridges it todevice.slimmedwithplatform: "android".estimate({ operation: "boot" })is larger by the pass budget when slim is enabled and the spec is notfull, and unchanged otherwise.- An
android.slim.categoriesthat is not an array of non-empty strings is rejected at config load, naming the key. simlock eventsdocumentation check: both EVENTS.md tables listdevice.slimmedfor both platforms.- Slow lane, real emulator: after a slim lease,
pm list packages -don the guest contains every default-category package the image carries andcom.google.android.gmsis enabled;simlock eventsshowsdevice.slimmedwithplatform: "android"; after release, the next lease of the same spec is granted after adevice.reclaimedwith strategysnapshotand the disabled set is unchanged; a--fulllease of the same spec has no package disabled.
Done when
- Every test above passes and
pnpm checkis green. - On a full device no
pmcommand runs on any boot. docs/CONFIGURATION.mdlists the categories and what stops working; both EVENTS.md files describe the Androiddevice.slimmed;docs/internal/KNOWN-PITFALLS.mdhas the Android section.- The slow lane passes on a machine with an Android SDK.
- The PR body walks the Completion conditions of Opt-in slim mode for Android emulators #159.
Out of scope
- Guest settings of any kind.
- Uninstalling packages or altering the system image.
- A per-lease choice of categories.
- Any change to the capacity RAM budget.
Depends on
- Boot Android emulators slim: guest RAM, low-RAM mode, and the feature profile #163
- Gateway: remote agents lease and reach physical devices on workers #442
- Heartbeats keep the simlock lease alive #473
- simlock ending a simlock lease ends the agent-device session first #474
Approval
- Approved for delivery
Written by an agent.
Activity
V3RON commentedon Oct 1, 2026
Spec updated: Done when amended for #172; Technical spec marked for revision against #172.
Written by an agent.
V3RON commentedon Oct 10, 2026
Spec updated: Depends on gains #442, the last task of #428 (physical devices), because both edit the same files.
Written by an agent.
V3RON commentedon Oct 12, 2026
Spec updated: Depends on gains #473. Feature #466 (agent-device through simlock) edits the same files or bumps the protocol, and the maintainer chose to deliver it first.
Written by an agent.
V3RON commentedon Oct 12, 2026
Spec updated: Depends on gains #474. Feature #466 edits the same files, and the maintainer chose to deliver it first.
Written by an agent.
V3RON commentedon Oct 12, 2026
Closed as not planned: the approach changed. Android slim will be a guest RAM cap only, with the size set by the operator. Disabling background packages is dropped. Low-RAM mode becomes something a lease request asks for, kept apart from slim. A new issue will cover slim Android emulators and link back here.
Written by an agent.