AIDL supports annotations that give the AIDL compiler extra info about the annotated element, which also affects the generated stub code.
The syntax is similar to that of Java:
@AnnotationName(argument1=value, argument2=value) AidlEntity
Here, AnnotationName
is the name of the annotation, and AidlEntity
is an AIDL entity like interface Foo
, void method()
, or int arg
. An annotation is attached to the entity that follows it.
Some annotations can have arguments set inside the parentheses, as shown above. Annotations that don't have an argument don't need the parenthesis. For example:
@AnnotationName AidlEntity
These annotations are not the same as the Java annotations, although they look very similar. Users can't define custom AIDL annotations; the annotations are all predefined. Some annotations affect only a certain backend and are no-op in other backends. They have different restrictions where they can be attached to.
Here is the list of predefined AIDL annotations:
Annotations | Added in Android version |
---|---|
nullable | 7 |
utf8InCpp | 7 |
VintfStability | 11 |
UnsupportedAppUsage | 10 |
Hide | 11 |
Backing | 11 |
NdkOnlyStableParcelable | 14 |
JavaOnlyStableParcelable | 11 |
JavaDerive | 12 |
JavaPassthrough | 12 |
FixedSize | 12 |
Descriptor | 12 |
nullable
nullable
declares that the value of the annotated entity may not be provided.
This annotation can only be attached to method return types, method parameters, and parcelable fields.
interface IFoo { // method return types @nullable Data method(); // method parameters void method2(in @nullable Data d); } parcelable Data { // parcelable fields @nullable Data d; }
Annotations can't be attached to primitive types. The following is an error.
void method(in @nullable int a); // int is a primitive type
This annotation is no-op for the Java backend. This is because, in Java, all non-primitive types are passed by reference, which could be null
.
In the CPP backend, @nullable T
maps to std::unique_ptr<T>
in Android 11 or lower, and to std::optional<T>
in Android 12 or higher.
In the NDK backend, @nullable T
always maps to std::optional<T>
.
In the Rust backend, @nullable T
always maps to Option<T>
.
For a list-like type L
such as T[]
or List<T>
, @nullable L
maps to std::optional<std::vector<std::optional<T>>>
(or std::unique_ptr<std::vector<std::unique_ptr<T>>>
in case of the CPP backend for Android 11 or lower).
There is an exception to this mapping. When T
is IBinder
or an AIDL interface, @nullable
is no-op for all backends except for Rust. In other words, both @nullable IBinder
and IBinder
equally map to android::sp<IBinder>
, which is already nullable because it is a strong pointer (CPP reads still enforce nullability, but the type is still android::sp<IBinder>
). In Rust, these types are nullable
only if annotated with @nullable
. They map to Option<T>
if annotated.
Beginning with Android 13, @nullable(heap=true)
can be used for parcelable fields to model recursive types. @nullable(heap=true)
can't be used with method parameters or return types. When annotated with it, the field is mapped to a heap-allocated reference std::unique_ptr<T>
in the CPP/NDK backends. @nullable(heap=true)
is no-op in the Java backend.
utf8InCpp
utf8InCpp
declares that a String
is represented in UTF8 format for the CPP backend. As its name indicates, the annotation is a no-op for other backends. Specifically, String
is always UTF16 in the Java backend and UTF8 in the NDK backend.
This annotation can be attached anywhere the String
type can be used, including return values, parameters, constant declarations, and parcelable fields.
For the CPP backend, @utf8InCpp String
in AIDL maps to std::string
, whereas String
without the annotation maps to android::String16
where UTF16 is used.
Note that the existence of the utf8InCpp
annotation doesn't change the way strings are transmitted over the wire. Strings are always transmitted as UTF16 over the wire. A utf8InCpp
annotated string is converted to UTF16 before it is transmitted. When a string is received, it is converted from UTF16 to UTF8 if it was annotated as utf8InCpp
.
VintfStability
VintfStability
declares that a user-defined type (interface, parcelable, and enum) can be used across the system and vendor domains. See AIDL for HALs for more about system-vendor interoperability.
The annotation doesn't change the signature of the type, but when it is set, the instance of the type is marked as stable so that it can travel across the vendor and system processes.
The annotation can only be attached to user-defined type declarations as shown here:
@VintfStability interface IFoo { .... } @VintfStability parcelable Data { .... } @VintfStability enum Type { .... }
When a type is annotated with VintfStability
, any other type that is referenced in the type should also be annotated as such. In the following example, Data
and IBar
should both be annotated with VintfStability
.
@VintfStability interface IFoo { void doSomething(in IBar b); // references IBar void doAnother(in Data d); // references Data } @VintfStability // required interface IBar {...} @VintfStability // required parcelable Data {...}
In addition, the AIDL files defining types annotated with VintfStability
can only be built using the aidl_interface
Soong module type, with the stability
property set to "vintf"
.
aidl_interface { name: "my_interface", srcs: [...], stability: "vintf", }
UnsupportedAppUsage
The UnsupportedAppUsage
annotation denotes that the annotated AIDL type is part of the non-SDK interface that has been accessible for legacy apps. See Restrictions on non-SDK interfaces for more information about the hidden APIs.
The UnsupportedAppUsage
annotation doesn't affect the behavior of the generated code. The annotation only annotates the generated Java class with the Java annotation of the same name.
// in AIDL @UnsupportedAppUsage interface IFoo {...} // in Java @android.compat.annotation.UnsupportedAppUsage public interface IFoo {...}
This is a no-op for non-Java backends.
Backing
The Backing
annotation specifies the storage type of an AIDL enum type.
@Backing(type="int") enum Color { RED, BLUE, }
In the CPP backend, this emits a C++ enum class of type int32_t
.
enum class Color : int32_t { RED = 0, BLUE = 1, }
If the annotation is omitted, the type
is assumed to be byte
, which maps to int8_t
for the CPP backend.
The type
argument can be set only to the following integral types:
byte
(8-bit wide)int
(32-bit wide)long
(64-bit wide)
NdkOnlyStableParcelable
NdkOnlyStableParcelable
marks a parcelable declaration (not definition) as stable so that it can be referenced from other stable AIDL types. This is like JavaOnlyStableParcelable
, but NdkOnlyStableParcelable
marks a parcelable declaration as stable for the NDK backend instead of for Java.
To use this parcelable:
- You must specify
ndk_header
. - You must have an NDK library specifying the parcelable and the library must be compiled into the library. For example, in the core build system on a
cc_*
module, usestatic_libs
orshared_libs
. Foraidl_interface
, add the library underadditional_shared_libraries
inAndroid.bp
.
JavaOnlyStableParcelable
JavaOnlyStableParcelable
marks a parcelable declaration (not definition) as stable so that it can be referenced from other stable AIDL types.
Stable AIDL requires that all user-defined types are stable. For parcelables, being stable requires that its fields are explicitly described in the AIDL source file.
parcelable Data { // Data is a structured parcelable. int x; int y; } parcelable AnotherData { // AnotherData is also a structured parcelable Data d; // OK, because Data is a structured parcelable }
If the parcelable was unstructured (or just declared), then it can't be referenced.
parcelable Data; // Data is NOT a structured parcelable parcelable AnotherData { Data d; // Error }
JavaOnlyStableParcelable
lets you to override the check when the parcelable you are referencing is already safely available as part of the Android SDK.
@JavaOnlyStableParcelable parcelable Data; parcelable AnotherData { Data d; // OK }
JavaDerive
JavaDerive
automatically generates methods for parcelable types in the Java backend.
@JavaDerive(equals = true, toString = true) parcelable Data { int number; String str; }
The annotation requires additional parameters to control what to generate. The supported parameters are:
equals=true
generatesequals
andhashCode
methods.toString=true
generatestoString
method that prints the name of the type and fields. For example:Data{number: 42, str: foo}
JavaDefault
JavaDefault
, added in Android 13, controls whether the default implementation versioning support is generated (for setDefaultImpl
). This support is no longer generated by default in order to save space.
JavaPassthrough
JavaPassthrough
lets the generated Java API be annotated with an arbitrary Java annotation.
The following annotations in AIDL
@JavaPassthrough(annotation="@android.annotation.Alice") @JavaPassthrough(annotation="@com.android.Alice(arg=com.android.Alice.Value.A)")
become
@android.annotation.Alice @com.android.Alice(arg=com.android.Alice.Value.A)
in the generated Java code.
The value of the annotation
parameter is directly emitted. The AIDL compiler doesn't look into the value of the parameter. If there is any Java-level syntax error, it won't be caught by the AIDL compiler but by the Java compiler.
This annotation can be attached to any AIDL entity. This annotation is a no-op for non-Java backends.
RustDerive
RustDerive
automatically implements traits for generated Rust types.
The annotation requires additional parameters to control what to generate. The supported parameters are:
Copy=true
Clone=true
Ord=true
PartialOrd=true
Eq=true
PartialEq=true
Hash=true
For explanations of these traits, see https://doc.rust-lang.org.
FixedSize
FixedSize
marks a structured parcelable as fixed size. Once marked, the parcelable won't be allowed to have new fields added to it. All fields of the parcelable must also be fixed sized types, including primitive types, enums, fixed-size arrays, and other parcelables marked with FixedSize
.
This doesn't provide any guarantee across different bitnesses and shouldn't be relied on for mixed-bitness communication.
Descriptor
Descriptor
forcibly specifies the interface descriptor of an interface.
package android.foo; @Descriptor(value="android.bar.IWorld") interface IHello {...}
The descriptor of this interface is android.bar.IWorld
. If the Descriptor
annotation is missing, the descriptor would be android.foo.IHello
.
This is useful for renaming an already published interface. Making the descriptor of the renamed interface the same as the descriptor of the interface before the renaming allows the two interfaces to talk to each other.
@hide in comments
The AIDL compiler recognizes @hide
in comments and passes it through to Java output for metalava to pickup. This comment ensures that the Android build system knows that AIDL APIs are not SDK APIs.
@deprecated in comments
The AIDL compiler recognizes @deprecated
in comments as a tag to identify an AIDL entity that should no longer be used.
interface IFoo { /** @deprecated use bar() instead */ void foo(); void bar(); }
Each backend marks deprecated entities with a backend-specific annotation or attribute so that the client code is warned if it refers the deprecated entities. For example, the @Deprecated
annotation and the @deprecated
tag are attached to the Java generated code.