tree: baafefba28bcc44b3c514f2c80fb791981a08bc4 [path history] [tgz]
  1. src/
  2. testutils/
  3. Android.bp
  4. AndroidManifest.xml
  5. OWNERS
  6. README.md
packages/SettingsLib/Ipc/README.md

Service IPC library

This library provides a kind of IPC (inter-process communication) framework based on Android bound service with Messenger.

Following benefits are offered by the library to improve and simplify IPC development:

  • Enforce permission check for every API implementation to avoid security vulnerability.
  • Allow modular API development for better code maintenance (no more huge Service class).
  • Prevent common mistakes, e.g. Service context leaking, ServiceConnection management.

Overview

In this manner of IPC, Service works with Handler to deal with different types of Message objects.

Under the hood, each API is represented as a Message object:

  • what: used to identify API.
  • data: payload of the API parameters and result.

This could be mapped to the ApiHandler interface abstraction exactly. Specifically, the API implementation needs to provide:

  • An unique id for the API.
  • How to marshall/unmarshall the request and response.
  • Whether the given request is permitted.

Threading model

MessengerService starts a dedicated HandlerThread to handle requests. ApiHandler implementation uses Kotlin suspend, which allows flexible threading model on top of the Kotlin coroutines.

Caveats

IPC request data is transmitted with Bundle (see MessageCodec),

  • Bundle keys should not be changed to avoid breaking backward compatibility.
  • If strong backward compatibility is required, consider using serialization framework (e.g. Protobuf) and marshall/unmarshall with byte array.
  • When parcelable is used (e.g. Bundle.putParcelable), the class name must be the same across apps. Thus add @Keep annotation to the parcelable class if Proguard is enabled.

Usage

The service provider should extend MessengerService and provide API implementations. In AndroidManifest.xml, declare <service> with permission, intent filter, etc. if needed.

Meanwhile, the service client implements MessengerServiceClient with API descriptors to make requests.

Here is an example:

import android.app.Application
import android.content.Context
import android.content.Intent
import android.os.Bundle

class EchoService : MessengerService(listOf(EchoApiImpl()), PermissionChecker { _, _, _ -> true })

class EchoServiceClient(context: Context) : MessengerServiceClient(context) {
  private val echoApi = EchoApi()

  override val serviceIntentFactory = { Intent("example.intent.action.ECHO") }

  suspend fun echo(data: String?): String? = invoke(context.packageName, echoApi, data).await()
}

open class EchoApi : ApiDescriptor<String?, String?> {
  private val codec =
    object : MessageCodec<String?> {
      override fun encode(data: String?) = Bundle(1).apply { putString("data", data) }

      override fun decode(data: Bundle): String? = data.getString("data", null)
    }

  override val id: Int
    get() = 1

  override val requestCodec: MessageCodec<String?>
    get() = codec

  override val responseCodec: MessageCodec<String?>
    get() = codec
}

// This is not needed by EchoServiceClient.
class EchoApiImpl : EchoApi(), ApiHandler<String?, String?> {
  override suspend fun invoke(
    application: Application,
    callingPid: Int,
    callingUid: Int,
    request: String?,
  ): String? = request

  override fun hasPermission(
    application: Application,
    callingPid: Int,
    callingUid: Int,
    request: String?,
  ): Boolean = (request?.length ?: 0) <= 5
}