alanv | da56d5d | 2021-02-12 09:00:29 -0800 | [diff] [blame^] | 1 | # Integrating proprietary components |
| 2 | |
| 3 | go/androidx/open_source |
| 4 | |
| 5 | <!--* |
| 6 | # Document freshness: For more information, see go/fresh-source. |
| 7 | freshness: { owner: 'alanv' reviewed: '2021-02-11' } |
| 8 | *--> |
| 9 | |
| 10 | [TOC] |
| 11 | |
| 12 | One of the core principles of Jetpack is "Developed as open-source and |
| 13 | compatible with AOSP Android," but what does that mean in practice? This guide |
| 14 | provides specific, technical guidance on developing an open-source library and |
| 15 | interacting with proprietary or closed-source libraries and services. |
| 16 | |
| 17 | ## Why do we care? |
| 18 | |
| 19 | ### Compatibility with AOSP ecosystem |
| 20 | |
| 21 | The Android Open-Source Project enables a diverse ecosystem of devices with a |
| 22 | wide array of software environments in which our libraries will operate. Many of |
| 23 | those devices are certified to run Play Services, but it's important for our |
| 24 | libraries to work on all devices that are certified as Android -- even those |
| 25 | with no Google software installed. |
| 26 | |
| 27 | * Features provided by primary artifacts **must** be able to function on AOSP |
| 28 | devices without the presence of proprietary components like Play Services |
| 29 | |
| 30 | ### Testing and testability |
| 31 | |
| 32 | Isolating behavior makes it easier to write reliable and targeted tests, but |
| 33 | introducing dependencies on proprietary components makes this difficult. In a |
| 34 | well-abstracted library, developers should be able to write integration tests |
| 35 | against the library's documented API surface without concerning themselves with |
| 36 | the implementation details of a backing service. |
| 37 | |
| 38 | * Features provided by primary artifacts that may be backed by proprietary |
| 39 | components **must** be written in way that makes it feasible for a developer |
| 40 | to write and delegate to their own backing implementation |
| 41 | |
| 42 | ## Developer choice |
| 43 | |
| 44 | Developers should be able to choose between proprietary components; however, |
| 45 | libraries are also encouraged to provide a sensible default. |
| 46 | |
| 47 | * Features provided by primary artifacts that may be backed by proprietary |
| 48 | components **must** allow developers to choose a specific backing component |
| 49 | and **must not** hard-code proprietary components as the default choice |
| 50 | * Libraries _may_ use a ranking or filtering heuristic based on platform APIs |
| 51 | such as permissions, presence on the system image, or other properties of |
| 52 | applications and packages |
| 53 | |
| 54 | ## Open protocols |
| 55 | |
| 56 | Third-party developers should be able to provide their own backing services, |
| 57 | which means service discovery mechanisms, communication protocols, and API |
| 58 | surfaces used to implement a backing service must be publicly available for |
| 59 | implementation. |
| 60 | |
| 61 | Third-party developers should also be able to validate that their implementation |
| 62 | conforms to the expectations of the library. Library developers should already |
| 63 | be writing tests to cover their backing service, e.g. that a service |
| 64 | implementing a protocol or interface is correct, and in many cases these tests |
| 65 | will be suitable for third-party developers to verify their own implementations. |
| 66 | |
| 67 | While we recommend that developers provide a stub backing implementation in a |
| 68 | `-testing` artifact or use one in their own unit tests, we do not require one to |
| 69 | be provided; only that it is possible to write one. |
| 70 | |
| 71 | ## Examples of policy violations |
| 72 | |
| 73 | * A primary artifact uses `Intent` handling as a service discovery mechanism |
| 74 | and hard-codes a reference to `com.google.android` as a ranking heuristic. |
| 75 | * **What's wrong?** This conflicts with the developer choice principle. |
| 76 | Primary artifacts must remain neutral regarding specific proprietary |
| 77 | components. |
| 78 | * **How to fix?** This library should use an alternative ranking heuristic |
| 79 | that takes advantage of platform APIs such as granted permissions or |
| 80 | presence of the component on the system image (see |
| 81 | [FLAG_SYSTEM](https://developer.android.com/reference/android/content/pm/ApplicationInfo#FLAG_SYSTEM) |
| 82 | and |
| 83 | [FLAG_UPDATED_SYSTEM_APP](https://developer.android.com/reference/android/content/pm/ApplicationInfo#FLAG_UPDATED_SYSTEM_APP)). |
| 84 | The library will also need to provide an API that allows developers to |
| 85 | choose an explicit ranking or default component. |
| 86 | * A primary artifact uses reflection to delegate to a specific fully-qualified |
| 87 | class name. This class is provided by an optional library that delegates to |
| 88 | Play Services. |
| 89 | * **What's wrong?** This is another situation where the library is |
| 90 | limiting developer choice. Features in primary artifacts which may |
| 91 | delegate to proprietary services must allow developers to choose a |
| 92 | different delegate. Reflection on a fully-qualified class name does |
| 93 | _not_ allow multiple delegates to exist on the classpath and is not a |
| 94 | suitable service discovery mechanism. |
| 95 | * **How to fix?** This library should use a more suitable service |
| 96 | discovery mechanism that allows multiple providers to coexist and |
| 97 | ensures the the developer is able to choose among them. |
| 98 | * A primary artifact provides a service discovery mechanism that allows |
| 99 | multiple providers and exposes an API that lets the developer specify a |
| 100 | preference. Communication with the service is managed through a `Bundle` |
| 101 | where they keys, values, and behaviors are documented outside of Jetpack. |
| 102 | * **What's wrong?** This conflicts with the open protocols principle. |
| 103 | Third-party developers should be able to implement their own backing |
| 104 | services, but using a `Bundle` with a privately-documented protocol |
| 105 | means that (1) it is not possible to write adqeuate tests in Jetpack and |
| 106 | (2) developers outside of Google cannot feasibly write correct backing |
| 107 | implementations. |
| 108 | * **How to fix?** At a minimum, the developer should fully document the |
| 109 | keys, values, and behavior expected by the protocol; however, in this |
| 110 | case we would strongly recommend replacing or wrapping `Bundle` with a |
| 111 | strongly-typed and documented API surface and robust suite of tests to |
| 112 | ensure implementations on either side of the protocol are behaving |
| 113 | correctly. |
| 114 | * A primary artifact provides an `interface` and an API that allows developers |
| 115 | to specify a backing service using classes that implement that interface. |
| 116 | The `interface` API surface has several `@hide` methods annotated with |
| 117 | `@RestrictTo(LIBRARY_GROUP)`. |
| 118 | * **What's wrong?** This is another open protocols issue. Third-party |
| 119 | developers should be able to implement their own backing services, but |
| 120 | using a partially-private `interface` means that only Jetpack libraries |
| 121 | can feasibly provide a backing implementation. |
| 122 | * **How to fix?** At a minimum, the developer should make the `interface` |
| 123 | fully public and documented so that it can be implemented by a |
| 124 | third-party. They should also provide robust tests for the default |
| 125 | backing implementation with the expectation that third-party developers |
| 126 | will use this to verify their own custom implementations. |