Repository navigation
fix(mobile): require an unlocked device for the Android Approve action - #8245
Conversation
Both Android cards now send Approve through an invisible gate activity that starts the headless approval once the device is unlocked, and the action carries the platform's authentication requirement from Android 12. A declined unlock leaves the request in place for an unlocked tap, and API levels without requestDismissKeyguard keep the request until then.
The contract suite reads the Kotlin sources: every Approve action now goes through the gate activity, the action carries the platform authentication requirement where it exists, and the dismissal path keeps its work order.
|
bot: Captures from the Android agent cards on the emulator (API 36, owned fake-account test data, local stack). Approve and Open stay on the card while the device is locked; tapping Approve there brings up device authentication before the approval runs. Both captures are of the dev-only glanceable fixtures that post the real notification through the production Follow and push path. |
Code Review SummaryStatus: No Issues Found | Recommendation: Merge Executive SummaryThe incremental commit removes the gate activity's show-when-locked flags so the keyguard stays present for the dismissal request; the removal is consistent with Assumptions: [INFERENCE] The behavior of Files Reviewed (1 file)
Previous Findings
Previous Review Summaries (3 snapshots, latest commit 33f5d09)Current summary above is authoritative. Previous snapshots are kept for context only. Previous review (commit 33f5d09)Status: No Issues Found | Recommendation: Merge Executive SummaryThe incremental change only retargets two code comments: the manifest now describes the action receiver as handling dismissal and quiet-alarm broadcasts, and Files Reviewed (2 files)
Previous Findings
Previous review (commit 98ee139)Status: No Issues Found | Recommendation: Merge Executive SummaryThe incremental change only updates four code comments to cite "Android: actions" rule 4 for notification dismissal, matching the renumbered spec where rule 3 is now the unlocked-device rule and rule 4 is dismissal; all four citations were verified against Files Reviewed (4 files)
Previous review (commit 73c9698)Status: 1 Issue Found | Recommendation: Address before merge Executive SummaryThe Android Approve presence gate is implemented consistently on both cards (activity PendingIntent, API 31+ authentication flag, unlocked-only enqueue, keyguard callback guards); the single finding is a spec renumbering that leaves four code comments citing the wrong rule. Overview
Issue Details (click to expand)SUGGESTION
Files Reviewed (9 files)
Reviewed by deepseek-flash-latest · Input: 36.1K · Output: 5.8K · Cached: 282.1K Review guidance: REVIEW.md from base branch |
The Android actions list gains the unlocked-device rule before the dismissal rule, so the four comments that cite the dismissal now cite rule 4.
The manifest note for the action receiver names the dismissal and quiet-alarm broadcasts, and the shared answer module names the gate activity that starts the headless approval on Android.
|
bot: E2E captures from the API 36 emulator running the dev client built from this branch against the local dev stack (owned fake account, dev-only glanceable fixture cards). The shade was expanded and the card actions driven through the uiautomator bounds read in the same command as each capture.
The device logs and the backend read for the authentication path are quoted in the pull request verification section. |
eshurakov
left a comment
There was a problem hiding this comment.
Shallow review: no critical issues found.




Evidence
Evidence: demonstrated — on the API 36 and API 30 emulators, each running the dev client built from this branch against the local dev stack, Approve completes the device authentication step first and then runs the approval, and the card shows the answer.
What changed
Approve on both Android agent cards now runs only for an unlocked device.
ActiveAgentsApprovalActivity: an invisible, single-use activity in its owntask (
exported=false, excluded from recents, translucent, no content). Itdraws nothing and stays behind the keyguard, so the dismissal it asks for is
what presents the device credential. It runs the approval when the device is
already unlocked; otherwise it asks the keyguard to dismiss itself (API 26+)
and runs the approval on
onDismissSucceeded. An authentication step that isnot completed returns to the card with the approval still available. API 24-25
have no
requestDismissKeyguardand no action authentication flag, so theapproval runs from an unlocked tap.
ApprovalGate: the single construction of the Approve transport for bothcards.
PendingIntent.getActivityto the gate activity, the action built withsetAuthenticationRequired(true)from API 31, and the uniqueOneTimeWorkRequest(KEEP) that runsKiloActiveAgentsApprove.ActiveAgentsLiveUpdateModule.approvePendingIntentandSessionNotifications.approveIntentnow build the Approve action throughApprovalGate; Open and New agent keep their own intents, unchanged.ActiveAgentsActionReceiverkeeps itsACTION_APPROVEbranch under the sameunlocked-device rule and otherwise only handles dismissal and the quiet alarm.
KEY_ORGANIZATION_IDwork-input constant removed..specs/mobile-session-live-activity.md"Android: actions" carries theunlocked-device rule, with a changelog entry.
Verification
Two emulators on this work VM, each with the dev client built from this branch
and signed in to an owned fake account on the local dev stack (
nextjs:3000,Metro :8081,
adb reversefor every mobile port). The cards come from thedev-only glanceable fixtures, which post through the production Follow path;
their approval key names no live request, so the revalidating read the approval
issues answers 404 and the card settles after the answer.
Transport, both cards (
dumpsys notification --noredact):API 36 emulator, device protected by a PIN
Approve on the expanded card completes the device authentication step first;
the approval starts after it:
The backend logs the revalidating read for the approval (
cliSessionsV2.get,404 for the fixture's key), and the card then lists Open alone.
An authentication step that is not completed returns to the shade; the
expanded card still lists Approve and Open.
With no authentication step outstanding, Approve starts the activity 34 ms
after the tap, the worker completes SUCCESS, and the same backend read
follows.
API 30 emulator, device protected by a PIN
The
requestDismissKeyguardpath, entered by starting the same activity with thesame extras the action carries:
The PIN pad is on screen (
PIN areaand the keypad in the uiautomator dump); afterthe PIN,
KeyguardViewMediator: handleKeyguardDone, thenWM-WorkerWrapper: Worker result SUCCESS for Work [... ActiveAgentsApproveWorker],then the backend
cliSessionsV2.getread.Unproven: no API 24-25 device was available. There the activity finishes without
asking, which is the rule for those levels (no
requestDismissKeyguard, no actionauthentication flag).
Production prerequisites
None.
Proxy answers
The owner's proxy gave these answers in the owner's place.
Answer: Activity on every API + authentication flag on 31+
Confidence: 0.75
Answer: No: keep the action, now unlock-gated
Confidence: 0.75
Answer: Fail closed: ignore the tap while locked
Confidence: 0.70
Answer: Nothing: finish silently, card keeps Approve
Confidence: 0.70