| ## 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). |