Skip to content

Commit 16faa5b

Browse files
committed
Version 0.2.2
1 parent dcee3a9 commit 16faa5b

File tree

7 files changed

+128
-23
lines changed

7 files changed

+128
-23
lines changed

CHANGELOG.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Changes
2+
3+
## 0.2.2
4+
* Fixed support for newest Android versions: `check_attached_intent()` does not work, `PermissionRequest` never returns the result of being permitted, both are caused by the bad implementation of `PartialEq` for `DeviceInfo`.
5+
* Added `UsbSerial` trait to prepare for driver implementations of non-CDC serial adapters.
6+
* The serial handler can be turned into `nusb` transfer queues for asynchronous operations, this can be done after serial configuration.
7+
8+
## 0.2.1
9+
* Added `HotplugWatch`;
10+
* `DeviceInfo::request_permission()` now returns a result of `Option<PermissionRequest>` instead of `()`.
11+
12+
## 0.2.0
13+
* Switched to `nusb` for USB data transfering, instead of calling Java methods for reading and writing.
14+
* `serialport` became a required dependency; the optional feature for `rusb` (which may not work on some Android devices) is removed.
15+
16+
## 0.1.1
17+
* Fixed doc.rs build problem.
18+
19+
## 0.1.0
20+
* Initial release.

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "android-usbser"
3-
version = "0.2.1"
3+
version = "0.2.2"
44
authors = ["wuwbobo2021 <wuwbobo@outlook.com>"]
55
edition = "2021"
66
license = "MIT OR Apache-2.0"
@@ -32,7 +32,7 @@ nusb = "0.1.12"
3232
serialport = "4.6"
3333
futures-core = "0.3"
3434
futures-lite = "2.5"
35-
jni-min-helper = { version = "0.2.2", features = ["futures"] }
35+
jni-min-helper = { version = "0.2.6", features = ["futures"] }
3636

3737
[lib]
3838
name = "android_usbser"

lib.rs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,27 @@ pub mod usb {
5454
}
5555
}
5656

57+
use nusb::transfer::{Queue, RequestBuffer};
58+
59+
/// Serial driver implementations inside this crate should implement this trait.
60+
///
61+
/// TODO: add crate-level functions `probe() -> Result<Vec<DeviceInfo>, Error>`
62+
/// and `open(dev_info: &DeviceInfo, timeout: Duration) -> Result<Box<dyn UsbSerial>, Error>`.
63+
pub trait UsbSerial: serialport::SerialPort {
64+
/// Sets baudrate, parity check mode, data bits and stop bits.
65+
fn configure(&mut self, conf: &SerialConfig) -> std::io::Result<()>;
66+
67+
/// Takes `nusb` transfer queues of the read endpoint and the write endpoint.
68+
/// This can be called after serial configuration to do asynchronous operations.
69+
fn into_queues(self) -> (Queue<RequestBuffer>, Queue<Vec<u8>>);
70+
71+
#[doc(hidden)]
72+
fn sealer(_: private::Internal);
73+
}
74+
5775
use serialport::{DataBits, Parity, StopBits};
5876

59-
/// Sets baudrate, parity check mode, data bits and stop bits.
77+
/// Serial parameters including baudrate, parity check mode, data bits and stop bits.
6078
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
6179
pub struct SerialConfig {
6280
pub baud_rate: u32,
@@ -156,3 +174,9 @@ impl std::fmt::Display for SerialConfig {
156174
write!(f, "{baud_rate},{parity},{data_bits},{stop_bits}")
157175
}
158176
}
177+
178+
mod private {
179+
/// Used as a parameter of the hidden function in sealed traits.
180+
#[derive(Debug)]
181+
pub struct Internal;
182+
}

ser_cdc.rs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@ use std::{
33
time::Duration,
44
};
55

6-
use crate::usb::{self, DeviceInfo, InterfaceInfo, SyncReader, SyncWriter};
76
use crate::SerialConfig;
8-
use nusb::transfer::{Control, ControlType, Direction, Recipient};
7+
use crate::{
8+
usb::{self, DeviceInfo, InterfaceInfo, SyncReader, SyncWriter},
9+
UsbSerial,
10+
};
11+
use nusb::transfer::{Control, ControlType, Direction, Queue, Recipient, RequestBuffer};
912

1013
use serialport::{DataBits, Parity, SerialPort, StopBits};
1114

@@ -354,3 +357,15 @@ impl SerialPort for CdcSerial {
354357
Err(err_unsupported_op())
355358
}
356359
}
360+
361+
impl UsbSerial for CdcSerial {
362+
fn configure(&mut self, conf: &SerialConfig) -> std::io::Result<()> {
363+
self.set_config(*conf)
364+
}
365+
366+
fn into_queues(self) -> (Queue<RequestBuffer>, Queue<Vec<u8>>) {
367+
(self.reader.into(), self.writer.into())
368+
}
369+
370+
fn sealer(_: crate::private::Internal) {}
371+
}

