Skip to content

Commit 94f4575

Browse files
J05HM0N5TERcocool97
authored andcommitted
feat: adb list
ADB List (ls) for both direct device communication and for communication though the adb server.
1 parent 023e403 commit 94f4575

File tree

1 file changed

+174
-174
lines changed
  • adb_client/src/device/commands

1 file changed

+174
-174
lines changed
Lines changed: 174 additions & 174 deletions
Original file line numberDiff line numberDiff line change
@@ -1,174 +1,174 @@
1-
use crate::{
2-
device::{
3-
adb_message_device::ADBMessageDevice, ADBTransportMessage, MessageCommand,
4-
MessageSubcommand,
5-
},
6-
ADBListItem, ADBListItemType, ADBMessageTransport, Result, RustADBError,
7-
};
8-
use byteorder::ByteOrder;
9-
use byteorder::LittleEndian;
10-
use std::str;
11-
12-
impl<T: ADBMessageTransport> ADBMessageDevice<T> {
13-
/// List the entries in the given directory on the device.
14-
/// note: path uses internal file paths, so Documents is at /storage/emulated/0/Documents
15-
pub(crate) fn list(&mut self, path: &str) -> Result<Vec<ADBListItem>> {
16-
self.begin_synchronization()?;
17-
18-
let output = self.handle_list(path);
19-
20-
self.end_transaction()?;
21-
output
22-
}
23-
24-
/// Request amount of bytes from transport, potentially across payloads
25-
///
26-
/// This automatically request a new payload by sending back "Okay" and waiting for the next payload
27-
/// It reads the request bytes across the existing payload, and if there is not enough bytes left,
28-
/// reads the rest from the next payload
29-
///
30-
/// Current index
31-
/// ┼───────────────┼ Requested
32-
/// ┌─────────────┐
33-
/// ┌───────────────┼───────┐ │
34-
/// └───────────────────────┘
35-
/// Current └─────┘
36-
/// payload Wanted in
37-
/// Next payload
38-
fn read_bytes_from_transport(
39-
requested_bytes: &usize,
40-
current_index: &mut usize,
41-
transport: &mut T,
42-
payload: &mut Vec<u8>,
43-
local_id: &u32,
44-
remote_id: &u32,
45-
) -> Result<Vec<u8>> {
46-
if *current_index + requested_bytes <= payload.len() {
47-
// if there is enough bytes in this payload
48-
// Copy from existing payload
49-
let slice = &payload[*current_index..*current_index + requested_bytes];
50-
*current_index += requested_bytes;
51-
Ok(slice.to_vec())
52-
} else {
53-
// Read the rest of the existing payload, then continue with the next message
54-
let mut slice = Vec::new();
55-
let bytes_read_from_existing_payload = payload.len() - *current_index;
56-
slice.extend_from_slice(
57-
&payload[*current_index..*current_index + bytes_read_from_existing_payload],
58-
);
59-
60-
// Request the next message
61-
let send_message =
62-
ADBTransportMessage::new(MessageCommand::Okay, *local_id, *remote_id, &[]);
63-
transport.write_message(send_message)?;
64-
// Read the new message
65-
*payload = transport.read_message()?.into_payload();
66-
let bytes_read_from_new_payload = requested_bytes - bytes_read_from_existing_payload;
67-
slice.extend_from_slice(&payload[..bytes_read_from_new_payload]);
68-
*current_index = bytes_read_from_new_payload;
69-
Ok(slice)
70-
}
71-
}
72-
73-
fn handle_list(&mut self, path: &str) -> Result<Vec<ADBListItem>> {
74-
// TODO: use LIS2 to support files over 2.14 GB in size.
75-
// SEE: https://github.com/cstyan/adbDocumentation?tab=readme-ov-file#adb-list
76-
let local_id = self.get_local_id()?;
77-
let remote_id = self.get_remote_id()?;
78-
{
79-
let mut len_buf = Vec::from([0_u8; 4]);
80-
LittleEndian::write_u32(&mut len_buf, path.len() as u32);
81-
82-
let subcommand_data = MessageSubcommand::List; //.with_arg(path.len() as u32);
83-
84-
let mut serialized_message =
85-
bincode::serialize(&subcommand_data).map_err(|_e| RustADBError::ConversionError)?;
86-
87-
serialized_message.append(&mut len_buf);
88-
let mut path_bytes: Vec<u8> = Vec::from(path.as_bytes());
89-
serialized_message.append(&mut path_bytes);
90-
91-
let message = ADBTransportMessage::new(
92-
MessageCommand::Write,
93-
local_id,
94-
remote_id,
95-
&serialized_message,
96-
);
97-
self.send_and_expect_okay(message)?;
98-
}
99-
100-
let mut list_items = Vec::new();
101-
102-
let transport = self.get_transport_mut();
103-
let mut payload = transport.read_message()?.into_payload();
104-
let mut current_index = 0;
105-
loop {
106-
// Loop though the response for all the entries
107-
const STATUS_CODE_LENGTH_IN_BYTES: usize = 4;
108-
let status_code = Self::read_bytes_from_transport(
109-
&STATUS_CODE_LENGTH_IN_BYTES,
110-
&mut current_index,
111-
transport,
112-
&mut payload,
113-
&local_id,
114-
&remote_id,
115-
)?;
116-
match str::from_utf8(&status_code)? {
117-
"DENT" => {
118-
// Read the file mode, size, mod time and name length in one go, since all their sizes are predictable
119-
const U32_SIZE_IN_BYTES: usize = 4;
120-
const SIZE_OF_METADATA: usize = U32_SIZE_IN_BYTES * 4;
121-
let metadata = Self::read_bytes_from_transport(
122-
&SIZE_OF_METADATA,
123-
&mut current_index,
124-
transport,
125-
&mut payload,
126-
&local_id,
127-
&remote_id,
128-
)?;
129-
let mode = metadata[..U32_SIZE_IN_BYTES].to_vec();
130-
let size = metadata[U32_SIZE_IN_BYTES..2 * U32_SIZE_IN_BYTES].to_vec();
131-
let time = metadata[2 * U32_SIZE_IN_BYTES..3 * U32_SIZE_IN_BYTES].to_vec();
132-
let name_len = metadata[3 * U32_SIZE_IN_BYTES..4 * U32_SIZE_IN_BYTES].to_vec();
133-
134-
let mode = LittleEndian::read_u32(&mode);
135-
let size = LittleEndian::read_u32(&size);
136-
let time = LittleEndian::read_u32(&time);
137-
let name_len = LittleEndian::read_u32(&name_len) as usize;
138-
// Read the file name, since it requires the length from the name_len
139-
let name_buf = Self::read_bytes_from_transport(
140-
&name_len,
141-
&mut current_index,
142-
transport,
143-
&mut payload,
144-
&local_id,
145-
&remote_id,
146-
)?;
147-
let name = String::from_utf8(name_buf)?;
148-
149-
// First 9 bits are the file permissions
150-
let permissions = mode & 0b111111111;
151-
// Bits 14 to 16 are the file type
152-
let item_type = match (mode >> 13) & 0b111 {
153-
0b010 => ADBListItemType::Directory,
154-
0b100 => ADBListItemType::File,
155-
0b101 => ADBListItemType::Symlink,
156-
type_code => return Err(RustADBError::UnknownFileMode(type_code)),
157-
};
158-
let entry = ADBListItem {
159-
item_type,
160-
name,
161-
time,
162-
size,
163-
permissions,
164-
};
165-
list_items.push(entry);
166-
}
167-
"DONE" => {
168-
return Ok(list_items);
169-
}
170-
x => log::error!("Got an unknown response {}", x),
171-
}
172-
}
173-
}
174-
}
1+
use crate::{
2+
device::{
3+
adb_message_device::ADBMessageDevice, ADBTransportMessage, MessageCommand,
4+
MessageSubcommand,
5+
},
6+
ADBListItem, ADBListItemType, ADBMessageTransport, Result, RustADBError,
7+
};
8+
use byteorder::ByteOrder;
9+
use byteorder::LittleEndian;
10+
use std::str;
11+
12+
impl<T: ADBMessageTransport> ADBMessageDevice<T> {
13+
/// List the entries in the given directory on the device.
14+
/// note: path uses internal file paths, so Documents is at /storage/emulated/0/Documents
15+
pub(crate) fn list(&mut self, path: &str) -> Result<Vec<ADBListItem>> {
16+
self.begin_synchronization()?;
17+
18+
let output = self.handle_list(path);
19+
20+
self.end_transaction()?;
21+
output
22+
}
23+
24+
/// Request amount of bytes from transport, potentially across payloads
25+
///
26+
/// This automatically request a new payload by sending back "Okay" and waiting for the next payload
27+
/// It reads the request bytes across the existing payload, and if there is not enough bytes left,
28+
/// reads the rest from the next payload
29+
///
30+
/// Current index
31+
/// ┼───────────────┼ Requested
32+
/// ┌─────────────┐
33+
/// ┌───────────────┼───────┐ │
34+
/// └───────────────────────┘
35+
/// Current └─────┘
36+
/// payload Wanted in
37+
/// Next payload
38+
fn read_bytes_from_transport(
39+
requested_bytes: &usize,
40+
current_index: &mut usize,
41+
transport: &mut T,
42+
payload: &mut Vec<u8>,
43+
local_id: &u32,
44+
remote_id: &u32,
45+
) -> Result<Vec<u8>> {
46+
if *current_index + requested_bytes <= payload.len() {
47+
// if there is enough bytes in this payload
48+
// Copy from existing payload
49+
let slice = &payload[*current_index..*current_index + requested_bytes];
50+
*current_index += requested_bytes;
51+
Ok(slice.to_vec())
52+
} else {
53+
// Read the rest of the existing payload, then continue with the next message
54+
let mut slice = Vec::new();
55+
let bytes_read_from_existing_payload = payload.len() - *current_index;
56+
slice.extend_from_slice(
57+
&payload[*current_index..*current_index + bytes_read_from_existing_payload],
58+
);
59+
60+
// Request the next message
61+
let send_message =
62+
ADBTransportMessage::new(MessageCommand::Okay, *local_id, *remote_id, &[]);
63+
transport.write_message(send_message)?;
64+
// Read the new message
65+
*payload = transport.read_message()?.into_payload();
66+
let bytes_read_from_new_payload = requested_bytes - bytes_read_from_existing_payload;
67+
slice.extend_from_slice(&payload[..bytes_read_from_new_payload]);
68+
*current_index = bytes_read_from_new_payload;
69+
Ok(slice)
70+
}
71+
}
72+
73+
fn handle_list(&mut self, path: &str) -> Result<Vec<ADBListItem>> {
74+
// TODO: use LIS2 to support files over 2.14 GB in size.
75+
// SEE: https://github.com/cstyan/adbDocumentation?tab=readme-ov-file#adb-list
76+
let local_id = self.get_local_id()?;
77+
let remote_id = self.get_remote_id()?;
78+
{
79+
let mut len_buf = Vec::from([0_u8; 4]);
80+
LittleEndian::write_u32(&mut len_buf, path.len() as u32);
81+
82+
let subcommand_data = MessageSubcommand::List; //.with_arg(path.len() as u32);
83+
84+
let mut serialized_message =
85+
bincode::serialize(&subcommand_data).map_err(|_e| RustADBError::ConversionError)?;
86+
87+
serialized_message.append(&mut len_buf);
88+
let mut path_bytes: Vec<u8> = Vec::from(path.as_bytes());
89+
serialized_message.append(&mut path_bytes);
90+
91+
let message = ADBTransportMessage::new(
92+
MessageCommand::Write,
93+
local_id,
94+
remote_id,
95+
&serialized_message,
96+
);
97+
self.send_and_expect_okay(message)?;
98+
}
99+
100+
let mut list_items = Vec::new();
101+
102+
let transport = self.get_transport_mut();
103+
let mut payload = transport.read_message()?.into_payload();
104+
let mut current_index = 0;
105+
loop {
106+
// Loop though the response for all the entries
107+
const STATUS_CODE_LENGTH_IN_BYTES: usize = 4;
108+
let status_code = Self::read_bytes_from_transport(
109+
&STATUS_CODE_LENGTH_IN_BYTES,
110+
&mut current_index,
111+
transport,
112+
&mut payload,
113+
&local_id,
114+
&remote_id,
115+
)?;
116+
match str::from_utf8(&status_code)? {
117+
"DENT" => {
118+
// Read the file mode, size, mod time and name length in one go, since all their sizes are predictable
119+
const U32_SIZE_IN_BYTES: usize = 4;
120+
const SIZE_OF_METADATA: usize = U32_SIZE_IN_BYTES * 4;
121+
let metadata = Self::read_bytes_from_transport(
122+
&SIZE_OF_METADATA,
123+
&mut current_index,
124+
transport,
125+
&mut payload,
126+
&local_id,
127+
&remote_id,
128+
)?;
129+
let mode = metadata[..U32_SIZE_IN_BYTES].to_vec();
130+
let size = metadata[U32_SIZE_IN_BYTES..2 * U32_SIZE_IN_BYTES].to_vec();
131+
let time = metadata[2 * U32_SIZE_IN_BYTES..3 * U32_SIZE_IN_BYTES].to_vec();
132+
let name_len = metadata[3 * U32_SIZE_IN_BYTES..4 * U32_SIZE_IN_BYTES].to_vec();
133+
134+
let mode = LittleEndian::read_u32(&mode);
135+
let size = LittleEndian::read_u32(&size);
136+
let time = LittleEndian::read_u32(&time);
137+
let name_len = LittleEndian::read_u32(&name_len) as usize;
138+
// Read the file name, since it requires the length from the name_len
139+
let name_buf = Self::read_bytes_from_transport(
140+
&name_len,
141+
&mut current_index,
142+
transport,
143+
&mut payload,
144+
&local_id,
145+
&remote_id,
146+
)?;
147+
let name = String::from_utf8(name_buf)?;
148+
149+
// First 9 bits are the file permissions
150+
let permissions = mode & 0b111111111;
151+
// Bits 14 to 16 are the file type
152+
let item_type = match (mode >> 13) & 0b111 {
153+
0b010 => ADBListItemType::Directory,
154+
0b100 => ADBListItemType::File,
155+
0b101 => ADBListItemType::Symlink,
156+
type_code => return Err(RustADBError::UnknownFileMode(type_code)),
157+
};
158+
let entry = ADBListItem {
159+
item_type,
160+
name,
161+
time,
162+
size,
163+
permissions,
164+
};
165+
list_items.push(entry);
166+
}
167+
"DONE" => {
168+
return Ok(list_items);
169+
}
170+
x => log::error!("Got an unknown response {}", x),
171+
}
172+
}
173+
}
174+
}

0 commit comments

Comments
 (0)