Kiosk mode

In an all-in-one deployment, your app is what a merchant's staff use all day. Kiosk mode locks the terminal to your app, so that staff cannot reach Android's launcher, settings or other apps. The terminal's payment screens still come up to take payments.

Build kiosk mode into your app with standard Android APIs. Use the ChipDNA Mobile SDK for what an app cannot do itself: taking payments, and blocking the notification panel.

Is this for me?

Use this approach if your app runs on the Axium terminal and is the main app on it. See All-in-One Development.

NMI's payment app, TransactionInitiatorUI, has a kiosk mode of its own. That one is for terminals driven by an app on a separate device, where NMI's app is the main app. In an all-in-one deployment, leave it off.

❗️

Never pass ParameterKeys.KioskModeEnabled from your app. It turns on TransactionInitiatorUI's kiosk mode, which makes TransactionInitiatorUI the Home app in place of yours.

Before you start

This page assumes your app already takes payments through the ChipDNA Mobile SDK on the terminal. If it does not yet, see Setup for development and the SmartPOS: Ingenico Axium section.

What kiosk mode covers

When staff...With kiosk mode onHow
Press HomeYour app comes backYour app is the Home app
Pull down the notification panelIt is blocked for the whole terminalThe SDK's NotificationPanelAllowed setting
Update your app, or restart the terminalKiosk mode stays onThe alias and the Home choice are kept

An app cannot make itself the Home app silently: someone has to accept Android's prompt on the terminal each time kiosk mode is turned on. The Recents screen, Android's list of recently used apps that opens from the Recents button, cannot be blocked by an app; see Limitations.

Become the Home app

Declare an activity-alias with the HOME category that points at your main activity, and ship it switched off. Make the activity singleTask, so that Home brings back the screen that is already open:

<activity
    android:name=".MainActivity"
    android:exported="true"
    android:launchMode="singleTask">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

<activity-alias
    android:name=".KioskHomeActivity"
    android:enabled="false"
    android:exported="true"
    android:targetActivity=".MainActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.HOME" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity-alias>

To turn kiosk mode on, switch the alias on, then ask to become the default Home app:

val homeAlias = ComponentName(context, "com.example.yourapp.KioskHomeActivity")

// Without DONT_KILL_APP, Android stops your app's process.
packageManager.setComponentEnabledSetting(
    homeAlias,
    PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
    PackageManager.DONT_KILL_APP
)

val roleManager = getSystemService(RoleManager::class.java)
if (!roleManager.isRoleHeld(RoleManager.ROLE_HOME)) {
    requestHomeRole.launch(roleManager.createRequestRoleIntent(RoleManager.ROLE_HOME))
}

requestHomeRole is an ActivityResultLauncher, registered as a property of the activity. In Android's prompt, choose your app, then Set as default.

To turn kiosk mode off, switch the alias off. Your app is then no longer a Home app, and Home goes to the terminal's other Home app, normally the Android launcher:

packageManager.setComponentEnabledSetting(
    homeAlias,
    PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
    PackageManager.DONT_KILL_APP
)

Switching the alias off also closes any screen that was opened through it, so if staff should stay in your app, start your main activity again afterwards.

Write the alias's class name out in full: your app's namespace followed by .KioskHomeActivity. Do not build it from context.packageName, which is the application ID and can differ from the namespace.

Block the notification panel

The notification panel leads to quick settings and, from there, to Android's settings. An app cannot stop it being pulled down, so have NMI's payment app block it, through connectAndConfigure:

ChipDnaMobile.getInstance().connectAndConfigure(Parameters().apply {
    add(ParameterKeys.NotificationPanelAllowed, ParameterValues.FALSE)
})

The panel is then blocked for the whole terminal, whether or not your kiosk mode is on. Pass ParameterValues.TRUE to allow it again.

📘

This needs TransactionInitiatorUI v1.4.0 or later, and ChipDNA Mobile SDK 5.4.2 or later, which added NotificationPanelAllowed.

  • Result = TRUE in onConnectAndConfigureFinished means TransactionInitiatorUI has taken the setting. Normally the terminal has already applied it by then. If the terminal is busy, for example during an update or just after it starts, the setting is applied as soon as the terminal is free, without another call; until then getStatus returns ParameterKeys.KioskSettingsApplied = FALSE.
  • Send the setting again after each successful connect, in case TransactionInitiatorUI has restarted, but only a value your app has actually set. Sending a default overwrites the setting made on TransactionInitiatorUI's Admin screen. Don't send back the NotificationPanelAllowed that getStatus reports either: it is whether the panel is allowed right now, not the stored setting.
  • A change made later on TransactionInitiatorUI's Admin screen replaces your setting, and then applies only while TransactionInitiatorUI's kiosk mode is on: with it off, the panel is allowed again. Send your setting again to block it.
  • AdminSettingsNotSupported and NotificationPanelAllowedInvalid will not succeed on a retry. See the SmartPOS: Ingenico Axium section for the full list of errors.
❗️

The default Admin screen passcode, 141309, must be changed. Every terminal starts with it, and TransactionInitiatorUI's Admin screen can open Android's settings, so anyone who knows it can get out of kiosk mode. When getStatus returns ParameterKeys.AdminPasscodeIsDefault = TRUE, set your own passcode with ParameterKeys.NewAdminPasscode on connectAndConfigure. The Admin screen also asks for a new passcode the first time it is opened with the default.

See the SmartPOS: Ingenico Axium section for the passcode rules.

Open the Admin screen

TransactionInitiatorUI's Admin screen opens from its welcome screen: tap the top-left corner of the screen five times in quick succession, each tap within 0.7 seconds of the one before. It only opens while no payment or update is running. Nothing on screen marks the corner, so cardholders have nothing to find. The passcode screen opens first; on a terminal still using the default passcode, the Admin screen then asks for a new one.

Coming back after a payment

The payment screen stays in front once the payment ends. Bring your app back to the foreground from onTransactionFinishedListener, as described in Returning your application to the foreground.

Updates and restarts

Android keeps the alias switched on, and your app as the default Home app, across an update of your app and a restart of the terminal. There is no need to turn kiosk mode off before an update through HEM, or to switch the alias on again afterwards.

After a restart, the payment software's start-up screen can be in front of your app until Home is pressed. Don't start your own activity over it from a BOOT_COMPLETED receiver: the payment software must finish starting before TransactionInitiatorUI can use it.

Testing it

On a real terminal:

  1. Turn kiosk mode on. Android asks which Home app to use: choose your app, then Set as default.
  2. Press Home from anywhere, and check that your app comes back.
  3. Block the notification panel. It shouldn't open, with kiosk mode on or off. Allow it, and it opens again.
  4. Take a payment, and check that your app comes back to the front when it ends.
  5. Restart the terminal, then press Home. Your app should come back, with kiosk mode still on.
  6. Turn kiosk mode off. Home goes to the Android launcher.

Limitations

  • The Recents screen cannot be blocked by an app. Staff can open it and switch to another app that is already running; pressing Home brings your app back.
  • If TransactionInitiatorUI stops answering in the middle of a payment, the SDK keeps asking it for the result, and onTransactionFinishedListener is never called. Over the terminal's local connection, terminateTransaction cannot end the payment either. Restart your app to recover. The payment may have been approved, so before staff take it again, call getTransactionInformation with the payment's UserReference once the SDK has connected, to see its outcome. Store each payment's UserReference somewhere that survives a restart, such as SharedPreferences, until the payment finishes.


Did this page help you?