# Mojo Java Bindings API
This document is a subset of the [Mojo documentation](/mojo/README.md).
[TOC]
## Overview
The Mojo Java Bindings API leverages the [Java System API](/mojo/public/java/system/README.md)
to provide a more natural set of primitives for communicating over Mojo message
pipes. Combined with generated code from the [Mojom IDL and bindings generator](/mojo/public/tools/bindings/README.md),
users can easily connect interface clients and implementations across arbitrary
intra- and inter-process boundaries.
This document provides a brief guide to Java Bindings API usage with example
code snippets. Some foundational knowledge of Mojo and mojom interface
definitions is assumed. Familiarity with the [C++ bindings](/mojo/public/cpp/bindings/README.md)
may be useful for comparison but isn't strictly required. For detailed API
references you can consult the class definitions in [this directory](https://cs.chromium.org/chromium/src/mojo/public/java/bindings/src/org/chromium/mojo/bindings/).
# Should I or should I not use the Java bindings?
Java bindings typically only make sense on the browser side and for Android
devices where there is some service that's unique to Android. As such, Java
bindings are typically rarely used, but they are used sometimes. When
implementing a cross-platform feature or service, it's probably more appropriate
to use C++ Mojo bindings and (if necessary) plumb the platform-specific code
implemented in Java via JNI.
C++ bindings have more features than the Java bindings. In simple cases, Java
bindings provide enough functionality to do everything by themselves. However,
for complex usage, it may be necessary to instead use the C++ bindings and plumb
data through JNI. See the section on notable lacking features below.
However, working within Java can avoid boilerplate and complex memory management
between C++ and Java.
If your service doesn't really need to be implemented in Java (because it
doesn't depend on or benefit from any Java-exclusive APIs), you should probably
just write the whole thing in C++.
Implementing Mojo interfaces in Java is subject to the same Chromium IPC
[practices](/docs/security/mojo.md#Security) and [review](/docs/security/ipc-reviews.md)
processes. However, most reviewers are used to reviewing C++ code.
# Notable lacking features
This is not an exhaustive list.
## AssociatedInterfaces
These are not supported.
## ReportBadMessage
There is no way for Java to report a bad message, which is the standard practice
when the renderer sends a message that indicates it's misbehaving (and perhaps
compromised).
The best you can do if you receive a bad message is to just ignore the
request. However, try to close any Proxy objects or InterfaceRequests that might
not otherwise have needed closing.
If the IPC call usually expects a response (via a callback), but the callback
has not been called and the callback gets garbage collected, this will lead to
the Mojo connection being disconnected. The involvement of the garbage collector
makes this somewhat non-deterministic, so this behavior should not be relied
upon.
## Missing type conversions (StructTraits)
There are a few non-primitive but common standard library-like mojom types. The
C++ bindings allow the definition of custom conversions between C++ and mojom
types via StructTraits. The auto-generated Java interfaces don't implement these
custom conversions as they don't support StructTraits, so you don't get the
obvious conversions between these mojom and Java types. This isn't necessarily a
deal-breaker though, as you can still implement the conversions yourself (with
care).
An example is `String16`, representing a UTF-16 string. For `String16`, C++ can
convert transparently to/from `std::u16string`. However, in Java, this merely
produces something which instead resembles the underlying mojom struct - which
just contains a `short[]` array.
## Synchronous calls
You cannot perform synchronous method calls _from_ Java. However, Java can still
receive sync calls (as `[Sync]` annotations only affect the behavior on the
caller side). In general, you shouldn't need to make synchronous calls as they
have many [disadvantages](/mojo/public/cpp/bindings/README.md#think-carefully-before-you-decide-to-use-sync-calls).
# Key differences compared to C++ bindings
The Java bindings' APIs don't particularly use the terms "receiver" and
"remote". Instead, the terms "Interface" and "Proxy" might be used. However,
this document will often use a mixture of these terms, as "pending", "remotes",
and "receivers" are terms used in mojom.
In C++, you'll typically have a `pending_receiver<InterfaceType>` which you then
bind to an implementation of `InterfaceType`. In Java, this is an
`InterfaceRequest<InterfaceType>`, which can be bound to your implementation of
`InterfaceType`.
In C++, you typically have to hold on to the receiver for as long as you want it
alive, or deliberately use a "self-owned" receiver that stays alive as long as
the other side of the IPC connection needs it. In Java, Mojo will keep the
underlying IPC connection and your interface implementation (receiver) alive as
long as the other end stays alive. In practice, this behaves somewhat like
self-owned receivers.
In C++, there is generally a greater distinction between remotes and
pending_remotes. In Java, you will typically just deal with an
`InterfaceType.Proxy` object, which can be used as either a remote or
pending_remote interchangeably.
# Object and connection lifetimes
When implementing an IPC interface, you'll also need to implement a `close()`
method and an `onConnectionError()` method. The `close()` method will
automatically be called whenever the IPC disconnects (either gracefully or due
to an error). The `onConnectionError()` method will only be called (in addition
to `close()`) when the connection ends due to an error.
A Mojo connection that's bound to an implementation object will keep that object
alive. The connection is maintained by Mojo so long as the other (remote) end is
alive (bound or unbound). Note that calling the `close()` method on your
interface implementation yourself won't stop calls from coming in! In java, the
receiver generally isn't able to unilaterally end a connection cleanly.
A Proxy object will keep any connection it's bound to alive until you call
`close()` on it.
No longer needed Proxy objects and InterfaceRequests which are to be discarded
without binding should be closed. **Failing to do so can lead to resource leaks,
produce warnings, or lead to non-deterministic crashes**. Failing to close Proxy
objects may produce warnings in log output such as
`java.lang.IllegalStateException: Warning: Router objects should be explicitly closed when no longer required otherwise you may leak handles.`.
Failing to close InterfaceRequests may produce `Handle was not closed.`
warnings.
# Types
## Primitive types
| mojom type | non-nullable type | nullable type | Comment |
|------------|-------------------|---------------|---------|
| `bool` | `boolean` | `Boolean` | |
| `int8`, `uint8` | `byte` | `Byte` | Signed and unsigned[^1] |
| `int16`, `uint16` | `short` | `Short` | Signed and unsigned[^1] |
| `int32`, `uint32` | `int` | `Integer` | Signed and unsigned[^1] |
| `int64`, `uint64` | `long` | `Long` | Signed and unsigned[^1] |
| `string` | `String` | `String` | Character encoding[^2] |
| `array<T>` | `T[]` | `T[]` | |
| `array<T, N>` | `T[]` | `T[]` | |
| `map<S, T>` | `Map<S, T>` | `Map<S, T>` | |
| `handle` | `Handle` | `Handle` | |
| `handle<message_pipe>` | `MessagePipeHandle` | `MessagePipeHandle` | |
| `handle<shared_buffer>` | `SharedBufferHandle` | `SharedBufferHandle` | |
| `handle<data_pipe_producer>` | `DataPipe.ProducerHandle` | `DataPipe.ProducerHandle` | |
| `handle<data_pipe_consumer>` | `DataPipe.ConsumerHandle` | `DataPipe.ConsumerHandle` | |
| `pending_remote<T>` | `T` or `T.Proxy` | `T` or `T.Proxy` | Automatic conversions[^3] |
| `pending_receiver<T>` | `InterfaceRequest<T>` | `InterfaceRequest<T>` | |
| `pending_associated_remote<T>` | - | - | Unsupported[^4] |
| `pending_associated_receiver<T>` | - | - | Unsupported[^4] |
[^1]: Both signed and unsigned integers use the same Java types. Java has
well-defined overflow and underflow behavior. If you need to represent the
larger half of an unsigned number space, use the negative number space of
the Java integer types. For example, a uint32 with value 0xFFFFFFFF, is
represented by a -1 int in Java; 0xFFFFFFFE is -2; etc.
[^2]: Mojom strings are UTF-8 encoded, whilst Java strings are UTF-16
encoded. The Java Mojo bindings will automatically translate between the two
encodings for you. However, you should bear in mind that certain invalid
Unicode strings (for example, unpaired surrogates) can result in lossy
conversions. (If you're dealing with binary data, you should not be using
strings anyway.)
[^3]: Passing a `pending_remote<InterfaceType>` over IPC to Java will cause it
to be automatically converted to an `InterfaceType.Proxy` before it is
passed to any of your interface implementations. Proxy objects can also be
passed over IPC as pending_remotes. Java's Mojo bindings do not really make
a distinction between pending and non-pending remotes as C++ does and will
just automatically bind, unbind, and rebind them as needed when crossing IPC
boundaries. See the section on passing interfaces through IPC for details.
[^4]: Associated interfaces are not supported in Java. Attempting to use them
will cause an `AssociatedInterfaceNotSupportedException` or
`AssociatedInterfaceRequestNotSupportedException`.
## Enums
```
enum CoffeeType {
LATTE,
ESPRESSO,
CAPPUCCINO,
};
```
```java
// Non-nullable enum
@CoffeeType.EnumType long coffeeType = CoffeeType.LATTE;
// Nullable enum
@CoffeeType.EnumType Long coffeeType = null;
```
Enums are just integers with an annotation.
The constants for the different variants of an enum are available directly under
the enum's generated Java class and use SCREAMING_SNAKE_CASE. (Note that this is
unlike the tags for unions.)
## Structs
```
struct BrewCoffeeRequest {
CoffeeType coffee_type;
uint64 beans;
double litres_of_water;
double litres_of_milk;
double kilos_of_sugar;
string? customer_name;
// Set to null to automatically allocate some cups
uint64? cups;
};
```
```java
// Create an instance. You should not make any assumptions about the default
// state as it may be invalid.
BrewCoffeeRequest request = new BrewCoffeeRequest();
// Make sure to set all the fields. Each data member is public without setters
// or getters.
request.coffeeType = CoffeeType.LATTE;
request.beans = 30;
request.litresOfWater = 0.448;
request.litresOfMilk = 0.05;
request.kilosOfSugar = 0.002;
request.customerName = null;
request.cups = new Long(2);
```
As there are no getters or setters, there are no nullness checks when reading or
writing to the data members of a struct. The members of a newly constructed
struct object which are specified as non-nullable in the mojom may in fact be
null-initialized and need to be filled out before the struct is sent across
IPC. Serialization and deserialization when being sent or received across a Mojo
channel will both perform nullness checks. If you receive a struct over IPC, it
is guaranteed to comply with the nullability specified in the mojom file.
## Unions
```
union BrewCoffeeResponse {
uint64 cups_of_coffee;
string error_message;
};
```
```java
// Create an instance. You should not make any assumptions about the default
// value as it may be invalid.
BrewCoffeeResponse response = new BrewCoffeeResponse();
// Set it to the cups_of_coffee variant with a well-defined value.
response.setCupsOfCoffee(2);
// Alternatively, set it to the error_message variant.
response.setErrorMessage("I am a teapot");
// Reading a union
switch (response.which()) {
case BrewCoffeeResponse.Tag.CupsOfCoffee:
Log.i(TAG, "We got %d cups of coffee.\n", response.getCupsOfCoffee());
break;
case BrewCoffeeResponse.Tag.ErrorMessage:
Log.e(TAG, "Could not brew coffee: %s\n.", response.getErrorMessage());
break;
default:
// Your implementation is probably out of sync with the mojom.
throw new IllegalArgumentException("Unknown tag");
}
```
The content of a newly constructed union object may be null-initialized, even if
the variant is specified as non-nullable in the mojom. Serialization and
deserialization when being sent or received across a Mojo channel will generally
both perform nullness checks. **(crbug.com/337849882: Invalid union data
received over IPC may deserialize into a union with a default-constructed
state. This may result in null content for a non-nullable variant.)**
When using a getter, the autogenerated mojo code will assert that the union has
the correct variant/tag. (This is [roughly equivalent to a DCHECK](/styleguide/java/java.md#asserts).)
Java Unions internally hold the data for separate variants under separate member
variables. These members are not cleared/nullified when other variants are set
and therefore may continue to hold references to any nested data under them.
The constants for the different tags/variants of a union are available under a
static Tag class under the union's generated Java class and use
PascalCase. (Note that this is unlike the variants for enums.)
# Interfaces
Consider the following interface used as an example in the following sections:
```
interface CoffeeMachine {
const double kUsCupInLitres = 0.236588;
const double kUsLegalCupInLitres = 0.24;
const double kImperialCupInLitres = 0.284131;
EnterLowPowerMode();
PerformCleaningCycle() => ();
BrewCoffee(BrewCoffeeRequest request) => (BrewCoffeeResponse response);
};
```
## Making IPC calls
```java
// Constant names are converted to Java style
Log.i(TAG, "There are %f US cups in 1 litre",
1.0 / CoffeeMachine.US_CUP_IN_LITRES);
// coffeeMachine is of type CoffeeMachine.Proxy (which is itself an
// implementation of CoffeeMachine).
// Calling a method with no input, output, or even a completion callback.
coffeeMachine.enterLowPowerMode();
// Calling a method with no input or output, but that has a response callback
// for completion.
CoffeeMachine.PerformCleaningCycle_Response callback =
new CoffeeMachine.PerformCleaningCycle_Response() {
@Override
public void call() {
Log.i(TAG, "The cleaning cycle has finished");
}
};
coffeeMachine.performCleaningCycle(callback);
// Calling a method with an input and an output.
CoffeeMachine.BrewCoffee_Response callback =
new CoffeeMachine.BrewCoffee_Response() {
@Override
public void call(BrewCoffeeResponse response) {
// Handle the result of the brew coffee request here.
}
};
coffeeMachine.brewCoffee(brewCoffeeRequest, callback);
```
Calling these methods is asynchronous and will not block the caller.
Note that calling a method with legal inputs will not itself ever fail with an
exception if there is an IPC problem. Instead, you should set a
`ConnectionErrorHandler` on your Proxy objects if you wish to detect errors.
```java
// coffeeMachine is of type CoffeeMachine.Proxy
ConnectionErrorHandler connectionErrorHandler = new ConnectionErrorHandler() {
@Override
public void onConnectionError(MojoException e) {
// Handle the error.
}
};
coffeeMachine.getProxyHandler().setErrorHandler(connectionErrorHandler);
```
Once you are done with your Proxy object, assuming you haven't transferred it
across IPC, make sure to close it.
```java
coffeeMachine.close();
```
Holding onto an unclosed Proxy will generally keep an IPC connection alive
(unless the other side unilaterally closes it). If you forget to close it, you
may leak resources (on both sides of the IPC!), get warnings, or trigger
non-deterministic crashes.
## Receiving IPC calls
```java
class CoffeeMachineImpl implements CoffeeMachine {
@Override
public void onConnectionError(MojoException e) {
// Handle an error here. Note that close() will also be called.
}
@Override
public void close() {
// Closed - either gracefully or through an error.
}
@Override
public void enterLowPowerMode() {
Log.i(TAG, "Zzz...");
}
@Override
public void performCleaningCycle(
CoffeeMachine.PerformCleaningCycle_Response callback) {
Log.i(TAG, "Cleaned!");
callback.call();
}
@Override
public void brewCoffee(
BrewCoffeeRequest request,
CoffeeMachine.BrewCoffee_Response callback) {
CoffeeMachine.BrewCoffeeResponse response =
new CoffeeMachine.BrewCoffeeResponse();
response.setErrorMessage("I am a teapot");
callback.call(response);
}
}
```
When implementing a receiver for your interface, you will need to override the
`onConnectionError(MojoException)` and `close()` methods in addition to your
mojom-defined methods.
Whilst it's technically possible for a single implementation object to be bound
as the handler for multiple channels or receivers, you should avoid this as the
`onConnectionError(MojoException)` and `close()` methods do not provide any
explicit information about which channel their calls relate to. It's also
conceptually confusing if you continue to use a `Closeable` that may have been
closed.
Note that a Mojo connection that's bound to an implementation object will keep
that implementation object alive. However, your implementation object has no
inherent influence over the lifetime of the connection (after all, you're just
implementing an interface).
# Registering, mapping, binding, and passing interfaces
## Working with interface brokers
Interface brokers are how the renderer process usually obtains access to an IPC
interface. In Java, this will typically bind the receiver implementations for
you.
See a few examples from [code search](https://source.chromium.org/search?q=language:java%20class:InterfaceRegistrar&sq=).
There are a few examples for general registrars for Android WebView and Chrome
on Android. You will likely be able to re-use one of these for your purposes.
Adding your implementation to the binder mapping happens in the normal C++
`*_bindings.cc` or `*_binders.cc` files, which are subject to IPC security
reviews. The mapping code will route over to the Java implementation. An
`InterfaceRegistrar`, `InterfaceRegistry`, and a factory for your implementation
class is typically used to perform the binding of an implementation to an
interface that's obtained via the browser interface broker.
Much as how different layers or components use different `*_bindings.cc` and
`*_binders.cc` files to expose your IPC implementation via interface brokers,
there are also different interface registrars. Sometimes, their
approaches/implementations can be a little inconsistent. Choosing which
interface registrar to use, or creating an entirely new one, is dependent on the
use case and is outside of the scope of this document. We will only briefly look
at adding a hypothetical blink-defined "CoffeeMachine" interface to Android
WebView as an example.
`//android_webview/browser/aw_content_browser_client_receiver_bindings.cc`:
```c++
template <typename Interface>
void ForwardToJavaFrame(content::RenderFrameHost* render_frame_host,
mojo::PendingReceiver<Interface> receiver) {
render_frame_host->GetJavaInterfaces()->GetInterface(std::move(receiver));
}
void BindCoffeeMachineReceiver(
content::RenderFrameHost* render_frame_host,
mojo::PendingReceiver<blink::mojom::CoffeeMachine> receiver) {
// Optional: if you wanted to perform any pre-binding checks, such as to
// make sure that the origin is secure, etc, now is the time to do it:
const url::Origin& origin = render_frame_host->GetLastCommittedOrigin();
if (!network::IsOriginPotentiallyTrustworthy(origin)) {
mojo::ReportBadMessage(
"Attempted to access CoffeeMachine from non-trustworthy origin.");
return;
};
ForwardToJavaFrame<blink::mojom::CoffeeMachine>(
render_frame_host, std::move(receiver));
}
void AwContentBrowserClient::RegisterBrowserInterfaceBindersForFrame(
content::RenderFrameHost* render_frame_host,
mojo::BinderMapWithContext<content::RenderFrameHost*>* map) {
map->Add<blink::mojom::CoffeeMachine>(
base::BindRepeating(&BindCoffeeMachineReceiver));
// ... Mappings for other interfaces
}
```
`//android_webview/java/src/org/chromium/android_webview/AwInterfaceRegistrar.java`:
```java
class AwInterfaceRegistrar {
@CalledByNative
private static void registerMojoInterfaces() {
InterfaceRegistrar.Registry.addRenderFrameHostRegistrar(
new AndroidWebviewRenderFrameHostInterfaceRegistrar());
}
private static class AndroidWebviewRenderFrameHostInterfaceRegistrar
implements InterfaceRegistrar<RenderFrameHost> {
@Override
public void registerInterfaces(
InterfaceRegistry registry,
RenderFrameHost renderFrameHost) {
// A made up example:
registry.addInterface(
CoffeeMachineImpl.MANAGER,
new CoffeeMachineImplFactory(renderFrameHost));
// ... Calls for other interfaces.
}
}
}
```
`CoffeeMachineImplFactory.java`:
```java
public class CoffeeMachineImplFactory implements InterfaceFactory {
private final RenderFrameHost mRenderFrameHost;
public CoffeeMachineFactory(RenderFrameHost renderFrameHost) {
mRenderFrameHost = renderFrameHost;
}
@Override
public CoffeeMachineImpl createImpl() {
return new CoffeeMachineImpl(mRenderFrameHost);
}
}
```
However, if you're passing pending_receivers around as part of the IPC calls of
your interfaces, you will need to do the binding yourself, as described in the
next sections.
## Passing interfaces through IPC calls (pending_receivers and pending_remotes)
Consider the following interfaces used as an example in the following sections:
```
interface CoffeeMachine {
BrewCoffee(BrewCoffeeRequest request) => (BrewCoffeeResponse response);
// ...
};
interface Employee {
ProvideCoffeeAccess(pending_remote<CoffeeMachine> coffee_machine);
};
interface Kitchen {
RequestCoffeeAccess(pending_receiver<CoffeeMachine> coffee_machine);
};
```
### Receiving a pending_receiver and binding to it
There is a bit of a terminology mismatch in Java compared to C++/mojom where
Java uses some older phrasing. Notably, pending_receivers are called
"InterfaceRequests" in Java (this actually used to be the old term used in other
bindings like C++). Once received on the Java side, these can then be bound to
an implementation.
```java
class KitchenImpl implements Kitchen {
// ...
@Override
public void requestCoffeeAccess(
InterfaceRequest<CoffeeMachine> interfaceRequest) {
CoffeeMachine coffeeMachine = new CoffeeMachineImpl();
CoffeeMachine.MANAGER.bind(coffeeMachine, interfaceRequest);
}
}
```
If you do not intend to bind an implementation to an InterfaceRequest and
instead wish to just discard it, you should `close()` it.
### Receiving a pending_remote and using it
Pending remotes are automatically bound and converted to Proxy objects when
received.
```java
class EmployeeImpl implements Employee {
// ...
@Override
public void provideCoffeeAccess(CoffeeMachine coffeeMachine) {
// We can immediately use the remote/proxy
coffeeMachine.brewCoffee(mMyUsualPlease, mDrinkCoffeeCallback);
coffeeMachine.close();
// Or, we could pass the proxy onto somewhere else, and it will be
// unbound from the proxy and converted from a remote back into a
// pending remote.
mColleague.provideCoffeeAccess(coffeeMachine);
// Do not close() if passed elsewhere.
}
}
```
If you are finished with the remote/proxy (and have not passed it on elsewhere),
you should `close()` it.
### Creating and sending pending_receivers and pending_remotes
You can create a connected pair of an `InterfaceType.Proxy` and an
`InterfaceRequest<InterfaceType>`. You can then either use these yourself or
send them over IPC.
```java
// import org.chromium.mojo.system.Pair;
// import org.chromium.mojo.system.impl.CoreImpl;
Pair<CoffeeMachine.Proxy, InterfaceRequest<CoffeeMachine>> pair =
CoffeeMachine.MANAGER.getInterfaceRequest(CoreImpl.getInstance());
// Both the proxy and the interface request can used by us or be sent over IPC.
// Note that calls can be queued via the proxy/remote even before the
// pending_receiver/interface request is bound to an implementation.
employee.provideCoffeeAccess(pair.first);
kitchen.requestCoffeeAccess(pair.second);
```
There is a shortcut if you want to provide the other side of the IPC with a
remote to an implementation you own. Instead of creating a pair of a Proxy and
an InterfaceRequest, you can directly supply your implementation object instead
of a proxy and Mojo will create, bind, and send a pending remote for you:
```java
CoffeeMachine coffeeMachine = new CoffeeMachineImpl()
employee.provideCoffeeAccess(coffeeMachine);
```
Note that whether you send a pending_remote one way or send a pending_receiver
the other way can have an effect on ownership, lifetimes, and performance. It
may be better to supply a pending_receiver as an argument to a method call than
to wait for a pending_remote from the response callback - especially if you want
to queue up messages before all binding has taken place.
# Threading
When receiving IPC calls, Mojo will invoke your interface implementation's
methods on the thread/sequence on which the implementation was bound. This also
extends to the `close` and `onConnectionError` methods. (This is similar to how
things work in C++.)
You should generally only use the Mojo APIs from within a valid sequence, though
purely manipulating mojom-generated Java data types (structs, enums, unions) is
OK. Various Mojo method calls will crash if invoked from outside of a valid
sequence.
Note that ordinary Proxy objects are not thread safe and should generally only
be used from the thread on which they are created. However, you can create a
thread safe wrapper around your proxy object using
`InterfaceType.MANAGER.buildThreadSafeProxy(originalProxy)`, which will forward
calls to the thread on which the thread-safe proxy wrapper was created. You must
therefore build the thread-safe proxy from the thread which owns the original
proxy.