mirror of
https://github.com/jkcoxson/idevice.git
synced 2026-03-02 14:36:16 +01:00
Implement core device app launching
This commit is contained in:
@@ -3,7 +3,7 @@
|
|||||||
use log::warn;
|
use log::warn;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
use crate::{obf, IdeviceError, ReadWrite, RsdService};
|
use crate::{obf, pretty_print_plist, IdeviceError, ReadWrite, RsdService};
|
||||||
|
|
||||||
use super::CoreDeviceServiceClient;
|
use super::CoreDeviceServiceClient;
|
||||||
|
|
||||||
@@ -25,7 +25,7 @@ pub struct AppServiceClient<R: ReadWrite> {
|
|||||||
inner: CoreDeviceServiceClient<R>,
|
inner: CoreDeviceServiceClient<R>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Clone, Debug)]
|
||||||
pub struct AppListEntry {
|
pub struct AppListEntry {
|
||||||
#[serde(rename = "isRemovable")]
|
#[serde(rename = "isRemovable")]
|
||||||
pub is_removable: bool,
|
pub is_removable: bool,
|
||||||
@@ -48,6 +48,23 @@ pub struct AppListEntry {
|
|||||||
pub version: Option<String>,
|
pub version: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Clone, Debug)]
|
||||||
|
pub struct LaunchResponse {
|
||||||
|
#[serde(rename = "processIdentifierVersion")]
|
||||||
|
pub process_identifier_version: u32,
|
||||||
|
#[serde(rename = "processIdentifier")]
|
||||||
|
pub pid: u32,
|
||||||
|
#[serde(rename = "executableURL")]
|
||||||
|
pub executable_url: ExecutableUrl,
|
||||||
|
#[serde(rename = "auditToken")]
|
||||||
|
pub audit_token: Vec<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Clone, Debug)]
|
||||||
|
pub struct ExecutableUrl {
|
||||||
|
pub relative: String,
|
||||||
|
}
|
||||||
|
|
||||||
impl<R: ReadWrite> AppServiceClient<R> {
|
impl<R: ReadWrite> AppServiceClient<R> {
|
||||||
pub async fn new(stream: R) -> Result<Self, IdeviceError> {
|
pub async fn new(stream: R) -> Result<Self, IdeviceError> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
@@ -96,4 +113,57 @@ impl<R: ReadWrite> AppServiceClient<R> {
|
|||||||
|
|
||||||
Ok(desd)
|
Ok(desd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn launch_application(
|
||||||
|
&mut self,
|
||||||
|
bundle_id: impl Into<String>,
|
||||||
|
arguments: &[&str],
|
||||||
|
kill_existing: bool,
|
||||||
|
start_suspended: bool,
|
||||||
|
environment: Option<plist::Dictionary>,
|
||||||
|
platform_options: Option<plist::Dictionary>,
|
||||||
|
) -> Result<LaunchResponse, IdeviceError> {
|
||||||
|
let bundle_id = bundle_id.into();
|
||||||
|
|
||||||
|
let req = crate::plist!({
|
||||||
|
"applicationSpecifier": {
|
||||||
|
"bundleIdentifier": {
|
||||||
|
"_0": bundle_id
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"arguments": arguments, // Now this will work directly
|
||||||
|
"environmentVariables": environment.unwrap_or_default(),
|
||||||
|
"standardIOUsesPseudoterminals": true,
|
||||||
|
"startStopped": start_suspended,
|
||||||
|
"terminateExisting": kill_existing,
|
||||||
|
"user": {
|
||||||
|
"shortName": "mobile"
|
||||||
|
},
|
||||||
|
"platformSpecificOptions": plist::Value::Data(crate::util::plist_to_xml_bytes(&platform_options.unwrap_or_default())),
|
||||||
|
},
|
||||||
|
"standardIOIdentifiers": {}
|
||||||
|
})
|
||||||
|
.into_dictionary()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let res = self
|
||||||
|
.inner
|
||||||
|
.invoke("com.apple.coredevice.feature.launchapplication", Some(req))
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let res = match res
|
||||||
|
.as_dictionary()
|
||||||
|
.and_then(|r| r.get("processToken"))
|
||||||
|
.and_then(|x| plist::from_value(x).ok())
|
||||||
|
{
|
||||||
|
Some(r) => r,
|
||||||
|
None => {
|
||||||
|
warn!("CoreDevice res did not contain parsable processToken");
|
||||||
|
return Err(IdeviceError::UnexpectedResponse);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,6 +48,15 @@ async fn main() {
|
|||||||
.action(clap::ArgAction::SetTrue),
|
.action(clap::ArgAction::SetTrue),
|
||||||
)
|
)
|
||||||
.subcommand(Command::new("list").about("Lists the images mounted on the device"))
|
.subcommand(Command::new("list").about("Lists the images mounted on the device"))
|
||||||
|
.subcommand(
|
||||||
|
Command::new("launch")
|
||||||
|
.about("Launch the app on the device")
|
||||||
|
.arg(
|
||||||
|
Arg::new("bundle_id")
|
||||||
|
.required(true)
|
||||||
|
.help("The bundle ID to launch"),
|
||||||
|
),
|
||||||
|
)
|
||||||
.get_matches();
|
.get_matches();
|
||||||
|
|
||||||
if matches.get_flag("about") {
|
if matches.get_flag("about") {
|
||||||
@@ -97,6 +106,21 @@ async fn main() {
|
|||||||
.await
|
.await
|
||||||
.expect("Failed to get apps");
|
.expect("Failed to get apps");
|
||||||
println!("{apps:#?}");
|
println!("{apps:#?}");
|
||||||
|
} else if let Some(matches) = matches.subcommand_matches("launch") {
|
||||||
|
let bundle_id: &String = match matches.get_one("bundle_id") {
|
||||||
|
Some(b) => b,
|
||||||
|
None => {
|
||||||
|
eprintln!("No bundle ID passed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let res = asc
|
||||||
|
.launch_application(bundle_id, &[], false, false, None, None)
|
||||||
|
.await
|
||||||
|
.expect("no launch");
|
||||||
|
|
||||||
|
println!("{res:#?}");
|
||||||
} else {
|
} else {
|
||||||
eprintln!("Invalid usage, pass -h for help");
|
eprintln!("Invalid usage, pass -h for help");
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user