testsAndMisc/focus_owner
Krzysztof kuhy Rudnicki 2d4d2a2eb2
Some checks failed
Pre-commit checks / pre-commit (push) Waiting to run
Shell tests / shell-tests (push) Waiting to run
Shell tests / arch-tests (push) Waiting to run
Python tests / test (push) Has been cancelled
Block by hiding apps, which the phone cannot undo
The network layer was the wrong place for this. Tested remotely today: opening RethinkDNS and switching off its device blocklists un-blocked YouTube in a few taps, and no device owner API can stop that -- an on-device filter app always owns its own rules.

App hiding is different. 'pm unhide' from adb shell throws a SecurityException and the package stays hidden; only the device owner can reverse it, and reaching that costs a factory reset. So the block moves there, scoped to an explicit allowlist and the home geofence.

Both whitelists are rewritten from the apps that actually exist on this phone. The old ones described the rooted Blackview. Every browser is excluded now, so at home a link does not open at all -- confirmed as intended.

LAUNCHER_PACKAGE was still the Blackview's launcher, which is not installed here. The exporter refused to render the policy twice over it -- precisely what that check is for.

The network layer stays a soft deterrent by choice: pinned and uninstall-blocked, but without lockdown, and its blocklists remain switchable.

Private DNS is implemented but ships disabled. stunnel logged zero connections from the handset across two pin attempts, so the router does not forward 853.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014DqEd1uUhyT52KjaNeLanC
2026-08-11 19:49:11 +02:00
..
android Block by hiding apps, which the phone cannot undo 2026-08-11 19:49:11 +02:00
assets Block by hiding apps, which the phone cannot undo 2026-08-11 19:49:11 +02:00
docs focus_owner: keep the unhide path reachable, never hide the enforcer 2026-08-10 15:33:18 +02:00
lib Let the app set its own home, and lock VPN configuration 2026-08-10 22:17:31 +02:00
scripts Provision home coordinates out of git; layer the workout signal; drop the test hook 2026-08-09 21:42:20 +02:00
test Let the app set its own home, and lock VPN configuration 2026-08-10 22:17:31 +02:00
.gitignore Make focus policy and blocklist usable without root; fix workout CDN unblock 2026-08-05 09:15:02 +02:00
.metadata Make focus policy and blocklist usable without root; fix workout CDN unblock 2026-08-05 09:15:02 +02:00
analysis_options.yaml Make focus policy and blocklist usable without root; fix workout CDN unblock 2026-08-05 09:15:02 +02:00
pubspec.lock Make focus policy and blocklist usable without root; fix workout CDN unblock 2026-08-05 09:15:02 +02:00
pubspec.yaml Exempt the generated policy asset from prettier 2026-08-05 09:40:44 +02:00
README.md Make focus policy and blocklist usable without root; fix workout CDN unblock 2026-08-05 09:15:02 +02:00

focus_owner

Device Owner companion app for unrooted focus mode. Currently inert: it can be provisioned as device owner, but it applies no restrictions.

Why the escape hatch ships first

dpm remove-active-admin does not work on a device owner. The only exit that avoids a factory reset is DevicePolicyManager.clearDeviceOwnerApp(), callable solely by this package on itself. An app that locks the device down before proving it can let go leaves a soft-brick recoverable only through fastboot — painful on a locked bootloader, and impossible to reach if DISALLOW_FACTORY_RESET is ever set.

So: DevicePolicyBridge.releaseDeviceOwner() is implemented, unit-tested, and reachable from the app's main screen with no PC attached. Enforcement is added only after that path has been exercised on a provisioned device.

Provisioning (NOT done yet — requires a factory reset)

dpm set-device-owner refuses to run while any account exists on the device. The Pixel 6a has 6, so provisioning means a wipe. See ~/.claude/projects/-home-kuhy-testsAndMisc/memory/device-owner-not-root-banking-works.md for what does and does not survive that, and for the Google Wallet open risk.

Once wiped, before adding any account:

adb install -r build/app/outputs/flutter-apk/app-debug.apk
adb shell dpm set-device-owner com.kuhy.focus_owner/.FocusDeviceAdminReceiver

Then immediately verify tap-to-pay and banking apps, while the release button is still reachable.

Build and verify

cd ~/testsAndMisc/focus_owner
flutter analyze && flutter test
flutter build apk --debug
adb -s 23181JEGR08034 install -r build/app/outputs/flutter-apk/app-debug.apk
adb -s 23181JEGR08034 shell am start -n com.kuhy.focus_owner/.MainActivity

The status screen reads live state over the platform channel; "Android SDK 36" confirms the Kotlin bridge is working.

Still to do

  • Consume the policy JSON from python_pkg/focus_policy (export.py).
  • Add enforcement (setApplicationHidden, addUserRestriction, setAlwaysOnVpnPackage) — only after the release path is verified on-device.
  • Verify setApplicationHidden survives a reboot. pm suspend and pm disable-user both do NOT; do not assume the DPM equivalent behaves the same until measured.