This feature allows third party apps to communicate directly with the Android Ensemble MDM by binding to a service in Ensemble and using the Messenger class to communicate with its remote process.
The official Android developer guide for this topic can be found here.
TABLE OF CONTENTS
Instructions
Message Codes
1 - Start
- This is sent from Ensemble to the service after its been bound.
2 - Enter Kiosk Mode:
- This is sent from the 3rd party application to Ensemble.
- Ensemble will then place that 3rd party app into a single app kiosk mode state until the app calls 'Exit Kiosk Mode' or the admin removes this 3rd party app from the Messenger policy.
- An exit PIN should be setup inside policies if the end user should not be able to exit this mode.
3 - Exit Kiosk Mode:
- This is sent from the 3rd party application to Ensemble.
- Ensemble will remove that 3rd party app from single app kiosk mode.
4 - Reload Policies: User Interaction Required:
- This is sent from the 3rd party application to Ensemble.
- Ensemble will download the latest policies from the server and wait for the user to approve applying them.
5 - Reload Policies: Forced:
- This is sent from the 3rd party application to Ensemble.
- Ensemble will download the latest policies from the server and show the UI while applying them .
6 - Reload Policies: Silent:
- This is sent from the 3rd party application to Ensemble.
- Ensemble will download the latest policies from the server and apply them in the background.
11 - Handled
- This is sent from Ensemble to the 3rd party application.
- Ensemble sends this once the action corresponding to the last message has been completed.
12 - Not Handled
- This is sent from Ensemble to the 3rd party application.
- Ensemble sends this once the action corresponding to the last message has failed.
Sending Messages to Ensemble
Android Manifest
<queries> <package android:name="com.conversasolutions.ensemble" /> </queries>
Binding the Service
/** Messenger for communicating with the service. */ private var mService: Messenger? = null /** Flag indicating whether we have called bind on the service. */ private var bound: Boolean = false /** * Class for interacting with the main interface of the service. */ private val mConnection = object : ServiceConnection { override fun onServiceConnected(className: ComponentName, service: IBinder) { // This is called when the connection with the service has been // established, giving us the object we can use to // interact with the service. We are communicating with the // service using a Messenger, so here we get a client-side // representation of that from the raw IBinder object. mService = Messenger(service) bound = true } override fun onServiceDisconnected(className: ComponentName) { // This is called when the connection with the service has been // unexpectedly disconnected -- that is, its process crashed. mService = null bound = false } } private fun sendMessage(message: Int) { if (!bound) return // Create and send a message to the service, using a supported 'what' value val msg: Message = Message.obtain(null, message, 0, 0) try { mService?.send(msg) } catch (e: RemoteException) { e.printStackTrace() } } override fun onStart() { super.onStart() // Bind to the service val intent = Intent() intent.component = ComponentName("com.conversasolutions.ensemble", "com.conversasolutions.ensemble.services.MessageService") val result = bindService(intent, mConnection, BIND_AUTO_CREATE) Log.i("MessageService", "Bound to Ensemble $result") } override fun onStop() { super.onStop() // Unbind from the service if (bound) { unbindService(mConnection) bound = false } }
Receiving Messages from Ensemble
Android Manifest
<uses-permission android:name="com.conversasolutions.ensemble.permission.MESSAGING"/>
<service android:name=".MessageService" android:exported="true" android:permission="com.conversasolutions.ensemble.permission.MESSAGING"> </service>
MessageService.kt
class MessageService: Service() { companion object { const val START = 1 const val ENTER_KIOSK_MODE = 2 const val EXIT_KIOSK_MODE = 3 const val RELOAD_POLICIES_MANUAL = 4 const val RELOAD_POLICIES_FORCED = 5 const val RELOAD_POLICIES_SILENT = 6 const val HANDLED = 11 const val NOT_HANDLED = 12 val messageStateLiveData = MutableLiveData<String>() } private lateinit var mMessenger: Messenger internal class IncomingHandler(looper: Looper?) : Handler(looper!!) { override fun handleMessage(msg: Message) { super.handleMessage(msg) when (msg.what){ START -> { Log.i("MessageService", "Received message START") messageStateLiveData.postValue("Start") } ENTER_KIOSK_MODE -> { Log.i("MessageService", "Received message ENTER_KIOSK_MODE") messageStateLiveData.postValue("Enter Forced Kiosk Mode") } EXIT_KIOSK_MODE -> { Log.i("MessageService", "Received message EXIT_KIOSK_MODE") messageStateLiveData.postValue("Exit Forced Kiosk Mode") } HANDLED -> { Log.i("MessageService", "Received message HANDLED") messageStateLiveData.postValue("Handled") } NOT_HANDLED -> { Log.i("MessageService", "Received message NOT_HANDLED") messageStateLiveData.postValue("Not Handled") } else -> { Log.i("MessageService", "Received message Unknown") messageStateLiveData.postValue("Unknown") } } } } override fun onBind(p0: Intent?): IBinder? { mMessenger = Messenger(IncomingHandler(mainLooper)) return mMessenger.binder } }