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
From version 2.0.0 the agent updates from RDS: in Device panel → Agent version choose the version and press Save target version; the screen downloads and installs it on its next connection. Details.
On Android, installation is only automatic on rooted devices. On the others (non-rooted tablets, phones and TVs) Android shows its install window over the content and someone at the screen has to press Install. On Android 8 or later, Install unknown apps must be allowed for Digital Signage RDS in Android settings beforehand. If the install is declined or fails, it is not retried until you choose another target version.
Versions before 2.0.0 cannot be updated remotely: install the latest APK by hand from Downloads.
Playback and tap reports
- From version 2.5.0 the agent reports what it actually plays (proof of play).
- From version 2.5.2, in Web Kiosk mode, it also reports how many taps the screen receives and where.
The data is in Analytics.
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 |