Question was already answered but I'd like to provide an example of how to accomplish this with Messenger's binder instead of AIDL.
It's possible to make the Binder you return in the Service's onBind() adaptable and dynamic to whether the client that connected to the service was a component from another process/app (in which case we'd want to return to them the Messenger's binder to establish IPC), or if the client that connected to the service was a component from within our own process/app (in which case we'd want to return a custom Binder object that has a reference to the service).
All we need to do is to specify an action with the intents that we use to bind the service from either client, and in the service, we filter for that action, and return a Binder depending on what the intent's action is.
First we declare MyService in the manifest with the following intent filters:
<service
android:name=".MyService"
android:exported="true">
<intent-filter>
<action android:name="foreignProcess" />
<action android:name="localComponent" />
</intent-filter>
</service>
Then in MyService, we declare, we make sure to return different Binders in onBind() based on the intent's action:
class MyService: Service() {
private var clientMessenger: Messenger? = null
private val incomingHandler = object : Handler(Looper.getMainLooper()) {
override fun handleMessage(msgFromClient: Message) {
super.handleMessage(msgFromClient)
clientMessenger = msgFromClient.replyTo
val receivedBundle = msgFromClient.data
}
}
// public method that we can call from Activities/Fragments, etc that bind to our service.
fun doStuff() {
}
// depending on the action of the intent that was used to bind to the service, we return the appropriate Binder.
override fun onBind(intent: Intent?): IBinder {
return when (intent?.action) {
"localComponent" -> MyBinder()
"foreignProcess" -> Messenger(incomingHandler).binder
else -> Messenger(incomingHandler).binder
}
}
inner class MyBinder : Binder() {
val service: MyService
get() = this@MyService
}
}
Now in the component (like an Activity or Fragment) that exists within our own app (i.e. exists in the same process with the service), we create an intent with the action "localComponent" and use that to bind to the service:
fun bindToServiceThroughMyLocalComponent(context: Context) {
val conn = object: ServiceConnection {
override fun onServiceConnected(name: ComponentName?, binder: IBinder?) {
val myService = (binder as MyService.MyBinder).service
myService.doStuff()
}
override fun onServiceDisconnected(name: ComponentName?) {}
}
val intent = Intent(context, MyService::class.java).apply {
setAction("localComponent")
}
context.bindService(intent, conn, Context.BIND_AUTO_CREATE)
}
And in the client app (which exists in a different process), we create an intent with the action "foreignProcess" and use it to bind to the service:
fun bindToServiceThroughForeignProcess(context: Context) {
val incomingHandler = object: Handler(Looper.getMainLooper()) {
override fun handleMessage(msgFromService: Message) {
super.handleMessage(msgFromService)
val receivedBundle = msgFromService.data
}
}
val conn = object: ServiceConnection {
override fun onServiceConnected(name: ComponentName?, binder: IBinder?) {
val clientMessenger = Messenger(incomingHandler)
val clientMessage = Message.obtain(null, 0, clientMessenger)
val bundle = Bundle()
bundle.putString("message", "hello world")
clientMessage.data = bundle
clientMessage.replyTo = clientMessenger // pass our clientMessenger to the clientMessage to establish IPC
Messenger(binder).send(clientMessage)
}
override fun onServiceDisconnected(name: ComponentName?) {}
}
val intent = Intent("foreignProcess")
context.bindService(intent, conn, Context.BIND_AUTO_CREATE)
}
Now our Service's onBind() returns different Binders depending on what the different clients specified in their intent's actions.