| ## Android Framework-specific rules [FW] <a name="framework"></a> |
| |
| These rules are about APIs, patterns, and data structures that are specific to |
| APIs and functionality built into the Android framework (`Bundle`s, |
| `Parcelable`s, etc.). |
| |
| ### Intent builders should use the `create*Intent()` pattern <a name="framework-intent-builder"></a> |
| |
| Creators for intents should use methods named `createFooIntent()`. |
| |
| ### Use `Bundle`s instead of creating new general-purpose data structures <a name="framework-bundle"></a> |
| |
| Instead of creating a new type/class to hold various args or various types, |
| consider simply using a `Bundle` instead. |
| |
| ### Parcelable implementations must have public `CREATOR` field <a name="framework-parcelable-creator"></a> |
| |
| Parcelable inflation is exposed through CREATOR, not raw constructors. If a |
| class implements `Parcelable`, then its `CREATOR` field must also public API and |
| the class constructor taking a `Parcel` argument must be private. |
| |
| ### Use `CharSequence` for UI strings <a name="framework-charsequence-ui"></a> |
| |
| When a string will be presented in a user interface, use `CharSequence` to allow |
| for `Spannable`s. |
| |
| If it’s just a key or some other non-user-visible label or value, `String` is |
| fine. |
| |
| ### Avoid using Enums <a name="framework-avoid-enum"></a> |
| |
| [IntDef](https://developer.android.com/reference/kotlin/androidx/annotation/IntDef)s |
| must be used over `enum`s in all platform APIs, and should be strongly |
| considered in unbundled, library APIs. Only use enums when you are certain new |
| values will not be added. |
| |
| Benefits of`IntDef` |
| |
| * Enables adding values over time |
| * Kotlin `when` statements can |
| [fail at runtime](https://youtrack.jetbrains.com/issue/KT-30473) if they |
| become no-longer-exhaustive due to an added enum value in platform. |
| * No class/objects used at runtime, only primitive |
| * While R8 / Minfication can avoid this cost for unbundled library APIs, |
| this optimization cannot affect platform API classes. |
| |
| Benefits of Enum |
| |
| * Idiomatic language feature of Java, Kotlin |
| * Enables exhaustive switch, `when` statement usage |
| * Note - values must not change over time, see above |
| * Clearly scoped, and discoverable naming |
| * Enables compile time verification |
| * e.g. a `when` statement in kotlin that returns a value |
| * Is a functioning class that can implement interfaces, have static helpers, |
| expose member/extension methods, fields. |
| |
| ### Follow Android package layering hierarchy <a name="framework-package-layering"></a> |
| |
| The `android.*` package hierarchy has an implicit ordering, where lower-level |
| packages cannot depend on higher-level packages. |
| |
| ### Avoid referring to Google, other companies, and their products <a name="framework-mentions-google"></a> |
| |
| The Android platform is an open-source project and aims to be vendor neutral. |
| The API should be generic and equally usable by system integrators or |
| applications with the requisite permissions. |
| |
| ### Parcelable implementations should be `final` <a name="framework-parcelable-final"></a> |
| |
| Parcelable classes defined by the platform are always loaded from |
| `framework.jar`, so it’s invalid for an app to try overriding a `Parcelable` |
| implementation. |
| |
| If the sending app extends a `Parcelable`, the receiving app won’t have the |
| sender’s custom implementation to unpack with. Note about backward |
| compatibility: if your class historically wasn’t final, but didn’t have a |
| publicly available constructor, you still can mark it `final`. |
| |
| ### Methods calling into system process should rethrow `RemoteException` as `RuntimeException` <a name="framework-rethrow-remoteexception"></a> |
| |
| `RemoteException` is typically thrown by internal AIDL, and indicates that the |
| system process has died, or the app is trying to send too much data. In both |
| cases, public API should rethrow as a `RuntimeException` to ensure that apps |
| don’t accidentally persist security or policy decisions. |
| |
| If you know the other side of a `Binder` call is the system process, this simple |
| boilerplate code is the best-practice: |
| |
| ```java {.good} |
| try { |
| ... |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| ``` |
| |
| ### Throw specific exceptions for API changes <a name="framework-app-compat-exception"></a> |
| |
| Public API behaviors might change across API levels and cause app crashes (for |
| instance to enforce new security policies). |
| |
| When the API needs to throw for a request that was previously valid, throw a new |
| specific exception instead of a generic one. For instance, |
| `ExportedFlagRequired` instead of `SecurityException` (and |
| `ExportedFlagRequired` can extend `SecurityException`). |
| |
| It will help app developers and tools to more easily detect these API behavior |
| changes. |
| |
| ### Implement copy constructor instead of clone() <a name="framework-avoid-clone"></a> |
| |
| Use of the Java `clone()` method is strongly discouraged due to the lack of API |
| guarantees provided by the `Object` class and difficulties inherent in extending |
| classes that use `clone()`. Instead, use a copy constructor that takes an object |
| of the same type. |
| |
| ```java {.good} |
| /** |
| * Constructs a shallow copy of {@code other}. |
| */ |
| public Foo(Foo other) |
| ``` |
| |
| Classes that rely on a Builder for construction should consider adding a Builder |
| copy constructor to allow modifications to the copy. |
| |
| ```java {.good} |
| public class Foo { |
| public static final class Builder { |
| /** |
| * Constructs a Foo builder using data from {@code other}. |
| */ |
| public Builder(Foo other) |
| ``` |
| |
| ### Use `ParcelFileDescriptor` over `FileDescriptor`. <a name="framework-parcelfiledescriptor"></a> |
| |
| The `java.io.FileDescriptor` object has a poor definition of ownership, which |
| can result in obscure use-after-close bugs. Instead, APIs should return or |
| accept `ParcelFileDescriptor` instances. Legacy code can convert between PFD and |
| FD if needed using |
| [dup()](https://developer.android.com/reference/android/os/ParcelFileDescriptor.html##dup\(java.io.FileDescriptor\)) |
| or |
| [getFileDescriptor()](https://developer.android.com/reference/android/os/ParcelFileDescriptor.html##getFileDescriptor\(\)). |
| |
| ### Avoid using odd-sized numerical values. <a name="framework-avoid-short-byte"></a> |
| |
| Avoid using `short` or `byte` values directly, since they often limit how you |
| might be able to evolve the API in the future. |
| |
| ### Avoid using BitSet. <a name="framework-avoid-bitset"></a> |
| |
| `java.util.BitSet` is great for implementation but not for public API. It's |
| mutable, requires an allocation for high-frequency method calls, and does not |
| provide semantic meaning for what each bit represents. |
| |
| For high-performance scenarios, use an `int` or `long` with `@IntDef`. For |
| low-performance scenarios, consider a `Set<EnumType>`. For raw binary data, use |
| `byte[]`. |
| |
| ### Prefer `android.net.Uri`. <a name="framework-android-uri"></a> |
| |
| `android.net.Uri` is the preferred encapsulation for URIs in Android APIs. |
| |
| Avoid `java.net.URI`, because it is overly strict in parsing URIs, and never use |
| `java.net.URL`, because its definition of equality is severely broken. |
| |
| ### Hide annotations marked as `@IntDef`, `@LongDef` or `@StringDef` <a name="framework-hide-typedefs"></a> |
| |
| Annotations marked as `@IntDef`, `@LongDef` or `@StringDef` denote a set of |
| valid constants that can be passed to an API. However, when they are exported as |
| APIs themselves, the compiler inlines the constants and only the (now useless) |
| values remain in the annotation's API stub (for the platform) or JAR (for |
| libraries). |
| |
| As such, usages of these annotations must be marked `@hide` in the platform or |
| `@hide` and `RestrictTo.Scope.LIBRARY)` in libraries. They must be marked |
| `@Retention(RetentionPolicy.SOURCE)` in both cases to ensure they do not appear |
| in API stubs or JARs. |
| |
| ```java |
| /** @hide */ |
| @RestrictTo(RestrictTo.Scope.LIBRARY) |
| @Retention(RetentionPolicy.SOURCE) |
| @IntDef({ |
| STREAM_TYPE_FULL_IMAGE_DATA, |
| STREAM_TYPE_EXIF_DATA_ONLY, |
| }) |
| public @interface ExifStreamType {} |
| ``` |
| |
| When building the platform SDK and library AARs, a tool extracts the annotations |
| and bundles them separately from the compiled sources. Android Studio reads this |
| bundled format and enforces the type definitions. |
| |
| ### Do not add new setting provider keys <a name="framework-avoid-settings"></a> |
| |
| Do not expose new keys from |
| [`Settings.Global`](https://developer.android.com/reference/android/provider/Settings.Global), |
| [`Settings.System`](https://developer.android.com/reference/android/provider/Settings.System), |
| and/or |
| [`Settings.Secure`](https://developer.android.com/reference/android/provider/Settings.Secure). |
| |
| Instead, add a proper getter and setter Java API in a relevant class, which is |
| typically a "manager" class. Add a listener mechanism or a broadcast to notify |
| clients of changes as needed. |
| |
| `SettingsProvider` settings have a number of problems compared to |
| getters/setters: |
| |
| * No type safety. |
| * No unified way to provide a default value. |
| * No proper way to customize permissions. |
| * For example, it's not possible to protect your setting with a custom |
| permission. |
| * No proper way to add custom logic properly. |
| * For example, it's not possible to change setting A's value depending on |
| setting B's value. |
| |
| Example: |
| [`Settings.Secure.LOCATION_MODE`](https://developer.android.com/reference/android/provider/Settings.Secure##LOCATION_MODE) |
| has existed for a long time, but the location team has deprecated it for a |
| proper Java API |
| [`LocationManager.isLocationEnabled()`](https://developer.android.com/reference/android/location/LocationManager##isLocationEnabled\(\)) |
| and the |
| [`MODE_CHANGED_ACTION`](https://developer.android.com/reference/android/location/LocationManager##MODE_CHANGED_ACTION) |
| broadcast, which gave the team a lot more flexibility, and the semantics of the |
| APIs are a lot clearer now. |
| |
| ### Do not extend `Activity` and `AsyncTask` <a name="framework-forbidden-super-class"></a> |
| |
| `AsyncTask` is an implementation detail. Instead, expose a listener or, in |
| androidx, a `ListenableFuture` API instead. |
| |
| `Activity` subclasses are impossible to compose. Extending activity for your |
| feature makes it incompatible with other features that require users to do the |
| same. Instead, rely on composition by using tools such as |
| [LifecycleObserver](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:lifecycle/lifecycle-common/src/main/java/androidx/lifecycle/LifecycleObserver.java;l=26?q=LifecycleObserver&ss=androidx%2Fplatform%2Fframeworks%2Fsupport). |
| |
| ### Use the `Context`'s `getUser()` <a name="framework-context-user"></a> |
| |
| Classes bound to a `Context`, such as anything returned from |
| `Context.getSystemService()` should use the user bound to the `Context` instead |
| of exposing members that target specific users. |
| |
| ```java {.good} |
| class FooManager { |
| Context mContext; |
| |
| void fooBar() { |
| mIFooBar.fooBarForUser(mContext.getUser()); |
| } |
| } |
| ``` |
| |
| ```java {.bad} |
| class FooManager { |
| Context mContext; |
| |
| Foobar getFoobar() { |
| // Bad: doesn't appy mContext.getUserId(). |
| mIFooBar.fooBarForUser(Process.myUserHandle()); |
| } |
| |
| Foobar getFoobar() { |
| // Also bad: doesn't appy mContext.getUserId(). |
| mIFooBar.fooBar(); |
| } |
| |
| Foobar getFoobarForUser(UserHandle user) { |
| mIFooBar.fooBarForUser(user); |
| } |
| } |
| ``` |
| |
| Exception: A method may accept a user argument if it accepts values that don't |
| represent a single user, such as `UserHandle.ALL`. |
| |
| ### Use `UserHandle` instead of plain `int`s <a name="framework-userhandle"></a> |
| |
| `UserHandle` is preferred to ensure type safety and avoid conflating user IDs |
| with uids. |
| |
| ```java {.good} |
| Foobar getFoobarForUser(UserHandle user); |
| ``` |
| |
| ```java {.bad} |
| Foobar getFoobarForUser(int userId); |
| ``` |
| |
| Where unavoidable, `int`s representing a user ID must be annotated with |
| `@UserIdInt`. |
| |
| ```java |
| Foobar getFoobarForUser(@UserIdInt int user); |
| ``` |
| |
| ### Prefer listeners/callbacks to broadcast intents <a name="framework-avoid-broadcast"></a> |
| |
| Broadcast intents are very powerful, but they've resulted in emergent behaviors |
| that can negatively impact system health, and so new broadcast intents should be |
| added judiciously. |
| |
| Here are some specific concerns which result in us discouraging the introduction |
| of new broadcast intents: |
| |
| * When sending broadcasts without the `FLAG_RECEIVER_REGISTERED_ONLY` flag, |
| they will force-start any applications which aren't already running. While |
| this can sometimes be a desired outcome, it can result in stampeding of |
| dozens of apps, negatively impacting system health. We'd recommend using |
| alternative strategies, such as `JobScheduler`, to better coordinate when |
| various preconditions are met. |
| |
| * When sending broadcasts, there is little ability to filter or adjust the |
| content delivered to apps. This makes it difficult or impossible to respond |
| to future privacy concerns, or introduce behavior changes based on the |
| target SDK of the receiving app. |
| |
| * Since broadcast queues are a shared resource, they can become overloaded and |
| may not result in timely delivery of your event. We've observed several |
| broadcast queues in the wild which have an end-to-end latency of 10 minutes |
| or longer. |
| |
| For these reasons, we encourage new features to consider using |
| listeners/callbacks or other facilities such as `JobScheduler` instead of |
| broadcast intents. |
| |
| In cases where broadcast intents still remain the ideal design, here are some |
| best-practices that should be considered: |
| |
| * If possible, use `Intent.FLAG_RECEIVER_REGISTERED_ONLY` to limit your |
| broadcast to apps that are already running. For example, `ACTION_SCREEN_ON` |
| uses this design to avoid waking up apps. |
| * If possible, use `Intent.setPackage()` or `Intent.setComponent()` to target |
| the broadcast at a specific app of interest. For example, |
| `ACTION_MEDIA_BUTTON` uses this design to focus on the current app handling |
| playback controls. |
| * If possible, define your broadcast as a `<protected-broadcast>` to ensure |
| that malicious apps can't impersonate the OS. |