1# Service IPC library 2 3This library provides a kind of IPC (inter-process communication) framework 4based on Android 5[bound service](https://developer.android.com/develop/background-work/services/bound-services) 6with [Messenger](https://developer.android.com/reference/android/os/Messenger). 7 8Following benefits are offered by the library to improve and simplify IPC 9development: 10 11- Enforce permission check for every API implementation to avoid security 12 vulnerability. 13- Allow modular API development for better code maintenance (no more huge 14 Service class). 15- Prevent common mistakes, e.g. Service context leaking, ServiceConnection 16 management. 17 18## Overview 19 20In this manner of IPC, 21[Service](https://developer.android.com/reference/android/app/Service) works 22with [Handler](https://developer.android.com/reference/android/os/Handler) to 23deal with different types of 24[Message](https://developer.android.com/reference/android/os/Message) objects. 25 26Under the hood, each API is represented as a `Message` object: 27 28- [what](https://developer.android.com/reference/android/os/Message#what): 29 used to identify API. 30- [data](https://developer.android.com/reference/android/os/Message#getData\(\)): 31 payload of the API parameters and result. 32 33This could be mapped to the `ApiHandler` interface abstraction exactly. 34Specifically, the API implementation needs to provide: 35 36- An unique id for the API. 37- How to marshall/unmarshall the request and response. 38- Whether the given request is permitted. 39 40## Threading model 41 42`MessengerService` starts a dedicated 43[HandlerThread](https://developer.android.com/reference/android/os/HandlerThread) 44to handle requests. `ApiHandler` implementation uses Kotlin `suspend`, which 45allows flexible threading model on top of the 46[Kotlin coroutines](https://kotlinlang.org/docs/coroutines-overview.html). 47 48## Usage 49 50The service provider should extend `MessengerService` and provide API 51implementations. In `AndroidManifest.xml`, declare `<service>` with permission, 52intent filter, etc. if needed. 53 54Meanwhile, the service client implements `MessengerServiceClient` with API 55descriptors to make requests. 56 57Here is an example: 58 59```kotlin 60import android.app.Application 61import android.content.Context 62import android.content.Intent 63import android.os.Bundle 64import kotlinx.coroutines.runBlocking 65 66class EchoService : 67 MessengerService( 68 listOf(EchoApiImpl), 69 PermissionChecker { _, _, _ -> true }, 70 ) 71 72class EchoServiceClient(context: Context) : MessengerServiceClient(context) { 73 override val serviceIntentFactory: () -> Intent 74 get() = { Intent("example.intent.action.ECHO") } 75 76 fun echo(data: String?): String? = 77 runBlocking { invoke(context.packageName, EchoApi, data).await() } 78} 79 80object EchoApi : ApiDescriptor<String?, String?> { 81 private val codec = 82 object : MessageCodec<String?> { 83 override fun encode(data: String?) = 84 Bundle(1).apply { putString("data", data) } 85 86 override fun decode(data: Bundle): String? = data.getString("data", null) 87 } 88 89 override val id: Int 90 get() = 1 91 92 override val requestCodec: MessageCodec<String?> 93 get() = codec 94 95 override val responseCodec: MessageCodec<String?> 96 get() = codec 97} 98 99// This is not needed by EchoServiceClient. 100object EchoApiImpl : ApiHandler<String?, String?>, 101 ApiDescriptor<String?, String?> by EchoApi { 102 override suspend fun invoke( 103 application: Application, 104 myUid: Int, 105 callingUid: Int, 106 request: String?, 107 ): String? = request 108 109 override fun hasPermission( 110 application: Application, 111 myUid: Int, 112 callingUid: Int, 113 request: String?, 114 ): Boolean = (request?.length ?: 0) <= 5 115} 116``` 117