usb_conn.rs

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ const EXTRA_DEVICE: &str = "device";
1414
const ACTION_USB_PERMISSION: &str = "rust.android_usbser.USB_PERMISSION"; // custom
1515
const EXTRA_PERMISSION_GRANTED: &str = "permission";
1616

17-
/// Gets a gloabal reference of `android.hardware.usb.UsbManager`.
17+
/// Gets a global reference of `android.hardware.usb.UsbManager`.
1818
#[inline(always)]
1919
pub(crate) fn usb_manager() -> Result<&'static jni::objects::JObject<'static>, Error> {
2020
use std::sync::OnceLock;
@@ -50,7 +50,7 @@ fn get_usb_manager() -> Result<jni::objects::GlobalRef, Error> {
5050
}
5151
}
5252

53-
/// Checks if the Android application is opened by an intent with
53+
/// Checks if the Android context is an activity opened by an intent of
5454
/// `android.hardware.usb.action.USB_DEVICE_ATTACHED`. If so, it takes the `DeviceInfo`
5555
/// for the caller to open the device.
5656
///
@@ -84,7 +84,7 @@ pub fn check_attached_intent() -> Result<DeviceInfo, Error> {
8484
}
8585
let dev_info = get_extra_device(&intent_startup)?;
8686
if dev_info.check_connection() {
87-
Ok(dev_info)
87+
Ok(dev_info)
8888
} else {
8989
Err(Error::from(ErrorKind::NotConnected))
9090
}
@@ -124,19 +124,19 @@ pub fn watch_devices() -> Result<HotplugWatch, Error> {
124124
/// Stream of device connection / disconnection events.
125125
#[derive(Debug)]
126126
pub struct HotplugWatch {
127-
waiter: BroadcastWaiter
127+
waiter: BroadcastWaiter,
128128
}
129129

130130
/// Event returned from the `HotplugWatch` stream.
131131
#[derive(Clone, Debug)]
132132
pub enum HotplugEvent {
133133
Connected(DeviceInfo),
134-
Disconnected(DeviceInfo)
134+
Disconnected(DeviceInfo),
135135
}
136136

137137
#[derive(Debug)]
138138
struct HotplugWatchFuture<'a> {
139-
watch: &'a mut HotplugWatch
139+
watch: &'a mut HotplugWatch,
140140
}
141141

