Android API Guidelines

This document is intended to be a guide for developers to understand the general principles that the API Council enforces in API reviews.

In addition to following these guidelines when writing APIs, developers should run the API Lint tool, which encodes many of these rules in checks that it runs against APIs.

Think of this as the guide to the rules that are obeyed by that Lint tool, plus general advice on rules that cannot be easily codified into that tool.

API Lint tool

API Lint is integrated into the Metalava static analysis tool and runs as part of the platform build. You can run it manually from an AOSP platform checkout using m checkapi. To run the tool from a Jetpack checkout, run the ./gradlew checkApi task.

API rules

One of the difficulties in concrete rules is applying them to a platform that was developed without strict guidelines from the beginning, so some of the existing APIs may not adhere. In some cases, the right choice might be to go with what is consistent with APIs in the same general area of the code, rather than in the ideal rules laid out herein.

The rules are a work in progress and will be added to in the future as other patterns emerge from future API reviews.

API basics

This category pertains to the core aspects of an Android API.

All APIs must be implemented

Irrespective of an API's audience (public, @SystemApi, etc.), all API surfaces must be implemented when merged or exposed as API. Merging API stubs with implementation to come at a later date would be a violation of this guideline.

Unimplemented API surfaces have multiple issues:

  • There is no guarantee that a proper or complete surface has been exposed. Until an API is tested or used by clients, there is no way to verify a client has the appropriate APIs to be able to use the feature.
  • APIs without implementation cannot be tested in Developer Previews
  • APIs without implementation cannot be tested in CTS
  • By definition, APIs without implementation are not IC complete

All APIs must be tested

This is in line with CTS requirements and implementation expectation.

Testing API surfaces provides a base guarantee that the API surface is usable and we've exposed all the necessary aspects. Testing for existence is not sufficient; the API functionality itself must be tested.

A change that adds a new API should include corresponding CTS tests in the same Gerrit topic. This is enforced by go/api-test-coverage presubmit.

APIs should also be testable. You should be able to answer the question, “How will an app developer test code that uses your API?”

All APIs must be documented

Documentation is a key part of API usability. While the syntax of an API surface may seem obvious, any new clients will not understand the semantics, behavior, or context behind the API.

All platform APIs must be associated with a flag

All platform APIs must be associated with a feature flag using the @FlaggedApi annotation.

Note: For FAQ on API flagging, please visit FAQ on API Flagging page.

All generated APIs must be compliant with the guidelines

APIs generated by tools must follow the API guidelines just the same as hand-written code.

Tools that are discouraged for generating APIs:

Coding style [S]

This category pertains to the general coding style that developers should use, especially in the public API.

Follow standard Java coding conventions, except where noted

Android's platform coding conventions are documented for external contributors here:

https://source.android.com/source/code-style.html

Overall, we tend to follow standard Java coding conventions.

Please also review the Kotlin-Java interop guide for best practices related to writing Kotlin-friendly APIs in Java. Some of these guidelines are reflected in the recommendations on this site; however, the link may contain slightly newer information that has not yet been propagated.

Acronyms should not be capitalized in method names

For example: method name should be runCtsTests and not runCTSTests.

Names shouldn't end with Impl

This exposes implementation details, avoid that.

Services

Handling Intents in system-bound developer services

Services that are intended to be extended by the developer and bound by the system, for example abstract services like NotificationListenerService, may respond to an Intent action from the system. Such services should meet the following criteria:

  1. Define a SERVICE_INTERFACE string constant on the class containing the fully-qualified class name of the service. This constant must be annotated with @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION).
  2. Document on the class that a developer must add an <intent-filter> to their AndroidManifest.xml in order to receive Intents from the platform.
  3. Strongly consider adding a system-level permission to prevent rogue apps from sending Intents to developer services.

Kotlin-Java interop

See the official Android Kotlin-Java interop guide for a full list of guidelines. Select guidelines have been copied to this guide to improve discoverability.

API visibility

Some Kotlin APIs, like suspend funs, aren't intended to be used by Java developers; however, do not attempt to control language-specific visibility using @JvmSynthetic as it has side-effects on how the API is presented in debuggers that make debugging more difficult.

Please see the Kotlin-Java interop guide or Async guide for specific guidance.

Companion objects

Kotlin uses companion object to expose static members. In some cases, these will show up from Java on an inner class named Companion rather than on the containing class. Companion classes may show as empty classes in API text files -- that is working as intended.

To maximize compatibility with Java, annotate companion objects' non-const fields with @JvmField and public functions with @JvmStatic to expose them directly on the containing class.

companion object {
  @JvmField val BIG_INTEGER_ONE = BigInteger.ONE
  @JvmStatic fun fromPointF(pointf: PointF) {
    /* ... */
  }
}

XML schemas

If an XML schema serves as a stable interface between components, that schema must be explicitly specified and must evolve in a backward-compatible manner, similar to other Android APIs. For example, the structure of XML elements and attributes must be preserved similar to how methods and variables are maintained on other Android API surfaces.

NOTE API Council does not explicitly review XML schemas and AOSP does not have tooling to automatically ensure compatibility for XML schemas or parsing. Proceed with caution.

Deprecation best-practices

If you'd like to deprecate an XML element or attribute, you can add the xs:annotation marker, but you must continue to support any existing XML files by following the typical @SystemApi evolution lifecycle.

<xs:element name="foo">
    <xs:complexType>
        <xs:sequence>
            <xs:element name="name" type="xs:string">
                <xs:annotation name="Deprecated"/>
            </xs:element>
        </xs:sequence>
    </xs:complexType>
</xs:element>

Element types must be preserved

Schemas support the sequence element, choice element and all elements as child elements of complexType element. However, these child elements differ in the number and order of their child elements, so modifying an existing type would be an incompatible change.

If you want to modify an existing type, the best-practice is to deprecate the old type and introduce a new type to replace it.

<!-- Original "sequence" value -->
<xs:element name="foo">
    <xs:complexType>
        <xs:sequence>
            <xs:element name="name" type="xs:string">
                <xs:annotation name="Deprecated"/>
            </xs:element>
        </xs:sequence>
    </xs:complexType>
</xs:element>

<!-- New "choice" value -->
<xs:element name="fooChoice">
    <xs:complexType>
        <xs:choice>
            <xs:element name="name" type="xs:string"/>
        </xs:choice>
    </xs:complexType>
</xs:element>