mirror of
https://github.com/google/comprehensive-rust.git
synced 2025-03-21 14:46:37 +02:00
Binder/AIDL content updates (#1618)
This commit is contained in:
parent
4d0f23e51a
commit
f5f2c6b925
@ -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)
|
||||
|
5
src/android/aidl/birthday-service.md
Normal file
5
src/android/aidl/birthday-service.md
Normal file
@ -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.
|
@ -0,0 +1,6 @@
|
||||
package com.example.birthdayservice;
|
||||
|
||||
parcelable BirthdayInfo {
|
||||
String name;
|
||||
int years;
|
||||
}
|
@ -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();
|
||||
}
|
@ -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
|
||||
}
|
||||
|
@ -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::Strong<dyn IBirthdayService>, binder::StatusCode>
|
||||
{
|
||||
binder::get_interface(SERVICE_IDENTIFIER)
|
||||
}
|
||||
|
||||
/// Call the birthday service.
|
||||
fn main() -> Result<(), binder::Status> {
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
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::<dyn IBirthdayService>(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<String> {
|
||||
Ok(self.name.clone())
|
||||
}
|
||||
|
||||
fn years(&self) -> binder::Result<i32> {
|
||||
Ok(self.age as i32)
|
||||
}
|
||||
}
|
||||
// ANCHOR_END: InfoProvider
|
||||
|
@ -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<String> {
|
||||
Ok(format!("Happy Birthday {name}, congratulations with the {years} years!"))
|
||||
}
|
||||
// ANCHOR_END: IBirthdayService
|
||||
|
||||
fn wishWithInfo(&self, info: &BirthdayInfo) -> binder::Result<String> {
|
||||
Ok(format!(
|
||||
"Happy Birthday {}, congratulations with the {} years!",
|
||||
info.name, info.years,
|
||||
))
|
||||
}
|
||||
|
||||
fn wishWithProvider(
|
||||
&self,
|
||||
provider: &Strong<dyn IBirthdayInfoProvider>,
|
||||
) -> binder::Result<String> {
|
||||
Ok(format!(
|
||||
"Happy Birthday {}, congratulations with the {} years!",
|
||||
provider.name()?,
|
||||
provider.years()?,
|
||||
))
|
||||
}
|
||||
|
||||
fn wishWithErasedProvider(
|
||||
&self,
|
||||
provider: &SpIBinder,
|
||||
) -> binder::Result<String> {
|
||||
// Convert the `SpIBinder` to a concrete interface.
|
||||
let provider =
|
||||
provider.clone().into_interface::<dyn IBirthdayInfoProvider>()?;
|
||||
|
||||
Ok(format!(
|
||||
"Happy Birthday {}, congratulations with the {} years!",
|
||||
provider.name()?,
|
||||
provider.years()?,
|
||||
))
|
||||
}
|
||||
|
||||
// ANCHOR: wishFromFile
|
||||
fn wishFromFile(
|
||||
&self,
|
||||
info_file: &ParcelFileDescriptor,
|
||||
) -> binder::Result<String> {
|
||||
// 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
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
```
|
@ -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!
|
||||
```
|
38
src/android/aidl/example-service/changing-definition.md
Normal file
38
src/android/aidl/example-service/changing-definition.md
Normal file
@ -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<String>;
|
||||
}
|
||||
```
|
||||
|
||||
<details>
|
||||
|
||||
- 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<T>`.
|
||||
- Return values are translated to returning a `Vec<T>`.
|
||||
|
||||
</details>
|
46
src/android/aidl/example-service/changing-implementation.md
Normal file
46
src/android/aidl/example-service/changing-implementation.md
Normal file
@ -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<String> {
|
||||
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"),
|
||||
],
|
||||
)?;
|
||||
```
|
||||
|
||||
<details>
|
||||
|
||||
- TODO: Move code snippets into project files where they'll actually be built?
|
||||
|
||||
</details>
|
48
src/android/aidl/example-service/client.md
Normal file
48
src/android/aidl/example-service/client.md
Normal file
@ -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!
|
||||
```
|
||||
|
||||
<details>
|
||||
|
||||
- `Strong<dyn IBirthdayService>` 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.
|
||||
|
||||
</details>
|
@ -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
|
1
src/android/aidl/example-service/implementation.md
Normal file
1
src/android/aidl/example-service/implementation.md
Normal file
@ -0,0 +1 @@
|
||||
# Implementation
|
25
src/android/aidl/example-service/interface.md
Normal file
25
src/android/aidl/example-service/interface.md
Normal file
@ -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}}
|
||||
```
|
||||
|
||||
<details>
|
||||
|
||||
- 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`.
|
||||
|
||||
</details>
|
36
src/android/aidl/example-service/server.md
Normal file
36
src/android/aidl/example-service/server.md
Normal file
@ -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}}
|
||||
```
|
||||
|
||||
<details>
|
||||
|
||||
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.
|
||||
|
||||
</details>
|
33
src/android/aidl/example-service/service-bindings.md
Normal file
33
src/android/aidl/example-service/service-bindings.md
Normal file
@ -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<String>;
|
||||
}
|
||||
```
|
||||
|
||||
Your service will need to implement this trait, and your client will use this
|
||||
trait to talk to the service.
|
||||
|
||||
<details>
|
||||
|
||||
- The generated bindings can be found at
|
||||
`out/soong/.intermediates/<path to module>/`.
|
||||
- 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.
|
||||
|
||||
</details>
|
28
src/android/aidl/example-service/service.md
Normal file
28
src/android/aidl/example-service/service.md
Normal file
@ -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}}
|
||||
```
|
||||
|
||||
<details>
|
||||
|
||||
- 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?
|
||||
|
||||
</details>
|
@ -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}}
|
||||
```
|
@ -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.
|
@ -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}}
|
||||
```
|
9
src/android/aidl/types.md
Normal file
9
src/android/aidl/types.md
Normal file
@ -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.
|
20
src/android/aidl/types/arrays.md
Normal file
20
src/android/aidl/types/arrays.md
Normal file
@ -0,0 +1,20 @@
|
||||
# Array Types
|
||||
|
||||
The array types (`T[]`, `byte[]`, and `List<T>`) 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<T>` |
|
||||
| Return | `Vec<T>` |
|
||||
|
||||
<details>
|
||||
|
||||
- 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<T>`.
|
||||
|
||||
</details>
|
40
src/android/aidl/types/file-descriptor.md
Normal file
40
src/android/aidl/types/file-descriptor.md
Normal file
@ -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}}
|
||||
}
|
||||
```
|
||||
|
||||
<details>
|
||||
|
||||
- `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.
|
||||
|
||||
</details>
|
39
src/android/aidl/types/objects.md
Normal file
39
src/android/aidl/types/objects.md
Normal file
@ -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}}
|
||||
}
|
||||
```
|
||||
|
||||
<details>
|
||||
|
||||
- Note the usage of `BnBirthdayInfoProvider`. This serves the same purpose as
|
||||
`BnBirthdayService` that we saw previously.
|
||||
|
||||
</details>
|
30
src/android/aidl/types/parcelables.md
Normal file
30
src/android/aidl/types/parcelables.md
Normal file
@ -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}}
|
||||
}
|
||||
```
|
14
src/android/aidl/types/primitives.md
Normal file
14
src/android/aidl/types/primitives.md
Normal file
@ -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` | |
|
Loading…
x
Reference in New Issue
Block a user