142142
impl HotplugWatch {
@@ -217,7 +217,7 @@ impl DeviceInfo {
217217
let usb_man = usb_manager()?;
218218
let env = &mut jni_attach_vm().map_err(jerr)?;
219219
env.call_method(
220-
&usb_man,
220+
usb_man,
221221
"hasPermission",
222222
"(Landroid/hardware/usb/UsbDevice;)Z",
223223
&[self.internal.as_obj().into()],
@@ -235,10 +235,10 @@ impl DeviceInfo {
235235
}
236236

237237
/// Performs a permission request for the device.
238-
///
238+
///
239239
/// Returns `Ok(None)` if the permission is already granted. Otherwise it returns a
240240
/// `PermissionRequest` handler.
241-
///
241+
///
242242
/// The activity might be paused by `requestPermission()` here, but resumed on receving result.
243243
/// The state of `PermissionRequest` can be checked on `android_activity::MainEvent::Resume`,
244244
/// Otherwise block in a background thread (it wouldn't be paused/resumed automatically).
@@ -279,7 +279,7 @@ impl DeviceInfo {
279279
.map_err(jerr)?;
280280

281281
env.call_method(
282-
&usb_man,
282+
usb_man,
283283
"requestPermission",
284284
"(Landroid/hardware/usb/UsbDevice;Landroid/app/PendingIntent;)V",
285285
&[(&self.internal).into(), (&pending).into()],
@@ -291,7 +291,12 @@ impl DeviceInfo {
291291
return Ok(None); // almost impossible
292292
}
293293
BroadcastWaiter::build([ACTION_USB_PERMISSION])
294-
.map(|waiter| Some(PermissionRequest { dev_info: self.clone(), waiter }))
294+
.map(|waiter| {
295+
Some(PermissionRequest {
296+
dev_info: self.clone(),
297+
waiter,
298+
})
299+
})
295300
.map_err(jerr)
296301
}
297302

@@ -305,7 +310,7 @@ impl DeviceInfo {
305310
let env = &mut jni_attach_vm().map_err(jerr)?;
306311
let conn = env
307312
.call_method(
308-
&usb_man,
313+
usb_man,
309314
"openDevice",
310315
"(Landroid/hardware/usb/UsbDevice;)Landroid/hardware/usb/UsbDeviceConnection;",
311316
&[(&self.internal).into()],
@@ -331,15 +336,15 @@ impl DeviceInfo {
331336
#[derive(Debug)]
332337
pub struct PermissionRequest {
333338
dev_info: DeviceInfo,
334-
waiter: BroadcastWaiter
339+
waiter: BroadcastWaiter,
335340
}
336341

337342
impl PermissionRequest {
338343
/// Returns a reference of the associated `DeviceInfo` which can be cloned.
339344
pub fn device_info(&self) -> &DeviceInfo {
340345
&self.dev_info
341346
}
342-
347+
343348
/// Checks if the request has completed.
344349
pub fn responsed(&self) -> bool {
345350
self.waiter.count_received() > 0

usb_info.rs

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ pub fn list_devices() -> Result<Vec<DeviceInfo>, Error> {
99
let env = &mut jni_attach_vm().map_err(jerr)?;
1010
let mut devices = Vec::new();
1111
let ref_dev_list = env
12-
.call_method(&usb_man, "getDeviceList", "()Ljava/util/HashMap;", &[])
12+
.call_method(usb_man, "getDeviceList", "()Ljava/util/HashMap;", &[])
1313
.get_object(env)
1414
.map_err(jerr)?;
1515
let map_dev = env.get_map(&ref_dev_list).map_err(jerr)?;
@@ -56,7 +56,9 @@ pub struct DeviceInfo {
5656
/// USB protocol version.
5757
#[getset(get = "pub")]
5858
version: Option<String>,
59-
/// Device serial ID string.
59+
/// Device serial ID string. FIXME: On Android 10 and above, this is always `None`
60+
/// if this struct is created before gaining permission for the device. To read it,
61+
/// call `list_devices()` and find the device again after the permission is granted.
6062
#[getset(get = "pub")]
6163
serial_number: Option<String>,
6264

@@ -112,7 +114,16 @@ impl DeviceInfo {
112114
info.version = Some(get_string_field(env, dev, "getVersion")?);
113115
info.manufacturer_string = get_string_field(env, dev, "getManufacturerName").ok();
114116
info.product_string = get_string_field(env, dev, "getProductName").ok();
115-
info.serial_number = get_string_field(env, dev, "getSerialNumber").ok();
117+
info.serial_number = if android_api_level() < 29 {
118+
get_string_field(env, dev, "getSerialNumber").ok()
119+
} else {
120+
// Avoid printing `java.lang.SecurityException: User has not given permission...`
121+
env.call_method(dev, "getSerialNumber", "()Ljava/lang/String;", &[])
122+
.map_err(jni_clear_ex_silent)
123+
.get_object(env)
124+
.and_then(|o| o.get_string(env))
125+
.ok()
126+
}
116127
}
117128
Ok(info)
118129
}
@@ -150,10 +161,16 @@ impl PartialEq for DeviceInfo {
150161
fn eq(&self, other: &Self) -> bool {
151162
// Check `android.hardware.usb.UsbDevice.equals()` source code:
152163
// it may compare both `UsbDevice` only by name (`path_name`).
164+
if let (Some(self_ser), Some(other_ser)) =
165+
(self.serial_number.as_ref(), other.serial_number.as_ref())
166+
{
167+
if self_ser != other_ser {
168+
return false;
169+
}
170+
}
153171
self.vendor_id == other.vendor_id
154172
&& self.product_id == other.product_id
155173
&& self.path_name == other.path_name
156-
&& self.serial_number == other.serial_number
157174
}
158175
}
159176

usb_sync.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,18 @@ impl SyncReader {
7474
}
7575
}
7676

77+
impl From<ReadQueue> for SyncReader {
78+
fn from(value: ReadQueue) -> Self {
79+
Self::new(value)
80+
}
81+
}
82+
83+
impl From<SyncReader> for ReadQueue {
84+
fn from(value: SyncReader) -> Self {
85+
value.queue
86+
}
87+
}
88+
7789
/// Synchronous wrapper of a `nusb` OUT transfer queue.
7890
pub struct SyncWriter {
7991
queue: WriteQueue,
@@ -135,3 +147,15 @@ impl SyncWriter {
135147
result
136148
}
137149
}
150+
151+
impl From<WriteQueue> for SyncWriter {
152+
fn from(value: WriteQueue) -> Self {
153+
Self::new(value)
154+
}
155+
}
156+
157+
impl From<SyncWriter> for WriteQueue {
158+
fn from(value: SyncWriter) -> Self {
159+
value.queue
160+
}
161+
}

0 commit comments

Comments
 (0)