Ken Rockot | 686e413 | 2017-04-26 00:03:31 | [diff] [blame] | 1 | # Mojo Java Bindings API |
Ken Rockot | 929282c | 2018-05-02 17:07:29 | [diff] [blame] | 2 | This document is a subset of the [Mojo documentation](/mojo/README.md). |
rockot | f59d2d6 | 2017-04-01 02:49:08 | [diff] [blame] | 3 | |
| 4 | [TOC] |
| 5 | |
| 6 | ## Overview |
| 7 | |
Ashley Newson | 278599d | 2024-05-22 15:11:02 | [diff] [blame] | 8 | The Mojo Java Bindings API leverages the [Java System API](/mojo/public/java/system/README.md) |
| 9 | to provide a more natural set of primitives for communicating over Mojo message |
| 10 | pipes. Combined with generated code from the [Mojom IDL and bindings generator](/mojo/public/tools/bindings/README.md), |
| 11 | users can easily connect interface clients and implementations across arbitrary |
| 12 | intra- and inter-process boundaries. |
rockot | f59d2d6 | 2017-04-01 02:49:08 | [diff] [blame] | 13 | |
Ashley Newson | 278599d | 2024-05-22 15:11:02 | [diff] [blame] | 14 | This document provides a brief guide to Java Bindings API usage with example |
| 15 | code snippets. Some foundational knowledge of Mojo and mojom interface |
| 16 | definitions is assumed. Familiarity with the [C++ bindings](/mojo/public/cpp/bindings/README.md) |
| 17 | may be useful for comparison but isn't strictly required. For detailed API |
| 18 | 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/). |
| 19 | |
| 20 | # Should I or should I not use the Java bindings? |
| 21 | |
| 22 | Java bindings typically only make sense on the browser side and for Android |
| 23 | devices where there is some service that's unique to Android. As such, Java |
| 24 | bindings are typically rarely used, but they are used sometimes. When |
| 25 | implementing a cross-platform feature or service, it's probably more appropriate |
| 26 | to use C++ Mojo bindings and (if necessary) plumb the platform-specific code |
| 27 | implemented in Java via JNI. |
| 28 | |
| 29 | C++ bindings have more features than the Java bindings. In simple cases, Java |
| 30 | bindings provide enough functionality to do everything by themselves. However, |
| 31 | for complex usage, it may be necessary to instead use the C++ bindings and plumb |
| 32 | data through JNI. See the section on notable lacking features below. |
| 33 | |
| 34 | However, working within Java can avoid boilerplate and complex memory management |
| 35 | between C++ and Java. |
| 36 | |
| 37 | If your service doesn't really need to be implemented in Java (because it |
| 38 | doesn't depend on or benefit from any Java-exclusive APIs), you should probably |
| 39 | just write the whole thing in C++. |
| 40 | |
| 41 | Implementing Mojo interfaces in Java is subject to the same Chromium IPC |
| 42 | [practices](/docs/security/mojo.md#Security) and [review](/docs/security/ipc-reviews.md) |
| 43 | processes. However, most reviewers are used to reviewing C++ code. |
| 44 | |
| 45 | # Notable lacking features |
| 46 | |
| 47 | This is not an exhaustive list. |
| 48 | |
| 49 | ## AssociatedInterfaces |
| 50 | |
| 51 | These are not supported. |
| 52 | |
| 53 | ## ReportBadMessage |
| 54 | |
| 55 | There is no way for Java to report a bad message, which is the standard practice |
| 56 | when the renderer sends a message that indicates it's misbehaving (and perhaps |
| 57 | compromised). |
| 58 | |
| 59 | The best you can do if you receive a bad message is to just ignore the |
| 60 | request. However, try to close any Proxy objects or InterfaceRequests that might |
| 61 | not otherwise have needed closing. |
| 62 | |
| 63 | If the IPC call usually expects a response (via a callback), but the callback |
| 64 | has not been called and the callback gets garbage collected, this will lead to |
| 65 | the Mojo connection being disconnected. The involvement of the garbage collector |
| 66 | makes this somewhat non-deterministic, so this behavior should not be relied |
| 67 | upon. |
| 68 | |
| 69 | ## Missing type conversions (StructTraits) |
| 70 | |
| 71 | There are a few non-primitive but common standard library-like mojom types. The |
| 72 | C++ bindings allow the definition of custom conversions between C++ and mojom |
| 73 | types via StructTraits. The auto-generated Java interfaces don't implement these |
| 74 | custom conversions as they don't support StructTraits, so you don't get the |
| 75 | obvious conversions between these mojom and Java types. This isn't necessarily a |
| 76 | deal-breaker though, as you can still implement the conversions yourself (with |
| 77 | care). |
| 78 | |
| 79 | An example is `String16`, representing a UTF-16 string. For `String16`, C++ can |
| 80 | convert transparently to/from `std::u16string`. However, in Java, this merely |
| 81 | produces something which instead resembles the underlying mojom struct - which |
| 82 | just contains a `short[]` array. |
| 83 | |
| 84 | ## Synchronous calls |
| 85 | |
| 86 | You cannot perform synchronous method calls _from_ Java. However, Java can still |
| 87 | receive sync calls (as `[Sync]` annotations only affect the behavior on the |
| 88 | caller side). In general, you shouldn't need to make synchronous calls as they |
| 89 | have many [disadvantages](/mojo/public/cpp/bindings/README.md#think-carefully-before-you-decide-to-use-sync-calls). |
| 90 | |
| 91 | # Key differences compared to C++ bindings |
| 92 | |
| 93 | The Java bindings' APIs don't particularly use the terms "receiver" and |
| 94 | "remote". Instead, the terms "Interface" and "Proxy" might be used. However, |
| 95 | this document will often use a mixture of these terms, as "pending", "remotes", |
| 96 | and "receivers" are terms used in mojom. |
| 97 | |
| 98 | In C++, you'll typically have a `pending_receiver<InterfaceType>` which you then |
| 99 | bind to an implementation of `InterfaceType`. In Java, this is an |
| 100 | `InterfaceRequest<InterfaceType>`, which can be bound to your implementation of |
| 101 | `InterfaceType`. |
| 102 | |
| 103 | In C++, you typically have to hold on to the receiver for as long as you want it |
| 104 | alive, or deliberately use a "self-owned" receiver that stays alive as long as |
| 105 | the other side of the IPC connection needs it. In Java, Mojo will keep the |
| 106 | underlying IPC connection and your interface implementation (receiver) alive as |
| 107 | long as the other end stays alive. In practice, this behaves somewhat like |
| 108 | self-owned receivers. |
| 109 | |
| 110 | In C++, there is generally a greater distinction between remotes and |
| 111 | pending_remotes. In Java, you will typically just deal with an |
| 112 | `InterfaceType.Proxy` object, which can be used as either a remote or |
| 113 | pending_remote interchangeably. |
| 114 | |
| 115 | # Object and connection lifetimes |
| 116 | |
| 117 | When implementing an IPC interface, you'll also need to implement a `close()` |
| 118 | method and an `onConnectionError()` method. The `close()` method will |
| 119 | automatically be called whenever the IPC disconnects (either gracefully or due |
| 120 | to an error). The `onConnectionError()` method will only be called (in addition |
| 121 | to `close()`) when the connection ends due to an error. |
| 122 | |
| 123 | A Mojo connection that's bound to an implementation object will keep that object |
| 124 | alive. The connection is maintained by Mojo so long as the other (remote) end is |
| 125 | alive (bound or unbound). Note that calling the `close()` method on your |
| 126 | interface implementation yourself won't stop calls from coming in! In java, the |
| 127 | receiver generally isn't able to unilaterally end a connection cleanly. |
| 128 | |
| 129 | A Proxy object will keep any connection it's bound to alive until you call |
| 130 | `close()` on it. |
| 131 | |
| 132 | No longer needed Proxy objects and InterfaceRequests which are to be discarded |
| 133 | without binding should be closed. **Failing to do so can lead to resource leaks, |
| 134 | produce warnings, or lead to non-deterministic crashes**. Failing to close Proxy |
| 135 | objects may produce warnings in log output such as |
| 136 | `java.lang.IllegalStateException: Warning: Router objects should be explicitly closed when no longer required otherwise you may leak handles.`. |
| 137 | Failing to close InterfaceRequests may produce `Handle was not closed.` |
| 138 | warnings. |
| 139 | |
| 140 | # Types |
| 141 | |
| 142 | ## Primitive types |
| 143 | |
| 144 | | mojom type | non-nullable type | nullable type | Comment | |
| 145 | |------------|-------------------|---------------|---------| |
| 146 | | `bool` | `boolean` | `Boolean` | | |
| 147 | | `int8`, `uint8` | `byte` | `Byte` | Signed and unsigned[^1] | |
| 148 | | `int16`, `uint16` | `short` | `Short` | Signed and unsigned[^1] | |
| 149 | | `int32`, `uint32` | `int` | `Integer` | Signed and unsigned[^1] | |
| 150 | | `int64`, `uint64` | `long` | `Long` | Signed and unsigned[^1] | |
| 151 | | `string` | `String` | `String` | Character encoding[^2] | |
| 152 | | `array<T>` | `T[]` | `T[]` | | |
| 153 | | `array<T, N>` | `T[]` | `T[]` | | |
| 154 | | `map<S, T>` | `Map<S, T>` | `Map<S, T>` | | |
| 155 | | `handle` | `Handle` | `Handle` | | |
| 156 | | `handle<message_pipe>` | `MessagePipeHandle` | `MessagePipeHandle` | | |
| 157 | | `handle<shared_buffer>` | `SharedBufferHandle` | `SharedBufferHandle` | | |
| 158 | | `handle<data_pipe_producer>` | `DataPipe.ProducerHandle` | `DataPipe.ProducerHandle` | | |
| 159 | | `handle<data_pipe_consumer>` | `DataPipe.ConsumerHandle` | `DataPipe.ConsumerHandle` | | |
| 160 | | `pending_remote<T>` | `T` or `T.Proxy` | `T` or `T.Proxy` | Automatic conversions[^3] | |
| 161 | | `pending_receiver<T>` | `InterfaceRequest<T>` | `InterfaceRequest<T>` | | |
| 162 | | `pending_associated_remote<T>` | - | - | Unsupported[^4] | |
| 163 | | `pending_associated_receiver<T>` | - | - | Unsupported[^4] | |
| 164 | |
| 165 | |
| 166 | [^1]: Both signed and unsigned integers use the same Java types. Java has |
| 167 | well-defined overflow and underflow behavior. If you need to represent the |
| 168 | larger half of an unsigned number space, use the negative number space of |
| 169 | the Java integer types. For example, a uint32 with value 0xFFFFFFFF, is |
| 170 | represented by a -1 int in Java; 0xFFFFFFFE is -2; etc. |
| 171 | |
| 172 | [^2]: Mojom strings are UTF-8 encoded, whilst Java strings are UTF-16 |
| 173 | encoded. The Java Mojo bindings will automatically translate between the two |
| 174 | encodings for you. However, you should bear in mind that certain invalid |
| 175 | Unicode strings (for example, unpaired surrogates) can result in lossy |
| 176 | conversions. (If you're dealing with binary data, you should not be using |
| 177 | strings anyway.) |
| 178 | |
| 179 | [^3]: Passing a `pending_remote<InterfaceType>` over IPC to Java will cause it |
| 180 | to be automatically converted to an `InterfaceType.Proxy` before it is |
| 181 | passed to any of your interface implementations. Proxy objects can also be |
| 182 | passed over IPC as pending_remotes. Java's Mojo bindings do not really make |
| 183 | a distinction between pending and non-pending remotes as C++ does and will |
| 184 | just automatically bind, unbind, and rebind them as needed when crossing IPC |
| 185 | boundaries. See the section on passing interfaces through IPC for details. |
| 186 | |
| 187 | [^4]: Associated interfaces are not supported in Java. Attempting to use them |
| 188 | will cause an `AssociatedInterfaceNotSupportedException` or |
| 189 | `AssociatedInterfaceRequestNotSupportedException`. |
| 190 | |
| 191 | ## Enums |
| 192 | |
| 193 | ``` |
| 194 | enum CoffeeType { |
| 195 | LATTE, |
| 196 | ESPRESSO, |
| 197 | CAPPUCCINO, |
| 198 | }; |
| 199 | ``` |
| 200 | |
| 201 | ```java |
| 202 | // Non-nullable enum |
| 203 | @CoffeeType.EnumType long coffeeType = CoffeeType.LATTE; |
| 204 | // Nullable enum |
| 205 | @CoffeeType.EnumType Long coffeeType = null; |
| 206 | ``` |
| 207 | |
| 208 | Enums are just integers with an annotation. |
| 209 | |
| 210 | The constants for the different variants of an enum are available directly under |
| 211 | the enum's generated Java class and use SCREAMING_SNAKE_CASE. (Note that this is |
| 212 | unlike the tags for unions.) |
| 213 | |
| 214 | ## Structs |
| 215 | |
| 216 | ``` |
| 217 | struct BrewCoffeeRequest { |
| 218 | CoffeeType coffee_type; |
| 219 | uint64 beans; |
| 220 | double litres_of_water; |
| 221 | double litres_of_milk; |
| 222 | double kilos_of_sugar; |
| 223 | string? customer_name; |
| 224 | // Set to null to automatically allocate some cups |
| 225 | uint64? cups; |
| 226 | }; |
| 227 | ``` |
| 228 | |
| 229 | ```java |
| 230 | // Create an instance. You should not make any assumptions about the default |
| 231 | // state as it may be invalid. |
| 232 | BrewCoffeeRequest request = new BrewCoffeeRequest(); |
| 233 | // Make sure to set all the fields. Each data member is public without setters |
| 234 | // or getters. |
| 235 | request.coffeeType = CoffeeType.LATTE; |
| 236 | request.beans = 30; |
| 237 | request.litresOfWater = 0.448; |
| 238 | request.litresOfMilk = 0.05; |
| 239 | request.kilosOfSugar = 0.002; |
| 240 | request.customerName = null; |
| 241 | request.cups = new Long(2); |
| 242 | ``` |
| 243 | |
| 244 | As there are no getters or setters, there are no nullness checks when reading or |
| 245 | writing to the data members of a struct. The members of a newly constructed |
| 246 | struct object which are specified as non-nullable in the mojom may in fact be |
| 247 | null-initialized and need to be filled out before the struct is sent across |
| 248 | IPC. Serialization and deserialization when being sent or received across a Mojo |
| 249 | channel will both perform nullness checks. If you receive a struct over IPC, it |
| 250 | is guaranteed to comply with the nullability specified in the mojom file. |
| 251 | |
| 252 | ## Unions |
| 253 | |
| 254 | ``` |
| 255 | union BrewCoffeeResponse { |
| 256 | uint64 cups_of_coffee; |
| 257 | string error_message; |
| 258 | }; |
| 259 | ``` |
| 260 | |
| 261 | ```java |
| 262 | // Create an instance. You should not make any assumptions about the default |
| 263 | // value as it may be invalid. |
| 264 | BrewCoffeeResponse response = new BrewCoffeeResponse(); |
| 265 | // Set it to the cups_of_coffee variant with a well-defined value. |
| 266 | response.setCupsOfCoffee(2); |
| 267 | // Alternatively, set it to the error_message variant. |
| 268 | response.setErrorMessage("I am a teapot"); |
| 269 | |
| 270 | // Reading a union |
| 271 | switch (response.which()) { |
| 272 | case BrewCoffeeResponse.Tag.CupsOfCoffee: |
| 273 | Log.i(TAG, "We got %d cups of coffee.\n", response.getCupsOfCoffee()); |
| 274 | break; |
| 275 | case BrewCoffeeResponse.Tag.ErrorMessage: |
| 276 | Log.e(TAG, "Could not brew coffee: %s\n.", response.getErrorMessage()); |
| 277 | break; |
| 278 | default: |
| 279 | // Your implementation is probably out of sync with the mojom. |
| 280 | throw new IllegalArgumentException("Unknown tag"); |
| 281 | } |
| 282 | ``` |
| 283 | |
| 284 | The content of a newly constructed union object may be null-initialized, even if |
| 285 | the variant is specified as non-nullable in the mojom. Serialization and |
| 286 | deserialization when being sent or received across a Mojo channel will generally |
| 287 | both perform nullness checks. **(crbug.com/337849882: Invalid union data |
| 288 | received over IPC may deserialize into a union with a default-constructed |
| 289 | state. This may result in null content for a non-nullable variant.)** |
| 290 | |
| 291 | When using a getter, the autogenerated mojo code will assert that the union has |
| 292 | the correct variant/tag. (This is [roughly equivalent to a DCHECK](/styleguide/java/java.md#asserts).) |
| 293 | |
| 294 | Java Unions internally hold the data for separate variants under separate member |
| 295 | variables. These members are not cleared/nullified when other variants are set |
| 296 | and therefore may continue to hold references to any nested data under them. |
| 297 | |
| 298 | The constants for the different tags/variants of a union are available under a |
| 299 | static Tag class under the union's generated Java class and use |
| 300 | PascalCase. (Note that this is unlike the variants for enums.) |
| 301 | |
| 302 | # Interfaces |
| 303 | |
| 304 | Consider the following interface used as an example in the following sections: |
| 305 | |
| 306 | ``` |
| 307 | interface CoffeeMachine { |
| 308 | const double kUsCupInLitres = 0.236588; |
| 309 | const double kUsLegalCupInLitres = 0.24; |
| 310 | const double kImperialCupInLitres = 0.284131; |
| 311 | |
| 312 | EnterLowPowerMode(); |
| 313 | PerformCleaningCycle() => (); |
| 314 | BrewCoffee(BrewCoffeeRequest request) => (BrewCoffeeResponse response); |
| 315 | }; |
| 316 | ``` |
| 317 | |
| 318 | ## Making IPC calls |
| 319 | |
| 320 | ```java |
| 321 | // Constant names are converted to Java style |
| 322 | Log.i(TAG, "There are %f US cups in 1 litre", |
| 323 | 1.0 / CoffeeMachine.US_CUP_IN_LITRES); |
| 324 | |
| 325 | // coffeeMachine is of type CoffeeMachine.Proxy (which is itself an |
| 326 | // implementation of CoffeeMachine). |
| 327 | |
| 328 | // Calling a method with no input, output, or even a completion callback. |
| 329 | coffeeMachine.enterLowPowerMode(); |
| 330 | |
| 331 | // Calling a method with no input or output, but that has a response callback |
| 332 | // for completion. |
| 333 | CoffeeMachine.PerformCleaningCycle_Response callback = |
| 334 | new CoffeeMachine.PerformCleaningCycle_Response() { |
| 335 | @Override |
| 336 | public void call() { |
| 337 | Log.i(TAG, "The cleaning cycle has finished"); |
| 338 | } |
| 339 | }; |
| 340 | coffeeMachine.performCleaningCycle(callback); |
| 341 | |
| 342 | // Calling a method with an input and an output. |
| 343 | CoffeeMachine.BrewCoffee_Response callback = |
| 344 | new CoffeeMachine.BrewCoffee_Response() { |
| 345 | @Override |
| 346 | public void call(BrewCoffeeResponse response) { |
| 347 | // Handle the result of the brew coffee request here. |
| 348 | } |
| 349 | }; |
| 350 | coffeeMachine.brewCoffee(brewCoffeeRequest, callback); |
| 351 | ``` |
| 352 | |
| 353 | Calling these methods is asynchronous and will not block the caller. |
| 354 | |
| 355 | Note that calling a method with legal inputs will not itself ever fail with an |
| 356 | exception if there is an IPC problem. Instead, you should set a |
| 357 | `ConnectionErrorHandler` on your Proxy objects if you wish to detect errors. |
| 358 | |
| 359 | ```java |
| 360 | // coffeeMachine is of type CoffeeMachine.Proxy |
| 361 | ConnectionErrorHandler connectionErrorHandler = new ConnectionErrorHandler() { |
| 362 | @Override |
| 363 | public void onConnectionError(MojoException e) { |
| 364 | // Handle the error. |
| 365 | } |
| 366 | }; |
| 367 | coffeeMachine.getProxyHandler().setErrorHandler(connectionErrorHandler); |
| 368 | ``` |
| 369 | |
| 370 | Once you are done with your Proxy object, assuming you haven't transferred it |
| 371 | across IPC, make sure to close it. |
| 372 | |
| 373 | ```java |
| 374 | coffeeMachine.close(); |
| 375 | ``` |
| 376 | |
| 377 | Holding onto an unclosed Proxy will generally keep an IPC connection alive |
| 378 | (unless the other side unilaterally closes it). If you forget to close it, you |
| 379 | may leak resources (on both sides of the IPC!), get warnings, or trigger |
| 380 | non-deterministic crashes. |
| 381 | |
| 382 | ## Receiving IPC calls |
| 383 | |
| 384 | ```java |
| 385 | class CoffeeMachineImpl implements CoffeeMachine { |
| 386 | @Override |
| 387 | public void onConnectionError(MojoException e) { |
| 388 | // Handle an error here. Note that close() will also be called. |
| 389 | } |
| 390 | |
| 391 | @Override |
| 392 | public void close() { |
| 393 | // Closed - either gracefully or through an error. |
| 394 | } |
| 395 | |
| 396 | @Override |
| 397 | public void enterLowPowerMode() { |
| 398 | Log.i(TAG, "Zzz..."); |
| 399 | } |
| 400 | |
| 401 | @Override |
| 402 | public void performCleaningCycle( |
| 403 | CoffeeMachine.PerformCleaningCycle_Response callback) { |
| 404 | Log.i(TAG, "Cleaned!"); |
| 405 | callback.call(); |
| 406 | } |
| 407 | |
| 408 | @Override |
| 409 | public void brewCoffee( |
| 410 | BrewCoffeeRequest request, |
| 411 | CoffeeMachine.BrewCoffee_Response callback) { |
| 412 | CoffeeMachine.BrewCoffeeResponse response = |
| 413 | new CoffeeMachine.BrewCoffeeResponse(); |
| 414 | response.setErrorMessage("I am a teapot"); |
| 415 | callback.call(response); |
| 416 | } |
| 417 | } |
| 418 | ``` |
| 419 | |
| 420 | When implementing a receiver for your interface, you will need to override the |
| 421 | `onConnectionError(MojoException)` and `close()` methods in addition to your |
| 422 | mojom-defined methods. |
| 423 | |
| 424 | Whilst it's technically possible for a single implementation object to be bound |
| 425 | as the handler for multiple channels or receivers, you should avoid this as the |
| 426 | `onConnectionError(MojoException)` and `close()` methods do not provide any |
| 427 | explicit information about which channel their calls relate to. It's also |
| 428 | conceptually confusing if you continue to use a `Closeable` that may have been |
| 429 | closed. |
| 430 | |
| 431 | Note that a Mojo connection that's bound to an implementation object will keep |
| 432 | that implementation object alive. However, your implementation object has no |
| 433 | inherent influence over the lifetime of the connection (after all, you're just |
| 434 | implementing an interface). |
| 435 | |
| 436 | # Registering, mapping, binding, and passing interfaces |
| 437 | |
| 438 | ## Working with interface brokers |
| 439 | |
| 440 | Interface brokers are how the renderer process usually obtains access to an IPC |
| 441 | interface. In Java, this will typically bind the receiver implementations for |
| 442 | you. |
| 443 | |
| 444 | See a few examples from [code search](https://source.chromium.org/search?q=language:java%20class:InterfaceRegistrar&sq=). |
| 445 | There are a few examples for general registrars for Android WebView and Chrome |
| 446 | on Android. You will likely be able to re-use one of these for your purposes. |
| 447 | |
| 448 | Adding your implementation to the binder mapping happens in the normal C++ |
| 449 | `*_bindings.cc` or `*_binders.cc` files, which are subject to IPC security |
| 450 | reviews. The mapping code will route over to the Java implementation. An |
| 451 | `InterfaceRegistrar`, `InterfaceRegistry`, and a factory for your implementation |
| 452 | class is typically used to perform the binding of an implementation to an |
| 453 | interface that's obtained via the browser interface broker. |
| 454 | |
| 455 | Much as how different layers or components use different `*_bindings.cc` and |
| 456 | `*_binders.cc` files to expose your IPC implementation via interface brokers, |
| 457 | there are also different interface registrars. Sometimes, their |
| 458 | approaches/implementations can be a little inconsistent. Choosing which |
| 459 | interface registrar to use, or creating an entirely new one, is dependent on the |
| 460 | use case and is outside of the scope of this document. We will only briefly look |
| 461 | at adding a hypothetical blink-defined "CoffeeMachine" interface to Android |
| 462 | WebView as an example. |
| 463 | |
| 464 | `//android_webview/browser/aw_content_browser_client_receiver_bindings.cc`: |
| 465 | |
| 466 | ```c++ |
| 467 | template <typename Interface> |
| 468 | void ForwardToJavaFrame(content::RenderFrameHost* render_frame_host, |
| 469 | mojo::PendingReceiver<Interface> receiver) { |
| 470 | render_frame_host->GetJavaInterfaces()->GetInterface(std::move(receiver)); |
| 471 | } |
| 472 | |
| 473 | void BindCoffeeMachineReceiver( |
| 474 | content::RenderFrameHost* render_frame_host, |
| 475 | mojo::PendingReceiver<blink::mojom::CoffeeMachine> receiver) { |
| 476 | // Optional: if you wanted to perform any pre-binding checks, such as to |
| 477 | // make sure that the origin is secure, etc, now is the time to do it: |
| 478 | const url::Origin& origin = render_frame_host->GetLastCommittedOrigin(); |
| 479 | if (!network::IsOriginPotentiallyTrustworthy(origin)) { |
| 480 | mojo::ReportBadMessage( |
| 481 | "Attempted to access CoffeeMachine from non-trustworthy origin."); |
| 482 | return; |
| 483 | }; |
| 484 | |
| 485 | ForwardToJavaFrame<blink::mojom::CoffeeMachine>( |
| 486 | render_frame_host, std::move(receiver)); |
| 487 | } |
| 488 | |
| 489 | void AwContentBrowserClient::RegisterBrowserInterfaceBindersForFrame( |
| 490 | content::RenderFrameHost* render_frame_host, |
| 491 | mojo::BinderMapWithContext<content::RenderFrameHost*>* map) { |
| 492 | map->Add<blink::mojom::CoffeeMachine>( |
| 493 | base::BindRepeating(&BindCoffeeMachineReceiver)); |
| 494 | // ... Mappings for other interfaces |
| 495 | } |
| 496 | ``` |
| 497 | |
| 498 | `//android_webview/java/src/org/chromium/android_webview/AwInterfaceRegistrar.java`: |
| 499 | |
| 500 | ```java |
| 501 | class AwInterfaceRegistrar { |
| 502 | @CalledByNative |
| 503 | private static void registerMojoInterfaces() { |
| 504 | InterfaceRegistrar.Registry.addRenderFrameHostRegistrar( |
| 505 | new AndroidWebviewRenderFrameHostInterfaceRegistrar()); |
| 506 | } |
| 507 | |
| 508 | private static class AndroidWebviewRenderFrameHostInterfaceRegistrar |
| 509 | implements InterfaceRegistrar<RenderFrameHost> { |
| 510 | @Override |
| 511 | public void registerInterfaces( |
| 512 | InterfaceRegistry registry, |
| 513 | RenderFrameHost renderFrameHost) { |
| 514 | // A made up example: |
| 515 | registry.addInterface( |
| 516 | CoffeeMachineImpl.MANAGER, |
| 517 | new CoffeeMachineImplFactory(renderFrameHost)); |
| 518 | // ... Calls for other interfaces. |
| 519 | } |
| 520 | } |
| 521 | } |
| 522 | ``` |
| 523 | |
| 524 | `CoffeeMachineImplFactory.java`: |
| 525 | |
| 526 | ```java |
| 527 | public class CoffeeMachineImplFactory implements InterfaceFactory { |
| 528 | private final RenderFrameHost mRenderFrameHost; |
| 529 | |
| 530 | public CoffeeMachineFactory(RenderFrameHost renderFrameHost) { |
| 531 | mRenderFrameHost = renderFrameHost; |
| 532 | } |
| 533 | |
| 534 | @Override |
| 535 | public CoffeeMachineImpl createImpl() { |
| 536 | return new CoffeeMachineImpl(mRenderFrameHost); |
| 537 | } |
| 538 | } |
| 539 | ``` |
| 540 | |
| 541 | However, if you're passing pending_receivers around as part of the IPC calls of |
| 542 | your interfaces, you will need to do the binding yourself, as described in the |
| 543 | next sections. |
| 544 | |
| 545 | |
| 546 | |
| 547 | |
| 548 | |
| 549 | ## Passing interfaces through IPC calls (pending_receivers and pending_remotes) |
| 550 | |
| 551 | Consider the following interfaces used as an example in the following sections: |
| 552 | |
| 553 | ``` |
| 554 | interface CoffeeMachine { |
| 555 | BrewCoffee(BrewCoffeeRequest request) => (BrewCoffeeResponse response); |
| 556 | // ... |
| 557 | }; |
| 558 | |
| 559 | interface Employee { |
| 560 | ProvideCoffeeAccess(pending_remote<CoffeeMachine> coffee_machine); |
| 561 | }; |
| 562 | |
| 563 | interface Kitchen { |
| 564 | RequestCoffeeAccess(pending_receiver<CoffeeMachine> coffee_machine); |
| 565 | }; |
| 566 | ``` |
| 567 | |
| 568 | ### Receiving a pending_receiver and binding to it |
| 569 | |
| 570 | There is a bit of a terminology mismatch in Java compared to C++/mojom where |
| 571 | Java uses some older phrasing. Notably, pending_receivers are called |
| 572 | "InterfaceRequests" in Java (this actually used to be the old term used in other |
| 573 | bindings like C++). Once received on the Java side, these can then be bound to |
| 574 | an implementation. |
| 575 | |
| 576 | ```java |
| 577 | class KitchenImpl implements Kitchen { |
| 578 | // ... |
| 579 | @Override |
| 580 | public void requestCoffeeAccess( |
| 581 | InterfaceRequest<CoffeeMachine> interfaceRequest) { |
| 582 | CoffeeMachine coffeeMachine = new CoffeeMachineImpl(); |
| 583 | CoffeeMachine.MANAGER.bind(coffeeMachine, interfaceRequest); |
| 584 | } |
| 585 | } |
| 586 | ``` |
| 587 | |
| 588 | If you do not intend to bind an implementation to an InterfaceRequest and |
| 589 | instead wish to just discard it, you should `close()` it. |
| 590 | |
| 591 | ### Receiving a pending_remote and using it |
| 592 | |
| 593 | Pending remotes are automatically bound and converted to Proxy objects when |
| 594 | received. |
| 595 | |
| 596 | ```java |
| 597 | class EmployeeImpl implements Employee { |
| 598 | // ... |
| 599 | @Override |
| 600 | public void provideCoffeeAccess(CoffeeMachine coffeeMachine) { |
| 601 | // We can immediately use the remote/proxy |
| 602 | coffeeMachine.brewCoffee(mMyUsualPlease, mDrinkCoffeeCallback); |
| 603 | coffeeMachine.close(); |
| 604 | |
| 605 | // Or, we could pass the proxy onto somewhere else, and it will be |
| 606 | // unbound from the proxy and converted from a remote back into a |
| 607 | // pending remote. |
| 608 | mColleague.provideCoffeeAccess(coffeeMachine); |
| 609 | // Do not close() if passed elsewhere. |
| 610 | } |
| 611 | } |
| 612 | ``` |
| 613 | |
| 614 | If you are finished with the remote/proxy (and have not passed it on elsewhere), |
| 615 | you should `close()` it. |
| 616 | |
| 617 | ### Creating and sending pending_receivers and pending_remotes |
| 618 | |
| 619 | You can create a connected pair of an `InterfaceType.Proxy` and an |
| 620 | `InterfaceRequest<InterfaceType>`. You can then either use these yourself or |
| 621 | send them over IPC. |
| 622 | |
| 623 | ```java |
| 624 | // import org.chromium.mojo.system.Pair; |
| 625 | // import org.chromium.mojo.system.impl.CoreImpl; |
| 626 | |
| 627 | Pair<CoffeeMachine.Proxy, InterfaceRequest<CoffeeMachine>> pair = |
| 628 | CoffeeMachine.MANAGER.getInterfaceRequest(CoreImpl.getInstance()); |
| 629 | |
| 630 | // Both the proxy and the interface request can used by us or be sent over IPC. |
| 631 | // Note that calls can be queued via the proxy/remote even before the |
| 632 | // pending_receiver/interface request is bound to an implementation. |
| 633 | employee.provideCoffeeAccess(pair.first); |
| 634 | kitchen.requestCoffeeAccess(pair.second); |
| 635 | ``` |
| 636 | |
| 637 | There is a shortcut if you want to provide the other side of the IPC with a |
| 638 | remote to an implementation you own. Instead of creating a pair of a Proxy and |
| 639 | an InterfaceRequest, you can directly supply your implementation object instead |
| 640 | of a proxy and Mojo will create, bind, and send a pending remote for you: |
| 641 | |
| 642 | ```java |
| 643 | CoffeeMachine coffeeMachine = new CoffeeMachineImpl() |
| 644 | employee.provideCoffeeAccess(coffeeMachine); |
| 645 | ``` |
| 646 | |
| 647 | Note that whether you send a pending_remote one way or send a pending_receiver |
| 648 | the other way can have an effect on ownership, lifetimes, and performance. It |
| 649 | may be better to supply a pending_receiver as an argument to a method call than |
| 650 | to wait for a pending_remote from the response callback - especially if you want |
| 651 | to queue up messages before all binding has taken place. |
| 652 | |
| 653 | # Threading |
| 654 | |
| 655 | When receiving IPC calls, Mojo will invoke your interface implementation's |
| 656 | methods on the thread/sequence on which the implementation was bound. This also |
| 657 | extends to the `close` and `onConnectionError` methods. (This is similar to how |
| 658 | things work in C++.) |
| 659 | |
| 660 | You should generally only use the Mojo APIs from within a valid sequence, though |
| 661 | purely manipulating mojom-generated Java data types (structs, enums, unions) is |
| 662 | OK. Various Mojo method calls will crash if invoked from outside of a valid |
| 663 | sequence. |
| 664 | |
| 665 | Note that ordinary Proxy objects are not thread safe and should generally only |
| 666 | be used from the thread on which they are created. However, you can create a |
| 667 | thread safe wrapper around your proxy object using |
| 668 | `InterfaceType.MANAGER.buildThreadSafeProxy(originalProxy)`, which will forward |
| 669 | calls to the thread on which the thread-safe proxy wrapper was created. You must |
| 670 | therefore build the thread-safe proxy from the thread which owns the original |
| 671 | proxy. |