use std::collections::HashMap;
use std::ffi::{OsStr, OsString};
use std::io::{BufRead, BufReader};
use std::process::{ExitStatus, Output};
use std::sync::Mutex;
use std::time::Duration;
use lru::LruCache;
use cmd_lib::AsOsStr;
use crossbeam_channel::Receiver;
use lazy_static::lazy_static;
use regex::Regex;
use rustix::path::Arg;
use simple_cmd::debug::CommandDebug;
use simple_cmd::prelude::OutputExt;
use simple_cmd::CommandBuilder;
use crate::error::Error;
use crate::prelude::*;
use crate::result::Result;
use crate::traits::AsArg;
use crate::types::{
ActivityManager, ChmodOptions, DumpsysPriority, FFPlayOptions, InputSource, KeyCode,
KeyEventType, MotionEvent, PackageManager, PropType, Property, SELinuxType,
ScreenRecordOptions, SettingsType, Shell,
};
lazy_static! {
static ref RE_GET_PROPS: Regex =
Regex::new("(?m)^\\[(.*)\\]\\s*:\\s*\\[([^\\]]*)\\]$").unwrap();
static ref COMMANDS_CACHE: Mutex<LruCache<String, Option<String>>> =
Mutex::new(LruCache::new(std::num::NonZeroUsize::new(10).unwrap()));
}
pub(crate) fn handle_result(result: Output) -> Result<()> {
if result.error() && !result.kill() && !result.interrupt() {
Err(result.into())
} else {
Ok(())
}
}
fn make_keyevent_combination<I, S>(source: Option<InputSource>, keycodes: I) -> Vec<OsString>
where
I: IntoIterator<Item = S>,
S: Into<KeyCode>,
{
let mut args = vec!["input".into()];
if let Some(source) = source {
args.push(source.into());
}
args.push("keycombination".into());
args.extend(keycodes.into_iter().map(|k| k.into().as_os_str()));
args
}
fn make_keycode_combination<I>(source: Option<InputSource>, keycodes: I) -> Vec<OsString>
where
I: IntoIterator<Item = u32>,
{
let mut args = vec!["input".into()];
if let Some(source) = source {
args.push(source.into());
}
args.push("keycombination".into());
args.extend(keycodes.into_iter().map(|k| k.to_string().into()));
args
}
fn make_draganddrop(
source: Option<InputSource>,
duration: Option<Duration>,
from_pos: (i32, i32),
to_pos: (i32, i32),
) -> Vec<OsString> {
let mut args = vec!["input".into()];
if let Some(source) = source {
args.push(source.into());
}
args.push("draganddrop".into());
let pos0 = from_pos.0.to_string();
let pos1 = from_pos.1.to_string();
let pos2 = to_pos.0.to_string();
let pos3 = to_pos.1.to_string();
args.push(pos0.into());
args.push(pos1.into());
args.push(pos2.into());
args.push(pos3.into());
if let Some(duration) = duration {
args.push(duration.as_millis().to_string().into());
}
args
}
fn make_press(source: Option<InputSource>) -> Vec<OsString> {
let mut args = vec!["input".into()];
if let Some(source) = source {
args.push(source.into());
}
args.push("press".into());
args
}
fn make_swipe(
from_pos: (i32, i32),
to_pos: (i32, i32),
duration: Option<Duration>,
source: Option<InputSource>,
) -> Vec<OsString> {
let mut args = vec!["input".into()];
if let Some(source) = source {
args.push(source.into());
}
args.push("swipe".into());
let pos_string = format!(
"{:?} {:?} {:?} {:?}",
from_pos.0, from_pos.1, to_pos.0, to_pos.1
);
args.push(pos_string.into());
if let Some(duration) = duration {
args.push(duration.as_millis().to_string().into());
}
args
}
fn make_keyevent(
keycode: KeyCode,
event_type: Option<KeyEventType>,
source: Option<InputSource>,
) -> Vec<OsString> {
let mut args = vec!["input".into()];
if let Some(source) = source {
args.push(source.into());
}
args.push("keyevent".into());
if let Some(event_type) = event_type {
args.push(event_type.into());
}
args.push(keycode.to_string().into());
args
}
fn make_keycode(
keycode: u32,
event_type: Option<KeyEventType>,
source: Option<InputSource>,
) -> Vec<OsString> {
let mut args = vec!["input".into()];
if let Some(source) = source {
args.push(source.into());
}
args.push("keyevent".into());
if let Some(event_type) = event_type {
args.push(event_type.into());
}
args.push(keycode.to_string().into());
args
}
fn make_tap(position: (i32, i32), source: Option<InputSource>) -> Vec<OsString> {
let mut args = vec!["input".into()];
if let Some(source) = source {
args.push(source.into());
}
args.push("tap".into());
let pos0 = format!("{:?}", position.0);
let pos1 = format!("{:?}", position.1);
args.push(pos0.into());
args.push(pos1.into());
args
}
fn make_char(chr: char, source: Option<InputSource>) -> Vec<OsString> {
let mut args = vec!["input".into()];
if let Some(source) = source {
args.push(source.into());
}
args.push("text".into());
args.push(format!("{:}", chr).into());
args
}
fn make_text(text: &str, source: Option<InputSource>) -> Vec<OsString> {
let mut args = vec!["input".into()];
if let Some(source) = source {
args.push(source.into());
}
args.push("text".into());
args.push(format!("{:?}", text).into());
args
}
fn make_event(event: &str, code_type: i32, code: i32, value: i32) -> Vec<OsString> {
vec![
"sendevent".into(),
event.into(),
format!("{}", code_type).into(),
format!("{}", code).into(),
format!("{}", value).into(),
]
}
fn make_motion(source: Option<InputSource>, motion: MotionEvent, pos: (i32, i32)) -> Vec<OsString> {
let mut args = vec!["input".into()];
if let Some(source) = source {
args.push(source.into());
}
args.push("motionevent".into());
args.push(motion.into());
let pos0 = pos.0.to_string();
let pos1 = pos.1.to_string();
args.push(pos0.into());
args.push(pos1.into());
args
}
fn make_keyevents<I, S>(keycodes: I, source: Option<InputSource>) -> Vec<OsString>
where
I: IntoIterator<Item = S>,
S: Into<KeyCode>,
{
let mut args = vec!["input".into()];
if let Some(source) = source {
args.push(source.into());
}
args.push("keyevent".into());
args.extend(
keycodes
.into_iter()
.map(|k| k.into().into())
.collect::<Vec<OsString>>(),
);
args
}
fn make_keycodes<I>(keycodes: I, source: Option<InputSource>) -> Vec<OsString>
where
I: IntoIterator<Item = u32>,
{
let mut args = vec!["input".into()];
if let Some(source) = source {
args.push(source.into());
}
args.push("keyevent".into());
let keycodes_string = keycodes
.into_iter()
.map(|k| k.to_string().into())
.collect::<Vec<OsString>>();
args.extend(keycodes_string);
args
}
macro_rules! ro_build_property {
($name:tt, $key:tt, $typ:ty) => {
pub fn $name(&self) -> Result<$typ> {
self.getprop_with_type::<$typ>($key)
}
};
}
impl<'a> Shell<'a> {
pub fn exec<I, S>(
&self,
args: I,
cancel: Option<Receiver<()>>,
timeout: Option<Duration>,
) -> Result<Output>
where
I: IntoIterator<Item = S>,
S: AsRef<OsStr>,
{
let builder = CommandBuilder::shell(self.parent)
.args(args)
.signal(cancel)
.timeout(timeout);
Ok(builder.build().output()?)
}
pub fn try_exec<I, S>(
&self,
args: I,
cancel: Option<Receiver<()>>,
timeout: Option<Duration>,
) -> Result<Option<ExitStatus>>
where
I: IntoIterator<Item = S>,
S: AsRef<OsStr>,
{
let builder = CommandBuilder::shell(self.parent)
.args(args)
.signal(cancel)
.timeout(timeout);
Ok(builder.build().run()?)
}
pub fn is_root(&self) -> Result<bool> {
let whoami = self.whoami()?;
Ok(whoami.eq_ignore_ascii_case("root"))
}
pub fn whoami(&self) -> Result<String> {
let output = self.exec(vec!["whoami"], None, None)?;
Ok(Arg::as_str(&output.stdout)?.trim().to_owned())
}
pub fn mount<T: Arg>(&self, dir: T) -> Result<()> {
self.exec(vec!["mount -o rw,remount", dir.as_str()?], None, None)?;
Ok(())
}
pub fn unmount<T: Arg>(&self, dir: T) -> Result<()> {
self.exec(vec!["mount -o ro,remount", dir.as_str()?], None, None)?;
Ok(())
}
pub fn cat<P: Arg>(&self, path: P) -> Result<Vec<u8>> {
self.exec(vec!["cat", path.as_str()?], None, None)
.map(|s| s.stdout)
}
pub fn chmod<P: Arg>(&self, mode: ChmodOptions, path: Vec<P>) -> Result<Output> {
let mut args = vec!["chmod".into()];
args.extend(mode);
args.extend(
path.into_iter()
.map(|p| p.as_str().unwrap_or_default().into()),
);
self.exec(args, None, None)
}
pub fn check_avbctl(&self) -> Result<()> {
self.get_command_path("avbctl")
.map(|_| ())
.ok_or(Error::AvbctlNotInstalled)
}
#[allow(dead_code)]
pub fn has_avbctl(&self) -> Result<bool> {
match self.check_avbctl() {
Ok(_) => Ok(true),
Err(err) => match err {
Error::AvbctlNotInstalled => Ok(false),
_ => Err(err),
},
}
}
pub fn get_command_path<T: Arg>(&self, command: T) -> Option<String> {
if let Ok(command_string) = command.as_str() {
let mut binding = COMMANDS_CACHE.lock().unwrap();
let cache_key = format!("{}{}", self.parent.addr, command_string);
if let Some(cached) = binding.get(&cache_key) {
return cached.clone();
}
let value = self
.exec(
vec![format!("command -v {}", command_string).as_str()],
None,
None,
)
.and_then(|result| Ok(Arg::as_str(&result.stdout)?.trim().to_string()))
.and_then(|result| {
if result.is_empty() {
Err(std::io::ErrorKind::NotFound.into())
} else {
Ok(result)
}
})
.ok();
binding.put(cache_key, value.clone());
value
} else {
None
}
}
pub fn which<T: Arg>(&self, command: T) -> Option<String> {
if let Ok(command) = command.as_str() {
let output = self.exec(vec!["which", command], None, None);
if let Ok(output) = output {
simple_cmd::Vec8ToString::as_str(&output.stdout)
.map(|ss| String::from(ss.trim_end()))
} else {
None
}
} else {
None
}
}
pub fn get_verity(&self) -> Result<bool> {
let _ = self.check_avbctl()?;
let output = self.exec(vec!["avbctl", "get-verity"], None, None)?;
let string = Arg::as_str(&output.stdout)?;
Ok(string.contains("enabled"))
}
pub fn disable_verity(&self) -> Result<()> {
let _ = self.check_avbctl()?;
let output = self.exec(vec!["avbctl", "disable-verity"], None, None)?;
if output.error() {
Err(output.into())
} else {
Ok(())
}
}
pub fn enable_verity(&self) -> Result<()> {
let _ = self.check_avbctl()?;
let output = self.exec(vec!["avbctl", "enable-verity"], None, None)?;
if output.error() {
Err(output.into())
} else {
Ok(())
}
}
pub fn get_enforce(&self) -> Result<SELinuxType> {
let result = self.exec(vec!["getenforce"], None, None)?.stdout;
let enforce: SELinuxType = SELinuxType::try_from(result)?;
Ok(enforce)
}
pub fn set_enforce(&self, enforce: SELinuxType) -> Result<()> {
self.exec(vec!["setenforce".into(), enforce.as_arg()], None, None)
.map(|_| ())
}
pub fn is_screen_on(&self) -> Result<bool> {
let process_result = self.exec(
vec!["dumpsys input_method | egrep 'mInteractive=(true|false)'"],
None,
None,
)?;
let result = rustix::path::Arg::as_str(&process_result.stdout)
.map(|f| f.contains("mInteractive=true"))?;
Ok(result)
}
pub fn send_keyevent(
&self,
keycode: KeyCode,
event_type: Option<KeyEventType>,
source: Option<InputSource>,
) -> Result<()> {
let result = self.exec(make_keyevent(keycode, event_type, source), None, None)?;
handle_result(result)
}
pub fn send_keycode(
&self,
keycode: u32,
event_type: Option<KeyEventType>,
source: Option<InputSource>,
) -> Result<()> {
handle_result(self.exec(make_keycode(keycode, event_type, source), None, None)?)
}
pub fn send_swipe(
&self,
from_pos: (i32, i32),
to_pos: (i32, i32),
duration: Option<Duration>,
source: Option<InputSource>,
) -> Result<()> {
handle_result(self.exec(make_swipe(from_pos, to_pos, duration, source), None, None)?)
}
pub fn send_tap(&self, position: (i32, i32), source: Option<InputSource>) -> Result<()> {
let result = self.exec(make_tap(position, source), None, None)?;
handle_result(result)
}
pub fn send_event(&self, event: &str, code_type: i32, code: i32, value: i32) -> Result<()> {
handle_result(self.exec(make_event(event, code_type, code, value), None, None)?)
}
pub fn send_char(&self, chr: char, source: Option<InputSource>) -> Result<()> {
let result = self.exec(make_char(chr, source), None, None)?;
handle_result(result)
}
pub fn send_text(&self, text: &str, source: Option<InputSource>) -> Result<()> {
handle_result(self.exec(make_text(text, source), None, None)?)
}
pub fn send_motion(
&self,
source: Option<InputSource>,
motion: MotionEvent,
pos: (i32, i32),
) -> Result<()> {
handle_result(self.exec(make_motion(source, motion, pos), None, None)?)
}
pub fn send_draganddrop(
&self,
source: Option<InputSource>,
duration: Option<Duration>,
from_pos: (i32, i32),
to_pos: (i32, i32),
) -> Result<()> {
handle_result(self.exec(
make_draganddrop(source, duration, from_pos, to_pos),
None,
None,
)?)
}
pub fn send_press(&self, source: Option<InputSource>) -> Result<()> {
handle_result(self.exec(make_press(source), None, None)?)
}
pub fn send_keyevents<I, S>(&self, keycodes: I, source: Option<InputSource>) -> Result<()>
where
I: IntoIterator<Item = S>,
S: Into<KeyCode>,
{
handle_result(self.exec(make_keyevents(keycodes, source), None, None)?)
}
pub fn send_keycodes<I>(&self, keycodes: I, source: Option<InputSource>) -> Result<()>
where
I: IntoIterator<Item = u32>,
{
handle_result(self.exec(make_keycodes(keycodes, source), None, None)?)
}
pub fn send_keyevent_combination<I, S>(
&self,
source: Option<InputSource>,
keycodes: I,
) -> Result<()>
where
I: IntoIterator<Item = S>,
S: Into<KeyCode>,
{
handle_result(self.exec(make_keyevent_combination(source, keycodes), None, None)?)
}
pub fn send_keycode_combination<I>(
&self,
source: Option<InputSource>,
keycodes: I,
) -> Result<()>
where
I: IntoIterator<Item = u32>,
{
handle_result(self.exec(make_keycode_combination(source, keycodes), None, None)?)
}
pub fn try_send_keyevent_combination<I, S>(
&self,
source: Option<InputSource>,
keycodes: I,
) -> Result<Option<ExitStatus>>
where
I: IntoIterator<Item = S>,
S: Into<KeyCode>,
{
self.try_exec(make_keyevent_combination(source, keycodes), None, None)
}
pub fn try_send_keycode_combination<I>(
&self,
source: Option<InputSource>,
keycodes: I,
) -> Result<Option<ExitStatus>>
where
I: IntoIterator<Item = u32>,
{
self.try_exec(make_keycode_combination(source, keycodes), None, None)
}
pub fn try_send_keycodes<I>(
&self,
keycodes: I,
source: Option<InputSource>,
) -> Result<Option<ExitStatus>>
where
I: IntoIterator<Item = u32>,
{
self.try_exec(make_keycodes(keycodes, source), None, None)
}
pub fn try_send_keyevents<I, S>(
&self,
keycodes: Vec<KeyCode>,
source: Option<InputSource>,
) -> Result<Option<ExitStatus>>
where
I: IntoIterator<Item = S>,
S: Into<KeyCode>,
{
self.try_exec(make_keyevents(keycodes, source), None, None)
}
pub fn try_send_press(&self, source: Option<InputSource>) -> Result<Option<ExitStatus>> {
self.try_exec(make_press(source), None, None)
}
pub fn try_send_draganddrop(
&self,
source: Option<InputSource>,
duration: Option<Duration>,
from_pos: (i32, i32),
to_pos: (i32, i32),
) -> Result<Option<ExitStatus>> {
self.try_exec(
make_draganddrop(source, duration, from_pos, to_pos),
None,
None,
)
}
pub fn try_send_motion(
&self,
source: Option<InputSource>,
motion: MotionEvent,
pos: (i32, i32),
) -> Result<Option<ExitStatus>> {
self.try_exec(make_motion(source, motion, pos), None, None)
}
pub fn try_send_event(
&self,
event: &str,
code_type: i32,
code: i32,
value: i32,
) -> Result<Option<ExitStatus>> {
self.try_exec(make_event(event, code_type, code, value), None, None)
}
pub fn try_send_text(
&self,
text: &str,
source: Option<InputSource>,
) -> Result<Option<ExitStatus>> {
self.try_exec(make_text(text, source), None, None)
}
pub fn try_send_char(
&self,
chr: char,
source: Option<InputSource>,
) -> Result<Option<ExitStatus>> {
self.try_exec(make_char(chr, source), None, None)
}
pub fn try_send_tap(
&self,
position: (i32, i32),
source: Option<InputSource>,
) -> Result<Option<ExitStatus>> {
self.try_exec(make_tap(position, source), None, None)
}
pub fn try_send_keyevent(
&self,
keycode: KeyCode,
event_type: Option<KeyEventType>,
source: Option<InputSource>,
) -> Result<Option<ExitStatus>> {
self.try_exec(make_keyevent(keycode, event_type, source), None, None)
}
pub fn try_send_keycode(
&self,
keycode: u32,
event_type: Option<KeyEventType>,
source: Option<InputSource>,
) -> Result<Option<ExitStatus>> {
self.try_exec(make_keycode(keycode, event_type, source), None, None)
}
pub fn try_send_swipe(
&self,
from_pos: (i32, i32),
to_pos: (i32, i32),
duration: Option<Duration>,
source: Option<InputSource>,
) -> Result<Option<ExitStatus>> {
self.try_exec(make_swipe(from_pos, to_pos, duration, source), None, None)
}
pub fn get_events(&self) -> Result<Vec<(String, String)>> {
let result = self.exec(vec!["getevent", "-S"], None, None)?.stdout;
lazy_static! {
static ref RE: Regex = Regex::new(
"^add\\s+device\\s+[0-9]+:\\s(?P<event>[^\n]+)\\s*name:\\s*\"(?P<name>[^\"]+)\"\n?"
)
.unwrap();
}
let mut v: Vec<(String, String)> = vec![];
let mut string = Arg::as_str(&result)?;
loop {
let captures = RE.captures(string);
if let Some(cap) = captures {
let e = cap.name("event");
let n = cap.name("name");
if e.is_some() && n.is_some() {
v.push((
e.ok_or(Error::ParseInputError)?.as_str().to_string(),
n.ok_or(Error::ParseInputError)?.as_str().to_string(),
));
}
string = &string[cap[0].len()..]
} else {
break;
}
}
Ok(v)
}
pub fn file_mode<T: Arg>(&self, path: T) -> Result<file_mode::Mode> {
let output = Arg::as_str(
&self
.exec(vec!["stat", "-L", "-c", "'%a'", path.as_str()?], None, None)?
.stdout,
)?
.trim_end()
.parse::<u32>()?;
let mode = file_mode::Mode::from(output);
Ok(mode)
}
pub fn list_settings(&self, settings_type: SettingsType) -> Result<Vec<Property>> {
let output = self.exec(vec!["settings", "list", settings_type.into()], None, None)?;
let reader = BufReader::new(output.stdout.as_slice());
let hashmap = java_properties::read(reader)?;
let result = hashmap
.into_iter()
.map(|(key, value)| Property { key, value })
.collect();
Ok(result)
}
pub fn get_setting(&self, settings_type: SettingsType, key: &str) -> Result<Option<String>> {
let output = &self
.exec(
vec!["settings", "get", settings_type.into(), key],
None,
None,
)?
.stdout;
if !output.is_empty() {
let o = Arg::as_str(&output).map(|s| s.trim().to_string())?;
if &"null" == &o {
Ok(None)
} else {
Ok(Some(o))
}
} else {
Ok(None)
}
}
pub fn put_setting<S: Into<String>>(
&self,
settings_type: SettingsType,
key: &str,
value: S,
) -> Result<()> {
let result = self.exec(
vec!["settings", "put", settings_type.into(), key, &value.into()],
None,
None,
)?;
handle_result(result)
}
pub fn delete_setting(&self, settings_type: SettingsType, key: &str) -> Result<()> {
let result = self.exec(
vec!["settings", "delete", settings_type.into(), key],
None,
None,
)?;
handle_result(result)
}
pub fn ls<T: Arg>(&self, path: T, command_args: Option<Vec<OsString>>) -> Result<Vec<String>> {
let mut args = vec!["ls".as_os_str()];
if let Some(command_args) = command_args {
args.extend(command_args);
}
args.push(path.as_str()?.as_os_str());
let stdout = self.exec(args, None, None)?.stdout;
let lines = stdout.lines().filter_map(|s| s.ok()).collect();
Ok(lines)
}
pub fn dir_size<T: Arg>(&self, path: T, summary: bool) -> Result<Vec<(String, u64)>> {
let mut args = vec!["du".as_os_str()];
if summary {
args.push("-s".as_os_str());
}
args.push(path.as_str()?.as_os_str());
let stdout = self.exec(args, None, None)?.stdout;
let lines = stdout.lines().filter_map(|s| s.ok()).collect::<Vec<_>>();
let result = lines
.into_iter()
.filter_map(|line| {
let parts: Vec<&str> = line.split('\t').collect();
if parts.len() == 2 {
if let Ok(size) = parts[0].trim().parse::<u64>() {
return Some((parts[1].to_string(), size));
}
}
None
})
.collect();
Ok(result)
}
pub fn exists<T: Arg>(&self, path: T) -> Result<bool> {
self.test_file(path, "e")
}
pub fn rm<T: Arg>(&self, path: T, arguments: Vec<&str>) -> Result<()> {
let mut args = vec!["rm".as_os_str()];
for arg in arguments {
args.push(arg.into());
}
args.push(path.as_str()?.into());
let result = self.exec(args, None, None)?;
handle_result(result)
}
pub fn is_file<T: Arg>(&self, path: T) -> Result<bool> {
self.test_file(path, "f")
}
pub fn is_dir<T: Arg>(&self, path: T) -> Result<bool> {
self.test_file(path, "d")
}
pub fn is_symlink<T: Arg>(&self, path: T) -> Result<bool> {
self.test_file(path, "h")
}
pub fn test_file<T: Arg>(&self, path: T, mode: &str) -> Result<bool> {
let output = self.exec(
vec![format!("test -{:} {:?} && echo 1 || echo 0", mode, path.as_str()?).as_str()],
None,
None,
);
match simple_cmd::Vec8ToString::as_str(&output?.stdout) {
Some(s) => Ok(s.trim_end() == "1"),
None => Ok(false),
}
}
pub fn dumpsys_list(
&self,
proto_only: bool,
priority: Option<DumpsysPriority>,
) -> Result<Vec<String>> {
let mut args = vec!["dumpsys", "-l"];
if proto_only {
args.push("--proto");
}
if let Some(priority) = priority {
args.push("--priority");
args.push(priority.into());
}
let output: Vec<String> = self
.exec(args, None, None)?
.stdout
.lines()
.filter_map(|f| match f {
Ok(s) => {
let line = String::from(s.trim());
match line {
x if x.ends_with(':') => None,
x => Some(x),
}
}
Err(_) => None,
})
.collect();
Ok(output)
}
pub fn dumpsys(
&self,
service: Option<&str>,
arguments: Option<Vec<String>>,
timeout: Option<Duration>,
pid: bool,
thread: bool,
proto: bool,
skip: Option<Vec<String>>,
) -> Result<Output> {
let mut args = vec!["dumpsys".to_string()];
if let Some(timeout) = timeout {
args.push("-T".to_string());
args.push(timeout.as_millis().to_string());
}
if pid {
args.push("--pid".to_string());
} else if thread {
args.push("--thread".to_string());
}
if proto {
args.push("--proto".to_string());
}
if let Some(skip) = skip {
args.push("--skip".to_string());
args.push(skip.join(","));
} else if let Some(service) = service {
args.push(service.to_string());
if let Some(arguments) = arguments {
args.extend(arguments);
}
}
self.exec(args, None, None)
}
pub fn screen_record<T: Arg>(
&self,
options: Option<ScreenRecordOptions>,
output: T,
cancel: Option<Receiver<()>>,
) -> Result<Output> {
let mut args = vec!["screenrecord".into()];
if let Some(options) = options {
args.extend(options);
}
args.push(output.as_str()?.into());
let command = CommandBuilder::shell(self.parent).args(args).signal(cancel);
Ok(command.build().output()?)
}
pub fn screen_mirror(
&self,
screenrecord_options: ScreenRecordOptions,
play_options: FFPlayOptions,
cancel: Option<Receiver<()>>,
) -> Result<Output> {
let screenrecord_arg = format!(
"screenrecord --output-format=h264 {:} -",
screenrecord_options
);
let builder = CommandBuilder::shell(self.parent)
.args(vec![screenrecord_arg.as_str()])
.signal(cancel)
.with_debug(true);
let command1 = builder.build();
let mut command2 = std::process::Command::new(which::which("ffplay")?);
let ffplay_options = play_options;
command2.args(ffplay_options.into_iter());
command2.args(&[
"-loglevel",
"repeat+level+verbose",
"-an",
"-autoexit",
"-sync",
"video",
"-",
]);
if self.parent.debug {
command2.debug();
}
command1.pipe(command2).map_err(|e| Error::from(e))
}
pub fn save_screencap<T>(&self, path: T) -> Result<Output>
where
T: Arg,
{
self.exec(vec!["screencap", "-p", path.as_str()?], None, None)
}
ro_build_property!(build_ab_update, "ro.build.ab_update", String);
ro_build_property!(build_characteristics, "ro.build.characteristics", String);
ro_build_property!(build_date, "ro.build.date", String);
ro_build_property!(build_date_utc, "ro.build.date.utc", u64);
ro_build_property!(build_description, "ro.build.description", String);
ro_build_property!(build_display_id, "ro.build.display.id", String);
ro_build_property!(build_fingerprint, "ro.build.fingerprint", String);
ro_build_property!(build_flavor, "ro.build.flavor", String);
ro_build_property!(build_host, "ro.build.host", String);
ro_build_property!(build_id, "ro.build.id", String);
ro_build_property!(build_product, "ro.build.product", String);
ro_build_property!(build_tags, "ro.build.tags", String);
ro_build_property!(build_type, "ro.build.type", String);
ro_build_property!(build_user, "ro.build.user", String);
ro_build_property!(build_version_codename, "ro.build.version.codename", String);
ro_build_property!(
build_version_incremental,
"ro.build.version.incremental",
String
);
ro_build_property!(build_version_release, "ro.build.version.release", u16);
ro_build_property!(build_version_sdk, "ro.build.version.sdk", u16);
pub fn getprop_with_type<T: std::str::FromStr>(&self, key: &str) -> Result<T> {
let prop = self.getprop(key)?;
match prop.parse::<T>() {
Ok(value) => Ok(value),
Err(_err) => Err(Error::ParseInputError),
}
}
pub fn getprop(&self, key: &str) -> Result<String> {
let result = self
.exec(vec!["getprop", key], None, None)
.map(|s| s.stdout)?;
Ok(Arg::as_str(&result).map(|f| f.trim_end())?.to_string())
}
pub fn getprops(&self) -> Result<Vec<Property>> {
let output = self.exec(["getprop"], None, None);
let mut result: Vec<Property> = Vec::new();
for line in output?.stdout.lines().filter_map(|l| l.ok()) {
let captures = RE_GET_PROPS.captures(line.as_str());
if let Some(cap1) = captures {
let k = cap1.get(1);
let v = cap1.get(2);
if k.is_some() && v.is_some() {
result.push(Property {
key: k.ok_or(Error::ParseInputError)?.as_str().to_string(),
value: v.ok_or(Error::ParseInputError)?.as_str().to_string(),
});
}
}
}
Ok(result)
}
pub fn getprops_type(&self) -> Result<HashMap<String, PropType>> {
let output = self
.exec(vec!["getprop", "-T"], None, None)
.map(|s| s.stdout)?;
let hash_map = output
.lines()
.filter_map(|l| l.ok())
.into_iter()
.filter_map(|line| {
let captures = RE_GET_PROPS.captures(line.as_str());
if let Some(captures) = captures {
if captures.len() == 3 {
let k = captures.get(1).unwrap().as_str();
let v = captures.get(2).unwrap().as_str();
let prop_type = PropType::from(v);
Some((k.to_string(), prop_type))
} else {
None
}
} else {
None
}
})
.collect::<HashMap<_, _>>();
Ok(hash_map)
}
pub fn setprop<T: Arg>(&self, key: &str, value: T) -> Result<()> {
let mut new_value = value.as_str()?;
if new_value == "" {
new_value = "\"\""
}
self.exec(vec!["setprop", key, new_value], None, None)
.map(|_| ())
}
pub fn clear_prop(&self, key: &str) -> Result<()> {
self.setprop(key, "")
}
pub fn getprop_type(&self, key: &str) -> Result<PropType> {
self.exec(vec!["getprop", "-T", key], None, None)
.map(|s| s.stdout)
.map(|s| PropType::try_from(s))?
}
pub fn am(&self) -> ActivityManager<'_> {
ActivityManager { parent: self }
}
pub fn pm(&self) -> PackageManager<'_> {
PackageManager { parent: self }
}
}
#[cfg(test)]
mod test {
use file_mode::Mode;
use simple_cmd::prelude::OutputExt;
use std::time::Duration;
use strum::IntoEnumIterator;
use crate::test::test::*;
use crate::types::KeyCode::{
KEYCODE_1, KEYCODE_2, KEYCODE_3, KEYCODE_DPAD_DOWN, KEYCODE_DPAD_RIGHT, KEYCODE_HOME,
};
use crate::types::{
ChmodMode, ChmodModeSpecs, ChmodOptions, DumpsysPriority, InputSource, Intent, KeyCode,
MotionEvent, PropType, RebootType, SELinuxType, ScreenRecordOptions, SettingsType,
};
#[test]
fn test_chmod() {
init_log();
let client = connect_client(connection_from_tcpip());
client.root().expect("failed to root");
client.remount(false).expect("failed to remount");
let path = "/system/priv-app/ui-ip2300-release.apk";
let _ = client.shell().exists(path).expect("file does not exist");
let mode = ChmodModeSpecs {
owner: ChmodMode {
read: true,
write: true,
execute: false,
},
group: ChmodMode {
read: true,
write: false,
execute: false,
},
other: ChmodMode {
read: true,
write: false,
execute: false,
},
};
assert_eq!("644", mode.to_string());
let output = client
.shell()
.chmod(
ChmodOptions {
mode: mode,
recursive: true,
verbose: true,
..Default::default()
},
vec![path],
)
.expect("failed to chmod file");
println!("chmod output: {:#?}", output);
std::thread::sleep(Duration::from_secs(1));
let mode: Mode = client
.shell()
.file_mode(path)
.expect("failed to get file mode");
println!("mode: {mode}");
}
#[test]
fn test_who_am_i() {
init_log();
let client = connect_emulator();
let whoami = client.shell().whoami().expect("failed to get user");
println!("whoami: {whoami}");
assert!(!whoami.is_empty());
}
#[test]
fn test_is_root() {
init_log();
let client = connect_client(connection_from_tcpip());
let whoami = client.shell().whoami().expect("failed to get user");
println!("whoami: {whoami}");
let result: crate::result::Result<bool> = client.shell().is_root();
let is_root = result.expect("failed to get root status");
if whoami.eq_ignore_ascii_case("root") {
assert!(is_root);
} else {
assert!(!is_root);
}
}
#[test]
fn test_avbctl() {
init_log();
let client = connect_client(connection_from_tcpip());
let result = client.shell().has_avbctl();
eprintln!("result: {result:?}");
let result = client.shell().check_avbctl();
eprintln!("result: {result:?}");
}
#[test]
fn test_mount() {
init_log();
let client = connect_client(connection_from_tcpip());
client.root().expect("failed to root");
let _ = client.shell().mount("/system").expect("failed to mount");
let _ = client
.shell()
.unmount("/system")
.expect("failed to unmount");
}
#[test]
fn test_check_avbctl() {
init_log();
let client = connect_emulator();
client
.shell()
.check_avbctl()
.expect_err("check_avbctl should fail");
let client = connect_tcp_ip_client();
let _result = client
.shell()
.check_avbctl()
.expect("failed to check_avbctl");
}
#[test]
fn test_get_command_path() {
init_log();
let client = connect_emulator();
let path = client
.shell()
.get_command_path("sh")
.expect("failed to get sh path");
println!("path: {path}");
assert_eq!("/system/bin/sh", path);
let client = connect_tcp_ip_client();
let path = client
.shell()
.get_command_path("sh")
.expect("failed to get sh path");
println!("path: {path}");
assert_eq!("/system/bin/sh", path);
}
#[test]
fn test_which() {
init_log();
let client = connect_emulator();
let path = client.shell().which("sh").expect("failed to get sh path");
println!("path: {path}");
assert_eq!("/system/bin/sh", path);
let client = connect_tcp_ip_client();
let path = client.shell().which("sh").expect("failed to get sh path");
println!("path: {path}");
assert_eq!("/system/bin/sh", path);
}
#[test]
fn test_get_verity() {
init_log();
let client = connect_tcp_ip_client();
let has_avbctl = client
.shell()
.has_avbctl()
.expect("failed to check for avbctl");
println!("has_avbctl: {has_avbctl}");
assert!(has_avbctl);
let verity = client
.shell()
.get_verity()
.expect("failed to get verity status");
println!("verity status: {verity}");
}
#[test]
fn test_toggle_verity() {
init_log();
let client = connect_tcp_ip_client();
client.root().expect("failed to root");
let enabled = client.shell().get_verity().expect("failed to get verity");
println!("verity is enabled: {enabled}");
if enabled {
client
.shell()
.disable_verity()
.expect("failed to disable verity");
} else {
client
.shell()
.enable_verity()
.expect("failed to disable verity");
}
client.reboot(None).expect("failed to reboot device");
client
.wait_for_device(Some(Duration::from_secs(120)))
.expect("failed to wait for device");
client.root().expect("failed to root");
let verity_enabled = client.shell().get_verity().expect("failed to get verity");
println!("verity is now enabled: {verity_enabled}");
assert_ne!(enabled, verity_enabled);
}
#[test]
fn test_get_enforce() {
let client = connect_emulator();
let enforce: SELinuxType = client.shell().get_enforce().expect("failed to get enforce");
println!("enforce: {enforce}");
assert_eq!(SELinuxType::Enforcing, enforce);
let client = connect_tcp_ip_client();
let enforce = client.shell().get_enforce().expect("failed to get enforce");
println!("enforce: {enforce}");
}
#[test]
fn test_set_enforce() {
init_log();
let client = connect_tcp_ip_client();
root_client(&client);
let enforce = client.shell().get_enforce().expect("failed to get enforce");
println!("enforce: {enforce}");
if enforce == SELinuxType::Enforcing {
client
.shell()
.set_enforce(SELinuxType::Permissive)
.expect("failed to change enforce type");
} else {
client
.shell()
.set_enforce(SELinuxType::Enforcing)
.expect("failed to change enforce type");
}
let now_enforce = client.shell().get_enforce().expect("failed to get enforce");
println!("now enforce: {now_enforce}");
assert_ne!(enforce, now_enforce);
}
#[test]
fn test_is_screen_on() {
let client = connect_emulator();
let screen_on = client
.shell()
.is_screen_on()
.expect("failed to get screen status");
println!("is screen on: {screen_on}");
client
.shell()
.send_keyevent(KeyCode::KEYCODE_POWER, None, None)
.expect("failed to send keyevent");
std::thread::sleep(Duration::from_secs(2));
let screen_on2 = client
.shell()
.is_screen_on()
.expect("failed to get screen status");
println!("is screen on: {screen_on2}");
assert_ne!(screen_on, screen_on2);
}
#[test]
fn test_get_events() {
init_log();
let client = connect_emulator();
let events = client.shell().get_events().expect("failed to get events");
assert!(!events.is_empty());
println!("events: {:#?}", events);
}
#[test]
fn test_file_mode() {
init_log();
let client = connect_emulator();
let mode = client
.shell()
.file_mode("/system/build.prop")
.expect("failed to get file_mode");
println!("file mode: {}", mode);
}
#[test]
fn test_list_settings() {
init_log();
let client = connect_emulator();
for setting in SettingsType::iter() {
let settings = client
.shell()
.list_settings(setting)
.expect(&format!("failed to list {:?} settings", setting));
println!("setting[{setting:?}]: {settings:#?}");
assert!(!settings.is_empty());
}
}
#[test]
fn test_get_setting() {
init_log();
let client = connect_emulator();
let setting = client
.shell()
.get_setting(SettingsType::global, "theater_mode_on")
.expect("failed to get setting");
println!("setting: {setting:?}");
}
#[test]
fn test_put_setting() {
init_log();
let client = connect_emulator();
let setting = client
.shell()
.get_setting(SettingsType::global, "theater_mode_on")
.expect("failed to get setting")
.unwrap();
let new_value = if setting == "0" { "1" } else { "0" };
client
.shell()
.put_setting(SettingsType::global, "theater_mode_on", new_value)
.expect("failed to put setting");
let setting = client
.shell()
.get_setting(SettingsType::global, "theater_mode_on")
.expect("failed to read settings")
.unwrap();
assert_eq!(new_value, setting);
}
#[test]
fn test_delete_setting() {
init_log();
let client = connect_emulator();
client
.shell()
.put_setting(SettingsType::global, "my_custom_setting", "1")
.expect("failed to put settings");
let value = client
.shell()
.get_setting(SettingsType::global, "my_custom_setting")
.expect("failed to read settings")
.unwrap();
assert_eq!("1", value);
client
.shell()
.delete_setting(SettingsType::global, "my_custom_setting")
.expect("failed to delete settings");
let value = client
.shell()
.get_setting(SettingsType::global, "my_custom_setting")
.expect("failed to read settings");
assert_eq!(None, value);
}
#[test]
fn test_ls() {
init_log();
let client = connect_emulator();
let ls = client
.shell()
.ls("/system", None)
.expect("failed to list dir");
assert!(!ls.is_empty());
println!("ls: {:?}", ls);
}
#[test]
fn test_dir_size() {
init_log();
let client = connect_emulator();
let dir_size = client
.shell()
.dir_size("/system/app", false)
.expect("failed to get dir size");
assert!(!dir_size.is_empty());
dir_size.iter().for_each(|(name, size)| {
println!("{}: {}Kb", name, size);
});
}
#[test]
fn test_dumpsys_list() {
init_log();
let client = connect_emulator();
let list = client
.shell()
.dumpsys_list(false, Some(DumpsysPriority::NORMAL))
.expect("failed to list dumpsys");
assert!(!list.is_empty());
println!("list: {list:?}");
}
#[test]
fn test_dumpsys() {
init_log();
let client = connect_emulator();
let dump = client
.shell()
.dumpsys(Some("adb"), None, None, false, false, false, None)
.expect("failed to dumpsys meminfo");
println!("dump: {dump:?}");
assert!(dump.success());
assert!(!dump.stdout.is_empty());
}
#[test]
fn test_screen_record() {
init_log();
let client = connect_emulator();
let mut options = ScreenRecordOptions::default();
options.verbose = true;
options.timelimit = Some(Duration::from_secs(12));
let remote_file = "/sdcard/Download/screenrecord.mp4";
let local_file = temp_dir().join("screenrecord.mp4");
println!("local file: {local_file:?}");
if local_file.exists() {
std::fs::remove_file(&local_file).unwrap();
}
let receiver = sigint_notifier().unwrap();
match client
.shell()
.screen_record(Some(options), remote_file, Some(receiver))
{
Ok(t) => println!("Screen Record Ok: {:?}", t),
Err(e) => {
panic!("{:}", e)
}
}
println!("need to sleep a bit..");
std::thread::sleep(Duration::from_secs(2));
client.pull(remote_file, local_file.as_path()).unwrap();
assert!(local_file.exists());
if local_file.exists() {
std::fs::remove_file(&local_file).unwrap();
}
}
#[test]
fn test_screen_mirror() {
init_log();
let client = connect_emulator();
let signal = ctrl_channel().unwrap();
client
.shell()
.screen_mirror(Default::default(), Default::default(), Some(signal))
.expect("failed to screen mirror");
}
#[test]
fn test_save_screencap() {
init_log();
let client = connect_emulator();
assert!(client.shell().exists("/sdcard/Download").unwrap());
if client
.shell()
.exists("/sdcard/Download/screencap.png")
.unwrap()
{
client
.shell()
.rm("/sdcard/Download/screencap.png", vec![])
.unwrap();
}
client
.shell()
.save_screencap("/sdcard/Download/screencap.png")
.expect("save screencap failed");
assert!(client.shell().exists("/sdcard/Download").unwrap());
let tmp_dir = temp_dir();
println!("temp_dir: {tmp_dir:?}");
client
.pull("/sdcard/Download/screencap.png", tmp_dir)
.expect("failed to pull file");
client
.shell()
.rm("/sdcard/Download/screencap.png", vec![])
.unwrap();
}
#[test]
fn test_get_prop() {
init_log();
let client = connect_emulator();
let prop = client
.shell()
.getprop("ro.build.product")
.expect("failed to get prop");
assert!(!prop.is_empty());
assert!(prop.starts_with("emulator"));
println!("prop: {prop}");
let client = connect_emulator();
let prop = client
.shell()
.getprop_with_type::<u16>("ro.build.version.sdk")
.expect("failed to get prop");
println!("prop: {prop}");
}
#[test]
fn test_get_build_props() {
init_log();
let client = connect_tcp_ip_client();
println!(
"build_ab_update: {}",
client.shell().build_ab_update().unwrap()
);
println!(
"build_characteristics: {}",
client.shell().build_characteristics().unwrap()
);
println!("build_date: {}", client.shell().build_date().unwrap());
println!(
"build_date_utc: {}",
client.shell().build_date_utc().unwrap()
);
println!(
"build_description: {}",
client.shell().build_description().unwrap()
);
println!(
"build_display_id: {}",
client.shell().build_display_id().unwrap()
);
println!(
"build_fingerprint: {}",
client.shell().build_fingerprint().unwrap()
);
println!("build_flavor: {}", client.shell().build_flavor().unwrap());
println!("build_host: {}", client.shell().build_host().unwrap());
println!("build_id: {}", client.shell().build_id().unwrap());
println!("build_product: {}", client.shell().build_product().unwrap());
println!("build_tags: {}", client.shell().build_tags().unwrap());
println!("build_type: {}", client.shell().build_type().unwrap());
println!("build_user: {}", client.shell().build_user().unwrap());
println!(
"build_version_codename: {}",
client.shell().build_version_codename().unwrap()
);
println!(
"build_version_incremental: {}",
client.shell().build_version_incremental().unwrap()
);
println!(
"build_version_release: {}",
client.shell().build_version_release().unwrap()
);
println!(
"build_version_sdk: {}",
client.shell().build_version_sdk().unwrap()
);
}
#[test]
fn test_get_props() {
init_log();
let client = connect_emulator();
let props = client.shell().getprops().expect("failed to list props");
assert!(!props.is_empty());
let prop = props
.iter()
.find(|item| item.key == "ro.build.product")
.unwrap();
println!("prop = {prop:?}");
let prop2 = client.shell().getprop("ro.build.product").unwrap();
assert_eq!(prop.value, prop2);
}
#[test]
fn test_get_props_type() {
init_log();
let client = connect_emulator();
let props = client
.shell()
.getprops_type()
.expect("failed to get props type");
assert!(!props.is_empty());
let prop_type = props.get("ro.build.product").unwrap();
println!("prop type: {:?}", prop_type);
assert_eq!(&PropType::String, prop_type);
}
#[test]
fn test_set_prop() {
init_log();
let client = connect_emulator();
let prop = client
.shell()
.getprop("log.tag.stats_log")
.expect("failed to getprop");
let new_prop = if prop == "I" { "D" } else { "I" };
client
.shell()
.setprop("log.tag.stats_log", new_prop)
.expect("failed to set prop");
let prop = client
.shell()
.getprop("log.tag.stats_log")
.expect("failed to getprop");
assert_eq!(new_prop, prop);
}
#[test]
fn test_clean_prop() {
init_log();
let client = connect_emulator();
client
.shell()
.clear_prop("log.tag.stats_log")
.expect("failed to clear prop");
let prop = client
.shell()
.getprop("log.tag.stats_log")
.expect("failed to getprop");
assert_eq!("", prop);
client.shell().setprop("log.tag.stats_log", "I").unwrap();
}
#[test]
fn test_get_prop_type() {
init_log();
let client = connect_emulator();
let prop = client
.shell()
.getprop_type("log.tag.stats_log")
.expect("failed to get prop type");
assert_eq!(PropType::String, prop);
}
#[test]
fn test_send_swipe() {
init_log();
let client = connect_emulator();
client
.shell()
.send_swipe((0, 0), (500, 500), None, None)
.expect("failed to send swipe");
client
.shell()
.try_send_swipe((0, 0), (500, 500), None, None)
.expect("failed to send swipe");
}
#[test]
fn test_send_keyevent() {
init_log();
let client = connect_emulator();
client
.shell()
.send_keyevent(KeyCode::KEYCODE_SETTINGS, None, None)
.expect("failed to send keyevent");
println!("sleeping...");
std::thread::sleep(Duration::from_secs(5));
client
.shell()
.try_send_keyevent(KeyCode::KEYCODE_HOME, None, None)
.expect("failed to send keyevent");
}
#[test]
fn test_send_keycode() {
init_log();
let client = connect_emulator();
client
.shell()
.send_keycode(26, None, None)
.expect("failed to send keyevent");
println!("sleeping...");
std::thread::sleep(Duration::from_secs(5));
client
.shell()
.try_send_keycode(26, None, None)
.expect("failed to send keyevent");
}
#[test]
fn test_send_tap() {
init_log();
let client = connect_emulator();
client
.shell()
.send_tap((100, 100), None)
.expect("failed to send tap");
client
.shell()
.try_send_tap((200, 200), None)
.expect("failed to send tap");
}
#[test]
fn test_send_char() {
init_log();
let client = connect_emulator();
client
.shell()
.send_char('a', None)
.expect("failed to send char");
client
.shell()
.try_send_char('a', None)
.expect("failed to send tap");
}
#[test]
fn test_send_text() {
init_log();
let client = connect_emulator();
client
.shell()
.send_text("alessandro", None)
.expect("failed to send text");
client
.shell()
.try_send_text("alessandro", None)
.expect("failed to send text");
}
#[test]
fn test_send_event() {
init_log();
let client = connect_tcp_ip_client();
root_client(&client);
let events = client.shell().get_events().unwrap();
println!("events: {events:#?}");
let event = "/dev/input/event0";
client
.shell()
.send_event(event, 0x0001, 0x006a, 0x00000001)
.unwrap();
client
.shell()
.send_event(event, 0x0000, 0x0000, 0x00000000)
.unwrap();
client
.shell()
.try_send_event(event, 0x0001, 0x006a, 0x00000001)
.unwrap();
client
.shell()
.try_send_event(event, 0x0000, 0x0000, 0x00000000)
.unwrap();
}
#[test]
fn test_send_motion_event() {
init_log();
let client = connect_emulator();
client
.shell()
.send_motion(None, MotionEvent::DOWN, (100, 400))
.expect("failed to send motion event");
client
.shell()
.try_send_motion(None, MotionEvent::MOVE, (100, 200))
.expect("failed to send motion event");
client
.shell()
.try_send_motion(None, MotionEvent::UP, (100, 100))
.expect("failed to send motion event");
}
#[test]
fn test_send_draganddrop() {
init_log();
let client = connect_emulator();
client
.shell()
.send_draganddrop(
None,
Some(Duration::from_millis(400)),
(200, 400),
(100, 20),
)
.expect("failed to send drag and drop");
std::thread::sleep(Duration::from_secs(2));
client
.shell()
.try_send_draganddrop(
None,
Some(Duration::from_millis(400)),
(200, 300),
(100, 60),
)
.expect("failed to send drag and drop");
}
#[test]
fn test_send_press() {
init_log();
let client = connect_emulator();
client
.shell()
.send_press(None)
.expect("failed to send press");
std::thread::sleep(Duration::from_secs(1));
client
.shell()
.try_send_press(Some(InputSource::touchscreen))
.expect("failed to send press");
}
#[test]
fn test_send_keyevents() {
init_log();
let client = connect_emulator();
client
.shell()
.send_keyevents(
[
KEYCODE_HOME,
KEYCODE_DPAD_DOWN,
KEYCODE_DPAD_DOWN,
KEYCODE_DPAD_DOWN,
],
None,
)
.expect("failed to send key events");
std::thread::sleep(Duration::from_secs(1));
client
.shell()
.send_keyevents([KEYCODE_DPAD_RIGHT, KEYCODE_DPAD_RIGHT], None)
.expect("failed to send key events");
}
#[test]
fn test_send_keycodes() {
init_log();
let client = connect_emulator();
client
.shell()
.send_keycodes([83u32, 82u32], None)
.expect("failed to send key codes");
std::thread::sleep(Duration::from_secs(1));
client
.shell()
.send_keycodes([143, 142], None)
.expect("failed to send key codes");
}
#[test]
fn test_send_keyevent_combination() {
init_log();
let client = connect_emulator();
client
.shell()
.send_keyevent_combination(None, [KEYCODE_1, KEYCODE_2, KEYCODE_3])
.expect("failed to send key codes combination");
std::thread::sleep(Duration::from_secs(1));
client
.shell()
.try_send_keyevent_combination(None, [KEYCODE_1, KEYCODE_2, KEYCODE_3])
.expect("failed to send key codes combination");
}
#[test]
fn test_send_keycode_combination() {
init_log();
let client = connect_emulator();
client
.shell()
.send_keycode_combination(None, [83, 84, 85])
.expect("failed to send key codes combination");
std::thread::sleep(Duration::from_secs(1));
client
.shell()
.try_send_keycode_combination(None, [83, 84, 85])
.expect("failed to send key codes combination");
}
#[test]
fn test_dra() {
init_log();
let client = connect_tcp_ip_client();
client.reboot(Some(RebootType::Dra)).unwrap()
}
#[test]
fn test_factory_reset() {
init_log();
let client = connect_tcp_ip_client();
let intent = Intent {
action: Some("android.intent.action.FACTORY_RESET".to_string()),
data: None,
mime_type: None,
category: None,
component: None,
package: Some("android".to_string()),
user_id: None,
flags: 0,
receiver_foreground: true,
wait: false,
extra: Default::default(),
};
let _result = client.shell().am().broadcast(&intent).unwrap();
}
}