1mod controller;
22mod events;
23mod input;
24mod paths;
25mod tree;
26
27use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
28use std::sync::mpsc::{self, Receiver};
29use std::sync::{Arc, Mutex, PoisonError};
30use std::time::{Duration, Instant};
31
32use mirror::backend::{Backend, FrameSource, ReadReason, StreamItem, SystemClock, Timed};
33use protocol::code;
34use protocol::event::Stream;
35use protocol::message::{Call, Reply, Screenshot, ScreenshotParams};
36use protocol::session::{DisconnectReason, HostEvent};
37
38use self::controller::{ControllerFrames, FramePump, FrameStore};
39use self::events::AgentEvents;
40use self::input::SessionInput;
41use self::tree::AgentTree;
42use crate::adb;
43use crate::agent::{AgentInfo, Attached, Connection, DeployError, Deployer, Outcome, Stopped};
44
45pub use controller::{
46 ControllerLink, INPUT_QUIET, encode_jpeg, frame_meta, resize_rgb, rotate_rgb,
47};
48pub use events::stream_item;
49pub use input::{agent_call, input_ack, swipe_touches};
50pub use paths::{ControllerChoice, Path, Paths, Selection, Status, select_paths};
51pub use tree::{BACKEND, has_event_less_window, scale_from_density, snapshot_params};
52
53#[derive(Clone, Debug, PartialEq, Eq)]
55pub struct ReadOptions {
56 pub max_nodes: Option<u32>,
58 pub max_depth: Option<u32>,
60 pub idle_ms: u32,
64 pub settle_idle_ms: Option<u32>,
68 pub deadline: Duration,
70}
71
72impl Default for ReadOptions {
73 fn default() -> ReadOptions {
74 ReadOptions {
75 max_nodes: None,
76 max_depth: None,
77 idle_ms: 0,
78 settle_idle_ms: None,
79 deadline: Duration::from_secs(10),
80 }
81 }
82}
83
84#[derive(Clone, Debug, PartialEq, Eq)]
86pub struct SessionOptions {
87 pub read: ReadOptions,
88 pub input_deadline: Duration,
90 pub frame_width: u32,
92}
93
94impl Default for SessionOptions {
95 fn default() -> SessionOptions {
96 SessionOptions {
97 read: ReadOptions::default(),
98 input_deadline: Duration::from_secs(5),
99 frame_width: 540,
100 }
101 }
102}
103
104#[derive(Clone, Copy, Debug, PartialEq, Eq)]
106pub struct ReadStats {
107 pub node_count: u32,
109 pub serialized: u32,
111 pub windows: u32,
112 pub took: Duration,
115 pub reason: ReadReason,
116 pub cleared_cache: bool,
120}
121
122#[derive(Debug)]
124pub enum SessionError {
125 Adb(adb::Error),
126 Density(String),
128}
129
130impl std::fmt::Display for SessionError {
131 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
132 match self {
133 SessionError::Adb(error) => write!(f, "adb: {error}"),
134 SessionError::Density(output) => {
135 write!(
136 f,
137 "cannot read the display density from `wm density`: {output}"
138 )
139 }
140 }
141 }
142}
143
144impl std::error::Error for SessionError {}
145
146impl From<adb::Error> for SessionError {
147 fn from(error: adb::Error) -> SessionError {
148 SessionError::Adb(error)
149 }
150}
151
152#[derive(Clone)]
154pub struct Session {
155 inner: Arc<Inner>,
156}
157
158struct Inner {
160 connection: Connection,
161 agent: AgentInfo,
162 outcome: Outcome,
163 deployer: Option<Deployer>,
164 scale: f64,
165 options: SessionOptions,
166 paths: Paths,
168 controller: Option<ControllerLink>,
169 frames: Arc<Mutex<FrameStore>>,
170 frames_received: Arc<AtomicU64>,
171 frame_error: Arc<Mutex<Option<String>>>,
172 pump_stop: Arc<AtomicBool>,
173 items: Mutex<Option<Receiver<Timed<StreamItem>>>>,
175 event_less: AtomicBool,
177 foreign_input: Mutex<Option<Instant>>,
179 failed_resyncs: Arc<Mutex<Vec<Stream>>>,
181 last_read: Mutex<Option<ReadStats>>,
182 reads: AtomicU64,
183 controller_inputs: AtomicU64,
184 agent_inputs: AtomicU64,
185 controller_failed: Mutex<Option<String>>,
187}
188
189impl Inner {
190 fn note_foreign_input(&self, at: Instant) {
191 *self
192 .foreign_input
193 .lock()
194 .unwrap_or_else(PoisonError::into_inner) = Some(at);
195 }
196}
197
198impl Drop for Inner {
199 fn drop(&mut self) {
200 self.pump_stop.store(true, Ordering::SeqCst);
201 }
202}
203
204impl std::fmt::Debug for Session {
205 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
206 f.debug_struct("Session")
207 .field("agent", &self.inner.agent)
208 .field("scale", &self.inner.scale)
209 .field("controller", &self.inner.controller.is_some())
210 .finish_non_exhaustive()
211 }
212}
213
214const SHUTDOWN_DEADLINE: Duration = Duration::from_secs(2);
216
217const SCREENSHOT_DEADLINE: Duration = Duration::from_secs(10);
219
220impl Session {
221 pub fn start(
224 attached: Attached,
225 options: SessionOptions,
226 controller: Option<ControllerLink>,
227 ) -> Result<Session, SessionError> {
228 let selection = Selection {
229 paths: controller
230 .as_ref()
231 .map_or_else(|| Paths::agent(false, None), Paths::controller),
232 link: controller,
233 };
234 Session::start_selected(attached, options, selection)
235 }
236
237 pub fn start_selected(
240 attached: Attached,
241 options: SessionOptions,
242 selection: Selection,
243 ) -> Result<Session, SessionError> {
244 let density = attached.deployer().device().shell("wm density")?;
245 let text = density.stdout_text();
246 let scale = scale_from_density(&text)
247 .ok_or_else(|| SessionError::Density(format!("{text}{}", density.stderr_text())))?;
248 let (connection, agent, outcome, deployer) = attached.into_parts();
249 Ok(Session::over(
250 connection,
251 agent,
252 outcome,
253 Some(deployer),
254 scale,
255 options,
256 selection,
257 ))
258 }
259
260 pub(crate) fn over(
263 connection: Connection,
264 agent: AgentInfo,
265 outcome: Outcome,
266 deployer: Option<Deployer>,
267 scale: f64,
268 options: SessionOptions,
269 selection: Selection,
270 ) -> Session {
271 let Selection {
272 link: controller,
273 paths,
274 } = selection;
275 let (sender, items) = mpsc::channel();
276 let failed_resyncs = Arc::new(Mutex::new(Vec::new()));
277 let events = sender.clone();
278 let failed = Arc::clone(&failed_resyncs);
279 connection.set_sink(Box::new(move |at, event| {
280 if let HostEvent::ResyncFailed { stream, .. } = &event {
281 failed
282 .lock()
283 .unwrap_or_else(PoisonError::into_inner)
284 .push(*stream);
285 }
286 if let Some(item) = stream_item(event) {
287 let _ = events.send(Timed { at, item });
288 }
289 }));
290 let pump = FramePump {
291 store: Arc::new(Mutex::new(FrameStore::default())),
292 items: sender,
293 received: Arc::new(AtomicU64::new(0)),
294 stop: Arc::new(AtomicBool::new(false)),
295 error: Arc::new(Mutex::new(None)),
296 };
297 if let Some(link) = &controller {
298 link.start_frames(options.frame_width, pump.clone());
299 }
300 Session {
301 inner: Arc::new(Inner {
302 connection,
303 agent,
304 outcome,
305 deployer,
306 scale,
307 options,
308 paths,
309 controller,
310 frames: pump.store,
311 frames_received: pump.received,
312 frame_error: pump.error,
313 pump_stop: pump.stop,
314 items: Mutex::new(Some(items)),
315 event_less: AtomicBool::new(false),
316 foreign_input: Mutex::new(None),
317 failed_resyncs,
318 last_read: Mutex::new(None),
319 reads: AtomicU64::new(0),
320 controller_inputs: AtomicU64::new(0),
321 agent_inputs: AtomicU64::new(0),
322 controller_failed: Mutex::new(None),
323 }),
324 }
325 }
326
327 #[cfg(test)]
331 pub(crate) fn note_foreign_input(&self, at: Instant) {
332 self.inner.note_foreign_input(at);
333 }
334
335 pub fn status(&self) -> Status {
338 let inner = &*self.inner;
339 let controller_error = self.controller_error();
340 let mut paths = inner.paths.clone();
341 if controller_error.is_some() {
342 paths.input = Path::Agent;
343 }
344 Status {
345 serial: inner.agent.serial.clone(),
346 agent: inner.agent.agent.clone(),
347 protocol: inner.agent.protocol,
348 pid: inner.agent.pid,
349 socket: inner.agent.socket.clone(),
350 reused: inner.outcome == Outcome::Reused,
351 paths,
352 inputs: self.inputs(),
353 frames_received: self.frames_received(),
354 controller_error,
355 frame_error: self.frame_error(),
356 disconnected: self.close_reason().map(|reason| reason.to_string()),
357 }
358 }
359
360 pub fn backend(&self) -> Backend {
367 let items = self
368 .inner
369 .items
370 .lock()
371 .unwrap_or_else(PoisonError::into_inner)
372 .take()
373 .expect("the backend of a session is taken once");
374 let frames = self.inner.controller.as_ref().map(|_| {
375 Box::new(ControllerFrames {
376 store: Arc::clone(&self.inner.frames),
377 }) as Box<dyn FrameSource>
378 });
379 Backend {
380 tree: Box::new(AgentTree {
381 inner: Arc::clone(&self.inner),
382 }),
383 events: Box::new(AgentEvents { items }),
384 frames,
385 input: Box::new(SessionInput {
386 inner: Arc::clone(&self.inner),
387 }),
388 clock: Box::new(SystemClock),
389 }
390 }
391
392 pub fn agent(&self) -> &AgentInfo {
393 &self.inner.agent
394 }
395
396 pub fn outcome(&self) -> &Outcome {
398 &self.inner.outcome
399 }
400
401 pub fn scale(&self) -> f64 {
403 self.inner.scale
404 }
405
406 pub fn uses_controller(&self) -> bool {
408 self.inner.controller.is_some()
409 }
410
411 pub fn last_read(&self) -> Option<ReadStats> {
413 *self
414 .inner
415 .last_read
416 .lock()
417 .unwrap_or_else(PoisonError::into_inner)
418 }
419
420 pub fn reads(&self) -> u64 {
422 self.inner.reads.load(Ordering::SeqCst)
423 }
424
425 pub fn frames_received(&self) -> u64 {
427 self.inner.frames_received.load(Ordering::SeqCst)
428 }
429
430 pub fn inputs(&self) -> (u64, u64) {
432 (
433 self.inner.controller_inputs.load(Ordering::SeqCst),
434 self.inner.agent_inputs.load(Ordering::SeqCst),
435 )
436 }
437
438 pub fn controller_error(&self) -> Option<String> {
441 self.inner
442 .controller_failed
443 .lock()
444 .unwrap_or_else(PoisonError::into_inner)
445 .clone()
446 }
447
448 pub fn frame_error(&self) -> Option<String> {
451 self.inner
452 .frame_error
453 .lock()
454 .unwrap_or_else(PoisonError::into_inner)
455 .clone()
456 }
457
458 pub fn screenshot(&self, params: ScreenshotParams) -> Result<Screenshot, protocol::Error> {
460 match self
461 .inner
462 .connection
463 .call(Call::Screenshot(params), SCREENSHOT_DEADLINE)?
464 {
465 Reply::Screenshot(shot) => Ok(shot),
466 _ => Err(protocol::Error::new(
467 code::PROTOCOL_ERROR,
468 "the agent answered a screenshot request with another result",
469 )),
470 }
471 }
472
473 pub fn close_reason(&self) -> Option<DisconnectReason> {
475 self.inner.connection.close_reason()
476 }
477
478 pub fn stop(&self, timeout: Duration) -> Result<Stopped, DeployError> {
480 let inner = &*self.inner;
481 inner.pump_stop.store(true, Ordering::SeqCst);
482 match &inner.deployer {
483 Some(deployer) => deployer.stop_agent(
484 &inner.connection,
485 inner.agent.pid,
486 &inner.agent.socket,
487 timeout,
488 ),
489 None => {
490 let _ = inner.connection.call(Call::Shutdown, SHUTDOWN_DEADLINE);
491 inner.connection.close();
492 Ok(Stopped::Exited)
493 }
494 }
495 }
496}
497
498#[cfg(test)]
499mod tests;