diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 7390c82..2548002 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -1,3 +1,13 @@ -# Summary +# **Rsbinder** Development Guide -- [Chapter 1](./chapter_1.md) +**rsbinder** provides crates implemented in pure Rust that make Binder IPC available on both Android and Linux. + +- [Overview](./overview.md) + - [Architecture](./architecture.md) +- [Getting Started](./getting-started.md) + - [Installation](./installation.md) + - [Hello, World!](./hello-world.md) +- [Enable binder for Linux](./enable-binder-for-linux.md) + - [Arch Linux](./arch-linux.md) + - [TODO: Ubuntu Linux]() + - [TODO: Redhat Linux]() \ No newline at end of file diff --git a/src/arch-linux.md b/src/arch-linux.md new file mode 100644 index 0000000..dd04817 --- /dev/null +++ b/src/arch-linux.md @@ -0,0 +1,10 @@ +# Enable Binder IPC on Arch Linux + +Let's install the linux-zen kernel provided by Arch Linux. Zen kernel already includes Binder IPC. +``` +$ pacman -S linux-zen +``` + +For detailed instructions on installing a new kernel in Arch Linux, refer to the following URL. + +[https://wiki.archlinux.org/title/kernel](https://wiki.archlinux.org/title/kernel) diff --git a/src/architecture.md b/src/architecture.md new file mode 100644 index 0000000..e14b441 --- /dev/null +++ b/src/architecture.md @@ -0,0 +1,38 @@ +# Architecture + +```mermaid +--- +title: Binder IPC Architecture +--- +flowchart BT + AIDL + G[[Generated Rust Code + for Service and Client]] + S(Your Binder Service) + C(Binder Client) + H(HUB + Service Manager) + + AIDL-->|rsbinder-aidl compiler|G; + G-.->|Include|S; + G-.->|Include|C; + S-.->|Register Service|H; + C-.->|Query Service|H; + C<-->|Communication|S; +``` + +### Description of each component of the diagram +- AIDL + - The Android Interface Definition Language (AIDL) is a tool that lets users abstract away IPC. Given an interface (specified in a .aidl file), various build systems use the **rsbinder-aidl** crate to construct Rust bindings so that this interface can be used across processes, regardless of the runtime or bitness there. + - [https://source.android.com/docs/core/architecture/aidl](https://source.android.com/docs/core/architecture/aidl) +- Your Binder Service + - Include the Rust code generated from AIDL to create your service. + - Use rsbinder::hub to register your service with the HUB and wait for client requests. +- Binder Client + - Use the Rust code generated from AIDL to communicate with the service. + - Query the HUB to check if the required service is available and receive a Binder Object (rsbinder::SIBinder) for the service. + - Use the Binder Object to communicate with the Binder Service. +- HUB(Service Manager) + - rsbinder provides **rsb_hub**, a service manager for Linux. + - On Android, no additional work is required since the Android service manager is already running. + diff --git a/src/chapter_1.md b/src/chapter_1.md deleted file mode 100644 index b743fda..0000000 --- a/src/chapter_1.md +++ /dev/null @@ -1 +0,0 @@ -# Chapter 1 diff --git a/src/enable-binder-for-linux.md b/src/enable-binder-for-linux.md new file mode 100644 index 0000000..55dcdb5 --- /dev/null +++ b/src/enable-binder-for-linux.md @@ -0,0 +1,12 @@ +# Enable binder for Linux +Most Linux distributions do not have Binder IPC enabled by default, so additional steps are required to use it. + +If you are able to build the Linux kernel yourself, you can enable Binder IPC by adding the following kernel configuration options: +``` +CONFIG_ASHMEM=y +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ANDROID_BINDERFS=y +``` + +Let's examine methods to activate Binder IPC in some representative Linux distributions. \ No newline at end of file diff --git a/src/getting-started.md b/src/getting-started.md new file mode 100644 index 0000000..d8babaf --- /dev/null +++ b/src/getting-started.md @@ -0,0 +1,5 @@ +# Getting Started +If you are new to Binder IPC, +read the [Architecture](./architecture.md) document first. + +The Architecture document provides a comprehensive overview of Binder IPC, including its design, implementation, and usage. It will help you understand the basics of Binder IPC before you start using **rsbinder**. diff --git a/src/hello-world.md b/src/hello-world.md new file mode 100644 index 0000000..ab1d73b --- /dev/null +++ b/src/hello-world.md @@ -0,0 +1,170 @@ +# 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](https://source.android.com/docs/core/architecture/aidl). + +## 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 { + Ok(echo.to_owned()) + } +} + +fn main() -> std::result::Result<(), Box> { + 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> { + 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 = 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 +``` \ No newline at end of file diff --git a/src/installation.md b/src/installation.md new file mode 100644 index 0000000..ad0fbff --- /dev/null +++ b/src/installation.md @@ -0,0 +1,18 @@ +# Installation +Add the following configuration to your Cargo.toml file: + +``` +[dependencies] +rsbinder = "0.2.0" +lazy_static = "1" +async-trait = "0.1" + +[build-dependencies] +rsbinder-aidl = "0.2.0" +``` + +The crates purposes: +- **rsbinder**: This library provides various functionalities for Binder IPC, including communication with the Linux kernel and data serialization/deserialization. +- **lazy_static**: The code generated by the **rsbinder-aidl** compiler depends on the lazy_static crate. +- **async-trait**: The code generated by **rsbinder-aidl** creates both sync and async code. The async code depends on the async-trait crate. +- **rsbinder-aidl**: This is used for AIDL compilation and is invoked in build.rs. diff --git a/src/overview.md b/src/overview.md new file mode 100644 index 0000000..1d043e8 --- /dev/null +++ b/src/overview.md @@ -0,0 +1,17 @@ +# Overview +Welcome to **rsbinder**! + +**rsbinder** is a Rust library and toolset that enables you to utilize Binder IPC on Linux and Android OS. + +Binder IPC is an object-oriented IPC (Inter-Process Communication) mechanism that Google added to the Linux kernel for Android. Android uses Binder IPC for all process communication, and since 2015, it has been integrated into the Linux kernel, making it available on all Linux systems. + +However, since it is rarely used outside of Android, it is disabled by default in most Linux distributions. + +### Key Features of Binder IPC: + +- Object-oriented: Binder IPC provides a clean and intuitive object-oriented API for inter-process communication. +- Efficient: Binder IPC is designed for high performance and low overhead. +- Secure: Binder IPC provides strong security features to prevent unauthorized access and tampering. +- Versatile: Binder IPC can be used for a variety of purposes, including remote procedure calls, data sharing, and event notification. + +Before using Binder IPC, you must enable the feature in the kernel. Please refer to [Enable binder for Linux](./ch02-00-enable-binder-for-linux.md) for detailed instructions on setting it up. diff --git a/src/redhat-linux.md b/src/redhat-linux.md new file mode 100644 index 0000000..e69de29 diff --git a/src/ubuntu-linux.md b/src/ubuntu-linux.md new file mode 100644 index 0000000..e69de29