Introduction
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.
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 } }