Android agent — Operation
Lifecycle
Boot → MyReceiver (BOOT_COMPLETED) → AutoStartUp service → MainActivity
│
▼
Heartbeat to server
│
┌───────────┴────────────┐
▼ ▼
Server replies "0" No internet / not
(authorised) authorised → retry
│
▼
PlayerActivity (fullscreen)
│
▼
Reads local playlist.txt
│
▼
Loops MP4 / JPG / PNG playback
Content sync
The background service AutoStartUp:
- Polls the server every N seconds (configurable).
- Compares the server file list with the local one.
- Downloads missing or changed files, verifying integrity with CRC32. When a file is fully downloaded, an
.okcompanion file is created alongside it. - Rewrites
playlist.txt(JSON) with the current order and per-item durations. - The player re-reads
playlist.txtat the start of every loop — changes apply without restarting the app.
Supported formats
| Type | Details |
|---|---|
| Video | MP4 (H.264). Plays in full on every loop. |
| Static image | JPG, PNG. Duration set in the playlist. |
| GIF | Detected but skipped (not played). |
| Fallback | If no content is available, shows a default image (sat.jpg). |
Display modes
Two layouts selectable from Admin settings:
- Stretch fullscreen (
relative_layout_video) — uses the whole screen, may distort the ratio. - Letterbox (
player_content) — keeps the original ratio, adds bands if needed.
State reporting
On every heartbeat the agent sends:
accio=esticViu— "I'm alive" — checks authentication.accio=gps(every minute, not just coordinates) — includes model (Build.DEVICE), Android version (Build.VERSION.SDK_INT), uptime (SystemClock.elapsedRealtime(), including deep-sleep time), coverage and next reboot.accio=estat&estat=N— download / playback state:- 0 — not started
- 1 — downloading
- 2 — downloaded
- 3 — playing
These values are what you see in the Playlist status column in the panel.
Remote screenshots
From the panel, Actions → Screenshot triggers a capture from the device. The server injects a flag (screenshot:1) in the next gps response (≤ 60 s); AutoStartUp picks it up and delegates to ScreenshotCapture.
The agent tries three tiers, in decreasing capability:
Tier 1 — AccessibilityService (recommended, requires one-time setup)
ScreenshotAccessibilityService is a system service that captures the entire device screen — includes system UI, other apps, error / ANR dialogs, lockscreen and, crucially, the "app crashed" dialog if the kiosk has crashed. Survives Activity / Service crashes because Android automatically re-binds accessibility services.
Requirements:
- Android ≥ 11 (API 30). On older versions this tier is skipped.
- One-time activation per device:
- Settings → Accessibility → Installed apps → Digital Signage RDS
- Toggle on. Android warns that the service can read and manipulate events — confirm.
- After activation, nothing else is needed; the service re-binds at boot automatically.
If the service is not enabled, the agent logs at startup:
[SCREENSHOT] Full-screen captures disabled — enable 'Digital Signage RDS' in
Settings → Accessibility for captures that work even when the
kiosk app is not foreground.
Tier 2 — Frame of currently playing media (PlayerActivity)
If the AccessibilityService isn't active and the active mode is playlist, the agent:
- Snapshot UI-thread of the current file + playback position
- On a
HandlerThreaddecodes:- image (
.jpg/.png) →BitmapFactory.decodeFile - video →
MediaMetadataRetriever.getFrameAtTime(posMs, OPTION_CLOSEST)
- image (
This avoids the black rectangle problem caused by PixelCopy(Window) which cannot read the hardware overlay where the SurfaceView renders the video.
Tier 3 — PixelCopy of the window (KioskBrowserActivity / fallback)
For Web mode (WebView, no hardware overlay) or as last resort: PixelCopy.request(Window, …) on API ≥ 26 — captures GPU-accelerated WebView/Surface. View.draw(Canvas) on API 24-25.
Common to all three tiers
- JPEG quality 70 (~150-400 KB typical).
- Signed with HMAC-SHA256 v2 and uploaded to
/suport/upload_screenshot.php. - Doesn't require
MediaProjection: tiers 2 and 3 capture own content; tier 1 is a system service legitimately granted by the admin.
Crash recovery
MyExceptionHandler catches unhandled exceptions and restarts the app automatically. This ensures the device resumes playing content even after a transient failure.
Logs
The agent sends operational logs to the server. To view them from the panel: Devices → Actions → Logs.
Updates
The APK currently checks the most recent version and, if newer, downloads the new APK from the server (descarregar_apk.php). The exact behaviour depends on manufacturer settings; in some deployments updates are pushed remotely from Promotienda.
Known limitations
- On Android 8+ background services have restrictions — the agent may need additional permissions to keep syncing while the screen is off.
- On Android 10+ scoped storage requires
MANAGE_EXTERNAL_STORAGEor MediaStore. If your device is new and the app cannot write, contact support.
HTTP endpoints used
| Endpoint | Function |
|---|---|
suport/WebServiceTablet.php?accio=esticViu |
Heartbeat / auth |
suport/WebServiceTablet.php?accio=estat&estat=N |
Playlist state report |
descarregar_apk.php |
APK auto-update |