Firmware-faithful: moduli statici su disco, registration code da MAC, sfondo/colori #1
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "firmware-faithful-modules"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Allinea il player Android al firmware Linux ("il firmware è la verità"), chiudendo i bug del nero spot-creator, del registration code instabile e dello sfondo/colori.
Commit
get-player-modules+get-module-data/<id>.jsoninjson/(comekipanga-svc.py); il server li serve statici. Rimossi cache TTL, proxy-per-richiesta, retry/onModuleReady e il player sc nativo (SpotCreatorPlayer/ScModule). sc va sempre via WebView/sc.js. Risolve il nero dello spot-creator./sys/class/net), MT19937 di CPython byte-identico (6 test vector). Non dipende più da ANDROID_ID (scoped per firma) → non cambia mai più a reinstall/re-sign/data-wipe.#RRGGBBAAcon AA=opacità, alpha 0 = trasparente → si vede lo sfondo pagina (watermark) sotto. Sfondo e colori corretti.Verifica
Tutto provato a video su TV LDM-A(T982): spot-creator a 5 sale senza nero, watermark MULTISALA MODERNO visibile sotto le source, colori corretti, codice di registrazione stabile.
Nota operativa
Il device cambia registration code da
6937-2463-5680(vecchio, instabile, da ANDROID_ID) a0088-0814-9892(MAC-based, definitivo): re-registrazione una tantum, già fatta in test.Replicates the Linux player's lighttpd webroot on Android so ANY of the ~1200 operator templates renders unchanged — no graphic needs revising. - Base URLs use the firmware path: WebViews load /assets/kipanga/pages/<type>.html so a template's relative refs (../css, ../js, ../../../gallery, any depth) resolve EXACTLY as on the firmware. Directory depth must match — that's the whole point. - No bundled firmware tree in the APK (removed kipanga/{css,js} + the player_config_force fixtures). app/src/main/assets is now empty. - LocalMediaServer serves /assets/ and /skins/; on a miss it fetches the file on-demand from https://<host>/skin-preview/assets/<path> and caches it into the AssetUpgrader override dir — the firmware tree replicates onto the player by use, always live, never stale. assetHost set per applyConfig. - ASSET_SCHEMA_VERSION 2->3 + wipe the override on a schema bump, so every app/firmware update re-fetches the tree fresh (recreates the exact depth). - ContentWiper (do_update) now wipes ONLY the gallery; the firmware tree is refreshed exclusively on do_upgrade. Matches the canonical model: firmware = immutable server truth, gallery = per-site content. Verified on an LDM-A(T982): wiped the player to nothing + installed an asset-less APK; it rebuilt templates/css/js (proactive + on-demand) and the gallery from the server and rendered identically, page background included.Le TV signage sono landscape-locked a livello WindowManager: la policy ha solo rotazioni landscape, quindi setRequestedOrientation(PORTRAIT) e user_rotation forzato vengono ripristinati a landscape (flip-flop verificato su LDM-A T982). Soluzione: la rotazione 'verticale' si fa in-app ruotando il container di 90° (rotation 1 'destra') o 270° (rotation 3 'sinistra') su un framebuffer landscape; il pannello del totem e' montato fisicamente in verticale. applyContentOrientation dimensiona content_16_9 a portrait (swap w/h del pannello) e lo ruota; il render gira in un post{} cosi' applyPage misura il container portrait (1080x1920) e la skin firmware si dispone 9:16. Container ora STABILE (niente piu' flip-flop). applyDisplayConfig: framebuffer sempre landscape nativo (no swap) + user_rotation 0 (niente OS-rotation, che la TV non onora). Rimosso il vecchio setRequestedOrientation. Validato a video: grafica verticale destra dritta e leggibile.Allinea il player al loop fleet del firmware (kipanga-svc.py): - Heartbeat: aggiunti temperature (thermal_zone), load_average_15min (/proc/loadavg[2]), ram_usage, disk_usage, uptime (MINUTI, come il firmware), via DeviceMetrics. Prima inviava solo network/version/browser. - do_reboot=1 -> reboot device (su 0 reboot) con guard anti-loop persistito (last_reboot_ms in prefs, sopravvive al reboot). - monitor_poweron -> schermo ON/OFF via KEYCODE_WAKEUP/SLEEP (root), solo al cambio stato (il firmware usa turn_on/off_hdmi). - do_speedtest -> throughput download reale su sample gallery dal sync server, POST {speedtest_download, speedtest_upload} (upload non misurabile: niente sink lato API, il firmware usa speedtest.net -> 0.0). - do_screenshot -> cattura finestra player (Canvas) -> JPEG -> POST {filename, filedata=base64} a get-player-screenshot. - PlayerConfig: aggiunti do_reboot, do_speedtest, do_screenshot, monitor_poweron. Verificato a video: heartbeat 200 con temp 64.4C/load 4.22, monitor power ON.Il parco e' eterogeneo: le operazioni power/fleet senza API portabile (screen on/off, reboot) vanno dietro una strategy per-hardware, con fallback generico. - interface DeviceControl { setScreenPower, reboot, shutdown } + factory forThisDevice() che sceglie la strategy via detection. - GenericDeviceControl: su 0 (reboot affidabile; screen via keyevent best-effort, che NON spegne alcune TV signage tipo la T982 - per quello serve il vendor). - DahuaDeviceControl: dh-support SDK (AAR) -> bind dahua.support.ACTION_SYSTEM (com.dahua.skynet) -> enableBackLight/reboot/shutdown. enableBackLight e' l'UNICO modo di spegnere il pannello su Dahua dove i metodi generici falliscono. - Detection robusta: resolveService(ACTION_SYSTEM) != null, non solo presenza del package skynet. Su firmware skynet vecchio (V3.000.0000000.1, senza il support service) cade su Generic invece di bindare un service mancante. - applyMonitorPower + do_reboot passano da deviceControl (con fallback inline). - monitor_poweron richiede WAKE_LOCK (PARTIAL, CPU viva a schermo spento) + clear FLAG_KEEP_SCREEN_ON. NB: su QUESTA unita' di test il firmware skynet e' troppo vecchio per il support service, quindi lo screen-off Dahua non e' verificabile qui; funzionera' su Dahua con firmware >= branch 0000002. Reboot via generic gia' validato a video.Pair with the BootReceiver bring-up safety net (previous commit) so an OTA push of a new player APK via do_apk_upgrade can complete end-to-end on a non-DO totem with no operator at the device. Two mechanisms, both required: 1. android.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION (API 31+). Lets LauncherInstaller.commit() — already calling setRequireUserAction(USER_ACTION_NOT_REQUIRED) — actually skip the system confirmation dialog. Without this permission Android forces the dialog regardless of what the caller requested. 2. ConsentAutoTapService now also receives events from com.android.packageinstaller and com.google.android.packageinstaller, and matches affirmative button labels ("Installa"/"Install"/"Aggiorna"/ "Update"/"Update anyway", localised it+en). This catches the dialog on ROMs that ignore UPDATE_PACKAGES_WITHOUT_USER_ACTION (some OEMs do). Text-only matching, never the generic positive button id, and a FORBIDDEN_TEXTS guard never lets the service tap "Disinstalla"/ "Uninstall"/"Annulla"/"Cancel" even if those somehow share a resource id. Together with BootReceiver this means an OTA-pushed launcher upgrade can fully complete (install dialog auto-confirmed, services come up post- replace, screenshot/SSH/tunnel restored) with zero human intervention on the panel. Critical for the 200-300-unit fleet rollout: without these, any future buggy launcher OTA would lock remote access across the entire fleet, requiring onsite recovery per device.Two independent watchdogs that automatically recover a device which has slipped into a sticky bad state without remote help. Cover the failure modes that BootReceiver alone doesn't: services crashed mid-run, or the launcher itself wedged. (1) Per-minute service self-heal in WatchdogService: - Every SERVICE_CHECK_INTERVAL_MS (1 min) ActivityManager getRunningServices is queried for SshdService + ScreenshotService. - If a required service is missing, BootReceiver.startServices() restarts what's down (idempotent). - After MAX_FAILED_SERVICE_CHECKS (3) consecutive checks where a service is still missing despite restarts, Process.killProcess kills our own process. START_STICKY restarts WatchdogService and its onCreate brings the rest back via BootReceiver. Last-resort knob — shouldn't fire on a healthy fleet. (2) Daily 04:00 isolation reboot via new DailyHealthCheckReceiver: - AlarmManager.setExactAndAllowWhileIdle fires at 04:00 local time. - WatchdogService persists last AIDL heartbeat from the player into SharedPreferences (every ~6 beats to limit flash wear). - If no heartbeat for ≥24h → the launcher is isolated (player crash-looped, AIDL stuck, network gone). Reboot cascade: Runtime.exec("reboot") → "svc power reboot" → killProcess as last fallback. Cascade tolerates each step failing (production ROMs typically refuse the first two from app uid). - Re-arms one-shot each fire (better doze behavior than setRepeating). Scheduled from WatchdogService.onCreate so it survives reboots and OTA replaces. Manifest: - SCHEDULE_EXACT_ALARM (Android 12+): without it the alarm degrades to inexact and can drift hours — defeats the "exactly 04:00" SLA. - REBOOT permission: signature-protected on standard ROMs, granted on OEM signature scenarios; the cascade naturally falls through when not granted. - DailyHealthCheckReceiver declaration (exported=false, action-filtered). Together with BootReceiver, the launcher now has 3 layers of recovery: startup (BootReceiver) → mid-run (per-min self-heal) → end-of-day (24h isolation reboot). A device cannot stay silently broken longer than 24h without forcing itself back to a clean state.Last-resort recovery path for a device that's lost remote access AND can't be reached via Settings (minimal BT remote without HOME/MENU/ Settings, no USB-debug, no Wireless Debugging). Driven entirely from the panel D-pad — no console, no Kezi flag, no AIDL surface bump. Sequence: Su Su Giù Giù Sx Dx Sx Dx OK within KONAMI_TIMEOUT_MS (8s). Long enough to be impossible accidentally (no sane UI navigation matches), short enough that a stressed operator with a sluggish remote can finish it. PlayerActivity.onKeyDown tracks progression and resets on any non-matching key. On match: full-screen overlay with FACTORY_RESET_COUNTDOWN_S (10) second countdown. BACK during the countdown cancels and removes the overlay. At zero, DeviceControl.factoryReset() runs on Dispatchers.IO. If the strategy returns false (no path on this SKU), the overlay updates to "FACTORY RESET FALLITO / Verificare manualmente" so the operator doesn't think the call succeeded silently. DeviceControl additions: - Interface: factoryReset() : Boolean (alongside reboot/shutdown). - DahuaDeviceControl: call("factoryReset") via the dh-support SDK, sibling of the already-working call("reboot")/call("shutdown"). The exact method name on this firmware is undocumented — if callDHSystem returns code≠0 we report false and the overlay surfaces the failure. Confirmation that the panel actually wiped will come from observing it boot back into the OEM setup wizard. - GenericDeviceControl: runRoot("recovery --wipe_data") — equivalent of "wipe data / factory reset" from Android Recovery, on rooted boxes. This closes the recovery gap for the 0088-class scenario where the operator can only reach the panel with the minimal BT remote and no other input device works. The reset is meaningfully destructive (wipes /data, re-runs OEM setup) — the 10s countdown + BACK-to-cancel exists specifically to avoid accidental triggers.