mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-11-11 17:14:08 +01:00
phone_focus_mode is built for a rooted phone: the hosts layer is a read-only
bind mount over /system/etc/hosts, chosen so that even root-in-a-terminal
cannot overwrite it. None of that ports to a stock locked device. This splits
out the parts that are device-independent so one policy can drive both.
python_pkg/focus_policy parses config.sh rather than copying it, so the shell
config stays the source of truth and the two cannot drift. It rejects the
shipped REDACTED_* coordinate placeholder instead of defaulting to (0,0),
which would move "home" to the Atlantic and quietly disable the location gate.
blocklist.py converts the hosts file to domains for a DoT resolver or a local
VpnService. Three properties of the real 194k-line file make the obvious parser
wrong, and all three are handled: generate_hosts_file.sh *comments out*
deliberate unblocks rather than deleting them, so stripping '#' re-blocks
facebook/messenger/linkedin/4chan; the StevenBlack header carries loopback and
IPv6 housekeeping that would blackhole localhost; and ~1060 upstream annotation
tokens ('#[adware.zeno]', '"spam') render as address=/"spam/, which makes
dnsmasq refuse to start -- taking DNS down rather than blocking anything.
Verified with dnsmasq --test over all 185681 generated domains.
strip_workout_hosts.py had the same class of bug on the live rooted phone:
exact set membership meant the allowlisted googlevideo.com never matched the
dynamic r*---sn-*.googlevideo.com hosts YouTube actually streams from, so the
workout unblock was only partly working. Now suffix-matched (17 lines stripped,
was 14). The three new tests were checked against the original code in a
throwaway worktree; two of them fail there, so they pin the fix rather than
passing vacuously.
focus_owner is a device-owner app that deliberately does nothing yet. It ships
clearDeviceOwnerApp() implemented and tested first, because dpm
remove-active-admin does not work on a device owner: an app that locks the
device down before proving it can let go leaves a soft-brick recoverable only
through fastboot on a locked bootloader. Built, installed and launched on the
Pixel 6a; dumpsys afterwards confirms Device Owner Type -1 and no user
restrictions, i.e. the phone is unchanged.
Measured while scoping this, and not yet reflected in any enforcement code:
on Android 16 both pm suspend and pm disable-user are cleared by a reboot, so
shell-level app blocking is per-session only. setApplicationHidden is assumed
to persist under Device Owner but is untested.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AwepyL5Lxs5QKbKkMpxxxL
57 lines
2.2 KiB
Markdown
57 lines
2.2 KiB
Markdown
# 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:
|
|
|
|
```bash
|
|
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
|
|
|
|
```bash
|
|
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.
|