Android Tutorial Device Owner / TaskLock / Kiosk apps 2017

Status
Not open for further replies.
SS-2017-07-18_13.19.46.png


Starting from Android 5 (API 21) there is better support for kiosk applications.
Kiosk applications = applications that the user cannot exit from.
As you can see in this screenshot, the home button and recent apps button are missing. The top notifications drawer is also inaccessible.

This solution is good for devices that serve a specific purpose. For example point of sale solutions or tourist information stands.

Device Owner App

1. The first step is to create an app and provision it as the device owner app. Download the attached example, change its package name to any value you like and run it on the device in Release mode.

2. Connect the device to the PC in USB debug mode. Open command line and run the following command:
Replace your.package.name.here with the correct package name.
B4X:
adb shell dpm set-device-owner your.package.name.here/anywheresoftware.b4a.objects.AdminReceiver2

SS-2017-07-18_14.35.53.png


Existing user accounts on the device must first be removed (Settings - Accounts).

3. Add this code to the manifest editor (it is already there in the example project):
B4X:
AddApplicationText(<receiver android:name="anywheresoftware.b4a.objects.AdminReceiver2"
  android:permission="android.permission.BIND_DEVICE_ADMIN">
  <meta-data android:name="android.app.device_admin"
  android:resource="@xml/device_admin" />
  <intent-filter>
  <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
  </intent-filter>
</receiver>)
CreateResource(xml, device_admin.xml,
<device-admin xmlns:android="http://schemas.android.com/apk/res/android">
  <uses-policies>
  <limit-password />
  <reset-password />
  <force-lock />
  </uses-policies>
</device-admin>
)

4. See how the example code locks and unlocks the activity.

Note that for the solution to be more robust it is recommended to make the activity a home launcher activity.

This is done by adding this code to the manifest editor:
B4X:
AddActivityText(Main,
<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>
)

With this code you need to click on the home button once and then you will be asked to choose the home launcher app. Choose your app.
This is important to make sure that your app starts as soon as possible after a restart.
During development you might prefer to disable it.

It depends on Administrator library v1.1+: https://www.b4x.com/android/forum/threads/updates-to-internal-libraries.59340/#post-518017
 

Attachments

  • Kiosk.zip
    8.6 KB · Views: 2,351

Erel

B4X founder
Staff member
Licensed User
Longtime User
The program will exit. You can catch unhandled exceptions in Application_Error.

Another things that you can do for a kiosk application is to run another app with a foreground service that starts the main app with an intent every minute.

If the app is already running then nothing will happen (assuming that the main activity is visible). If it crashed for some reason then it will be restarted.
This is a good approach for apps that need to run for very long times.

The UI cloud devices, for example, use this technique and they run for months.
 

Star-Dust

Expert
Licensed User
Longtime User
Can you use it to create a screen locker?
 

Star-Dust

Expert
Licensed User
Longtime User
ok, Thank's
 

vbigdan

Member
Licensed User
Longtime User
Excellent! I've been implementing the previous version of kiosk for a few years now and this is a breath of fresh air (no home or recent apps presses or swipes down from top)

Question: is there any way to bypass the ADB element? Any manual way to set up the default account as admin? I've got over 500 tablets in various venues that will benefit from this update, and connecting every one will be possible but difficult.
 

Star-Dust

Expert
Licensed User
Longtime User

jareal

Member
Licensed User
Longtime User
I am having a problem with the example of the first post when setup as Home Launcher running on Android 6.0:
It seems not to Lock the app, and I always receive the toast "Screen unpinned" immediately following the "Screen pinned".

My devices with Android 5 have no problem, Home Launcher or not.
My devices with Android 6 and the app not Home Launcher also no problem.

Looking the unfiltered log of the problematic configuration it shows an intent LOCK_TASK_EXITING that is not present when it works.
OBS: Only changed the package name to match the name of the Owner App of my devices.

