feat: notification proxy (#70)

* init

* chore: clippy and fmt

* feat: ffi wrapper

* feat: multi-observe and timeout to notification proxy

* fix: nitpicks

1. proxy death its onw error in emun #69
2. make returned stream actual stream, copied from 54439b85dd/idevice/src/services/bt_packet_logger.rs (L126-L138)
This commit is contained in:
neo
2026-02-14 15:16:26 -05:00
committed by GitHub
parent 54439b85dd
commit bfe44e16e4
11 changed files with 756 additions and 0 deletions

View File

@@ -33,6 +33,7 @@ mod lockdown;
mod misagent;
mod mobilebackup2;
mod mounter;
mod notification_proxy_client;
mod notifications;
mod os_trace_relay;
mod pair;
@@ -113,6 +114,7 @@ async fn main() {
.with_subcommand("mobilebackup2", mobilebackup2::register())
.with_subcommand("mounter", mounter::register())
.with_subcommand("notifications", notifications::register())
.with_subcommand("notification_proxy", notification_proxy_client::register())
.with_subcommand("os_trace_relay", os_trace_relay::register())
.with_subcommand("pair", pair::register())
.with_subcommand("pcapd", pcapd::register())
@@ -214,6 +216,9 @@ async fn main() {
"notifications" => {
notifications::main(sub_args, provider).await;
}
"notification_proxy" => {
notification_proxy_client::main(sub_args, provider).await;
}
"os_trace_relay" => {
os_trace_relay::main(sub_args, provider).await;
}

View File

@@ -0,0 +1,85 @@
// Jackson Coxson
use idevice::{
IdeviceService, notification_proxy::NotificationProxyClient, provider::IdeviceProvider,
};
use jkcli::{CollectedArguments, JkArgument, JkCommand};
pub fn register() -> JkCommand {
JkCommand::new()
.help("Notification proxy")
.with_subcommand(
"observe",
JkCommand::new()
.help("Observe notifications from the device")
.with_argument(
JkArgument::new()
.with_help("The notification ID to observe")
.required(true),
),
)
.with_subcommand(
"post",
JkCommand::new()
.help("Post a notification to the device")
.with_argument(
JkArgument::new()
.with_help("The notification ID to post")
.required(true),
),
)
.subcommand_required(true)
}
pub async fn main(arguments: &CollectedArguments, provider: Box<dyn IdeviceProvider>) {
let mut client = NotificationProxyClient::connect(&*provider)
.await
.expect("Unable to connect to notification proxy");
let (subcommand, sub_args) = arguments.first_subcommand().unwrap();
let mut sub_args = sub_args.clone();
match subcommand.as_str() {
"observe" => {
let input: String = sub_args
.next_argument::<String>()
.expect("No notification ID passed");
let notifications: Vec<&str> = input.split_whitespace().collect();
client
.observe_notifications(&notifications)
.await
.expect("Failed to observe notifications");
loop {
tokio::select! {
_ = tokio::signal::ctrl_c() => {
println!("\nShutdown signal received, exiting.");
break;
}
result = client.receive_notification() => {
match result {
Ok(notif) => println!("Received notification: {}", notif),
Err(e) => {
eprintln!("Failed to receive notification: {}", e);
break;
}
}
}
}
}
}
"post" => {
let notification: String = sub_args
.next_argument::<String>()
.expect("No notification ID passed");
client
.post_notification(&notification)
.await
.expect("Failed to post notification");
}
_ => unreachable!(),
}
}