Add cs_fcrst factory reset APK reinstall handling#3233
Conversation
Handle the BES cs_fcrst UART command (PWR+FN2 long-long press) in asg_client by reinstalling the ASG client APK to reset the glasses. - Add FactoryResetEvent and dispatch cs_fcrst in McuEventParser - Add FactoryResetEventSubscriber: stop active recording, then install a staged local APK if present (update APK, else backup APK), otherwise download+install via the existing OTA pipeline - Register the subscriber on the peripheral event bus Co-authored-by: Nicolo Micheletti <michelettinik@gmail.com>
📋 PR Review Helper📱 Mobile App Build⏳ Waiting for build... 🕶️ ASG Client Build❌ Build failed (commit 📦 Previous build: (commit 🔀 Test Locallygh pr checkout 3233 |
PR Agent Orchestrator State{
"cycle": 9,
"fixRound": 3,
"totalReviewerRuns": 9,
"consecutiveNoNewReviews": 2,
"openFindings": [],
"resolvedFindings": [
{
"id": "443346",
"fingerprint": "asg_client/app/src/test/java/com/mentra/asg_client/io/peripheral/mcueventparsertest.java:241d08773c75",
"source": "standards",
"severity": "blocking",
"file": "asg_client/app/src/test/java/com/mentra/asg_client/io/peripheral/McuEventParserTest.java",
"line": 79,
"message": "McuEventParserTest claims to verify every BES command but has no case for the new cs_fcrst -> FactoryResetEvent mapping. Add a csFcrst_mapsToFactoryResetEvent test mirroring csShut_mapsToShutdownEvent.",
"status": "resolved",
"introducedCycle": 1,
"lastSeenCycle": 4
},
{
"id": "b50202",
"fingerprint": "asg_client/app/src/main/java/com/mentra/asg_client/service/core/handlers/subscribers/factoryreseteventsubscriber.java:75ba80779693",
"source": "standards",
"severity": "blocking",
"file": "asg_client/app/src/main/java/com/mentra/asg_client/service/core/handlers/subscribers/FactoryResetEventSubscriber.java",
"line": 1,
"message": "No FactoryResetEventSubscriberTest added, though all sibling subscribers (Shutdown/BesOtaAuth/Battery) have unit tests and this class has branching logic (staged APK -> backup APK -> OTA download, recording stop). Add a Robolectric test mirroring ShutdownEventSubscriberTest covering the install/fallback paths and the no-op for non-FactoryResetEvent.",
"status": "resolved",
"introducedCycle": 1,
"lastSeenCycle": 4
},
{
"id": "cd07e9",
"fingerprint": "asg_client/app/src/test/java/com/mentra/asg_client/io/peripheral/mcueventparsertest.java:01dc36ef2ec4",
"source": "standards",
"severity": "blocking",
"file": "asg_client/app/src/test/java/com/mentra/asg_client/io/peripheral/McuEventParserTest.java",
"line": 79,
"message": "McuEventParserTest claims to verify every BES command but still has no case for the new cs_fcrst -> FactoryResetEvent mapping. Add csFcrst_mapsToFactoryResetEvent mirroring csShut_mapsToShutdownEvent.",
"status": "resolved",
"introducedCycle": 2,
"lastSeenCycle": 4
},
{
"id": "38f817",
"fingerprint": "asg_client/app/src/main/java/com/mentra/asg_client/service/core/handlers/subscribers/factoryreseteventsubscriber.java:e59ceb3ed993",
"source": "standards",
"severity": "blocking",
"file": "asg_client/app/src/main/java/com/mentra/asg_client/service/core/handlers/subscribers/FactoryResetEventSubscriber.java",
"line": 1,
"message": "No FactoryResetEventSubscriberTest added, though sibling subscribers (Shutdown/BesOtaAuth/Battery) have unit tests and this class has branching logic (staged APK -> backup APK -> OTA download, recording stop). Add a Robolectric test mirroring ShutdownEventSubscriberTest covering install/fallback paths and the no-op for non-FactoryResetEvent.",
"status": "resolved",
"introducedCycle": 2,
"lastSeenCycle": 4
},
{
"id": "2b43fe",
"fingerprint": "asg_client/app/src/test/java/com/mentra/asg_client/io/peripheral/mcueventparsertest.java:c7acc5b1c1d4",
"source": "standards",
"severity": "blocking",
"file": "asg_client/app/src/test/java/com/mentra/asg_client/io/peripheral/McuEventParserTest.java",
"line": 79,
"message": "McuEventParserTest's class Javadoc claims to verify every BES command, but has no case for the new cs_fcrst -> FactoryResetEvent mapping. Add csFcrst_mapsToFactoryResetEvent mirroring csShut_mapsToShutdownEvent (line 77).",
"status": "resolved",
"introducedCycle": 3,
"lastSeenCycle": 4
},
{
"id": "a603ac",
"fingerprint": "asg_client/app/src/main/java/com/mentra/asg_client/service/core/handlers/subscribers/factoryreseteventsubscriber.java:51245e39c9d0",
"source": "standards",
"severity": "blocking",
"file": "asg_client/app/src/main/java/com/mentra/asg_client/service/core/handlers/subscribers/FactoryResetEventSubscriber.java",
"line": 1,
"message": "No FactoryResetEventSubscriberTest added, though sibling subscribers (Shutdown/BesOtaAuth/Battery) have unit tests and this class has branching logic (staged APK -> backup APK -> OTA download, recording stop). Add a Robolectric test mirroring ShutdownEventSubscriberTest covering the install/fallback paths and the no-op for non-FactoryResetEvent.",
"status": "resolved",
"introducedCycle": 3,
"lastSeenCycle": 4
}
],
"nitFindings": [
{
"id": "cd3f8d",
"fingerprint": "asg_client/app/src/main/java/com/mentra/asg_client/service/core/handlers/subscribers/factoryreseteventsubscriber.java:93487a521c47",
"source": "standards",
"severity": "nit",
"file": "asg_client/app/src/main/java/com/mentra/asg_client/service/core/handlers/subscribers/FactoryResetEventSubscriber.java",
"line": 0,
"message": "Commit author is 'Cursor Agent'; root AGENTS.md asks commits to reflect the responsible human author (co-author trailer already names a human, so non-blocking).",
"status": "open",
"introducedCycle": 1,
"lastSeenCycle": 1
},
{
"id": "51dca9",
"fingerprint": "asg_client/app/src/main/java/com/mentra/asg_client/service/core/handlers/subscribers/factoryreseteventsubscriber.java:57cfbe6d6d0f",
"source": "standards",
"severity": "nit",
"file": "asg_client/app/src/main/java/com/mentra/asg_client/service/core/handlers/subscribers/FactoryResetEventSubscriber.java",
"line": 29,
"message": "Member variables serviceManager/context don't use the mCamelCase 'm' prefix from asg_client/AGENTS.md, but they match the existing subscribers/ package style (e.g. ShutdownEventSubscriber). Consistent with neighbors; non-blocking.",
"status": "open",
"introducedCycle": 5,
"lastSeenCycle": 5
}
],
"frozenPair": [
"standards",
"bugbot"
],
"phase": "convergence",
"status": "budget_exhausted",
"lastPair": [],
"stagnationFixRounds": 0,
"lastOpenCount": 0,
"fingerprintReopenCounts": {},
"mutedFingerprints": []
} |
|
bugbot run |
🤖 PR Agent Review — cycle 9✅ No blocking findings · 0 blocking · 2 nits No model reviews ran this cycle. Updated automatically by the PR Agent Orchestrator each review cycle. Nits do not block merge. |
|
bugbot run |
|
PR Agent Orchestrator failed. See run: https://github.com/Mentra-Community/MentraOS/actions/runs/28086101432 |
|
bugbot run |
|
bugbot run |
|
bugbot run |
|
bugbot run |
When the fixer pushes commits via GITHUB_TOKEN, GitHub blocks CI triggers with conclusion=action_required (bot security gate). isCiFailed() was treating this as a real failure, causing the fixer to loop indefinitely with no open findings. Fix: - Remap action_required/waiting workflow runs to pending in fetchWorkflowStatuses so they are not treated as completed failures - Add awaitingApproval flag to CiCheckStatus - isCiGreen() treats awaiting-approval checks as non-blocking — a human reviewer will approve and trigger CI when they look at the PR anyway Co-authored-by: Cursor <cursoragent@cursor.com>
PR Agent HandoffExit reason: Budget exhausted — fix or review cycle cap reached.
CI status
Remaining blocking findingsNone Nits (informational)
Humans merge when ready. Agents do not auto-merge. Label |
PR Agent HandoffExit reason: Budget exhausted — fix or review cycle cap reached.
CI status
Remaining blocking findingsNone Nits (informational)
Humans merge when ready. Agents do not auto-merge. Label |
Summary
Wires the BES
cs_fcrstUART command intoasg_clientso that a factory-reset request from the glasses (user holds PWR+FN2 for ~10s) resets the device by (re)installing the ASG client APK.Previously
{"C":"cs_fcrst"}hit theMcuEventParserdefault:branch and was silently dropped.Behavior
On
cs_fcrst:OtaConstants.ASG_UPDATE_APK_PATHif present + readable (OtaHelper.installApk).OtaHelper.reinstallApkFromBackup()(validates + installsBACKUP_APK_PATH).OtaHelper.startOtaFromPhone()downloads, verifies, and installs from the OTA manifest (null-guarded ifOtaHelperisn't initialized).The install itself reuses the existing OEM SystemUI install broadcast (
com.xy.xsetting.action cmd=install) — the only on-device install mechanism in this app.Changes
FactoryResetEvent— new typed peripheral event (mirrorsShutdownEvent).McuEventParser— dispatchcase "cs_fcrst"next tocs_shut(+ import).FactoryResetEventSubscriber— performs the stop-recording + local-first / download-fallback install (modeled onShutdownEventSubscriber).ServiceInitializer— register the subscriber on the peripheral event bus (+ import).Design decisions
MASTER_CLEAR/wipeDatacapability regardless.lxy_glass_cmd_factoryreset()does not wait for a reply and nosr_fcrstreply command is defined, so none is sent (unlikecs_shut→sr_shut).AsgConstantscommand constant:McuEventParseruses inline command literals throughout, andAsgConstantsonly holds RGB-LED command strings — a lonecs_fcrstconstant would be inconsistent.Known caveat
startOtaFromPhone()is version-gated (installs only if the serverversionCodeis greater than the current one). A forced reinstall of the same version would require a small forced-download variant; flag if that behavior is desired.Testing
scripts/check-android-compile.sh asg(i.e.:app:compileDebugJavaWithJavac) → BUILD SUCCESSFUL (only pre-existing deprecation warnings)./storage/emulated/0/asg/asg_client_update.apk, send{"C":"cs_fcrst"}(or hold PWR+FN2 10s), and confirm the SystemUI install broadcast in logs (tagsASGClientOTA/FactoryResetEventSubscriber).