mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-11-11 00:27:01 +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
61 lines
2.2 KiB
Python
61 lines
2.2 KiB
Python
"""Render a :class:`FocusPolicy` into a backend-neutral JSON document.
|
|
|
|
The Device Owner app is written in Kotlin and cannot read ``config.sh`` or
|
|
import this package. It reads this JSON instead, so both enforcement backends
|
|
stay driven by one policy definition.
|
|
|
|
Sorted keys and sorted lists keep the output stable, so committing a rendered
|
|
policy produces a meaningful diff when the policy actually changes rather than
|
|
noise from set iteration order.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from typing import TYPE_CHECKING, Any
|
|
|
|
if TYPE_CHECKING:
|
|
from python_pkg.focus_policy.model import FocusPolicy
|
|
|
|
SCHEMA_VERSION = 1
|
|
|
|
|
|
def policy_to_dict(policy: FocusPolicy) -> dict[str, Any]:
|
|
"""Return a JSON-serialisable representation of ``policy``."""
|
|
curfew: dict[str, str] | None = None
|
|
if policy.curfew is not None:
|
|
curfew = {
|
|
"start": policy.curfew.start.strftime("%H:%M"),
|
|
"end": policy.curfew.end.strftime("%H:%M"),
|
|
}
|
|
|
|
return {
|
|
"schema_version": SCHEMA_VERSION,
|
|
"home": {
|
|
"latitude": policy.home.latitude,
|
|
"longitude": policy.home.longitude,
|
|
"radius_m": policy.home.radius_m,
|
|
"hysteresis_m": policy.home.hysteresis_m,
|
|
},
|
|
"curfew": curfew,
|
|
"launcher_package": policy.launcher_package,
|
|
"allowed_packages": sorted(policy.allowed_packages),
|
|
"night_allowed_packages": sorted(policy.night_allowed_packages),
|
|
"never_disable_prefixes": sorted(policy.never_disable_prefixes),
|
|
"workout_unblock_domains": sorted(policy.workout_unblock_domains),
|
|
"browser_packages": sorted(policy.browser_packages),
|
|
}
|
|
|
|
|
|
def policy_to_json(policy: FocusPolicy, *, redact_home: bool = False) -> str:
|
|
"""Return ``policy`` as pretty-printed JSON.
|
|
|
|
``redact_home`` blanks the coordinates so a rendered policy can be attached
|
|
to a bug report or committed as a fixture without disclosing where the user
|
|
lives. The radius and hysteresis are kept, since they carry no location.
|
|
"""
|
|
payload = policy_to_dict(policy)
|
|
if redact_home:
|
|
payload["home"] = dict(payload["home"], latitude=None, longitude=None)
|
|
return json.dumps(payload, indent=2, sort_keys=True, ensure_ascii=False)
|