Skip to main content

ios_core/services/instruments/
deviceinfo.rs

1//! DeviceInfo service – query sysmon attributes for sysmontap configuration.
2
3use tokio::io::{AsyncRead, AsyncWrite};
4
5use crate::services::dtx::codec::{DtxConnection, DtxError};
6use crate::services::dtx::types::{DtxPayload, NSObject};
7
8/// Info about a running process from `runningProcesses`.
9#[derive(Debug, Clone)]
10pub struct RunningProcess {
11    pub pid: u64,
12    pub name: String,
13    pub real_app_name: String,
14    pub is_application: bool,
15}
16
17/// Fetch sysmon system/process attributes needed for sysmontap setConfig:.
18pub struct DeviceInfoClient<S> {
19    conn: DtxConnection<S>,
20    channel_code: i32,
21}
22
23impl<S: AsyncRead + AsyncWrite + Unpin + Send> DeviceInfoClient<S> {
24    pub async fn connect(stream: S) -> Result<Self, DtxError> {
25        let mut conn = DtxConnection::new(stream);
26        let ch = conn.request_channel(super::DEVICE_INFO_SVC).await?;
27        Ok(Self {
28            conn,
29            channel_code: ch,
30        })
31    }
32
33    pub async fn system_attributes(&mut self) -> Result<Vec<plist::Value>, DtxError> {
34        self.get_attrs("sysmonSystemAttributes").await
35    }
36
37    pub async fn process_attributes(&mut self) -> Result<Vec<plist::Value>, DtxError> {
38        self.get_attrs("sysmonProcessAttributes").await
39    }
40
41    /// List all running processes on the device.
42    pub async fn running_processes(&mut self) -> Result<Vec<RunningProcess>, DtxError> {
43        let msg = self
44            .conn
45            .method_call(self.channel_code, "runningProcesses", &[])
46            .await?;
47        tracing::debug!("runningProcesses response: {:?}", msg.payload);
48
49        let arr = match &msg.payload {
50            DtxPayload::Response(NSObject::Array(a)) => a.clone(),
51            DtxPayload::MethodInvocation { args, .. } => {
52                // Some iOS versions return it as a method invocation arg
53                args.iter()
54                    .find_map(|a| {
55                        if let NSObject::Array(arr) = a {
56                            Some(arr.clone())
57                        } else {
58                            None
59                        }
60                    })
61                    .unwrap_or_default()
62            }
63            _ => return Ok(vec![]),
64        };
65
66        let mut result = Vec::with_capacity(arr.len());
67        for item in &arr {
68            if let NSObject::Dict(d) = item {
69                let pid = match d.get("pid") {
70                    Some(NSObject::Uint(n)) => *n,
71                    Some(NSObject::Int(n)) => *n as u64,
72                    _ => continue,
73                };
74                let name = d
75                    .get("name")
76                    .and_then(|v| {
77                        if let NSObject::String(s) = v {
78                            Some(s.clone())
79                        } else {
80                            None
81                        }
82                    })
83                    .unwrap_or_default();
84                let real_app_name = d
85                    .get("realAppName")
86                    .and_then(|v| {
87                        if let NSObject::String(s) = v {
88                            Some(s.clone())
89                        } else {
90                            None
91                        }
92                    })
93                    .unwrap_or_default();
94                let is_application = d
95                    .get("isApplication")
96                    .and_then(|v| {
97                        if let NSObject::Bool(b) = v {
98                            Some(*b)
99                        } else {
100                            None
101                        }
102                    })
103                    .unwrap_or(false);
104                result.push(RunningProcess {
105                    pid,
106                    name,
107                    real_app_name,
108                    is_application,
109                });
110            }
111        }
112        Ok(result)
113    }
114
115    async fn get_attrs(&mut self, method: &str) -> Result<Vec<plist::Value>, DtxError> {
116        let msg = self
117            .conn
118            .method_call(self.channel_code, method, &[])
119            .await?;
120        tracing::debug!("{method} response: {:?}", msg.payload);
121        match &msg.payload {
122            DtxPayload::Response(NSObject::Array(arr)) => Ok(arr
123                .iter()
124                .map(|v| match v {
125                    NSObject::String(s) => plist::Value::String(s.clone()),
126                    NSObject::Int(n) => plist::Value::Integer((*n).into()),
127                    NSObject::Uint(n) => plist::Value::Integer((*n as i64).into()),
128                    _ => plist::Value::String(format!("{v:?}")),
129                })
130                .collect()),
131            _ => Ok(vec![]),
132        }
133    }
134}