1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
//! Notificaitons service client for iOS instruments protocol.
//!
//! Monitor memory and app notifications
use crate::{
IdeviceError, ReadWrite,
dvt::{
message::AuxValue,
remote_server::{Channel, RemoteServerClient},
},
obf,
};
use plist::Value;
use tracing::warn;
#[derive(Debug)]
pub struct NotificationInfo {
pub notification_type: String,
pub mach_absolute_time: i64,
pub exec_name: String,
pub app_name: String,
pub pid: u32,
pub state_description: String,
}
#[derive(Debug)]
pub struct NotificationsClient<'a, R: ReadWrite> {
/// The underlying channel used for communication
pub channel: Channel<'a, R>,
}
impl<'a, R: ReadWrite> NotificationsClient<'a, R> {
/// Opens a new channel on the remote server client for app notifications
///
/// # Arguments
/// * `client` - The remote server client to connect with
///
/// # Returns
/// The client on success, IdeviceError on failure
pub async fn new(client: &'a mut RemoteServerClient<R>) -> Result<Self, IdeviceError> {
let channel = client
.make_channel(obf!(
"com.apple.instruments.server.services.mobilenotifications"
))
.await?; // Drop `&mut client` before continuing
Ok(Self { channel })
}
/// set the applicaitons and memory notifications enabled
pub async fn start_notifications(&mut self) -> Result<(), IdeviceError> {
let application_method = Value::String("setApplicationStateNotificationsEnabled:".into());
self.channel
.call_method(
Some(application_method),
Some(vec![AuxValue::archived_value(true)]),
false,
)
.await?;
let memory_method = Value::String("setMemoryNotificationsEnabled:".into());
self.channel
.call_method(
Some(memory_method),
Some(vec![AuxValue::archived_value(true)]),
false,
)
.await?;
Ok(())
}
/// Reads the next notification from the service
pub async fn get_notification(&mut self) -> Result<NotificationInfo, IdeviceError> {
let message = self.channel.read_message().await?;
let mut notification = NotificationInfo {
notification_type: "".to_string(),
mach_absolute_time: 0,
exec_name: String::new(),
app_name: String::new(),
pid: 0,
state_description: String::new(),
};
if let Some(aux) = message.aux {
for v in aux.values {
match v {
AuxValue::Array(a) => match ns_keyed_archive::decode::from_bytes(&a) {
Ok(archive) => {
if let Some(dict) = archive.into_dictionary() {
for (key, value) in dict.into_iter() {
match key.as_str() {
"mach_absolute_time" => {
if let Value::Integer(time) = value {
notification.mach_absolute_time =
time.as_signed().unwrap_or(0);
}
}
"execName" => {
if let Value::String(name) = value {
notification.exec_name = name;
}
}
"appName" => {
if let Value::String(name) = value {
notification.app_name = name;
}
}
"pid" => {
if let Value::Integer(pid) = value {
notification.pid =
pid.as_unsigned().unwrap_or(0) as u32;
}
}
"state_description" => {
if let Value::String(desc) = value {
notification.state_description = desc;
}
}
_ => {
warn!("Unknown notificaton key: {} = {:?}", key, value);
}
}
}
}
}
Err(e) => {
warn!("Failed to decode archive: {:?}", e);
}
},
_ => {
warn!("Non-array aux value: {:?}", v);
}
}
}
}
if let Some(Value::String(data)) = message.data {
notification.notification_type = data;
Ok(notification)
} else {
Err(IdeviceError::UnexpectedResponse(
"missing notification type string in message data".into(),
))
}
}
/// set the applicaitons and memory notifications disable
pub async fn stop_notifications(&mut self) -> Result<(), IdeviceError> {
let application_method = Value::String("setApplicationStateNotificationsEnabled:".into());
self.channel
.call_method(
Some(application_method),
Some(vec![AuxValue::archived_value(false)]),
false,
)
.await?;
let memory_method = Value::String("setMemoryNotificationsEnabled:".into());
self.channel
.call_method(
Some(memory_method),
Some(vec![AuxValue::archived_value(false)]),
false,
)
.await?;
Ok(())
}
}