Hello World!
This tutorial will guide you through creating a simple Binder service that echoes a string back to the client, and a client program that uses the service.
Create a new Rust project
Create a new Rust project using Cargo:
$ cargo new --lib hello
Create a library project for the common library used by both the client and service:
Modify Cargo.toml
In the hello project's Cargo.toml, add the following dependencies:
[package]
name = "hello"
version = "0.1.0"
publish = false
edition = "2021"
[dependencies]
rsbinder = "0.2.0"
lazy_static = "1"
async-trait = "0.1"
env_logger = "0.11"
[build-dependencies]
rsbinder-aidl = "0.2.0"
Add rsbinder, lazy_static, and async-trait to [dependencies], and add rsbinder-aidl to [build-dependencies].
Create a AIDL File
Create an aidl folder in the project's top directory to manage AIDL files:
$ mkdir -p aidl/hello
$ touch IHello.aidl
The reason for creating an additional hello folder is to create a space for the hello package.
Create an IHello.aidl file with the following contents:
package hello;
// Defining the IHello Interface
interface IHello {
// Defining the echo() Function.
// The function takes a single parameter of type String and returns a value of type String.
String echo(in String hello);
}
For more information on AIDL syntax, refer to the Android AIDL documentation.
Create the build.rs
Create a build.rs file to compile the AIDL file and generate Rust code:
use std::path::PathBuf;
fn main() {
rsbinder_aidl::Builder::new()
.source(PathBuf::from("aidl/hello/IHello.aidl"))
.output(PathBuf::from("hello.rs"))
.generate().unwrap();
}
This uses rsbinder-aidl to specify the AIDL source file("IHello.aidl") and the generated Rust file name("hello.rs"), and then generates the code.
Create a common library for Client and Service
For the Client and Service, create a library that includes the Rust code generated from AIDL.
Create src/lib.rs and add the following content.
// Include the code hello.rs generated from AIDL.
include!(concat!(env!("OUT_DIR"), "/hello.rs"));
// Set up to use the APIs provided in the code generated for Client and Service.
pub use crate::hello::IHello::*;
// Define the name of the service to be registered in the HUB(service manager).
pub const SERVICE_NAME: &str = "my.hello";
Create a service
Let's configure the src/bin/hello_service.rs file as follows.
use env_logger::Env;
use rsbinder::*;
use hello::*;
// Define the name of the service to be registered in the HUB(service manager).
struct IHelloService;
// Implement the IHello interface for the IHelloService.
impl Interface for IHelloService {
// Reimplement the dump method. This is optional.
fn dump(&self, writer: &mut dyn std::io::Write, _args: &[String]) -> Result<()> {
writeln!(writer, "Dump IHelloService")?;
Ok(())
}
}
// Implement the IHello interface for the IHelloService.
impl IHello for IHelloService {
// Implement the echo method.
fn echo(&self, echo: &str) -> rsbinder::status::Result<String> {
Ok(echo.to_owned())
}
}
fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
env_logger::Builder::from_env(Env::default().default_filter_or("warn")).init();
// Initialize ProcessState with the default binder path and the default max threads.
ProcessState::init_default();
// Start the thread pool.
// This is optional. If you don't call this, only one thread will be created to handle the binder transactions.
ProcessState::start_thread_pool();
// Create a binder service.
let service = BnHello::new_binder(IHelloService{});
// Add the service to binder service manager.
hub::add_service(SERVICE_NAME, service.as_binder())?;
// Join the thread pool.
// This is a blocking call. It will return when the thread pool is terminated.
Ok(ProcessState::join_thread_pool()?)
}
Create a client
Create the src/bin/hello_client.rs file and configure it as follows.
use env_logger::Env;
use rsbinder::*;
use hello::*;
fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
env_logger::Builder::from_env(Env::default().default_filter_or("warn")).init();
// Initialize ProcessState with the default binder path and the default max threads.
ProcessState::init_default();
// Create a Hello proxy from binder service manager.
let hello: rsbinder::Strong<dyn IHello> = hub::get_interface(SERVICE_NAME)
.expect(&format!("Can't find {SERVICE_NAME}"));
// Call echo method of Hello proxy.
println!("Result: {}", hello.echo("Hello World!")?);
Ok(())
}
Project folder and file structure
.
├── Cargo.toml
├── aidl
│ └── hello
│ └── IHello.aidl
├── build.rs
└── src
├── bin
│ ├── hello_client.rs
│ └── hello_service.rs
└── lib.rs
Run Hello Service and Client
- Run hello_service
$ cargo run --bin hello_service
- Run hello_client
$ cargo run --bin hello_client