[Beco.app/Beco.app.main](this:0xb4207000,id:99,api:1,p:4442,c:205) queueBuffer: fps=2.10 dur=22899.45 max=22031.79 min=5.23
Broadcast: Intent { act=android.app.action.DEVICE_POLICY_MANAGER_STATE_CHANGED flg=0x40000010 } ordered=false userid=0 callerApp=ProcessRecord{f8314d 844:system/1000}
setEnabledFunctions functions=mtp,adb, forceRestart=false
applyAcmFunction - sys.usb.acm_idx=,mAcmPortIdx=
applyAcmFunction - functions: mtp,adb
setKeyguardEnabled(true)
Set focused app to: AppWindowToken{a58629a token=Token{7c01960 ActivityRecord{8ecc963 u0 Beco.app/.main t11}}} old focus=AppWindowToken{a58629a token=Token{7c01960 ActivityRecord{8ecc963 u0 Beco.app/.main t11}}} moveFocusNow=false
Looking for focus: 7 = Window{56c0571 u0 StatusBar}, flags=-2122055608, canReceive=false
findFocusedWindow: Found new focus @ 5 = Window{558aaf2 u0 Beco.app/Beco.app.main}
notifyActivityDrawnForKeyguard: waiting=false Callers=com.android.server.wm.WindowManagerService.handleAppTransitionReadyLocked:10101 com.android.server.wm.WindowManagerService.performLayoutAndPlaceSurfacesLockedInner:10721 com.android.server.wm.WindowManagerService.performLayoutAndPlaceSurfacesLockedLoop:9465 com.android.server.wm.WindowManagerService.performLayoutAndPlaceSurfacesLocked:9412 com.android.server.wm.WindowManagerService.executeAppTransition:4601
Looking for focus: 7 = Window{56c0571 u0 StatusBar}, flags=-2122055608, canReceive=false
findFocusedWindow: Found new focus @ 5 = Window{558aaf2 u0 Beco.app/Beco.app.main}
enqueueToast pkg=android callback=android.widget.Toast$TN@cb03d50 duration=1
noteOperation: allowing code 11 uid 1000 package android
Show pkg=android callback=android.widget.Toast$TN@cb03d50
disable statusbar calling PID = 844
from settings cache , name = lock_screen_show_notifications , value = 1
[Built-in Screen (type:0)] fps:0.261930,dur:7635.63,max:7619.82,min:15.81
Broadcast: Intent { act=android.app.action.LOCK_TASK_ENTERING flg=0x10 cmp=Beco.app/anywheresoftware.b4a.objects.AdminReceiver2 (has extras) } ordered=false userid=0 callerApp=ProcessRecord{f8314d 844:system/1000}
disable statusbar calling PID = 844
Broadcast: Intent { act=android.app.action.LOCK_TASK_EXITING flg=0x10 cmp=Beco.app/anywheresoftware.b4a.objects.AdminReceiver2 } ordered=false userid=0 callerApp=ProcessRecord{f8314d 844:system/1000}
mDeviceProvisioned is true
mDeviceProvisioned is true
mDeviceProvisioned is true
updateNotificationShade: mUserSetup=true
enqueueToast pkg=android callback=android.widget.Toast$TN@cb6416f duration=1
noteOperation: allowing code 11 uid 1000 package android
disable1: 0x00000000 -> 0x00000000 (diff1: 0x00000000)
disable2: 0x00000000 -> 0x00000000 (diff2: 0x00000000)
from settings cache , name = lock_to_app_exit_locked , value = 0
disable: < expand icons alerts system_info back home recent clock search quick_settings >
Add to mViews: android.widget.LinearLayout{f27e74d V.E...... ......I. 0,0-0,0}, this = android.view.WindowManagerGlobal@9e80c7e
CanvasContext() 0x9b717000
hardware acceleration is enabled, this = ViewRoot{c63de7c Toast,ident = 39}
startOperation: allowing code 45 uid 1000 package android
addWindowToListInOrderLocked: win=Window{f20ef5a u0 Toast} Callers=com.android.server.wm.WindowManagerService.addWindow:2787 com.android.server.wm.Session.addToDisplay:171 android.view.ViewRootImpl.setView:643 android.view.WindowManagerGlobal.addView:319
Free window: Adding window Window{f20ef5a u0 Toast} at 8 of 8
[unnamed-205-102](this:0xb4db3000,id:102,api:0,p:-1,c:-1) BufferQueue core=(205:/system/bin/surfaceflinger)
BDC-Calling onReceive: intent=Intent { act=android.app.action.LOCK_TASK_ENTERING flg=0x10 cmp=Beco.app/anywheresoftware.b4a.objects.AdminReceiver2 (has extras) }, receiver=anywheresoftware.b4a.objects.AdminReceiver2@131d69e
[unnamed-205-102] this:0xb5cdf4e0, value:0xbed07650, iLen:6
[unnamed-205-102](this:0xb4db3000,id:102,api:0,p:-1,c:205) connect(C): consumer=(205:/system/bin/surfaceflinger) controlledByApp=false
[unnamed-205-102] this:0xb5cdf4e0, value:0xbed07670, iLen:6
[unnamed-205-102](this:0xb4db3000,id:102,api:0,p:-1,c:205) setConsumerName: unnamed-205-102
[Toast] this:0xb5cdf4e0, value:0xbed076c8, iLen:6
Beco.app.managerservice not found.
[Toast](this:0xb4db3000,id:102,api:0,p:-1,c:205) setConsumerName: Toast
[Toast](this:0xb4db3000,id:102,api:0,p:-1,c:205) setDefaultBufferSize: width=1 height=1
BDC-RECEIVER handled : 0 / ReceiverData{intent=Intent { act=android.app.action.LOCK_TASK_ENTERING flg=0x10 cmp=Beco.app/anywheresoftware.b4a.objects.AdminReceiver2 (has extras) } packageName=Beco.app resultCode=-1 resultData=null resultExtras=null}
[StatusBar](this:0xb4f15800,id:9,api:1,p:1162,c:205) queueBuffer: fps=0.26 dur=7671.93 max=7651.01 min=20.92
Looking for focus: 7 = Window{56c0571 u0 StatusBar}, flags=-2122055608, canReceive=false
findFocusedWindow: Found new focus @ 5 = Window{558aaf2 u0 Beco.app/Beco.app.main}
BDC-Calling onReceive: intent=Intent { act=android.app.action.LOCK_TASK_EXITING flg=0x10 cmp=Beco.app/anywheresoftware.b4a.objects.AdminReceiver2 }, receiver=anywheresoftware.b4a.objects.AdminReceiver2@dd0b57f
Beco.app.managerservice not found.
BDC-RECEIVER handled : 0 / ReceiverData{intent=Intent { act=android.app.action.LOCK_TASK_EXITING flg=0x10 cmp=Beco.app/anywheresoftware.b4a.objects.AdminReceiver2 } packageName=Beco.app resultCode=-1 resultData=null resultExtras=null}
[Toast](this:0xb4db3000,id:102,api:0,p:-1,c:205) setDefaultBufferSize: width=204 height=66

