TL;DR — what went wrong and what we need
The story in plain terms:
- ~A month ago we shipped ASG Client 37 to the fleet through the normal APK OTA channel. It installed into userland (
/data) like every normal app update.
- ASG Client 37 shipped with a silent bug that breaks its own APK update step for a large fraction of users. MTK and BES OTA still work fine — only the APK update path is broken. So these users are stranded on 37 and cannot pull any new APK the normal way.
- The fix is in ASG Client 39. But we can't deliver 39 through the normal APK channel, because the thing that delivers it is exactly what's broken on those units.
- Clever workaround we tried: bake ASG Client 39 into the MTK 06/26 firmware patch (
MentraLive_20260626), since MTK OTA still works. The idea: use the working firmware channel to smuggle 39 onto the device and bypass the busted APK path.
- Why it didn't work: an MTK patch writes the
/system partition. It successfully put 39 into /system — but Android was already running a /data copy of 37, installed earlier by the normal APK OTA. On Android, a /data-installed app shadows (overrides) the /system copy of the same app, and a system-image flash does not remove /data overlays. So the device now has:
/system = 39 (from the MTK patch) ✅ present but dormant
/data = 37 (old APK-OTA install) ← this is what actually runs ❌
- Result: the app is physically on the device as 39, but the phone still reports 37 and runs 37. The 39 is stranded, permanently masked, until something removes or upgrades the
/data/37 overlay.
All of this is confirmed on real hardware — see "Verified on a physical Mentra Live" below.
What we need (this ticket): a new MTK patch that ships 39 in /system and includes a tiny first-boot heal script that, on boot, notices the running ASG Client is < 39 and runs pm install -r -d of the /system 39 APK — which upgrades the /data overlay from 37 → 39. We proved this exact command fixes a real stranded device live (it flipped to 39, and stayed on 39 after reboot). No download, no network, no dependence on the broken 37 APK-OTA path — the 39 APK is already sitting in /system; we just tell PackageManager to use it.
Why a boot script and not something in the OTA zip: pm/PackageManager only exist after Android boots, not in recovery where the OTA updater-script runs. So the heal must live in the /system image and fire on sys.boot_completed. Full proposed script is below.
Main build-side unknown: the SELinux domain allowed to exec a shell and call pm post-boot in our MTK build. Everything else is spelled out.
Problem (detail)
ASG Client 37 (shipped ~a month ago via the normal APK OTA channel) has a bug that breaks the APK update step for a large fraction of users. MTK and BES OTA still work — only the APK step is busted. The fix is in ASG Client 39, but those users can't reach 39 through normal means because the very mechanism that would deliver it is broken.
To bypass this, the MTK 06/26 patch (MentraLive_20260626) was built to carry ASG Client 39 in the /system image. That got 39 onto /system/app/MentraOSLauncher/MentraOSLauncher.apk — but the device keeps running the old 37, because a /data overlay (the previously-installed APK-OTA copy of 37) shadows the /system copy. An Android system-image flash does not remove /data app overlays, so /system=39 is permanently masked by /data=37.
Verified on a physical Mentra Live (adb, 2026-07-07)
getprop ro.custom.ota.version → MentraLive_20260626
- Active/running package:
pm path com.mentra.asg_client → /data/app/... , dumpsys → versionCode=37, pkgFlags=[ SYSTEM … UPDATED_SYSTEM_APP ] (the UPDATED_SYSTEM_APP flag = a /data update is shadowing the system app)
- The actual
/system/app/MentraOSLauncher/MentraOSLauncher.apk on disk → versionCode=39 / 39.0 (confirmed via aapt2 dump badging)
/system 39 and /data 37 are signed with the same Mentra platform key (identical signer SHA-256), so a same-signer reinstall is signature-legal.
- Fix proven live:
pm install -r -d /system/app/MentraOSLauncher/MentraOSLauncher.apk upgraded the /data overlay 37 → 39; device runs 39 and stays on 39 across reboot.
The ask
Build a new MTK patch that:
- Ships ASG Client 39 in
/system (same as 06/26 — the APK is already there), and
- Adds a first-boot heal: if the active ASG Client is
< 39, reinstall the /system copy over the /data overlay so the device actually runs 39.
The 39 APK is already present in /system on these units, so no network / no download / no dependency on the busted 37 APK-OTA path is needed — we just re-point PackageManager at a file that's already on disk.
We're deliberately going the simple init-script route, not an app/recovery-worker route, to unblock the fleet fast.
Why a boot script and not the OTA zip's updater-script
pm / PackageManager do not exist in recovery, where an OTA updater-script runs. So the heal must run after Android boots (sys.boot_completed), from inside the /system image — not from the OTA zip itself.
Proposed payload (inline — tune paths/SELinux domain to our build)
/system/etc/init/asg-heal.rc
# Run the heal check after boot
on property:sys.boot_completed=1
exec_background u:r:su:s0 root -- /system/bin/sh /system/bin/asg-heal.sh
/system/bin/asg-heal.sh (mark executable, 0755)
#!/system/bin/sh
# Self-correcting heal: reinstall the /system 39 APK over the /data overlay whenever running <39.
# The <39 floor is BOTH the retry mechanism (failed install just retries next boot) AND the
# downgrade guard (a future 40 is >=39, so it's left untouched — never reinstall on !=39).
SYS_APK=/system/app/MentraOSLauncher/MentraOSLauncher.apk
CUR=$(dumpsys package com.mentra.asg_client | grep -m1 versionCode | sed 's/.*versionCode=\([0-9]*\).*/\1/')
if [ -n "$CUR" ] && [ "$CUR" -lt 39 ]; then
log -t asg-heal "ASG Client $CUR < 39, reinstalling from $SYS_APK"
pm install -r -d "$SYS_APK"
log -t asg-heal "pm install exit=$?"
fi
# on >=39 or unknown version: do nothing, no state to set
Design guarantees
- Self-correcting / no stuck state: if an install ever fails (full disk, transient PM hiccup, slow-boot race), the device is still
< 39 next boot, so the check simply retries. There is no one-shot flag to burn, so a transient failure can never permanently strand a unit.
- Cannot downgrade a future update: the condition is a floor (
< 39), never != or unconditional. When normal APK OTA later ships 40, 40 >= 39 → the script is a no-op and never touches it. (pm install -r -d allows downgrade, so keeping this a floor is load-bearing.)
- Safe to leave in the image permanently: no-op on any unit already ≥39; the only per-boot cost is one cheap
dumpsys version read.
- No prompt / no permission dance: runs as root under init, so
pm install is silent.
- Same-signer: verified
/system 39 and /data 37 share the Mentra platform key, so pm install -r is accepted.
Notes / things to confirm during build
- SELinux domain:
u:r:su:s0 above is a placeholder. Use whatever domain in our MTK build is permitted to exec a shell and invoke pm post-boot. This is the main build-side unknown.
- Confirm the
/system APK path (MentraOSLauncher.apk) is stable in the new image.
- Optional: after
pm install, log the resulting versionCode for telemetry so we can measure fleet heal rate.
Acceptance criteria
- A unit on MTK 06/26 (
/system=39, /data=37, running 37) takes the new MTK patch, reboots, and comes up running ASG Client 39 with no user action.
- Units already on ≥39 are unaffected (no-op), including a hypothetical future unit on 40 (must NOT be downgraded).
- If a boot-time install fails, the unit heals on a subsequent reboot (no permanent stuck state).
- Reboot-stable: nothing reverts the overlay back to 37 (verify no recovery-worker/backup path re-installs a 37 APK).
Follow-ups (not this ticket)
- Root-cause + fix note for the ASG 37 APK-OTA bug is already identified/fixed in 39; this ticket is only the delivery vehicle to get stuck users onto 39.
- Once the fleet is on ≥39, normal APK OTA resumes and this heal is dormant.
TL;DR — what went wrong and what we need
The story in plain terms:
/data) like every normal app update.MentraLive_20260626), since MTK OTA still works. The idea: use the working firmware channel to smuggle 39 onto the device and bypass the busted APK path./systempartition. It successfully put 39 into/system— but Android was already running a/datacopy of 37, installed earlier by the normal APK OTA. On Android, a/data-installed app shadows (overrides) the/systemcopy of the same app, and a system-image flash does not remove/dataoverlays. So the device now has:/system= 39 (from the MTK patch) ✅ present but dormant/data= 37 (old APK-OTA install) ← this is what actually runs ❌/data/37 overlay.All of this is confirmed on real hardware — see "Verified on a physical Mentra Live" below.
What we need (this ticket): a new MTK patch that ships 39 in
/systemand includes a tiny first-boot heal script that, on boot, notices the running ASG Client is< 39and runspm install -r -dof the/system39 APK — which upgrades the/dataoverlay from 37 → 39. We proved this exact command fixes a real stranded device live (it flipped to 39, and stayed on 39 after reboot). No download, no network, no dependence on the broken 37 APK-OTA path — the 39 APK is already sitting in/system; we just tell PackageManager to use it.Why a boot script and not something in the OTA zip:
pm/PackageManager only exist after Android boots, not in recovery where the OTA updater-script runs. So the heal must live in the/systemimage and fire onsys.boot_completed. Full proposed script is below.Main build-side unknown: the SELinux domain allowed to exec a shell and call
pmpost-boot in our MTK build. Everything else is spelled out.Problem (detail)
ASG Client 37 (shipped ~a month ago via the normal APK OTA channel) has a bug that breaks the APK update step for a large fraction of users. MTK and BES OTA still work — only the APK step is busted. The fix is in ASG Client 39, but those users can't reach 39 through normal means because the very mechanism that would deliver it is broken.
To bypass this, the MTK 06/26 patch (
MentraLive_20260626) was built to carry ASG Client 39 in the/systemimage. That got 39 onto/system/app/MentraOSLauncher/MentraOSLauncher.apk— but the device keeps running the old 37, because a/dataoverlay (the previously-installed APK-OTA copy of 37) shadows the/systemcopy. An Android system-image flash does not remove/dataapp overlays, so/system=39 is permanently masked by/data=37.Verified on a physical Mentra Live (adb, 2026-07-07)
getprop ro.custom.ota.version→MentraLive_20260626pm path com.mentra.asg_client→/data/app/...,dumpsys→ versionCode=37,pkgFlags=[ SYSTEM … UPDATED_SYSTEM_APP ](theUPDATED_SYSTEM_APPflag = a/dataupdate is shadowing the system app)/system/app/MentraOSLauncher/MentraOSLauncher.apkon disk → versionCode=39 / 39.0 (confirmed viaaapt2 dump badging)/system39 and/data37 are signed with the same Mentra platform key (identical signer SHA-256), so a same-signer reinstall is signature-legal.pm install -r -d /system/app/MentraOSLauncher/MentraOSLauncher.apkupgraded the/dataoverlay 37 → 39; device runs 39 and stays on 39 across reboot.The ask
Build a new MTK patch that:
/system(same as 06/26 — the APK is already there), and< 39, reinstall the/systemcopy over the/dataoverlay so the device actually runs 39.The 39 APK is already present in
/systemon these units, so no network / no download / no dependency on the busted 37 APK-OTA path is needed — we just re-point PackageManager at a file that's already on disk.We're deliberately going the simple init-script route, not an app/recovery-worker route, to unblock the fleet fast.
Why a boot script and not the OTA zip's updater-script
pm/ PackageManager do not exist in recovery, where an OTA updater-script runs. So the heal must run after Android boots (sys.boot_completed), from inside the/systemimage — not from the OTA zip itself.Proposed payload (inline — tune paths/SELinux domain to our build)
/system/etc/init/asg-heal.rc/system/bin/asg-heal.sh(mark executable, 0755)Design guarantees
< 39next boot, so the check simply retries. There is no one-shot flag to burn, so a transient failure can never permanently strand a unit.< 39), never!=or unconditional. When normal APK OTA later ships 40,40 >= 39→ the script is a no-op and never touches it. (pm install -r -dallows downgrade, so keeping this a floor is load-bearing.)dumpsysversion read.pm installis silent./system39 and/data37 share the Mentra platform key, sopm install -ris accepted.Notes / things to confirm during build
u:r:su:s0above is a placeholder. Use whatever domain in our MTK build is permitted to exec a shell and invokepmpost-boot. This is the main build-side unknown./systemAPK path (MentraOSLauncher.apk) is stable in the new image.pm install, log the resulting versionCode for telemetry so we can measure fleet heal rate.Acceptance criteria
/system=39,/data=37, running 37) takes the new MTK patch, reboots, and comes up running ASG Client 39 with no user action.Follow-ups (not this ticket)