use rand::Rng;
use std::time::Duration;
use crate::{
Result, RustADBError,
message_devices::{
adb_message_transport::ADBMessageTransport,
adb_session::ADBSession,
adb_transport_message::{
ADBTransportMessage, AUTH_RSAPUBLICKEY, AUTH_SIGNATURE, AUTH_TOKEN,
},
message_commands::{MessageCommand, MessageSubcommand},
models::ADBRsaKey,
utils::BinaryEncodable,
},
models::ADBLocalCommand,
};
#[derive(Debug)]
pub struct ADBMessageDevice<T: ADBMessageTransport> {
transport: T,
}
impl<T: ADBMessageTransport> ADBMessageDevice<T> {
pub fn new(transport: T) -> Self {
Self { transport }
}
pub(crate) fn get_transport_mut(&mut self) -> &mut T {
&mut self.transport
}
pub(crate) fn auth_handshake(
&mut self,
message: ADBTransportMessage,
private_key: &ADBRsaKey,
) -> Result<()> {
match message.header().command() {
MessageCommand::Auth => {
log::debug!("Authentication required");
}
_ => return Ok(()),
}
let auth_message = match message.header().arg0() {
AUTH_TOKEN => message,
v => {
return Err(RustADBError::ADBRequestFailed(format!(
"Received AUTH message with type != 1 ({v})"
)));
}
};
let sign = private_key.sign(auth_message.into_payload())?;
let message = ADBTransportMessage::try_new(MessageCommand::Auth, AUTH_SIGNATURE, 0, &sign)?;
self.transport.write_message(message)?;
let received_response = self.transport.read_message()?;
if received_response.header().command() == MessageCommand::Cnxn {
log::info!(
"Authentication OK, device info {}",
String::from_utf8(received_response.into_payload())?
);
return Ok(());
}
let mut pubkey = private_key.android_pubkey_encode()?.into_bytes();
pubkey.push(b'\0');
let message =
ADBTransportMessage::try_new(MessageCommand::Auth, AUTH_RSAPUBLICKEY, 0, &pubkey)?;
self.transport.write_message(message)?;
let response = self
.transport
.read_message_with_timeout(Duration::from_secs(10))
.and_then(|message| {
message.assert_command(MessageCommand::Cnxn)?;
Ok(message)
})?;
log::info!(
"Authentication OK, device info {}",
String::from_utf8(response.into_payload())?
);
Ok(())
}
pub(crate) fn open_synchronization_session(&mut self) -> Result<ADBSession<T>> {
self.open_session(&ADBLocalCommand::Sync)
}
pub(crate) fn open_session(&mut self, cmd: &ADBLocalCommand) -> Result<ADBSession<T>> {
let mut rng = rand::rng();
let local_id: u32 = rng.random();
let message = ADBTransportMessage::try_new(
MessageCommand::Open,
local_id, 0,
cmd.to_string().as_bytes(),
)?;
self.transport.write_message(message)?;
let response = self.transport.read_message()?;
if response.header().command() != MessageCommand::Okay {
return Err(RustADBError::ADBRequestFailed(format!(
"Open session failed: got {} in respone instead of OKAY",
response.header().command()
)));
}
if response.header().arg1() != local_id {
return Err(RustADBError::ADBRequestFailed(format!(
"Open session failed: respones used {} for our local_id instead of {local_id}",
response.header().arg1()
)));
}
Ok(ADBSession::new(
self.transport.clone(),
local_id,
response.header().arg0(),
))
}
pub(crate) fn end_transaction(&mut self, session: &mut ADBSession<T>) -> Result<()> {
let quit_buffer = MessageSubcommand::Quit.with_arg(0u32);
session.send_and_expect_okay(ADBTransportMessage::try_new(
MessageCommand::Write,
session.local_id(),
session.remote_id(),
&quit_buffer.encode(),
)?)?;
let _discard_close = self.transport.read_message()?;
Ok(())
}
}