From f5f2c6b9255dfc61a387d77265a9a69ed598dc3e Mon Sep 17 00:00:00 2001 From: Nicole L Date: Fri, 9 Feb 2024 15:11:10 -0800 Subject: [PATCH] Binder/AIDL content updates (#1618) --- src/SUMMARY.md | 21 +++-- src/android/aidl/birthday-service.md | 5 ++ .../example/birthdayservice/BirthdayInfo.aidl | 6 ++ .../IBirthdayInfoProvider.aidl | 21 +++++ .../birthdayservice/IBirthdayService.aidl | 24 +++++- .../aidl/birthday_service/src/client.rs | 77 ++++++++++++++++--- src/android/aidl/birthday_service/src/lib.rs | 67 +++++++++++++++- src/android/aidl/changing.md | 14 ---- src/android/aidl/client.md | 27 ------- .../example-service/changing-definition.md | 38 +++++++++ .../changing-implementation.md | 46 +++++++++++ src/android/aidl/example-service/client.md | 48 ++++++++++++ .../aidl/{ => example-service}/deploy.md | 6 +- .../aidl/example-service/implementation.md | 1 + src/android/aidl/example-service/interface.md | 25 ++++++ src/android/aidl/example-service/server.md | 36 +++++++++ .../aidl/example-service/service-bindings.md | 33 ++++++++ src/android/aidl/example-service/service.md | 28 +++++++ src/android/aidl/implementation.md | 15 ---- src/android/aidl/interface.md | 18 ----- src/android/aidl/server.md | 15 ---- src/android/aidl/types.md | 9 +++ src/android/aidl/types/arrays.md | 20 +++++ src/android/aidl/types/file-descriptor.md | 40 ++++++++++ src/android/aidl/types/objects.md | 39 ++++++++++ src/android/aidl/types/parcelables.md | 30 ++++++++ src/android/aidl/types/primitives.md | 14 ++++ 27 files changed, 612 insertions(+), 111 deletions(-) create mode 100644 src/android/aidl/birthday-service.md create mode 100644 src/android/aidl/birthday_service/aidl/com/example/birthdayservice/BirthdayInfo.aidl create mode 100644 src/android/aidl/birthday_service/aidl/com/example/birthdayservice/IBirthdayInfoProvider.aidl delete mode 100644 src/android/aidl/changing.md delete mode 100644 src/android/aidl/client.md create mode 100644 src/android/aidl/example-service/changing-definition.md create mode 100644 src/android/aidl/example-service/changing-implementation.md create mode 100644 src/android/aidl/example-service/client.md rename src/android/aidl/{ => example-service}/deploy.md (82%) create mode 100644 src/android/aidl/example-service/implementation.md create mode 100644 src/android/aidl/example-service/interface.md create mode 100644 src/android/aidl/example-service/server.md create mode 100644 src/android/aidl/example-service/service-bindings.md create mode 100644 src/android/aidl/example-service/service.md delete mode 100644 src/android/aidl/implementation.md delete mode 100644 src/android/aidl/interface.md delete mode 100644 src/android/aidl/server.md create mode 100644 src/android/aidl/types.md create mode 100644 src/android/aidl/types/arrays.md create mode 100644 src/android/aidl/types/file-descriptor.md create mode 100644 src/android/aidl/types/objects.md create mode 100644 src/android/aidl/types/parcelables.md create mode 100644 src/android/aidl/types/primitives.md diff --git a/src/SUMMARY.md b/src/SUMMARY.md index a1644cf7..e6237fc9 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -212,12 +212,21 @@ - [Binary](android/build-rules/binary.md) - [Library](android/build-rules/library.md) - [AIDL](android/aidl.md) - - [Interface](android/aidl/interface.md) - - [Implementation](android/aidl/implementation.md) - - [Server](android/aidl/server.md) - - [Deploy](android/aidl/deploy.md) - - [Client](android/aidl/client.md) - - [Changing API](android/aidl/changing.md) + - [Birthday Service Tutorial](android/aidl/birthday-service.md) + - [Interface](android/aidl/example-service/interface.md) + - [Service API](android/aidl/example-service/service-bindings.md) + - [Service](android/aidl/example-service/service.md) + - [Server](android/aidl/example-service/server.md) + - [Deploy](android/aidl/example-service/deploy.md) + - [Client](android/aidl/example-service/client.md) + - [Changing API](android/aidl/example-service/changing-definition.md) + - [Updating Implementations](android/aidl/example-service/changing-implementation.md) + - [AIDL Types](android/aidl/types.md) + - [Primitive Types](android/aidl/types/primitives.md) + - [Array Types](android/aidl/types/arrays.md) + - [Sending Objects](android/aidl/types/objects.md) + - [Parcelables](android/aidl/types/parcelables.md) + - [Sending Files](android/aidl/types/file-descriptor.md) - [Logging](android/logging.md) - [Interoperability](android/interoperability.md) - [With C](android/interoperability/with-c.md) diff --git a/src/android/aidl/birthday-service.md b/src/android/aidl/birthday-service.md new file mode 100644 index 00000000..f9632898 --- /dev/null +++ b/src/android/aidl/birthday-service.md @@ -0,0 +1,5 @@ +# Birthday Service Tutorial + +To illustrate how to use Rust with Binder, we're going to walk through the +process of creating a Binder interface. We're then going to both implement the +described service and write client code that talks to that service. diff --git a/src/android/aidl/birthday_service/aidl/com/example/birthdayservice/BirthdayInfo.aidl b/src/android/aidl/birthday_service/aidl/com/example/birthdayservice/BirthdayInfo.aidl new file mode 100644 index 00000000..8e966bc9 --- /dev/null +++ b/src/android/aidl/birthday_service/aidl/com/example/birthdayservice/BirthdayInfo.aidl @@ -0,0 +1,6 @@ +package com.example.birthdayservice; + +parcelable BirthdayInfo { + String name; + int years; +} diff --git a/src/android/aidl/birthday_service/aidl/com/example/birthdayservice/IBirthdayInfoProvider.aidl b/src/android/aidl/birthday_service/aidl/com/example/birthdayservice/IBirthdayInfoProvider.aidl new file mode 100644 index 00000000..111504fc --- /dev/null +++ b/src/android/aidl/birthday_service/aidl/com/example/birthdayservice/IBirthdayInfoProvider.aidl @@ -0,0 +1,21 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// ANCHOR: IBirthdayInfoProvider +package com.example.birthdayservice; + +interface IBirthdayInfoProvider { + String name(); + int years(); +} diff --git a/src/android/aidl/birthday_service/aidl/com/example/birthdayservice/IBirthdayService.aidl b/src/android/aidl/birthday_service/aidl/com/example/birthdayservice/IBirthdayService.aidl index 9dfb9588..5c310e05 100644 --- a/src/android/aidl/birthday_service/aidl/com/example/birthdayservice/IBirthdayService.aidl +++ b/src/android/aidl/birthday_service/aidl/com/example/birthdayservice/IBirthdayService.aidl @@ -12,11 +12,33 @@ // See the License for the specific language governing permissions and // limitations under the License. -// ANCHOR: IBirthdayService package com.example.birthdayservice; +import com.example.birthdayservice.IBirthdayInfoProvider; +import com.example.birthdayservice.BirthdayInfo; + +// ANCHOR: IBirthdayService /** Birthday service interface. */ interface IBirthdayService { /** Generate a Happy Birthday message. */ String wishHappyBirthday(String name, int years); + // ANCHOR_END: IBirthdayService + + // ANCHOR: with_info + /** The same thing, but with a parcelable. */ + String wishWithInfo(in BirthdayInfo info); + // ANCHOR_END: with_info + + // ANCHOR: with_info_provider + /** The same thing, but using a binder object. */ + String wishWithProvider(IBirthdayInfoProvider provider); + + /** The same thing, but using `IBinder`. */ + String wishWithErasedProvider(IBinder provider); + // ANCHOR_END: with_info_provider + + // ANCHOR: with_file + /** The same thing, but loads info from a file. */ + String wishFromFile(in ParcelFileDescriptor infoFile); + // ANCHOR_END: with_file } diff --git a/src/android/aidl/birthday_service/src/client.rs b/src/android/aidl/birthday_service/src/client.rs index 5e6acb8d..261fcb61 100644 --- a/src/android/aidl/birthday_service/src/client.rs +++ b/src/android/aidl/birthday_service/src/client.rs @@ -12,21 +12,22 @@ // See the License for the specific language governing permissions and // limitations under the License. -// ANCHOR: main //! Birthday service. +use com_example_birthdayservice::aidl::com::example::birthdayservice::BirthdayInfo::BirthdayInfo; +use com_example_birthdayservice::aidl::com::example::birthdayservice::IBirthdayInfoProvider::{ + BnBirthdayInfoProvider, IBirthdayInfoProvider, +}; use com_example_birthdayservice::aidl::com::example::birthdayservice::IBirthdayService::IBirthdayService; -use com_example_birthdayservice::binder; +use com_example_birthdayservice::binder::{self, BinderFeatures, ParcelFileDescriptor}; +use std::error::Error; +use std::fs::File; +use std::io::prelude::*; +// ANCHOR: main const SERVICE_IDENTIFIER: &str = "birthdayservice"; -/// Connect to the BirthdayService. -pub fn connect() -> Result, binder::StatusCode> -{ - binder::get_interface(SERVICE_IDENTIFIER) -} - /// Call the birthday service. -fn main() -> Result<(), binder::Status> { +fn main() -> Result<(), Box> { let name = std::env::args().nth(1).unwrap_or_else(|| String::from("Bob")); let years = std::env::args() .nth(2) @@ -34,8 +35,64 @@ fn main() -> Result<(), binder::Status> { .unwrap_or(42); binder::ProcessState::start_thread_pool(); - let service = connect().expect("Failed to connect to BirthdayService"); + let service = binder::get_interface::(SERVICE_IDENTIFIER) + .map_err(|_| "Failed to connect to BirthdayService")?; + + // Call the service. let msg = service.wishHappyBirthday(&name, years)?; println!("{msg}"); + // ANCHOR_END: main + + // ANCHOR: wish_with_info + service.wishWithInfo(&BirthdayInfo { name: name.clone(), years })?; + // ANCHOR_END: wish_with_info + + // ANCHOR: wish_with_provider + + // Create a binder object for the `IBirthdayInfoProvider` interface. + let provider = BnBirthdayInfoProvider::new_binder( + InfoProvider { name: name.clone(), age: years as u8 }, + BinderFeatures::default(), + ); + + // Send the binder object to the service. + service.wishWithProvider(&provider)?; + + // Perform the same operation but passing the provider as an `SpIBinder`. + service.wishWithErasedProvider(&provider.as_binder())?; + // ANCHOR_END: wish_with_provider + + // ANCHOR: wish_with_file + + // Open a file and put the birthday info in it. + let mut file = File::create("/data/local/tmp/birthday.info").unwrap(); + writeln!(file, "{name}")?; + writeln!(file, "{years}")?; + + // Create a `ParcelFileDescriptor` from the file and send it. + let file = ParcelFileDescriptor::new(file); + service.wishFromFile(&file)?; + // ANCHOR_END: wish_with_file + Ok(()) } + +// ANCHOR: InfoProvider +/// Rust struct implementing the `IBirthdayInfoProvider` interface. +struct InfoProvider { + name: String, + age: u8, +} + +impl binder::Interface for InfoProvider {} + +impl IBirthdayInfoProvider for InfoProvider { + fn name(&self) -> binder::Result { + Ok(self.name.clone()) + } + + fn years(&self) -> binder::Result { + Ok(self.age as i32) + } +} +// ANCHOR_END: InfoProvider diff --git a/src/android/aidl/birthday_service/src/lib.rs b/src/android/aidl/birthday_service/src/lib.rs index 0b783ea8..1a6e5f32 100644 --- a/src/android/aidl/birthday_service/src/lib.rs +++ b/src/android/aidl/birthday_service/src/lib.rs @@ -12,11 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. -// ANCHOR: IBirthdayService //! Implementation of the `IBirthdayService` AIDL interface. +use com_example_birthdayservice::aidl::com::example::birthdayservice::IBirthdayInfoProvider::IBirthdayInfoProvider; use com_example_birthdayservice::aidl::com::example::birthdayservice::IBirthdayService::IBirthdayService; -use com_example_birthdayservice::binder; +use com_example_birthdayservice::aidl::com::example::birthdayservice::BirthdayInfo::BirthdayInfo; +use com_example_birthdayservice::binder::{self, ParcelFileDescriptor, SpIBinder, Strong}; +use std::fs::File; +use std::io::Read; +// ANCHOR: IBirthdayService /// The `IBirthdayService` implementation. pub struct BirthdayService; @@ -26,4 +30,63 @@ impl IBirthdayService for BirthdayService { fn wishHappyBirthday(&self, name: &str, years: i32) -> binder::Result { Ok(format!("Happy Birthday {name}, congratulations with the {years} years!")) } + // ANCHOR_END: IBirthdayService + + fn wishWithInfo(&self, info: &BirthdayInfo) -> binder::Result { + Ok(format!( + "Happy Birthday {}, congratulations with the {} years!", + info.name, info.years, + )) + } + + fn wishWithProvider( + &self, + provider: &Strong, + ) -> binder::Result { + Ok(format!( + "Happy Birthday {}, congratulations with the {} years!", + provider.name()?, + provider.years()?, + )) + } + + fn wishWithErasedProvider( + &self, + provider: &SpIBinder, + ) -> binder::Result { + // Convert the `SpIBinder` to a concrete interface. + let provider = + provider.clone().into_interface::()?; + + Ok(format!( + "Happy Birthday {}, congratulations with the {} years!", + provider.name()?, + provider.years()?, + )) + } + + // ANCHOR: wishFromFile + fn wishFromFile( + &self, + info_file: &ParcelFileDescriptor, + ) -> binder::Result { + // Convert the file descriptor to a `File`. `ParcelFileDescriptor` wraps + // an `OwnedFd`, which can be cloned and then used to create a `File` + // object. + let mut info_file = info_file + .as_ref() + .try_clone() + .map(File::from) + .expect("Invalid file handle"); + + let mut contents = String::new(); + info_file.read_to_string(&mut contents).unwrap(); + + let mut lines = contents.lines(); + let name = lines.next().unwrap(); + let years: i32 = lines.next().unwrap().parse().unwrap(); + + Ok(format!("Happy Birthday {name}, congratulations with the {years} years!")) + } + // ANCHOR_END: wishFromFile } diff --git a/src/android/aidl/changing.md b/src/android/aidl/changing.md deleted file mode 100644 index 37172812..00000000 --- a/src/android/aidl/changing.md +++ /dev/null @@ -1,14 +0,0 @@ -# Changing API - -Let us extend the API with more functionality: we want to let clients specify a -list of lines for the birthday card: - -```java -package com.example.birthdayservice; - -/** Birthday service interface. */ -interface IBirthdayService { - /** Generate a Happy Birthday message. */ - String wishHappyBirthday(String name, int years, in String[] text); -} -``` diff --git a/src/android/aidl/client.md b/src/android/aidl/client.md deleted file mode 100644 index 85af8adb..00000000 --- a/src/android/aidl/client.md +++ /dev/null @@ -1,27 +0,0 @@ -# AIDL Client - -Finally, we can create a Rust client for our new service. - -_birthday_service/src/client.rs_: - -```rust,ignore -{{#include birthday_service/src/client.rs:main}} -``` - -_birthday_service/Android.bp_: - -```javascript -{{#include birthday_service/Android.bp:birthday_client}} -``` - -Notice that the client does not depend on `libbirthdayservice`. - -Build, push, and run the client on your device: - -```shell -{{#include ../build_all.sh:birthday_client}} -``` - -```text -Happy Birthday Charlie, congratulations with the 60 years! -``` diff --git a/src/android/aidl/example-service/changing-definition.md b/src/android/aidl/example-service/changing-definition.md new file mode 100644 index 00000000..27a85acc --- /dev/null +++ b/src/android/aidl/example-service/changing-definition.md @@ -0,0 +1,38 @@ +# Changing API + +Let us extend the API with more functionality: we want to let clients specify a +list of lines for the birthday card: + +```java +package com.example.birthdayservice; + +/** Birthday service interface. */ +interface IBirthdayService { + /** Generate a Happy Birthday message. */ + String wishHappyBirthday(String name, int years, in String[] text); +} +``` + +This results in an updated trait definition for `IBirthdayService`: + +```rust,ignore +trait IBirthdayService { + fn wishHappyBirthday( + &self, + name: &str, + years: i32, + text: &[String], + ) -> binder::Result; +} +``` + +
+ +- Note how the `String[]` in the AIDL definition is translated as a `&[String]` + in Rust, i.e. that idiomatic Rust types are used in the generated bindings + wherever possible: + - `in` array arguments are translated to slices. + - `out` and `inout` args are translated to `&mut Vec`. + - Return values are translated to returning a `Vec`. + +
diff --git a/src/android/aidl/example-service/changing-implementation.md b/src/android/aidl/example-service/changing-implementation.md new file mode 100644 index 00000000..f72fb9a4 --- /dev/null +++ b/src/android/aidl/example-service/changing-implementation.md @@ -0,0 +1,46 @@ +# Updating Client and Service + +Update the client and server code to account for the new API. + +_birthday_service/src/lib.rs_: + +```rust,ignore +impl IBirthdayService for BirthdayService { + fn wishHappyBirthday( + &self, + name: &str, + years: i32, + text: &[String], + ) -> binder::Result { + let mut msg = format!( + "Happy Birthday {name}, congratulations with the {years} years!", + ); + + for line in text { + msg.push('\n'); + msg.push_str(line); + } + + Ok(msg) + } +} +``` + +_birthday_service/src/client.rs_: + +```rust,ignore +let msg = service.wishHappyBirthday( + &name, + years, + &[ + String::from("Habby birfday to yuuuuu"), + String::from("And also: many more"), + ], +)?; +``` + +
+ +- TODO: Move code snippets into project files where they'll actually be built? + +
diff --git a/src/android/aidl/example-service/client.md b/src/android/aidl/example-service/client.md new file mode 100644 index 00000000..4213496d --- /dev/null +++ b/src/android/aidl/example-service/client.md @@ -0,0 +1,48 @@ +# AIDL Client + +Finally, we can create a Rust client for our new service. + +_birthday_service/src/client.rs_: + +```rust,ignore +use com_example_birthdayservice::aidl::com::example::birthdayservice::IBirthdayService::IBirthdayService; +use com_example_birthdayservice::binder; + +{{#include ../birthday_service/src/client.rs:main}} +} +``` + +_birthday_service/Android.bp_: + +```javascript +{{#include ../birthday_service/Android.bp:birthday_client}} +``` + +Notice that the client does not depend on `libbirthdayservice`. + +Build, push, and run the client on your device: + +```shell +{{#include ../../build_all.sh:birthday_client}} +``` + +```text +Happy Birthday Charlie, congratulations with the 60 years! +``` + +
+ +- `Strong` is the trait object representing the service + that the client has connected to. + - `Strong` is a custom smart pointer type for Binder. It handles both an + in-process ref count for the service trait object, and the global Binder ref + count that tracks how many processes have a reference to the object. + - Note that the trait object that the client uses to talk to the service uses + the exact same trait that the server implements. For a given Binder + interface, there is a single Rust trait generated that both client and + server use. +- Use the same service identifier used when registering the service. This should + ideally be defined in a common crate that both the client and server can + depend on. + +
diff --git a/src/android/aidl/deploy.md b/src/android/aidl/example-service/deploy.md similarity index 82% rename from src/android/aidl/deploy.md rename to src/android/aidl/example-service/deploy.md index bcb7728a..b04de8a6 100644 --- a/src/android/aidl/deploy.md +++ b/src/android/aidl/example-service/deploy.md @@ -3,13 +3,13 @@ We can now build, push, and start the service: ```shell -{{#include ../build_all.sh:birthday_server}} +{{#include ../../build_all.sh:birthday_server}} ``` In another terminal, check that the service runs: ```shell -{{#include ../build_all.sh:service_check_birthday_server}} +{{#include ../../build_all.sh:service_check_birthday_server}} ``` ```text @@ -19,7 +19,7 @@ Service birthdayservice: found You can also call the service with `service call`: ```shell -{{#include ../build_all.sh:service_call_birthday_server}} +{{#include ../../build_all.sh:service_call_birthday_server}} ``` ```text diff --git a/src/android/aidl/example-service/implementation.md b/src/android/aidl/example-service/implementation.md new file mode 100644 index 00000000..d2557ff7 --- /dev/null +++ b/src/android/aidl/example-service/implementation.md @@ -0,0 +1 @@ +# Implementation diff --git a/src/android/aidl/example-service/interface.md b/src/android/aidl/example-service/interface.md new file mode 100644 index 00000000..0beed647 --- /dev/null +++ b/src/android/aidl/example-service/interface.md @@ -0,0 +1,25 @@ +# AIDL Interfaces + +You declare the API of your service using an AIDL interface: + +_birthday_service/aidl/com/example/birthdayservice/IBirthdayService.aidl_: + +```java +{{#include ../birthday_service/aidl/com/example/birthdayservice/IBirthdayService.aidl:IBirthdayService}} +} +``` + +_birthday_service/aidl/Android.bp_: + +```javascript +{{#include ../birthday_service/aidl/Android.bp}} +``` + +
+ +- Note that the directory structure under the `aidl/` directory needs to match + the package name used in the AIDL file, i.e. the package is + `com.example.birthdayservice` and the file is at + `aidl/com/example/IBirthdayService.aidl`. + +
diff --git a/src/android/aidl/example-service/server.md b/src/android/aidl/example-service/server.md new file mode 100644 index 00000000..bf0f6567 --- /dev/null +++ b/src/android/aidl/example-service/server.md @@ -0,0 +1,36 @@ +# AIDL Server + +Finally, we can create a server which exposes the service: + +_birthday_service/src/server.rs_: + +```rust,ignore +{{#include ../birthday_service/src/server.rs:main}} +``` + +_birthday_service/Android.bp_: + +```javascript +{{#include ../birthday_service/Android.bp:birthday_server}} +``` + +
+ +The process for taking a user-defined service implementation (in this case the +`BirthdayService` type, which implements the `IBirthdayService`) and starting it +as a Binder service has multiple steps, and may appear more complicated than +students are used to if they've used Binder from C++ or another language. +Explain to students why each step is necessary. + +1. Create an instance of your service type (`BirthdayService`). +1. Wrap the service object in corresponding `Bn*` type (`BnBirthdayService` in + this case). This type is generated by Binder and provides the common Binder + functionality that would be provided by the `BnBinder` base class in C++. We + don't have inheritance in Rust, so instead we use composition, putting our + `BirthdayService` within the generated `BnBinderService`. +1. Call `add_service`, giving it a service identifier and your service object + (the `BnBirthdayService` object in the example). +1. Call `join_thread_pool` to add the current thread to Binder's thread pool and + start listening for connections. + +
diff --git a/src/android/aidl/example-service/service-bindings.md b/src/android/aidl/example-service/service-bindings.md new file mode 100644 index 00000000..0acb3ea6 --- /dev/null +++ b/src/android/aidl/example-service/service-bindings.md @@ -0,0 +1,33 @@ +# Generated Service API + +Binder generates a trait corresponding to the interface definition. trait to +talk to the service. + +_birthday_service/aidl/com/example/birthdayservice/IBirthdayService.aidl_: + +```java +{{#include ../birthday_service/aidl/com/example/birthdayservice/IBirthdayService.aidl:IBirthdayService}} +} +``` + +_Generated trait_: + +```rust,ignore +trait IBirthdayService { + fn wishHappyBirthday(&self, name: &str, years: i32) -> binder::Result; +} +``` + +Your service will need to implement this trait, and your client will use this +trait to talk to the service. + +
+ +- The generated bindings can be found at + `out/soong/.intermediates//`. +- Point out how the generated function signature, specifically the argument and + return types, correspond the interface definition. + - `String` for an argument results in a different Rust type than `String` as a + return type. + +
diff --git a/src/android/aidl/example-service/service.md b/src/android/aidl/example-service/service.md new file mode 100644 index 00000000..d9b1c8f8 --- /dev/null +++ b/src/android/aidl/example-service/service.md @@ -0,0 +1,28 @@ +# Service Implementation + +We can now implement the AIDL service: + +_birthday_service/src/lib.rs_: + +```rust,ignore +use com_example_birthdayservice::aidl::com::example::birthdayservice::IBirthdayService::IBirthdayService; +use com_example_birthdayservice::binder; + +{{#include ../birthday_service/src/lib.rs:IBirthdayService}} +} +``` + +_birthday_service/Android.bp_: + +```javascript +{{#include ../birthday_service/Android.bp:libbirthdayservice}} +``` + +
+ +- Point out the path to the generated `IBirthdayService` trait, and explain why + each of the segments is necessary. +- TODO: What does the `binder::Interface` trait do? Are there methods to + override? Where source? + +
diff --git a/src/android/aidl/implementation.md b/src/android/aidl/implementation.md deleted file mode 100644 index ed948534..00000000 --- a/src/android/aidl/implementation.md +++ /dev/null @@ -1,15 +0,0 @@ -# Service Implementation - -We can now implement the AIDL service: - -_birthday_service/src/lib.rs_: - -```rust,ignore -{{#include birthday_service/src/lib.rs:IBirthdayService}} -``` - -_birthday_service/Android.bp_: - -```javascript -{{#include birthday_service/Android.bp:libbirthdayservice}} -``` diff --git a/src/android/aidl/interface.md b/src/android/aidl/interface.md deleted file mode 100644 index f1380049..00000000 --- a/src/android/aidl/interface.md +++ /dev/null @@ -1,18 +0,0 @@ -# AIDL Interfaces - -You declare the API of your service using an AIDL interface: - -_birthday_service/aidl/com/example/birthdayservice/IBirthdayService.aidl_: - -```java -{{#include birthday_service/aidl/com/example/birthdayservice/IBirthdayService.aidl:IBirthdayService}} -``` - -_birthday_service/aidl/Android.bp_: - -```javascript -{{#include birthday_service/aidl/Android.bp}} -``` - -Add `vendor_available: true` if your AIDL file is used by a binary in the vendor -partition. diff --git a/src/android/aidl/server.md b/src/android/aidl/server.md deleted file mode 100644 index d3af1807..00000000 --- a/src/android/aidl/server.md +++ /dev/null @@ -1,15 +0,0 @@ -# AIDL Server - -Finally, we can create a server which exposes the service: - -_birthday_service/src/server.rs_: - -```rust,ignore -{{#include birthday_service/src/server.rs:main}} -``` - -_birthday_service/Android.bp_: - -```javascript -{{#include birthday_service/Android.bp:birthday_server}} -``` diff --git a/src/android/aidl/types.md b/src/android/aidl/types.md new file mode 100644 index 00000000..ac414fed --- /dev/null +++ b/src/android/aidl/types.md @@ -0,0 +1,9 @@ +# Working With AIDL Types + +AIDL types translate into the appropriate idiomatic Rust type: + +- Primitive types map (mostly) to idiomatic Rust types. +- Collection types like slices, `Vec`s and string types are supported. +- References to AIDL objects and file handles can be sent between clients and + services. +- File handles and parcelables are fully supported. diff --git a/src/android/aidl/types/arrays.md b/src/android/aidl/types/arrays.md new file mode 100644 index 00000000..7b278433 --- /dev/null +++ b/src/android/aidl/types/arrays.md @@ -0,0 +1,20 @@ +# Array Types + +The array types (`T[]`, `byte[]`, and `List`) get translated to the +appropriate Rust array type depending on how they are used in the function +signature: + +| Position | Rust Type | +| ---------------------- | ------------- | +| `in` argument | `&[T]` | +| `out`/`inout` argument | `&mut Vec` | +| Return | `Vec` | + +
+ +- In Android 13 or higher, fixed-size arrays are supported, i.e. `T[N]` becomes + `[T; N]`. Fixed-size arrays can have multiple dimensions (e.g. int[3][4]). In + the Java backend, fixed-size arrays are represented as array types. +- Arrays in parcelable fields always get translated to `Vec`. + +
diff --git a/src/android/aidl/types/file-descriptor.md b/src/android/aidl/types/file-descriptor.md new file mode 100644 index 00000000..2866ef95 --- /dev/null +++ b/src/android/aidl/types/file-descriptor.md @@ -0,0 +1,40 @@ +# Sending Files + +Files can be sent between Binder clients/servers using the +`ParcelFileDescriptor` type: + +**birthday_service/aidl/com/example/birthdayservice/IBirthdayService.aidl**: + +```java +interface IBirthdayService { +{{#include ../birthday_service/aidl/com/example/birthdayservice/IBirthdayService.aidl:with_file}} +} +``` + +**birthday_service/src/client.rs**: + +```rust,ignore +fn main() { + binder::ProcessState::start_thread_pool(); + let service = connect().expect("Failed to connect to BirthdayService"); +{{#include ../birthday_service/src/client.rs:wish_with_file}} +} +``` + +**birthday_service/src/lib.rs**: + +```rust,ignore +impl IBirthdayService for BirthdayService { +{{#include ../birthday_service/src/lib.rs:wishFromFile}} +} +``` + +
+ +- `ParcelFileDescriptor` wraps an `OwnedFd`, and so can be created from a `File` + (or any other type that wraps an `OwnedFd`), and can be used to create a new + `File` handle on the other side. +- Other types of file descriptors can be wrapped and sent, e.g. TCP, UDP, and + UNIX sockets. + +
diff --git a/src/android/aidl/types/objects.md b/src/android/aidl/types/objects.md new file mode 100644 index 00000000..c35fed29 --- /dev/null +++ b/src/android/aidl/types/objects.md @@ -0,0 +1,39 @@ +# Sending Objects + +AIDL objects can be sent either as a concrete AIDL type or as the type-erased +`IBinder` interface: + +**birthday_service/aidl/com/example/birthdayservice/IBirthdayInfoProvider.aidl**: + +```java +{{#include ../birthday_service/aidl/com/example/birthdayservice/IBirthdayInfoProvider.aidl:IBirthdayInfoProvider}} +``` + +**birthday_service/aidl/com/example/birthdayservice/IBirthdayService.aidl**: + +```java +import com.example.birthdayservice.IBirthdayInfoProvider; + +interface IBirthdayService { +{{#include ../birthday_service/aidl/com/example/birthdayservice/IBirthdayService.aidl:with_info_provider}} +} +``` + +**birthday_service/src/client.rs**: + +```rust,ignore +{{#include ../birthday_service/src/client.rs:InfoProvider}} + +fn main() { + binder::ProcessState::start_thread_pool(); + let service = connect().expect("Failed to connect to BirthdayService"); +{{#include ../birthday_service/src/client.rs:wish_with_provider}} +} +``` + +
+ +- Note the usage of `BnBirthdayInfoProvider`. This serves the same purpose as + `BnBirthdayService` that we saw previously. + +
diff --git a/src/android/aidl/types/parcelables.md b/src/android/aidl/types/parcelables.md new file mode 100644 index 00000000..740295a3 --- /dev/null +++ b/src/android/aidl/types/parcelables.md @@ -0,0 +1,30 @@ +# Parcelables + +Binder for Rust supports sending parcelables directly: + +**birthday_service/aidl/com/example/birthdayservice/BirthdayInfo.aidl**: + +```java +{{#include ../birthday_service/aidl/com/example/birthdayservice/BirthdayInfo.aidl}} +``` + +**birthday_service/aidl/com/example/birthdayservice/IBirthdayService.aidl**: + +```java +import com.example.birthdayservice.BirthdayInfo; + +interface IBirthdayService { +{{#include ../birthday_service/aidl/com/example/birthdayservice/IBirthdayService.aidl:with_info}} +} +``` + +**birthday_service/src/client.rs**: + +```rust,ignore +fn main() { + binder::ProcessState::start_thread_pool(); + let service = connect().expect("Failed to connect to BirthdayService"); + +{{#include ../birthday_service/src/client.rs:wish_with_info}} +} +``` diff --git a/src/android/aidl/types/primitives.md b/src/android/aidl/types/primitives.md new file mode 100644 index 00000000..cc37d19e --- /dev/null +++ b/src/android/aidl/types/primitives.md @@ -0,0 +1,14 @@ +# Primitive Types + +Primitive types map (mostly) idiomatically: + +| AIDL Type | Rust Type | Note | +| --------- | --------- | ----------------------------------- | +| `boolean` | `bool` | | +| `byte` | `i8` | Note that bytes are signed. | +| `char` | `u16` | Note the usage of `u16`, NOT `u32`. | +| `int` | `i32` | | +| `long` | `i64` | | +| `float` | `f32` | | +| `double` | `f64` | | +| `String` | `String` | |