blob: fc6add8f1b99cb573ab0838e09fbd781ee0abead [file] [log] [blame] [view]
## Mainline specific patterns [ML] <a name="mainline"></a>
[Mainline](http://go/what-is-mainline) is a project to allow updating subsystems
("mainline modules") of the Android OS individually, rather than updating the
whole system image.
Mainline modules have to be "unbundled" from the core platform, which means all
the interactions between each module and the rest of the world have to be done
via formal (public or system) APIs.
There are certain design patterns mainline modules should follow. This section
describes them.
### The `[Module]FrameworkInitializer` pattern
If a mainline module needs to exposes `@SystemService` classes (e.g.
`JobScheduler`), use the following pattern:
- Expose a `[YourModule]FrameworkInitializer` class from your module. This
class needs to be in `$BOOTCLASSPATH`. Example:
[StatsFrameworkInitializer](https://cs.android.com/android/platform/superproject/+/master:packages/modules/StatsD/framework/java/android/os/StatsFrameworkInitializer.java)
- Mark it with `@SystemApi(client = MODULE_LIBRARIES)`.
- Add a `public static void registerServiceWrappers()` method to it.
- Use `SystemServiceRegistry.registerContextAwareService()` to register a
service manager class when it needs a reference to a `Context`.
- Use `SystemServiceRegistry.registerStaticService()` to register a service
manager class when it does not need a reference to a `Context`.
- Call the `registerServiceWrappers()` method from
[`SystemServiceRegistry`](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/app/SystemServiceRegistry.java)'s
static initializer.
### The `[Module]ServiceManager` pattern
Normally, in order to register system service binder objects and/or get
references to them, one would use
[`ServiceManager`](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/os/ServiceManager.java),
but mainline modules can't use it because it's hidden. This class is hidden
because mainline modules are not supposed to register or refer to system service
binder objects exposed by the non-updateable platform or by other modules.
Mainline modules can use the following pattern instead to be able to register
and get references to binder services that are implemented inside the module.
- Create a `[YourModule]ServiceManager` class, following the design of
[TelephonyServiceManager](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/os/TelephonyServiceManager.java)
- Expose the class as `@SystemApi`. If you only need to access it from
`$BOOTCLASSPATH` classes or system server classes, you can use
`@SystemApi(client = MODULE_LIBRARIES)`; otherwise `@SystemApi(client =
PRIVILEGED_APPS)` would work.
- This class would consists of:
- A hidden constructor, so only the non-updatable platform code can
instantiate it.
- ~~A nested class `ServiceRegisterer`~~ TODO(omakoto) b/245801672: It
shouldn't be an inner class. Create a sharable class for this.
- Public getter methods that return a `ServiceRegisterer` instance for a
specific name. If you have one binder object, then you need one getter
method. If you have two, then you need two getters.
- In
[`ActivityThread.initializeMainlineModules()`](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/app/ActivityThread.java),
instantiate this class, and pass it to a static method exposed by your
module. Normally, you add a static `@SystemApi(client =
MODULE_LIBRARIES)` API in your `FrameworkInitializer` class that takes
it.
This pattern would prevent other mainline modules from accessing these APIs
because there's no way for other modules to get an instance of
`[YourModule]ServiceManager`, even though the `get()` and `register()` APIs are
visible to them.
Here is how telephony [^telephonymodule] gets a reference to the telephony
service:
[code search link](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/telephony/java/android/telephony/TelephonyManager.java).
[^telephonymodule]: Telephony is not a mainline module (yet), but this still
shows a preferred pattern.
If your implements a service binder object in native code, you use
[the `AServiceManager` native APIs](https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/binder/ndk/include_platform/android/binder_manager.h).
These APIs correspond to the `ServiceManager` Java APIs but the native ones are
directly exposed to mainline modules. **Do not** use them to register or refer
to binder objects that are not owned by your module. If you expose a binder
object from native, your `[YourModule]ServiceManager.ServiceRegisterer` does not
need a `register()` method.
### Defining permissions in Mainline module
Mainline modules containing APKs may define (custom) permissions in their APK
`AndroidManifest.xml` in the same way as a regular APK.
> NOTE: Despite that the APEX `AndroidManifest.xml` currently also allows
> `<permission>` tags because it's sharing the parsing code with APKs, that is
> unsupported and may be removed in the future.
If the defined permission is only used internally within a module, its
permission name should be prefixed with the APK package name, e.g.:
```xml {.no-copy}
<permission
android:name="com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER"
android:protectionLevel="signature" />
```
If the defined permission is to be provided as part of an updatable platform API
to other apps, its permission name should be prefixed with "android.permission."
(like any non-updatable platform permission) plus the module package name, to
signal it's a platform API from a module while avoiding any naming conflicts,
e.g.:
```xml {.no-copy}
<permission
android:name="android.permission.health.READ_ACTIVE_CALORIES_BURNED"
android:label="@string/active_calories_burned_read_content_description"
android:protectionLevel="dangerous"
android:permissionGroup="android.permission-group.HEALTH" />
```
Then the module can expose this permission name as an API constant in its API
surface, e.g.
[`HealthPermissions.READ_ACTIVE_CALORIES_BURNED`](https://developer.android.com/reference/android/health/connect/HealthPermissions#READ_ACTIVE_CALORIES_BURNED).