1## Implementing compatibility {#compat} 2 3### Referencing new APIs {#compat-newapi} 4 5Generally, methods on library classes should be available to all devices above 6the library's `minSdkVersion`; however, the behavior of the method may vary 7based on platform API availability. 8 9For example, a method may delegate to a platform API on SDKs where the API is 10available, backport a subset of behavior on earlier SDKs, and no-op on very old 11SDKs. 12 13#### Checking device SDK version {#compat-sdk} 14 15The most common way of delegating to platform or backport implementations is to 16compare the device's `Build.VERSION.SDK_INT` field to a known-good SDK version; 17for example, the SDK in which a method first appeared or in which a critical bug 18was first fixed. 19 20When developing against pre-release SDKs where the `SDK_INT` has not been 21finalized, SDK checks **must** use `BuildCompat.isAtLeastX()` methods and 22**must** use a tip-of-tree `project` dependency to ensure that the 23implementation of `BuildCompat` stays up-to-date when the SDK is finalized. 24 25**Do not** assume that the next SDK release's `SDK_INT` will be N+1. The value 26is not finalized until SDK finalization happens, at which point the `isAtLeast` 27check will be updated. **Never** write your own check for a pre-release SDK. 28 29```java {.good} 30@NonNull 31public static List<Window> getAllWindows() { 32 if (BuildCompat.isAtLeastR()) { 33 return ApiRImpl.getAllWindows(); 34 } 35 return Collections.emptyList(); 36} 37``` 38 39```kotlin {.good} 40dependencies { 41 api(project(":core:core")) 42} 43``` 44 45#### Validating class verification 46 47To verify that your library does not raise class verification failures, look for 48`dex2oat` output during install time. 49 50You can generate class verification logs from test APKs. Simply call the 51class/method that should generate a class verification failure in a test. 52 53The test APK will generate class verification logs on install. 54 55```bash 56# Enable ART logging (requires root). Note the 2 pairs of quotes! 57adb root 58adb shell setprop dalvik.vm.dex2oat-flags '"--runtime-arg -verbose:verifier"' 59 60# Restart Android services to pick up the settings 61adb shell stop && adb shell start 62 63# Optional: clear logs which aren't relevant 64adb logcat -c 65 66# Install the app and check for ART logs 67# This line is what triggers log lines, and can be repeated 68adb install -d -r someApk.apk 69 70# it's useful to run this _during_ install in another shell 71adb logcat | grep 'dex2oat' 72... 73... I dex2oat : Soft verification failures in 74``` 75 76#### View constructors {#compat-view-constructors} 77 78The four-arg View constructor -- `View(Context, AttributeSet, int, int)` -- was 79added in SDK 21 and allows a developer to pass in an explicit default style 80resource rather than relying on a theme attribute to resolve the default style 81resource. Because this API was added in SDK 21, care must be taken to ensure 82that it is not called through any < SDK 21 code path. 83 84Views *may* implement a four-arg constructor in one of the following ways: 85 861. Do not implement. 871. Implement and annotate with `@RequiresApi(21)`. This means the three-arg 88 constructor **must not** call into the four-arg constructor. 89 90#### Device-specific issues {#compat-oem} 91 92Library code may work around device- or manufacturer-specific issues -- issues 93not present in AOSP builds of Android -- *only* if a corresponding CTS test 94and/or CDD policy is added to the next revision of the Android platform. Doing 95so ensures that such issues can be detected and fixed by OEMs. 96 97#### Handling `minSdkVersion` disparity {#compat-minsdk} 98 99Methods that only need to be accessible on newer devices, including 100`to<PlatformClass>()` methods, may be annotated with `@RequiresApi(<sdk>)` to 101indicate they must not be called when running on older SDKs. This annotation is 102enforced at build time by the `NewApi` lint check. 103 104#### Handling `targetSdkVersion` behavior changes {#compat-targetsdk} 105 106To preserve application functionality, device behavior at a given API level may 107change based on an application's `targetSdkVersion`. For example, if an app with 108`targetSdkVersion` set to API level 22 runs on a device with API level 29, all 109required permissions will be granted at installation time and the run-time 110permissions framework will emulate earlier device behavior. 111 112Libraries do not have control over the app's `targetSdkVersion` and -- in rare 113cases -- may need to handle variations in platform behavior. Refer to the 114following pages for version-specific behavior changes: 115 116* Android 14, 117 [API level 34](https://developer.android.com/about/versions/14/behavior-changes-14) 118* Android 13, 119 [API level 33](https://developer.android.com/about/versions/13/behavior-changes-13) 120* Android 12, 121 [API level 31](https://developer.android.com/about/versions/12/behavior-changes-12) 122* Android 11, 123 [API level 30](https://developer.android.com/about/versions/11/behavior-changes-11) 124* Android 10, 125 [API level 29](https://developer.android.com/about/versions/10/behavior-changes-10) 126* Android Pie (9.0), 127 [API level 28](https://developer.android.com/about/versions/pie/android-9.0-changes-28) 128* Android Oreo (8.0), 129 [API level 26](https://developer.android.com/about/versions/oreo/android-8.0-changes) 130* Android Nougat(7.0), 131 [API level 24](https://developer.android.com/about/versions/nougat/android-7.0-changes) 132* Android Lollipop (5.0), 133 [API level 21](https://developer.android.com/about/versions/lollipop/android-5.0-changes) 134* Android KitKat (4.4), 135 [API level 19](https://developer.android.com/about/versions/kitkat/android-4.4#Behaviors) 136 137#### Working around Lint issues {#compat-lint} 138 139In rare cases, Lint may fail to interpret API usages and yield a `NewApi` error 140and require the use of `@TargetApi` or `@SuppressLint('NewApi')` annotations. 141Both of these annotations are strongly discouraged and may only be used 142temporarily. They **must never** be used in a stable release. Any usage of these 143annotation **must** be associated with an active bug, and the usage must be 144removed when the bug is resolved. 145 146#### Java 8+ APIs and core library desugaring {#compat-desugar} 147 148The DEX compiler (D8) supports 149[API desugaring](https://developer.android.com/studio/write/java8-support-table) 150to enable usage of Java 8+ APIs on a broader range of platform API levels. 151Libraries using AGP 8.2+ can express the toolchain requirements necessary for 152desugaring to work as intended, but these requirements are only enforced for 153**apps** that are also building with AGP 8.2+. 154[While adoption of AGP 8.2+ remains low](https://issuetracker.google.com/172590889#comment12), 155AndroidX libraries **must not** rely on `coreLibraryDesugaring` to access Java 156language APIs on earlier platform API levels. For example, `java.time.*` may 157only be used in code paths targeting API level 26 and above. 158 159### Delegating to API-specific implementations {#delegating-to-api-specific-implementations} 160 161#### Referencing SDK constants {#sdk-constants} 162 163Generally speaking, platform and Mainline SDK constants should not be inlined. 164 165Constants that can be inlined by the compiler (most primitives and `String`s) 166should be referenced directly from the SDK rather than copying and pasting the 167value. This will raise an `InlinedApi` lint warning, which may be suppressed. 168 169``` 170public static class ViewCompat { 171 @Suppress("InlinedApi") 172 public static final int SOME_CONSTANT = View.SOME_CONSTANT 173} 174``` 175 176In rare cases, some SDK constants are not defined at compile-time and cannot be 177inlined by the compiler. In these cases, you will need to handle them like any 178other API using out-of-lining and version gating. 179 180``` 181public static final int RUNTIME_CONSTANT = 182 if (SDK_INT > 34) { Api34Impl.RUNTIME_CONSTANT } else { -1 } 183``` 184 185Developers **must not** inline platform or Mainline SDK constants that are not 186part of a finalized public SDK. **Do not** inline values from `@hide` constants 187or public constants in an unfinalized SDK. 188 189#### SDK-dependent reflection {#sdk-reflection} 190 191Note: The 192[BanUncheckedReflection](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:lint-checks/src/main/java/androidx/build/lint/BanUncheckedReflection.kt) 193lint check detects disallowed usages of reflection. 194 195Starting in API level 28, the platform restricts which 196[non-SDK interfaces](https://developer.android.com/distribute/best-practices/develop/restrictions-non-sdk-interfaces) 197can be accessed via reflection by apps and libraries. As a general rule, you 198will **not** be able to use reflection to access hidden APIs on devices with 199`SDK_INT` greater than `Build.VERSION_CODES.P` (28). 200 201In cases where a hidden API is a constant value, **do not** inline the value. 202Hidden APIs cannot be tested by CTS and carry no stability guarantees. 203 204Per go/platform-parity, on earlier devices or in cases where an API is marked 205with `@UnsupportedAppUsage`, reflection on hidden platform APIs is allowed 206**only** when an alternative public platform API exists in a later revision of 207the Android SDK. For example, the following implementation is allowed: 208 209```java 210public AccessibilityDelegate getAccessibilityDelegate(View v) { 211 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { 212 // Retrieve the delegate using a public API. 213 return v.getAccessibilityDelegate(); 214 } else if (Build.VERSION.SDK_INT >= 11) { 215 // Retrieve the delegate by reflecting on a private field. If the 216 // field does not exist or cannot be accessed, this will no-op. 217 if (sAccessibilityDelegateField == null) { 218 try { 219 sAccessibilityDelegateField = View.class 220 .getDeclaredField("mAccessibilityDelegate"); 221 sAccessibilityDelegateField.setAccessible(true); 222 } catch (Throwable t) { 223 sAccessibilityDelegateCheckFailed = true; 224 return null; 225 } 226 } 227 try { 228 Object o = sAccessibilityDelegateField.get(v); 229 if (o instanceof View.AccessibilityDelegate) { 230 return (View.AccessibilityDelegate) o; 231 } 232 return null; 233 } catch (Throwable t) { 234 sAccessibilityDelegateCheckFailed = true; 235 return null; 236 } 237 } else { 238 // There is no way to retrieve the delegate, even via reflection. 239 return null; 240 } 241``` 242 243Calls to public APIs added in pre-release revisions *must* be gated using 244`BuildCompat`: 245 246```java 247if (BuildCompat.isAtLeastQ()) { 248 // call new API added in Q 249} else if (Build.SDK_INT.VERSION >= 23) { 250 // make a best-effort using APIs that we expect to be available 251} else { 252 // no-op or best-effort given no information 253} 254``` 255 256#### Shadowing platform classes {#sdk-shadowing} 257 258Generally, libraries should **never** create new classes in the `android.*` 259namespace or re-define any classes that may be present in the boot classpath. 260**Do not** create a library class with the same fully-qualified name as one in 261the platform SDK, a Mainline module, sidecar JAR, or another library. Keep all 262classes within your own package based on your Maven group ID. 263 264The reverse also applies: the platform SDK, Mainline modules, sidecar JARs, and 265other libraries **must not** define classes in the `androidx.*` namespace. 266 267In *extremely limited* cases, the overhead of reflecting on a platform class may 268cause performance issues for apps on a scale that warrants using a compile-only 269stub of the platform class to avoid reflection. Any instances of this **must** 270be approved by Jetpack Working Group before submitting the change. 271 272### Inter-process communication {#ipc} 273 274Protocols and data structures used for IPC must support interoperability between 275different versions of libraries and should be treated similarly to public API. 276 277**Do not** design your own serialization mechanism or wire format for disk 278storage or inter-process communication. Preserving and verifying compatibility 279is difficult and error-prone. 280 281**Do not** expose your serialization mechanism in your API surface. Neither 282Stable AIDL nor Protobuf generate stable language APIs. 283 284Generally, any communication prototcol, handshake, etc. must maintain 285compatibility consistent with SemVer guidelines. Consider how your protocol will 286handle addition and removal of operations or constants, compatibility-breaking 287changes, and other modifications without crashing either the host or client 288process. 289 290We recommend the following IPC mechanisms, in order of preference: 291 292#### Stable AIDL <a name="ipc-stableaidl"></a> 293 294Stable AIDL is used by the Android platform and AndroidX to provide a 295platform-native IPC mechanism with strong inter-process compatibility 296guarantees. It supports a subset of standard AIDL. 297 298Use Stable AIDL if your library: 299 300- Needs to send and receive Android's `Parcelable` data types 301- Communicates directly with the Android platform, System UI, or other AOSP 302 components *or* is likely to do so in the future 303 304**Do not** use Stable AIDL to persist data to disk. 305 306##### Using Stable AIDL {#ipc-stableaidl-using} 307 308To add Stable AIDL definitions to your project: 309 3101. Add the Stable AIDL plugin to `build.gradle`: 311 312 ``` 313 plugins { 314 id("androidx.stableaidl") 315 } 316 ``` 317 3182. Enable the AIDL build feature and specify an initial version for your Stable 319 AIDL interfaces in `build.gradle`: 320 321 ``` 322 android { 323 buildFeatures { 324 aidl = true 325 } 326 buildTypes.all { 327 stableAidl { 328 version 1 329 } 330 } 331 } 332 ``` 333 3343. Migrate existing AIDL files or create new AIDL files under 335 `<project>/src/main/stableAidl` 336 3374. Generate an initial set of Stable AIDL API tracking files by running 338 339 ``` 340 ./gradlew :path:to:project:updateAidlApi 341 ``` 342 343##### Annotating unstable AIDL {#ipc-stableaidl-unstable} 344 345Once an API that relies on an IPC contract ships to production in an app, the 346contract is locked in and must maintain compatibility to prevent crashing either 347end of an inter-process communication channel. 348 349Developers **should** annotate unstable IPC classes with a `@RequiresOptIn` 350annotation explaining that they must not be used in production code. Libraries 351**must not** opt-in to these annotations when such classes are referenced 352internally, and should instead propagate the annotations to public API surfaces. 353 354A single annotation for this purpose may be defined per library or atomic group: 355 356```java 357/** 358 * Parcelables and AIDL-generated classes bearing this annotation are not 359 * guaranteed to be stable and must not be used for inter-process communication 360 * in production. 361 */ 362@RequiresOptIn 363public @interface UnstableAidlDefinition {} 364``` 365 366Generally speaking, at this point in time no libraries should have unstable 367`Parcelable` classes defined in source code, but for completeness: 368 369```java 370@UnstableAidlDefinition 371public class ResultReceiver implements Parcelable { ... } 372``` 373 374AIDL definition files under `src/aidl` should use `@JavaPassthrough` with a 375fully-qualified class name to annotate generated classes: 376 377```java 378@JavaPassthrough(annotation="@androidx.core.util.UnstableAidlDefinition") 379oneway interface IResultReceiver { 380 void send(int resultCode, in Bundle resultData); 381} 382``` 383 384For Stable AIDL, the build system enforces per-CL compatibility guarantees. No 385annotations are required for Stable AIDL definition files under 386`src/stableAidl`. 387 388#### Protobuf <a name="ipc-protobuf"></a> 389 390Protobuf is used by many Google applications and services to provide an IPC and 391disk persistence mechanism with strong inter-process compatibility guarantees. 392 393Use Protobuf if your library: 394 395- Communicates directly with other applications or services already using 396 Protobuf 397- Your data structure is complex and likely to change over time - Needs to 398 persist data to disk 399 400If your data includes `FileDescriptor`s, `Binder`s, or other platform-defined 401`Parcelable` data structures, consider using Stable AIDL instead. Protobuf 402cannot directly handle these types, and they will need to be stored alongside 403the serialized Protobuf bytes in a `Bundle`. 404 405See [Protobuf](#dependencies-protobuf) for more information on using protocol 406buffers in your library. 407 408WARNING While Protobuf is capable of maintaining inter-process compatibility, 409AndroidX does not currently provide compatibility tracking or enforcement. 410Library owners must perform their own validation. 411 412NOTE We are currently investigating the suitability of Square's 413[`wire` library](https://github.com/square/wire) for handling protocol buffers 414in Android libraries. If adopted, it will replace `proto` library dependencies. 415Libraries that expose their serialization mechanism in their API surface *will 416not be able to migrate*. 417 418#### Bundle <a name="ipc-bundle"></a> 419 420`Bundle` is used by the Android platform and AndroidX as a lightweight IPC 421mechanism. It has the weakest type safety and compatibility guarantees of any 422recommendation, and it has many caveats that make it a poor choice. 423 424In some cases, you may need to use a `Bundle` to wrap another IPC mechanism so 425that it can be passed through Android platform APIs, e.g. a `Bundle` that wraps 426a `byte[]` representing a serialized Protobuf. 427 428Use `Bundle` if your library: 429 430- Has a very simple data model that is unlikely to change in the future 431- Needs to send or receive `Binder`s, `FileDescriptor`s, or platform-defined 432 `Parcelable`s 433 ([example](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:core/core/src/main/java/androidx/core/graphics/drawable/IconCompat.java;l=820)) 434 435Caveats for `Bundle` include: 436 437- When running on Android S and below, accessing *any* entry in a `Bundle` 438 will result in the platform attempting to deserialize *every* entry. This 439 has been fixed in Android T and later with "lazy" bundles, but developers 440 should be careful when accessing `Bundle` on earlier platforms. If a single 441 entry cannot be loaded -- for example if a developer added a custom 442 `Parcelable` that doesn't exist in the receiver's classpath -- an exception 443 will be thrown when accessing *any* entry. 444- On all platforms, library code that receives `Bundle`s data from outside the 445 process **must** read the data defensively. See previous note regarding 446 additional concerns for Android S and below. 447- On all platforms, library code that sends `Bundle`s outside the process 448 *should* discourage clients from passing custom `Parcelable`s. 449- `Bundle` provides no versioning and Jetpack provides no affordances for 450 tracking the keys or value types associated with a `Bundle`. Library owners 451 are responsible for providing their own system for guaranteeing wire format 452 compatibility between versions. 453 454#### Versioned Parcelable <a name="ipc-versionedparcelable"></a> 455 456`VersionedParcelable` is a deprecated library that was intended to provide 457compatibility guarantees around the Android platform's `Parcelable` class; 458however, the initial version contained bugs and it was not actively maintained. 459 460Use `VersionedParcelable` if your library: 461 462- Is already using `VersionedParcelable` and you are aware of its 463 compatibility constraints 464 465**Do not** use `VersionedParcelable` in all other cases. 466 467#### Wire <a name="ipc-wire"></a> 468 469We are currently evaluating Square's [Wire](https://github.com/square/wire) as a 470front-end to Protobuf. If this library meets your team's needs based on your own 471research, feel free to use it. 472 473#### gRPC <a name="ipc-grpc"></a> 474 475Some clients have requested to use Google's [gRPC](https://grpc.io/) library to 476align with other Google products. It's okay to use gRPC for network 477communication or communication with libraries and services outside of AndroidX 478that are already using gRPC. 479 480**Do not** use gRPC to communicate between AndroidX libraries or with the 481Android platform. 482 483#### Parcelable <a name="ipc-parcelable"></a> 484 485**Do not** implement `Parcelable` for any class that may be used for IPC or 486otherwise exposed as public API. By default, `Parcelable` does not provide any 487compatibility guarantees and will result in crashes if fields are added or 488removed between library versions. If you are using Stable AIDL, you *may* use 489AIDL-defined parcelables for IPC but not public API. 490