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 Err(DroidrunError::NotSupported("drag is not implemented yet".into()))
155 }
156
157 async fn start_app(&self, package: &str, activity: Option<&str>) -> Result<String> {
160 let device = self.adb_device()?;
161 debug!("starting app {package} with activity {activity:?}");
162 match device.app_start(package, activity).await {
163 Ok(result) => Ok(result),
164 Err(e) => Ok(format!("Failed to start app {package}: {e}")),
165 }
166 }
167
168 async fn install_app(&self, path: &Path) -> Result<String> {
169 let device = self.adb_device()?;
170 let result = device.install(path, &["-g"]).await?;
171 Ok(result)
172 }
173
174 async fn get_apps(&self, include_system: bool) -> Result<Vec<AppInfo>> {
175 let portal = self.portal_client()?;
176 portal.get_apps(include_system).await
177 }
178
179 async fn list_packages(&self, include_system: bool) -> Result<Vec<String>> {
180 let device = self.adb_device()?;
181 let flags = if include_system {
182 vec![]
183 } else {
184 vec!["-3"]
185 };
186 let pkgs = device.list_packages(&flags).await?;
187 Ok(pkgs)
188 }
189
190 async fn screenshot(&self, hide_overlay: bool) -> Result<Vec<u8>> {
193 let portal = self.portal_client()?;
194 portal.take_screenshot(hide_overlay).await
195 }
196
197 async fn get_ui_tree(&self) -> Result<serde_json::Value> {
198 let portal = self.portal_client()?;
199 portal.get_state().await
200 }
201
202 async fn get_date(&self) -> Result<String> {
203 let device = self.adb_device()?;
204 device.get_date().await.map_err(|e| e.into())
205 }
206
207 fn supported_actions(&self) -> &HashSet<Action> {
210 &self.supported
211 }
212}