Data Types

Given a HIDL interface file, the Java HIDL backend generates Java interfaces, Stub, and Proxy code. It supports all scalar HIDL types ([u]int{8,16,32,64}_t, float, double, and enums), as well as strings, interfaces, safe_union types, struct types, and arrays and vectors of supported HIDL types. The Java HIDL backend does NOT support union types or fmq types. Android 11 adds support for the memory and handle types.

As Java runtime does not support the concept of unsigned integers natively, all unsigned types (and enums based on them) are silently treated as their signed equivalents, i.e. uint32_t becomes an int in the Java interface. No value conversion is performed; the implementor on the Java side must use the signed values as if they were unsigned.

Enums

Enums do not generate Java enum classes but are instead translated into inner classes containing a static constant definition for each enum case. If the enum class derives from some other enum class, it inherits that class's storage type. Enumerations based on an unsigned integer type are rewritten into their signed equivalent. Since the underlying type is a primitive, the default value for enum fields/variables is zero even when there is no zero enumerator.

For example, a SomeBaseEnum with a type of uint8_t:

enum SomeBaseEnum : uint8_t { foo = 3 };
enum SomeEnum : SomeBaseEnum {
    quux = 33,
    goober = 127
};

… becomes:

public final class SomeBaseEnum { public static final byte foo = 3; }
public final class SomeEnum {
    public static final byte foo = 3;
    public static final byte quux = 33;
    public static final byte goober = 127;
}

And:

enum SomeEnum : uint8_t {
    FIRST_CASE = 10,
    SECOND_CASE = 192
};

… is rewritten as:

public final class SomeEnum {
    static public final byte FIRST_CASE  = 10;  // no change
    static public final byte SECOND_CASE = -64;
}

Strings

Strings in Java are utf-8 or utf-16 but are converted to utf-8 as the common HIDL type when transported. Additionally, a String must not be null when passed into HIDL.

Handle and memory

Android 11 introduces Java support for the handle and memory types. They are translated into android.os.NativeHandle and android.os.HidlMemory, respectively. A null handle is considered valid, while a null memory is not.

In the generated server code, received memory and handle arguments are only valid within the scope of the method invocation. If the server implementation wants to extend their lifetime, they must be duplicated using their resepective dup() methods. The returned instance can be used beyond the method invocation and should be properly closed when done with.

In the generated client code, handles and memory instances that are sent as input arguments of the called method don't need to be duplicated nor kept valid after the method returns. However, handles and memory instances that are received as output arguments are automatically duplicated by the autogenerated code and must be properly closed when done with. This is true whether those return arguments appear as return values of the method (in the single return value case) or using the synchronous callback style (used in the multiple return value case).

For more information about duplication and closing, see the documentation of the Java classes.

Arrays and vectors

Arrays are translated into Java arrays and vectors are translated into ArrayList<T> where T is the appropriate object type, possibly wrapping scalar types such as vec<int32_t> => ArrayList<Integer>). For example:

takeAnArray(int32_t[3] array);
returnAVector() generates (vec<int32_t> result);

… becomes:

void takeAnArray(int[] array);
ArrayList<Integer> returnAVector();

Structures

Structures are translated into Java classes with a similar layout. For example:

struct Bar {
 vec<bool> someBools;
};
struct Foo {
 int32_t a;
 int8_t b;
 float[10] c;
 Bar d;
};

… becomes:

class Bar {
 public final ArrayList<Boolean> someBools = new ArrayList();
};
class Foo {
 public int a;
 public byte b;
 public final float[] c = new float[10];
 public final Bar d = new Bar();
}

Declared types

Each top-level type declared in types.hal gets its own .java output file (as required by Java). For example, the following types.hal file results in two extra files being created (Foo.java and Bar.java):

struct Foo {
 ...
};

struct Bar {
 ...

 struct Baz {
 };

 ...
};

The definition of Baz lives in a static inner class of Bar (in Bar.java).