mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-11-12 08:31:03 +01:00
The decision layer and setApplicationHidden were both built and tested but never connected -- the service logged a tick. Joining them meant porting the decision to Kotlin, because a background Service has no Flutter engine and starting one per alarm to answer a pure question would be slow and would add a failure mode at precisely the moment enforcement has to be reliable. That duplication is only safe if the two cannot drift, so the Kotlin suite restates the Dart cases one for one rather than testing something adjacent. It was checked for vacuity by asserting the hidden set contained a package that is not installed: 13 tests completed, 1 failed, and it was the right one. Show is applied before hide. If a pass is interrupted -- process killed, device sleeps -- the half that ran is the half that restores access rather than the half that removes it. The launcher, the protected prefixes and the app's own package are re-checked at the point of use as well as in the policy, because hiding the dialer would be unrecoverable from the device itself. An unreadable policy yields no decision rather than an empty allowlist, which would hide every app. The next run is scheduled in a finally, so an enforcement bug costs one tick instead of ending the chain. Note for the next session: JDK 26 is the system default and breaks the Android jlink transform; the Gradle tests need JAVA_HOME=/usr/lib/jvm/java-21-openjdk, which is what Flutter itself uses. NOT verified on device. Unit tests cover the decision, but no real hide/show pass has been observed -- a managed profile was created and the app made profile owner, and the UI confirmed the policy loaded, but the session ended before a pass was triggered. The profile was removed and user 0 verified clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AwepyL5Lxs5QKbKkMpxxxL
54 lines
4.3 KiB
JSON
54 lines
4.3 KiB
JSON
{
|
|
"intent": "Connect the enforcement decision to the DPM hide call so the service does the work it was scheduled to do.",
|
|
"scope": [
|
|
"focus_owner/android/.../FocusPolicy.kt - new: Kotlin policy parser",
|
|
"focus_owner/android/.../EnforcementDecision.kt - new: Kotlin decision layer",
|
|
"focus_owner/android/.../EnforcementRunner.kt - new: gathers inputs, applies the result",
|
|
"focus_owner/android/.../EnforcementService.kt - calls the runner instead of logging a tick",
|
|
"focus_owner/android/app/build.gradle.kts - JVM unit test dependencies",
|
|
"focus_owner/android/app/src/test/.../EnforcementDecisionTest.kt - new: 13 tests",
|
|
"Non-goal: no Device Owner provisioning; an on-device hide/show pass was NOT verified"
|
|
],
|
|
"changes": [
|
|
"The decision is now implemented twice, in Dart and Kotlin. That is deliberate: a background Service has no Flutter engine, and spinning one up per alarm to answer a pure question would be slow and would add a failure mode at exactly the moment enforcement must be reliable. The duplication is only safe because the Kotlin suite restates the Dart cases one for one.",
|
|
"EnforcementRunner applies show before hide. If a pass is interrupted - process killed, device sleeps - the half that has run is the half that restores access rather than the half that removes it.",
|
|
"The launcher, protected prefixes and the app's own package are re-checked at the point of use as well as in the policy. Hiding the dialer or the launcher would be unrecoverable from the device itself, so it gets defence in depth.",
|
|
"An unreadable policy returns no decision at all rather than an empty allowlist, which would hide every app on the device.",
|
|
"The next run is scheduled in a finally block, so an enforcement bug costs one tick rather than ending the chain."
|
|
],
|
|
"verification": [
|
|
{
|
|
"command": "JAVA_HOME=/usr/lib/jvm/java-21-openjdk ./gradlew :app:testDebugUnitTest",
|
|
"result": "pass",
|
|
"evidence": "13 tests, 0 failures. JDK 26 is the system default and breaks the Android jlink transform; Flutter itself uses JDK 21, which works."
|
|
},
|
|
{
|
|
"command": "mutation: assert the hidden set is a package that is not installed",
|
|
"result": "pass",
|
|
"evidence": "'13 tests completed, 1 failed' and the failure was exactly 'at home only non-allowlisted apps are hidden'. The suite is live, not vacuous. Reverted afterwards."
|
|
},
|
|
{
|
|
"command": "cd focus_owner && flutter analyze && flutter test",
|
|
"result": "pass",
|
|
"evidence": "'No issues found!' and 67 tests passing."
|
|
},
|
|
{
|
|
"command": "pm create-user --managed; dpm set-profile-owner; pm remove-user",
|
|
"result": "partial",
|
|
"evidence": "Profile 11 was created and the app set as profile owner, and the UI confirmed 'Admin active: yes' with the policy loaded (84 apps, curfew 23:00-05:00). The enforcement pass itself was NOT triggered before the session ended. The profile was removed; user 0 verified clean, Device Owner Type -1, Chrome hidden=false."
|
|
}
|
|
],
|
|
"risks": [
|
|
"THE WIRING IS UNVERIFIED ON DEVICE. Unit tests cover the decision; no real hide/show pass has been observed. The next session should recreate a managed profile and confirm a pass actually hides and restores.",
|
|
"The Kotlin and Dart decision layers can drift. The test suites pin them, but nothing mechanically diffs the two - a new case added to one may not be added to the other.",
|
|
"workoutActive is hardcoded false in EnforcementRunner. The signal classes exist in Dart but are not reachable from the Kotlin service, so the workout exception cannot currently fire.",
|
|
"Location permission is never requested at runtime. The runner reads a fix when granted and returns null otherwise, which fails closed into enforcing - correct, but it means the geofence is inert until the permission flow is built.",
|
|
"Unchanged: DO hiding in user 0 is unproven; only profile-owner hiding in a work profile has been measured."
|
|
],
|
|
"rollback": [
|
|
"git revert. The service no-ops without device owner, so nothing on the phone changes.",
|
|
"Nothing to undo on the device: the test profile is already removed and user 0 was verified unchanged.",
|
|
"After rollback validate: flutter analyze && flutter test pass, and dumpsys device_policy reports Device Owner Type: -1."
|
|
]
|
|
}
|