Android Audio Flagging Guide

This directory contains the aconfig flag definitions and the associated build files for Android Audio features.

Build Configuration

Namespaces

There are several global namespaces involved with flagging which must be maintained:

  • Mendel project: media_audio. Associated with mdb group and used by gantry. All aconfig flags in this namespace should be defined in this directory, and all flags defined in this directory should belong to this namespace.
  • Blueprint namespaces: the name field of any module is unique over a particular build namespace (for platform development purposes there is one).
  • Aconfig package: All flags generated by aconfig are converted by Halyard into Mendel flags of the form {PACKAGE}{FLAG}, where both fields are stripped of spaces, underscores and dashes. This combo must be globally unique (across Android).
  • Generated code: Both java and cpp aconfig library rules generate code which pollutes the namespace of the backend. This means that if two aconfig java libraries exist which consume any aconfig_declarations with packages which intersect, these libraries are incompatible to be linked together (and equivalent in cc). If the library is included in the framework, it is on the bootclasspath, so no other library for that package can exist. Additionally, the cpp backend generates header files with the same name as the unqualified name of the package which means include path conflicts are possible.

Naming convention

Given above, follow the following naming convention (given a {PACKAGE}).

Generally, aconfig_declarations and {backend}_aconfig_library should be 1-1, except in cases where different package configurations are needed with pre-processing with different classpaths. This is because multiple libraries with the same aconfig package cannot compile together.

aconfig_declarations {
    name: "{PACKAGE}-aconfig",
    package: "{PACKAGE}",
    srcs: ["{PACKAGE}.aconfig"]
}

{backend}_aconfig_library {
    name: "{PACKAGE}-aconfig-{backend}",
    aconfig_declarations: "{PACKAGE}-aconfig",
    defaults: ["audio-aconfig-{backend}-defaults"],
}

If the flags are required as part of the framework, the java backend is ingested into the framework via srcjar for cyclic dependency/simplicity reasons. Add the following

    ":{PACKAGE}-aconfig-java{.generated_srcjars}"

as a source in audio-framework-aconfig. This target is included in the file-list which compiles the framework. If a lib is included in this list, it is unecessary to add it in any other blueprint file for platform code (for non-host). For tests, the library should be statically linked (which shadows the bootclasspath), to get around @hide restrictions (this approach may change).

Visibility

Visibility should be controlled (private) so that flag cleanup remains maintainable by the team. This constraints the locations where extant flags need to be chased for removal.

Packaging preference

As a rule, prefer the most constrained package appropriate for a particular flag. This limits viral dependencies on a flag library which restricts where/how testing can occur. It also speeds up incremental rebuilds. Packages which end up in the framework should be avoided as much as possible, since they require rebuilding the framework, pollute the bootclasspath, and require special build handling to operate (srcjar generation, jarjar to access in CTS).

Utilize "java-like" package naming for all packages, to ensure packages are globally unique, and to avoid class conflicts if these flags are later included in java code.

Workflow

Usage Style

In general, prefer flags at the integration level, rather than at the implementation level. This allows flags to be queried in as few locations as possible such as by (parametrizing functions, classes, types, etc.) It also allows callers to make different decisions on when behavior can/should be changed (e.g. different flags could be used for different libraries). This follows dependency injection principles and ensures that global state has a smaller blast radius, and also makes it easier to test code by relying less on infrastructure. The more places a flag is queried, the more its implementation should be deferred upwards, either by the injection interfaces, or by explicit parametrization.

Flag scope

In general, flags should be scoped as precisely as possible while retaining orthogonality. If flags are too specific (multiple flags interact together to encompass a single change), then reasoning about independently switching flags becomes difficult. Other than all-on and all-of configurations, there is limited soak for flag combinations, so it is critical that flags interact with each other minimally (although emergent properties are inevitable). However, if flags are too general, graduating/reverting flags can carry too much behavior change, the build graph degenerates, and

Instant Staging

In general, it is recommended that the moment a flag is added to the tree, it should be moved to staging on gantry. There is no benefit to adding a flag but not moving into staging. This allows:

  1. Verification by treehugger in both pre/post-submit
  2. Automatic configuration for feature development for local engineering workflows
  3. Integration with other engineering features in development

FlaggedApi

FlaggedApi operates differently than other types of flags since these flags only graduate at the next API bump. These flags also must be exposed through the framework. FlaggedApis should only operate over the same set of flags as an implementation if the implementation is entirely related to the feature. Given the constraints on the flag lifetime, it is preferable to use a "regular" flag for implementation, which can integrate/soak/ship/clean-up faster.

Additionally, unlike "regular" flags, @FlaggedApis are not effectively soaked, so like non-trunk API development, they are heavily reliant on CTS to integrate.

On non-next configs, @FlaggedApi has no runtime control, but it is undefined for callers to call a FlaggedApi without querying the status of the flag. The build itself is not changed on staging. Anything which builds against the platform can see all @FlaggedApis.

Methods annotated FlaggedApis are changed for release finalization -- if an API is not in next, it is stripped from the Sdk (and left @hide). If an API graduated to next, it is included fully included in the Sdk, and the @FlaggedApi annotation is stripped.

TestApis

TestApis do not require flagging, since their existence in the tree implies that they should be accessible to callers (xTS not building on trunk enables this).

Api Changes

Currently, the flag infra does not support any type of Api modification (arguments, annotation, renaming, deletion, etc.) In any of these cases (including for SystemApi), exceptions will need to be granted.