Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Binder/AIDL content updates #1618

Merged
merged 23 commits into from
Feb 9, 2024
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
ca53547
Flesh out existing Binder content
randomPoison Dec 10, 2023
fcddc17
Move Birthday Service section into its own directory
randomPoison Dec 20, 2023
f634395
Add first pass at types section
randomPoison Dec 29, 2023
8756902
Remove unused string.md
randomPoison Dec 29, 2023
0d132ef
Add speaker notes for parcelables slide
randomPoison Dec 29, 2023
c94e0f2
Partially extend the BirthdayService example
randomPoison Jan 2, 2024
dc837fc
Fix binder object example
randomPoison Jan 12, 2024
829a75c
Fixup and flesh out the objects slide
randomPoison Jan 13, 2024
3af6844
Cleanup parcelables slide
randomPoison Jan 13, 2024
6b6f112
Implement the service side of things
randomPoison Jan 17, 2024
af905af
Implement service side of wishFromFile
randomPoison Jan 19, 2024
dd6365d
Cleanup birthday service code examples
randomPoison Jan 20, 2024
dde7958
Reword birthday-service.md
randomPoison Jan 20, 2024
1029f40
Re-organize birthday_service example
randomPoison Jan 20, 2024
9cbe86f
Improve wording in speaker notes
randomPoison Jan 20, 2024
d76d7f4
Misc code review cleanup
randomPoison Jan 20, 2024
7eb9cb7
Run formatter
randomPoison Jan 20, 2024
ea184a6
Normalize error handling in client.rs
randomPoison Jan 30, 2024
2b87d25
Misc minor improvements
randomPoison Jan 31, 2024
995005a
Remove comment about `vendor_available: true`
randomPoison Jan 31, 2024
bc415a1
Fix failing tests
randomPoison Jan 31, 2024
2f51ba2
Add note about arrays in parcelables
randomPoison Feb 1, 2024
f517a4e
Use `into_interface` to downcast `SpIBinder`
randomPoison Feb 9, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 15 additions & 6 deletions src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,12 +210,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)
Expand Down
5 changes: 5 additions & 0 deletions src/android/aidl/birthday-service.md
Original file line number Diff line number Diff line change
@@ -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.
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.example.birthdayservice;

parcelable BirthdayInfo {
String name;
int years;
}
Original file line number Diff line number Diff line change
@@ -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();
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
77 changes: 67 additions & 10 deletions src/android/aidl/birthday_service/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,30 +12,87 @@
// 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>> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hope people don't start doing this in their production code. Otoh I'm not sure it's worth talking about anyhow and thiserror here.

let name = std::env::args().nth(1).unwrap_or_else(|| String::from("Bob"));
let years = std::env::args()
.nth(2)
.and_then(|arg| arg.parse::<i32>().ok())
.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
68 changes: 66 additions & 2 deletions src/android/aidl/birthday_service/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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, BpBirthdayInfoProvider};
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;

Expand All @@ -26,4 +30,64 @@ 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> {
use binder::binder_impl::Proxy;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this re-exported by binder? If it's not but it's needed, we should fix that upstream.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does not currently appear to be re-exported.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Never mind, see the next comment.


// Convert the `IBinder` to a concrete interface.
let provider = BpBirthdayInfoProvider::from_binder(provider.clone())?;

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
}
14 changes: 0 additions & 14 deletions src/android/aidl/changing.md

This file was deleted.

27 changes: 0 additions & 27 deletions src/android/aidl/client.md

This file was deleted.

38 changes: 38 additions & 0 deletions src/android/aidl/example-service/changing-definition.md
Original file line number Diff line number Diff line change
@@ -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>
Loading
Loading