[Beco.app/Beco.app.main](this:0xb4db7800,id:107,api:1,p:4875,c:205) queueBuffer: fps=17.69 dur=2714.15 max=1849.67 min=12.83
Broadcast: Intent { act=android.app.action.DEVICE_POLICY_MANAGER_STATE_CHANGED flg=0x40000010 } ordered=false userid=0 callerApp=ProcessRecord{f8314d 844:system/1000}
Set focused app to: AppWindowToken{408a5a2 token=Token{cf1a4b5 ActivityRecord{6ccc8ec u0 Beco.app/.main t14}}} old focus=AppWindowToken{408a5a2 token=Token{cf1a4b5 ActivityRecord{6ccc8ec u0 Beco.app/.main t14}}} moveFocusNow=false
setEnabledFunctions functions=mtp,adb, forceRestart=false
applyAcmFunction - sys.usb.acm_idx=,mAcmPortIdx=
applyAcmFunction - functions: mtp,adb
setKeyguardEnabled(true)
Looking for focus: 7 = Window{56c0571 u0 StatusBar}, flags=-2122055608, canReceive=false
findFocusedWindow: Found new focus @ 5 = Window{e1eb050 u0 Beco.app/Beco.app.main}
from settings cache , name = lock_screen_show_notifications , value = 1
notifyActivityDrawnForKeyguard: waiting=false Callers=com.android.server.wm.WindowManagerService.handleAppTransitionReadyLocked:10101 com.android.server.wm.WindowManagerService.performLayoutAndPlaceSurfacesLockedInner:10721 com.android.server.wm.WindowManagerService.performLayoutAndPlaceSurfacesLockedLoop:9465 com.android.server.wm.WindowManagerService.performLayoutAndPlaceSurfacesLocked:9412 com.android.server.wm.WindowManagerService.executeAppTransition:4601
enqueueToast pkg=android callback=android.widget.Toast$TN@f901dd3 duration=1
Looking for focus: 7 = Window{56c0571 u0 StatusBar}, flags=-2122055608, canReceive=false
noteOperation: allowing code 11 uid 1000 package android
findFocusedWindow: Found new focus @ 5 = Window{e1eb050 u0 Beco.app/Beco.app.main}
[Built-in Screen (type:0)] fps:1.831054,dur:1092.27,max:1073.71,min:18.56
mDeviceProvisioned is true
mDeviceProvisioned is true
mDeviceProvisioned is true
updateNotificationShade: mUserSetup=true
disable statusbar calling PID = 844
Broadcast: Intent { act=android.app.action.LOCK_TASK_ENTERING flg=0x10 cmp=Beco.app/anywheresoftware.b4a.objects.AdminReceiver2 (has extras) } ordered=false userid=0 callerApp=ProcessRecord{f8314d 844:system/1000}
setKeyguardEnabled(false)
BDC-Calling onReceive: intent=Intent { act=android.app.action.LOCK_TASK_ENTERING flg=0x10 cmp=Beco.app/anywheresoftware.b4a.objects.AdminReceiver2 (has extras) }, receiver=anywheresoftware.b4a.objects.AdminReceiver2@6b3b1bf
Beco.app.managerservice not found.
BDC-RECEIVER handled : 0 / ReceiverData{intent=Intent { act=android.app.action.LOCK_TASK_ENTERING flg=0x10 cmp=Beco.app/anywheresoftware.b4a.objects.AdminReceiver2 (has extras) } packageName=Beco.app resultCode=-1 resultData=null resultExtras=null}
 
Last edited:

Erel

B4X founder
Staff member
Licensed User
Longtime User
It is probably not related, however it is recommended to use lower case package names.

The example works fine here running on Android 8 (and also worked with 7).

Might be a bug in Android 6. Note that you can run a kiosk app without setting it as the home launcher. The downside is that the default launcher will show for a few seconds when the device is restarted (until the Reboot service is started).
 
Status
Not open for further replies.
Top