droidrun_core/driver/
android.rs1use std::collections::HashSet;
3use std::path::Path;
4
5use async_trait::async_trait;
6use tracing::{debug, info};
7
8use droidrun_adb::AdbDevice;
9
10use crate::error::{DroidrunError, Result};
11use crate::portal::client::PortalClient;
12use crate::portal::keyboard;
13
14use super::{Action, AppInfo, DeviceDriver};
15
16const PORTAL_DEFAULT_TCP_PORT: u16 = 8080;
17
18pub struct AndroidDriver {
20 serial: Option<String>,
21 use_tcp: bool,
22 remote_tcp_port: u16,
23 device: Option<AdbDevice>,
24 portal: Option<PortalClient>,
25 connected: bool,
26 supported: HashSet<Action>,
27}
28
29impl AndroidDriver {
30 pub fn new(serial: Option<&str>, use_tcp: bool) -> Self {
32 let supported = HashSet::from([
33 Action::Tap,
34 Action::Swipe,
35 Action::InputText,
36 Action::PressKey,
37 Action::StartApp,
38 Action::InstallApp,
39 Action::Screenshot,
40 Action::GetUiTree,
41 Action::GetDate,
42 Action::GetApps,
43 Action::ListPackages,
44 Action::Drag,
45 ]);
46
47 Self {
48 serial: serial.map(|s| s.to_string()),
49 use_tcp,
50 remote_tcp_port: PORTAL_DEFAULT_TCP_PORT,
51 device: None,
52 portal: None,
53 connected: false,
54 supported,
55 }
56 }
57
58 pub fn adb_device(&self) -> Result<&AdbDevice> {
60 self.device.as_ref().ok_or(DroidrunError::NotConnected)
61 }
62
63 pub fn portal_client(&self) -> Result<&PortalClient> {
65 self.portal.as_ref().ok_or(DroidrunError::NotConnected)
66 }
67}
68
69#[async_trait]
70impl DeviceDriver for AndroidDriver {
71 async fn connect(&mut self) -> Result<()> {
74 if self.connected {
75 return Ok(());
76 }
77
78 let server = droidrun_adb::AdbServer::default();
80 let device = server.resolve_device(self.serial.as_deref()).await?;
81
82 let state = device.get_state().await?;
84 if !state.is_online() {
85 return Err(DroidrunError::Adb(droidrun_adb::AdbError::DeviceNotOnline(
86 state.to_string(),
87 )));
88 }
89
90 info!("connected to device: {}", device.serial);
91
92 let mut portal = PortalClient::new(device.clone(), self.use_tcp, self.remote_tcp_port);
94 portal.connect().await?;
95
96 keyboard::setup_keyboard(&device).await?;
98
99 self.device = Some(device);
100 self.portal = Some(portal);
101 self.connected = true;
102
103 Ok(())
104 }
105
106 async fn ensure_connected(&mut self) -> Result<()> {
107 if !self.connected {
108 self.connect().await?;
109 }
110 Ok(())
111 }
112
113 async fn tap(&self, x: i32, y: i32) -> Result<()> {
116 let device = self.adb_device()?;
117 device.tap(x, y).await?;
118 Ok(())
119 }
120
121 async fn swipe(
122 &self,
123 x1: i32,
124 y1: i32,
125 x2: i32,
126 y2: i32,
127 duration_ms: u32,
128 ) -> Result<()> {
129 let device = self.adb_device()?;
130 device.swipe(x1, y1, x2, y2, duration_ms).await?;
131 tokio::time::sleep(std::time::Duration::from_millis(duration_ms as u64)).await;
132 Ok(())
133 }
134
135 async fn input_text(&self, text: &str, clear: bool) -> Result<bool> {
136 let portal = self.portal_client()?;
137 portal.input_text(text, clear).await
138 }
139
140 async fn press_key(&self, keycode: i32) -> Result<()> {
141 let device = self.adb_device()?;
142 device.keyevent(keycode).await?;
143 Ok(())
144 }
145
146 async fn drag(
147 &self,
148 x1: i32,
149 y1: i32,
150 x2: i32,
151 y2: i32,
152 duration_ms: u32,
153 ) -> Result<()> {
154 let device = self.adb_device()?;
155 device.drag(x1, y1, x2, y2, duration_ms).await?;
156 Ok(())
157 }
158
159 async fn start_app(&self, package: &str, activity: Option<&str>) -> Result<String> {
162 let device = self.adb_device()?;
163 debug!("starting app {package} with activity {activity:?}");
164 match device.app_start(package, activity).await {
165 Ok(result) => Ok(result),
166 Err(e) => Ok(format!("Failed to start app {package}: {e}")),
167 }
168 }
169
170 async fn install_app(&self, path: &Path) -> Result<String> {
171 let device = self.adb_device()?;
172 let result = device.install(path, &["-g"]).await?;
173 Ok(result)
174 }
175
176 async fn get_apps(&self, include_system: bool) -> Result<Vec<AppInfo>> {
177 let portal = self.portal_client()?;
178 portal.get_apps(include_system).await
179 }
180
181 async fn list_packages(&self, include_system: bool) -> Result<Vec<String>> {
182 let device = self.adb_device()?;
183 let flags = if include_system {
184 vec![]
185 } else {
186 vec!["-3"]
187 };
188 let pkgs = device.list_packages(&flags).await?;
189 Ok(pkgs)
190 }
191
192 async fn screenshot(&self, hide_overlay: bool) -> Result<Vec<u8>> {
195 let portal = self.portal_client()?;
196 portal.take_screenshot(hide_overlay).await
197 }
198
199 async fn get_ui_tree(&self) -> Result<serde_json::Value> {
200 let portal = self.portal_client()?;
201 portal.get_state().await
202 }
203
204 async fn get_date(&self) -> Result<String> {
205 let device = self.adb_device()?;
206 device.get_date().await.map_err(|e| e.into())
207 }
208
209 fn supported_actions(&self) -> &HashSet<Action> {
212 &self.supported
213 }
214}