Agente Android — Funzionamento
Ciclo di vita
Boot → MyReceiver (BOOT_COMPLETED) → AutoStartUp service → MainActivity
│
▼
Heartbeat al server
│
┌───────────┴────────────┐
▼ ▼
Il server risponde "0" Senza internet / non
(autorizzato) autorizzato → nuovo tentativo
│
▼
PlayerActivity (fullscreen)
│
▼
Legge playlist.txt locale
│
▼
Riproduce in loop MP4 / JPG / PNG
Sincronizzazione dei contenuti
Il servizio in background AutoStartUp:
- Esegue il polling del server ogni N secondi (configurabile).
- Confronta l'elenco dei file del server con quelli locali.
- Scarica quelli mancanti o modificati, verificandone l'integrità con CRC32. Quando un file è scaricato per intero, accanto ad esso viene creato un file
.ok. - Riscrive
playlist.txt(JSON) con l'ordine attuale e le durate dei singoli elementi. - Il player rilegge
playlist.txtall'inizio di ogni giro di loop — le modifiche si applicano senza riavviare l'app.
Formati supportati
| Tipo | Dettagli |
|---|---|
| Video | MP4 (H.264). Viene riprodotto per intero a ogni giro. |
| Immagine statica | JPG, PNG. Durata configurata nella playlist. |
| GIF | Rilevato ma ignorato (non viene riprodotto). |
| Fallback | Se non è disponibile alcun contenuto, mostra un'immagine predefinita (sat.jpg). |
Modalità di visualizzazione
Due layout selezionabili dalle impostazioni Admin:
- Stretch a schermo intero (
relative_layout_video) — usa tutto lo schermo, può deformare le proporzioni. - Letterbox (
player_content) — mantiene le proporzioni originali, aggiungendo bande se necessario.
Report di stato
A ogni heartbeat l'agente invia:
accio=esticViu— "sono vivo" — verifica l'autenticazione.accio=gps(ogni minuto, non solo le coordinate) — include modello (Build.DEVICE), versione Android (Build.VERSION.SDK_INT), uptime (SystemClock.elapsedRealtime(), comprende il tempo in deep-sleep), copertura e prossimo riavvio.accio=estat&estat=N— stato di download / riproduzione:- 0 — non avviato
- 1 — download in corso
- 2 — scaricato
- 3 — in riproduzione
Questi sono i valori che vede nella colonna Stato playlist del pannello.
Screenshot remoti
Dal pannello, Azioni → Screenshot avvia un'acquisizione della schermata del dispositivo. Il server inserisce un flag (screenshot:1) nella risposta gps successiva (≤ 60 s); AutoStartUp lo rileva e delega a ScreenshotCapture.
L'agente tenta tre vie in ordine, dalla più alla meno capace:
Via 1 — AccessibilityService (consigliata, richiede una configurazione una tantum)
ScreenshotAccessibilityService è un servizio di sistema che cattura l'intera schermata del dispositivo — comprese la system UI, altre app, finestre di errore / ANR, la lockscreen e, soprattutto, la finestra "app crashed" se il kiosk è andato in crash. Sopravvive ai crash dell'Activity / del Service perché Android riavvia automaticamente i binding di accessibilità.
Requisiti:
- Android ≥ 11 (API 30). Nelle versioni precedenti questa via viene saltata.
- Attivazione una sola volta per dispositivo:
- Settings → Accessibility → App installate → Digital Signage RDS
- Attivare il toggle. Android avvisa che il servizio potrà leggere e manipolare gli eventi — confermare.
- Dopo l'attivazione non serve altro; il servizio si riassocia automaticamente all'avvio.
Se il servizio non è abilitato, all'avvio l'agente registra in agent.log:
[SCREENSHOT] Full-screen captures disabled — enable 'Digital Signage RDS' in
Settings → Accessibility for captures that work even when the
kiosk app is not foreground.
Via 2 — Frame del contenuto in riproduzione (PlayerActivity)
Se l'AccessibilityService non è attivo e la modalità attiva è playlist, l'agente:
- Esegue uno snapshot sul thread UI del file corrente + posizione di riproduzione
- In un
HandlerThreaddecodifica:- immagine (
.jpg/.png) →BitmapFactory.decodeFile - video →
MediaMetadataRetriever.getFrameAtTime(posMs, OPTION_CLOSEST)
- immagine (
Questo evita il problema del rettangolo nero causato da PixelCopy(Window), che non riesce a leggere l'overlay hardware del SurfaceView su cui viene renderizzato il video.
Via 3 — PixelCopy della finestra (KioskBrowserActivity / fallback)
Per la modalità Web (WebView, senza overlay hardware) o come ultima risorsa: PixelCopy.request(Window, …) su API ≥ 26 — cattura WebView/Surface accelerati dalla GPU. View.draw(Canvas) su API 24-25.
Comune alle tre vie
- JPEG qualità 70 (~150-400 KB tipici).
- Firmato con HMAC-SHA256 v2 e caricato su
/suport/upload_screenshot.php. - Non richiede
MediaProjection: le vie 2 e 3 catturano contenuto proprio; la via 1 è un servizio di sistema concesso legittimamente dall'amministratore.
Dettaglio del flusso nel pannello.
Crash recovery
MyExceptionHandler intercetta le eccezioni non gestite e riavvia automaticamente l'app. Questo garantisce che il dispositivo torni a riprodurre i contenuti anche dopo un guasto occasionale.
Log
L'agente invia i log operativi al server. Per consultarli dal pannello: Dispositivi → Azioni → Logs.
Aggiornamenti
Attualmente l'APK controlla la versione più recente e, se ne esiste una nuova, scarica il nuovo APK dal server (descarregar_apk.php). Il comportamento esatto dipende dalla configurazione del produttore; in alcune installazioni l'aggiornamento viene eseguito da remoto da Promotienda.
Limitazioni note
- Su Android 8+ i servizi in background hanno delle restrizioni — l'agente può richiedere permessi aggiuntivi per mantenere la sincronizzazione mentre lo schermo è spento.
- Su Android 10+ lo scoped storage obbliga a usare
MANAGE_EXTERNAL_STORAGEo MediaStore. Se il suo dispositivo è recente e l'app non riesce a scrivere, contatti l'assistenza.
Endpoint HTTP utilizzati
| Endpoint | Funzione |
|---|---|
suport/WebServiceTablet.php?accio=esticViu |
Heartbeat / auth |
suport/WebServiceTablet.php?accio=estat&estat=N |
Report dello stato della playlist |
descarregar_apk.php |
Auto-update dell'APK |