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