Phase 5.1: kids' Android client (Accessibility block overlay + tamper alert)
Add android/, a normal Gradle/Kotlin project with its own wrapper. It blocks by interruption: an Accessibility Service watches the foreground package and a full-screen overlay covers any listed app while the device is locked (FR-16). No Device Owner, no factory reset, no managed profile. The client subscribes to the retained restrict/<device>/config topic on the 8883 TLS plane, keeps the last config on disk so a broker-less device keeps its rules (FR-14), and reports tampering on its own status topic (FR-17): a disabled service, a revoked overlay permission, or a force-stop seen on the next start. The Control Service now parses the tamper field and raises an Apprise alert, so it reaches Sam. The block path and the tamper alert are proven on the headless pixel35 emulator on .27; see android/README.md and android/tools/emu-proof.sh. Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
@@ -18,6 +18,7 @@ import (
|
||||
"os/signal"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
@@ -152,6 +153,12 @@ func run() error {
|
||||
link *mqtt.Link
|
||||
pub web.Publisher
|
||||
)
|
||||
// alerts holds the Phase 9 engine once it exists. The broker link is built
|
||||
// before the web server (and therefore before the engine), so a status
|
||||
// payload that carries a tamper report (FR-17) reaches the engine through
|
||||
// this pointer. Absent a configured engine, the tamper is still logged by
|
||||
// the link and nothing is delivered.
|
||||
var alerts atomic.Pointer[alert.Engine]
|
||||
if cfg.MQTTEnabled {
|
||||
link, err = mqtt.New(mqtt.Options{
|
||||
Broker: cfg.MQTTBroker,
|
||||
@@ -166,6 +173,14 @@ func run() error {
|
||||
Source: st,
|
||||
Reports: st,
|
||||
OnConnected: func() { republish(ctx, log, link, st) },
|
||||
OnTamper: func(tctx context.Context, dev store.Device, t mqtt.Tamper) {
|
||||
engine := alerts.Load()
|
||||
if engine == nil {
|
||||
log.Warn("tamper received but alerts are disabled", "device", dev.Name, "kind", t.Kind)
|
||||
return
|
||||
}
|
||||
engine.Raise(tctx, dev, store.AlertTamper, alert.TamperMessage(dev, t.Kind, t.Detail))
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -193,6 +208,7 @@ func run() error {
|
||||
if cfg.AlertsEnabled {
|
||||
engine := alert.New(st, srv.Apprise(), log, cfg.AlertOfflineAfter, cfg.ScheduleLocation)
|
||||
engine.Interval = cfg.AlertInterval
|
||||
alerts.Store(engine)
|
||||
go engine.Run(ctx)
|
||||
if !cfg.AppriseConfigured() {
|
||||
log.Warn("alerts are enabled but Apprise is not configured; alerts will be recorded as undelivered")
|
||||
|
||||
Reference in New Issue
Block a user