Implement FFI for App Service

This commit is contained in:
Jackson Coxson
2025-07-19 23:50:03 -06:00
parent 9793610d94
commit f818225c8c
9 changed files with 709 additions and 6 deletions

View File

@@ -17,6 +17,7 @@ plist_plus = { version = "0.2.6", features = ["dynamic"] }
[features] [features]
afc = ["idevice/afc"] afc = ["idevice/afc"]
amfi = ["idevice/amfi"] amfi = ["idevice/amfi"]
core_device = ["idevice/core_device"]
core_device_proxy = ["idevice/core_device_proxy"] core_device_proxy = ["idevice/core_device_proxy"]
crashreportcopymobile = ["idevice/crashreportcopymobile"] crashreportcopymobile = ["idevice/crashreportcopymobile"]
debug_proxy = ["idevice/debug_proxy"] debug_proxy = ["idevice/debug_proxy"]
@@ -41,6 +42,7 @@ xpc = ["idevice/xpc"]
full = [ full = [
"afc", "afc",
"amfi", "amfi",
"core_device",
"core_device_proxy", "core_device_proxy",
"crashreportcopymobile", "crashreportcopymobile",
"debug_proxy", "debug_proxy",

View File

@@ -0,0 +1,663 @@
// Jackson Coxson
use std::ffi::{CStr, CString, c_char};
use std::os::raw::{c_float, c_int};
use std::ptr::{self, null_mut};
use idevice::core_device::AppServiceClient;
use idevice::tcp::stream::AdapterStream;
use idevice::{IdeviceError, ReadWrite, RsdService};
use crate::core_device_proxy::AdapterHandle;
use crate::rsd::RsdHandshakeHandle;
use crate::{IdeviceFfiError, RUNTIME, ffi_err};
/// Opaque handle to an AppServiceClient
pub struct AppServiceHandle(pub AppServiceClient<Box<dyn ReadWrite>>);
/// C-compatible app list entry
#[repr(C)]
pub struct AppListEntryC {
pub is_removable: c_int,
pub name: *mut c_char,
pub is_first_party: c_int,
pub path: *mut c_char,
pub bundle_identifier: *mut c_char,
pub is_developer_app: c_int,
pub bundle_version: *mut c_char, // NULL if None
pub is_internal: c_int,
pub is_hidden: c_int,
pub is_app_clip: c_int,
pub version: *mut c_char, // NULL if None
}
/// C-compatible launch response
#[repr(C)]
pub struct LaunchResponseC {
pub process_identifier_version: u32,
pub pid: u32,
pub executable_url: *mut c_char,
pub audit_token: *mut u32,
pub audit_token_len: usize,
}
/// C-compatible process token
#[repr(C)]
pub struct ProcessTokenC {
pub pid: u32,
pub executable_url: *mut c_char, // NULL if None
}
/// C-compatible signal response
#[repr(C)]
pub struct SignalResponseC {
pub pid: u32,
pub executable_url: *mut c_char, // NULL if None
pub device_timestamp: u64, // Unix timestamp
pub signal: u32,
}
/// C-compatible icon data
#[repr(C)]
pub struct IconDataC {
pub data: *mut u8,
pub data_len: usize,
pub icon_width: f64,
pub icon_height: f64,
pub minimum_width: f64,
pub minimum_height: f64,
}
/// Creates a new AppServiceClient using RSD connection
///
/// # Arguments
/// * [`provider`] - An adapter created by this library
/// * [`handshake`] - An RSD handshake from the same provider
/// * [`handle`] - Pointer to store the newly created handle
///
/// # Returns
/// An IdeviceFfiError on error, null on success
///
/// # Safety
/// `provider` and `handshake` must be valid pointers to handles allocated by this library
/// `handle` must be a valid pointer to a location where the handle will be stored
#[unsafe(no_mangle)]
pub unsafe extern "C" fn app_service_connect_rsd(
provider: *mut AdapterHandle,
handshake: *mut RsdHandshakeHandle,
handle: *mut *mut AppServiceHandle,
) -> *mut IdeviceFfiError {
if provider.is_null() || handshake.is_null() || handle.is_null() {
return ffi_err!(IdeviceError::FfiInvalidArg);
}
let res: Result<AppServiceClient<AdapterStream>, IdeviceError> = RUNTIME.block_on(async move {
let provider_ref = unsafe { &mut (*provider).0 };
let handshake_ref = unsafe { &mut (*handshake).0 };
AppServiceClient::connect_rsd(provider_ref, handshake_ref).await
});
match res {
Ok(client) => {
let boxed = Box::new(AppServiceHandle(client.box_inner()));
unsafe { *handle = Box::into_raw(boxed) };
null_mut()
}
Err(e) => ffi_err!(e),
}
}
/// Creates a new AppServiceClient from a socket
///
/// # Arguments
/// * [`socket`] - The socket to use for communication
/// * [`handle`] - Pointer to store the newly created handle
///
/// # Returns
/// An IdeviceFfiError on error, null on success
///
/// # Safety
/// `socket` must be a valid pointer to a handle allocated by this library
/// `handle` must be a valid pointer to a location where the handle will be stored
#[unsafe(no_mangle)]
pub unsafe extern "C" fn app_service_new(
socket: *mut Box<dyn ReadWrite>,
handle: *mut *mut AppServiceHandle,
) -> *mut IdeviceFfiError {
if socket.is_null() || handle.is_null() {
return ffi_err!(IdeviceError::FfiInvalidArg);
}
let socket = unsafe { Box::from_raw(socket) };
let res = RUNTIME.block_on(async move { AppServiceClient::new(*socket).await });
match res {
Ok(client) => {
let new_handle = AppServiceHandle(client);
unsafe { *handle = Box::into_raw(Box::new(new_handle)) };
null_mut()
}
Err(e) => ffi_err!(e),
}
}
/// Frees an AppServiceClient handle
///
/// # Safety
/// `handle` must be a valid pointer to a handle allocated by this library or NULL
#[unsafe(no_mangle)]
pub unsafe extern "C" fn app_service_free(handle: *mut AppServiceHandle) {
if !handle.is_null() {
let _ = unsafe { Box::from_raw(handle) };
}
}
/// Lists applications on the device
///
/// # Arguments
/// * [`handle`] - The AppServiceClient handle
/// * [`app_clips`] - Include app clips
/// * [`removable_apps`] - Include removable apps
/// * [`hidden_apps`] - Include hidden apps
/// * [`internal_apps`] - Include internal apps
/// * [`default_apps`] - Include default apps
/// * [`apps`] - Pointer to store the array of apps (caller must free)
/// * [`count`] - Pointer to store the number of apps
///
/// # Returns
/// An IdeviceFfiError on error, null on success
///
/// # Safety
/// `handle`, `apps`, and `count` must be valid pointers
#[unsafe(no_mangle)]
pub unsafe extern "C" fn app_service_list_apps(
handle: *mut AppServiceHandle,
app_clips: c_int,
removable_apps: c_int,
hidden_apps: c_int,
internal_apps: c_int,
default_apps: c_int,
apps: *mut *mut AppListEntryC,
count: *mut usize,
) -> *mut IdeviceFfiError {
if handle.is_null() || apps.is_null() || count.is_null() {
return ffi_err!(IdeviceError::FfiInvalidArg);
}
let client = unsafe { &mut (*handle).0 };
let res = RUNTIME.block_on(async move {
client
.list_apps(
app_clips != 0,
removable_apps != 0,
hidden_apps != 0,
internal_apps != 0,
default_apps != 0,
)
.await
});
match res {
Ok(app_list) => {
let mut c_apps = Vec::with_capacity(app_list.len());
for app in app_list {
let name = match CString::new(app.name) {
Ok(s) => s.into_raw(),
Err(_) => return ffi_err!(IdeviceError::FfiInvalidString),
};
let path = match CString::new(app.path) {
Ok(s) => s.into_raw(),
Err(_) => return ffi_err!(IdeviceError::FfiInvalidString),
};
let bundle_id = match CString::new(app.bundle_identifier) {
Ok(s) => s.into_raw(),
Err(_) => return ffi_err!(IdeviceError::FfiInvalidString),
};
let bundle_version = match app.bundle_version {
Some(v) => match CString::new(v) {
Ok(s) => s.into_raw(),
Err(_) => return ffi_err!(IdeviceError::FfiInvalidString),
},
None => ptr::null_mut(),
};
let version = match app.version {
Some(v) => match CString::new(v) {
Ok(s) => s.into_raw(),
Err(_) => return ffi_err!(IdeviceError::FfiInvalidString),
},
None => ptr::null_mut(),
};
c_apps.push(AppListEntryC {
is_removable: if app.is_removable { 1 } else { 0 },
name,
is_first_party: if app.is_first_party { 1 } else { 0 },
path,
bundle_identifier: bundle_id,
is_developer_app: if app.is_developer_app { 1 } else { 0 },
bundle_version,
is_internal: if app.is_internal { 1 } else { 0 },
is_hidden: if app.is_hidden { 1 } else { 0 },
is_app_clip: if app.is_app_clip { 1 } else { 0 },
version,
});
}
let mut c_apps = c_apps.into_boxed_slice();
let len = c_apps.len();
let ptr = c_apps.as_mut_ptr();
std::mem::forget(c_apps);
unsafe {
*apps = ptr;
*count = len;
}
null_mut()
}
Err(e) => ffi_err!(e),
}
}
/// Frees an array of AppListEntryC structures
///
/// # Safety
/// `apps` must be a valid pointer to an array allocated by app_service_list_apps
/// `count` must match the count returned by app_service_list_apps
#[unsafe(no_mangle)]
pub unsafe extern "C" fn app_service_free_app_list(apps: *mut AppListEntryC, count: usize) {
if !apps.is_null() && count > 0 {
let apps_slice = unsafe { std::slice::from_raw_parts_mut(apps, count) };
for app in apps_slice {
if !app.name.is_null() {
let _ = unsafe { CString::from_raw(app.name) };
}
if !app.path.is_null() {
let _ = unsafe { CString::from_raw(app.path) };
}
if !app.bundle_identifier.is_null() {
let _ = unsafe { CString::from_raw(app.bundle_identifier) };
}
if !app.bundle_version.is_null() {
let _ = unsafe { CString::from_raw(app.bundle_version) };
}
if !app.version.is_null() {
let _ = unsafe { CString::from_raw(app.version) };
}
}
let _ = unsafe { Vec::from_raw_parts(apps, count, count) };
}
}
/// Launches an application
///
/// # Arguments
/// * [`handle`] - The AppServiceClient handle
/// * [`bundle_id`] - Bundle identifier of the app to launch
/// * [`argv`] - NULL-terminated array of arguments
/// * [`argc`] - Number of arguments
/// * [`kill_existing`] - Whether to kill existing instances
/// * [`start_suspended`] - Whether to start suspended
/// * [`response`] - Pointer to store the launch response (caller must free)
///
/// # Returns
/// An IdeviceFfiError on error, null on success
///
/// # Safety
/// All pointer parameters must be valid
#[unsafe(no_mangle)]
pub unsafe extern "C" fn app_service_launch_app(
handle: *mut AppServiceHandle,
bundle_id: *const c_char,
argv: *const *const c_char,
argc: usize,
kill_existing: c_int,
start_suspended: c_int,
response: *mut *mut LaunchResponseC,
) -> *mut IdeviceFfiError {
if handle.is_null() || bundle_id.is_null() || response.is_null() {
return ffi_err!(IdeviceError::FfiInvalidArg);
}
let bundle_id_str = match unsafe { CStr::from_ptr(bundle_id) }.to_str() {
Ok(s) => s,
Err(_) => return ffi_err!(IdeviceError::FfiInvalidString),
};
let mut args = Vec::new();
if !argv.is_null() && argc > 0 {
let argv_slice = unsafe { std::slice::from_raw_parts(argv, argc) };
for &arg in argv_slice {
if !arg.is_null() {
if let Ok(arg_str) = unsafe { CStr::from_ptr(arg) }.to_str() {
args.push(arg_str);
}
}
}
}
let client = unsafe { &mut (*handle).0 };
let res = RUNTIME.block_on(async move {
client
.launch_application(
bundle_id_str,
&args,
kill_existing != 0,
start_suspended != 0,
None, // environment
None, // platform_options
)
.await
});
match res {
Ok(launch_response) => {
let executable_url = match CString::new(launch_response.executable_url.relative) {
Ok(s) => s.into_raw(),
Err(_) => return ffi_err!(IdeviceError::FfiInvalidString),
};
let audit_token_len = launch_response.audit_token.len();
let mut audit_token_vec = launch_response.audit_token.into_boxed_slice();
let audit_token_ptr = audit_token_vec.as_mut_ptr();
std::mem::forget(audit_token_vec);
let c_response = Box::new(LaunchResponseC {
process_identifier_version: launch_response.process_identifier_version,
pid: launch_response.pid,
executable_url,
audit_token: audit_token_ptr,
audit_token_len,
});
unsafe { *response = Box::into_raw(c_response) };
null_mut()
}
Err(e) => ffi_err!(e),
}
}
/// Frees a LaunchResponseC structure
///
/// # Safety
/// `response` must be a valid pointer allocated by app_service_launch_app
#[unsafe(no_mangle)]
pub unsafe extern "C" fn app_service_free_launch_response(response: *mut LaunchResponseC) {
if !response.is_null() {
let response = unsafe { Box::from_raw(response) };
if !response.executable_url.is_null() {
let _ = unsafe { CString::from_raw(response.executable_url) };
}
if !response.audit_token.is_null() && response.audit_token_len > 0 {
let _ = unsafe {
std::slice::from_raw_parts(response.audit_token, response.audit_token_len)
};
}
}
}
/// Lists running processes
///
/// # Arguments
/// * [`handle`] - The AppServiceClient handle
/// * [`processes`] - Pointer to store the array of processes (caller must free)
/// * [`count`] - Pointer to store the number of processes
///
/// # Returns
/// An IdeviceFfiError on error, null on success
///
/// # Safety
/// All pointer parameters must be valid
#[unsafe(no_mangle)]
pub unsafe extern "C" fn app_service_list_processes(
handle: *mut AppServiceHandle,
processes: *mut *mut ProcessTokenC,
count: *mut usize,
) -> *mut IdeviceFfiError {
if handle.is_null() || processes.is_null() || count.is_null() {
return ffi_err!(IdeviceError::FfiInvalidArg);
}
let client = unsafe { &mut (*handle).0 };
let res = RUNTIME.block_on(async move { client.list_processes().await });
match res {
Ok(process_list) => {
let mut c_processes = Vec::with_capacity(process_list.len());
for process in process_list {
let executable_url = match process.executable_url {
Some(url) => match CString::new(url.relative) {
Ok(s) => s.into_raw(),
Err(_) => return ffi_err!(IdeviceError::FfiInvalidString),
},
None => ptr::null_mut(),
};
c_processes.push(ProcessTokenC {
pid: process.pid,
executable_url,
});
}
let mut c_processes = c_processes.into_boxed_slice();
let len = c_processes.len();
let ptr = c_processes.as_mut_ptr();
std::mem::forget(c_processes);
unsafe {
*processes = ptr;
*count = len;
}
null_mut()
}
Err(e) => ffi_err!(e),
}
}
/// Frees an array of ProcessTokenC structures
///
/// # Safety
/// `processes` must be a valid pointer allocated by app_service_list_processes
/// `count` must match the count returned by app_service_list_processes
#[unsafe(no_mangle)]
pub unsafe extern "C" fn app_service_free_process_list(
processes: *mut ProcessTokenC,
count: usize,
) {
if !processes.is_null() && count > 0 {
let processes_slice = unsafe { std::slice::from_raw_parts_mut(processes, count) };
for process in processes_slice {
if !process.executable_url.is_null() {
let _ = unsafe { CString::from_raw(process.executable_url) };
}
}
let _ = unsafe { std::slice::from_raw_parts(processes, count) };
}
}
/// Uninstalls an application
///
/// # Arguments
/// * [`handle`] - The AppServiceClient handle
/// * [`bundle_id`] - Bundle identifier of the app to uninstall
///
/// # Returns
/// An IdeviceFfiError on error, null on success
///
/// # Safety
/// All pointer parameters must be valid
#[unsafe(no_mangle)]
pub unsafe extern "C" fn app_service_uninstall_app(
handle: *mut AppServiceHandle,
bundle_id: *const c_char,
) -> *mut IdeviceFfiError {
if handle.is_null() || bundle_id.is_null() {
return ffi_err!(IdeviceError::FfiInvalidArg);
}
let bundle_id_str = match unsafe { CStr::from_ptr(bundle_id) }.to_str() {
Ok(s) => s,
Err(_) => return ffi_err!(IdeviceError::FfiInvalidString),
};
let client = unsafe { &mut (*handle).0 };
let res = RUNTIME.block_on(async move { client.uninstall_app(bundle_id_str).await });
match res {
Ok(_) => null_mut(),
Err(e) => ffi_err!(e),
}
}
/// Sends a signal to a process
///
/// # Arguments
/// * [`handle`] - The AppServiceClient handle
/// * [`pid`] - Process ID
/// * [`signal`] - Signal number
/// * [`response`] - Pointer to store the signal response (caller must free)
///
/// # Returns
/// An IdeviceFfiError on error, null on success
///
/// # Safety
/// All pointer parameters must be valid
#[unsafe(no_mangle)]
pub unsafe extern "C" fn app_service_send_signal(
handle: *mut AppServiceHandle,
pid: u32,
signal: u32,
response: *mut *mut SignalResponseC,
) -> *mut IdeviceFfiError {
if handle.is_null() || response.is_null() {
return ffi_err!(IdeviceError::FfiInvalidArg);
}
let client = unsafe { &mut (*handle).0 };
let res = RUNTIME.block_on(async move { client.send_signal(pid, signal).await });
match res {
Ok(signal_response) => {
let executable_url = match signal_response.process.executable_url {
Some(url) => match CString::new(url.relative) {
Ok(s) => s.into_raw(),
Err(_) => return ffi_err!(IdeviceError::FfiInvalidString),
},
None => ptr::null_mut(),
};
let timestamp: std::time::SystemTime = signal_response.device_timestamp.into();
let timestamp = timestamp
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_millis() as u64;
let c_response = Box::new(SignalResponseC {
pid: signal_response.process.pid,
executable_url,
device_timestamp: timestamp,
signal: signal_response.signal,
});
unsafe { *response = Box::into_raw(c_response) };
null_mut()
}
Err(e) => ffi_err!(e),
}
}
/// Frees a SignalResponseC structure
///
/// # Safety
/// `response` must be a valid pointer allocated by app_service_send_signal
#[unsafe(no_mangle)]
pub unsafe extern "C" fn app_service_free_signal_response(response: *mut SignalResponseC) {
if !response.is_null() {
let response = unsafe { Box::from_raw(response) };
if !response.executable_url.is_null() {
let _ = unsafe { CString::from_raw(response.executable_url) };
}
}
}
/// Fetches an app icon
///
/// # Arguments
/// * [`handle`] - The AppServiceClient handle
/// * [`bundle_id`] - Bundle identifier of the app
/// * [`width`] - Icon width
/// * [`height`] - Icon height
/// * [`scale`] - Icon scale
/// * [`allow_placeholder`] - Whether to allow placeholder icons
/// * [`icon_data`] - Pointer to store the icon data (caller must free)
///
/// # Returns
/// An IdeviceFfiError on error, null on success
///
/// # Safety
/// All pointer parameters must be valid
#[unsafe(no_mangle)]
pub unsafe extern "C" fn app_service_fetch_app_icon(
handle: *mut AppServiceHandle,
bundle_id: *const c_char,
width: c_float,
height: c_float,
scale: c_float,
allow_placeholder: c_int,
icon_data: *mut *mut IconDataC,
) -> *mut IdeviceFfiError {
if handle.is_null() || bundle_id.is_null() || icon_data.is_null() {
return ffi_err!(IdeviceError::FfiInvalidArg);
}
let bundle_id_str = match unsafe { CStr::from_ptr(bundle_id) }.to_str() {
Ok(s) => s,
Err(_) => return ffi_err!(IdeviceError::FfiInvalidString),
};
let client = unsafe { &mut (*handle).0 };
let res = RUNTIME.block_on(async move {
client
.fetch_app_icon(bundle_id_str, width, height, scale, allow_placeholder != 0)
.await
});
match res {
Ok(icon) => {
let data_vec: Vec<u8> = icon.data.into();
let mut data_vec = data_vec.into_boxed_slice();
let data_len = data_vec.len();
let data_ptr = data_vec.as_mut_ptr();
std::mem::forget(data_vec);
let c_icon = Box::new(IconDataC {
data: data_ptr,
data_len,
icon_width: icon.icon_width,
icon_height: icon.icon_height,
minimum_width: icon.minimum_width,
minimum_height: icon.minimum_height,
});
unsafe { *icon_data = Box::into_raw(c_icon) };
null_mut()
}
Err(e) => ffi_err!(e),
}
}
/// Frees an IconDataC structure
///
/// # Safety
/// `icon_data` must be a valid pointer allocated by app_service_fetch_app_icon
#[unsafe(no_mangle)]
pub unsafe extern "C" fn app_service_free_icon_data(icon_data: *mut IconDataC) {
if !icon_data.is_null() {
let icon_data = unsafe { Box::from_raw(icon_data) };
if !icon_data.data.is_null() && icon_data.data_len > 0 {
let _ = unsafe { std::slice::from_raw_parts(icon_data.data, icon_data.data_len) };
}
}
}

View File

@@ -0,0 +1,3 @@
// Jackson Coxson
pub mod app_service;

View File

@@ -6,6 +6,8 @@ pub mod adapter;
pub mod afc; pub mod afc;
#[cfg(feature = "amfi")] #[cfg(feature = "amfi")]
pub mod amfi; pub mod amfi;
#[cfg(feature = "core_device")]
pub mod core_device;
#[cfg(feature = "core_device_proxy")] #[cfg(feature = "core_device_proxy")]
pub mod core_device_proxy; pub mod core_device_proxy;
#[cfg(feature = "debug_proxy")] #[cfg(feature = "debug_proxy")]

View File

@@ -130,13 +130,19 @@ pub struct IconUuid {
pub classes: Vec<String>, pub classes: Vec<String>,
} }
impl<R: ReadWrite> AppServiceClient<R> { impl<'a, R: ReadWrite + 'a> AppServiceClient<R> {
pub async fn new(stream: R) -> Result<Self, IdeviceError> { pub async fn new(stream: R) -> Result<Self, IdeviceError> {
Ok(Self { Ok(Self {
inner: CoreDeviceServiceClient::new(stream).await?, inner: CoreDeviceServiceClient::new(stream).await?,
}) })
} }
pub fn box_inner(self) -> AppServiceClient<Box<dyn ReadWrite + 'a>> {
AppServiceClient {
inner: self.inner.box_inner(),
}
}
pub async fn list_apps( pub async fn list_apps(
&mut self, &mut self,
app_clips: bool, app_clips: bool,

View File

@@ -9,7 +9,7 @@ use crate::{
}; };
mod app_service; mod app_service;
pub use app_service::{AppListEntry, AppServiceClient}; pub use app_service::*;
const CORE_SERVICE_VERSION: &str = "443.18"; const CORE_SERVICE_VERSION: &str = "443.18";
@@ -17,13 +17,19 @@ pub struct CoreDeviceServiceClient<R: ReadWrite> {
inner: RemoteXpcClient<R>, inner: RemoteXpcClient<R>,
} }
impl<R: ReadWrite> CoreDeviceServiceClient<R> { impl<'a, R: ReadWrite + 'a> CoreDeviceServiceClient<R> {
pub async fn new(inner: R) -> Result<Self, IdeviceError> { pub async fn new(inner: R) -> Result<Self, IdeviceError> {
let mut client = RemoteXpcClient::new(inner).await?; let mut client = RemoteXpcClient::new(inner).await?;
client.do_handshake().await?; client.do_handshake().await?;
Ok(Self { inner: client }) Ok(Self { inner: client })
} }
pub fn box_inner(self) -> CoreDeviceServiceClient<Box<dyn ReadWrite + 'a>> {
CoreDeviceServiceClient {
inner: self.inner.box_inner(),
}
}
pub async fn invoke( pub async fn invoke(
&mut self, &mut self,
feature: impl Into<String>, feature: impl Into<String>,

View File

@@ -23,7 +23,7 @@ impl<R: ReadWrite> RsdService for RestoreServiceClient<R> {
type Stream = R; type Stream = R;
} }
impl<R: ReadWrite> RestoreServiceClient<R> { impl<'a, R: ReadWrite + 'a> RestoreServiceClient<R> {
/// Creates a new Restore Service client a socket connection, /// Creates a new Restore Service client a socket connection,
/// and connects to the RemoteXPC service. /// and connects to the RemoteXPC service.
/// ///
@@ -35,6 +35,12 @@ impl<R: ReadWrite> RestoreServiceClient<R> {
Ok(Self { stream }) Ok(Self { stream })
} }
pub fn box_inner(self) -> RestoreServiceClient<Box<dyn ReadWrite + 'a>> {
RestoreServiceClient {
stream: self.stream.box_inner(),
}
}
/// Enter recovery /// Enter recovery
pub async fn enter_recovery(&mut self) -> Result<(), IdeviceError> { pub async fn enter_recovery(&mut self) -> Result<(), IdeviceError> {
let mut req = Dictionary::new(); let mut req = Dictionary::new();

View File

@@ -17,7 +17,7 @@ pub struct Http2Client<R: ReadWrite> {
cache: HashMap<u32, VecDeque<Vec<u8>>>, cache: HashMap<u32, VecDeque<Vec<u8>>>,
} }
impl<R: ReadWrite> Http2Client<R> { impl<'a, R: ReadWrite + 'a> Http2Client<R> {
/// Writes the magic and inits the caches /// Writes the magic and inits the caches
pub async fn new(mut inner: R) -> Result<Self, IdeviceError> { pub async fn new(mut inner: R) -> Result<Self, IdeviceError> {
inner.write_all(HTTP2_MAGIC).await?; inner.write_all(HTTP2_MAGIC).await?;
@@ -28,6 +28,13 @@ impl<R: ReadWrite> Http2Client<R> {
}) })
} }
pub fn box_inner(self) -> Http2Client<Box<dyn ReadWrite + 'a>> {
Http2Client {
inner: Box::new(self.inner),
cache: self.cache,
}
}
pub async fn set_settings( pub async fn set_settings(
&mut self, &mut self,
settings: Vec<frame::Setting>, settings: Vec<frame::Setting>,

View File

@@ -20,7 +20,7 @@ pub struct RemoteXpcClient<R: ReadWrite> {
reply_id: u64, reply_id: u64,
} }
impl<R: ReadWrite> RemoteXpcClient<R> { impl<'a, R: ReadWrite + 'a> RemoteXpcClient<R> {
pub async fn new(socket: R) -> Result<Self, IdeviceError> { pub async fn new(socket: R) -> Result<Self, IdeviceError> {
Ok(Self { Ok(Self {
h2_client: http2::Http2Client::new(socket).await?, h2_client: http2::Http2Client::new(socket).await?,
@@ -29,6 +29,14 @@ impl<R: ReadWrite> RemoteXpcClient<R> {
}) })
} }
pub fn box_inner(self) -> RemoteXpcClient<Box<dyn ReadWrite + 'a>> {
RemoteXpcClient {
h2_client: self.h2_client.box_inner(),
root_id: self.root_id,
reply_id: self.reply_id,
}
}
pub async fn do_handshake(&mut self) -> Result<(), IdeviceError> { pub async fn do_handshake(&mut self) -> Result<(), IdeviceError> {
self.h2_client self.h2_client
.set_settings( .set_settings(