Build a kiosk that survives the field
A kiosk in the field has no keyboard, no technician, and no patience for a stuck screen. This guide turns a KTM board into a device that boots straight into your app, refuses to be tampered with, and brings itself back after a power cut or a frozen night — using the device API end to end.
We build it in layers, each one a real call you can paste. Skip none of them — each closes a gap that becomes a support ticket later.
Every lockdown call here works from a normally-signed app, because the firmware is rooted. No platform key, no owner-mode dance. See Permissions and signing.
What you will build
A device that:
- Launches your app automatically on boot.
- Hides the status and navigation bars so there is no way out.
- Blocks app installation and USB storage so it cannot be tampered with.
- Reboots itself nightly to clear leaks, and recovers from a freeze via the hardware watchdog.
- Powers down out of hours and wakes before opening.
Layer 1 — Launch on boot
Your app has to come up on its own. That is a BOOT_COMPLETED receiver — it does
not need ItempApiPub, but it is the foundation everything else sits on. The full
recipe is in Start on boot; the short version:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<receiver android:name=".BootReceiver" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
public class BootReceiver extends BroadcastReceiver {
@Override public void onReceive(Context ctx, Intent it) {
Intent app = new Intent(ctx, MainActivity.class);
app.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
ctx.startActivity(app);
}
}
Layer 2 — Hide the system UI
Once your app is up, remove every route out of it. Two calls:
import com.itemp.itempapi.ItempApiPub;
// Call once your kiosk activity is in the foreground.
ItempApiPub.setStatusBarHide(context);
ItempApiPub.setNavigationBarHide(context);
Now there is no notification shade to pull down and no back/home/recents bar. To restore them during development:
ItempApiPub.setStatusBarShow(context);
ItempApiPub.setNavigationBarShow(context);
Once the bars are gone and installs are blocked (next layer), a hidden exit is the only way to service the device without reflashing. Build one now: a multi-tap on a corner, a long-press, a password dialog — something a passer-by will not find but a technician can use. Do this before the device leaves your bench.
Layer 3 — Block tampering
Stop anyone installing another app or pulling files off a USB stick.
// Whitelist: only your own package may be installed or updated.
String[] allow = { "com.yourcompany.kioskapp" };
ItempApiPub.intallBlock(context, allow, ItempApiPub.WHITELIST_MODE);
// Ignore USB mass storage entirely.
ItempApiPub.setUsbStorageBlock(context, true);
intallBlock is spelled exactly that way in the API — see
Kiosk and lockdown. Whitelist mode is the strong choice for a
fixed-function device: nothing but your app gets on.
To lift the lock during servicing:
ItempApiPub.intallBlock(context, null, ItempApiPub.DISABLED_MODE);
ItempApiPub.setUsbStorageBlock(context, false);
Layer 4 — Recover from freezes and leaks
Two independent safety nets. A nightly reboot clears slow memory leaks before they matter; the hardware watchdog catches a hard freeze the same night.
// Reboot every night at 04:00, when nobody is using it.
ItempApiPub.setRebootTime(context, "4", "0", true);
The scheduled reboot above is the recovery mechanism the device API
provides directly. For a hard freeze between reboots, a hardware watchdog — where
the board resets if it is not periodically "fed" — is the stronger net. On KTM
boards the watchdog is a platform feature rather than an ItempApiPub call;
confirm availability and the feed mechanism for your board with your account
manager. If you use one, feed it from your main work loop, never from a timer —
a timer keeps feeding while the app itself is frozen:
while (running) {
doWork();
updateUi();
// feed the watchdog here, if your board exposes one
Thread.sleep(1000);
}
See Power and scheduling for the reboot schedule.
A 04:00 reboot only helps if Layer 1 brings your app back up cleanly afterward. Test the full cycle: let the scheduled reboot fire and confirm the device lands back in your kiosk with the bars still hidden. A reboot that drops to the launcher is worse than no reboot.
Layer 5 — Power schedule (optional but cheap)
If the kiosk serves a business with opening hours, don't burn the panel 24/7.
// Power off at 22:00, back on at 07:30, every day.
ItempApiPub.setPowerOffTime(context, "22", "0", "0", true);
ItempApiPub.setPowerOnTime(context, "7", "30", "0", true);
Or keep the board running and just blank the screen — lighter, wakes instantly:
ItempApiPub.setBacklightEnable(context, false); // panel dark, board alive
// ...at opening...
ItempApiPub.setBacklightEnable(context, true);
Backlight-off keeps schedules and network alive; full power-off saves the most energy but needs standby power to wake. See Power and scheduling for the trade-off.
Putting it together
A minimal kiosk setup, run once when your activity first reaches the foreground:
void lockDown(Context context) {
// hide UI
ItempApiPub.setStatusBarHide(context);
ItempApiPub.setNavigationBarHide(context);
// block tampering
ItempApiPub.intallBlock(context,
new String[]{ "com.yourcompany.kioskapp" },
ItempApiPub.WHITELIST_MODE);
ItempApiPub.setUsbStorageBlock(context, true);
// recovery
ItempApiPub.setRebootTime(context, "4", "0", true);
// hours (optional)
ItempApiPub.setPowerOffTime(context, "22", "0", "0", true);
ItempApiPub.setPowerOnTime(context, "7", "30", "0", true);
}
Layer 1 (boot receiver) and the watchdog feed loop live outside this method — boot in the manifest, watchdog in your work loop.
Before you ship — the field checklist
- Hidden exit works and survives lockdown (you can get back in)
- Power-cut test: pull power, restore, device returns to the kiosk unattended
- Scheduled reboot lands back in the kiosk, bars still hidden
- If using a hardware watchdog: it resets on a simulated freeze
- USB stick is ignored; a second APK cannot install
- Backlight/power schedule fires at the right local time (check the system clock)
Related
- Kiosk and lockdown API — every lockdown call in detail
- Power and scheduling · Display
- Start on boot — the boot receiver in full
- System time — schedules run against this clock