1use crate::usb::{jerr, usb_manager, Error};
2use getset::*;
3use jni::{objects::JObject, sys::jint, JNIEnv};
4use jni_min_helper::*;
5
6pub fn list_devices() -> Result<Vec<DeviceInfo>, Error> {
8 let usb_man = usb_manager()?;
9 let mut devices = Vec::new();
10 jni_with_env(|env| {
11 let ref_dev_list = env
12 .call_method(usb_man, "getDeviceList", "()Ljava/util/HashMap;", &[])
13 .get_object(env)?;
14 let map_dev = env.get_map(&ref_dev_list)?;
15 let mut iter_dev = map_dev.iter(env)?;
16 while let Some((name, dev)) = iter_dev.next(env)? {
17 devices.push(DeviceInfo::build(env, &dev)?);
18 drop((env.auto_local(name), env.auto_local(dev)));
19 }
20 Ok(())
21 })
22 .map_err(jerr)?;
23 Ok(devices)
24}
25
26#[derive(Clone, CopyGetters, Getters)]
30pub struct DeviceInfo {
31 pub(crate) internal: jni::objects::GlobalRef,
32
33 #[getset(get_copy = "pub")]
35 vendor_id: u16,
36 #[getset(get_copy = "pub")]
38 product_id: u16,
39 #[getset(get_copy = "pub")]
41 class: u8,
42 #[getset(get_copy = "pub")]
44 subclass: u8,
45 #[getset(get_copy = "pub")]
47 protocol: u8,
48
49 #[getset(get = "pub")]
51 path_name: String,
52 #[getset(get = "pub")]
54 manufacturer_string: Option<String>,
55 #[getset(get = "pub")]
57 product_string: Option<String>,
58 #[getset(get = "pub")]
60 version: Option<String>,
61 #[getset(get = "pub")]
65 serial_number: Option<String>,
66
67 interfaces: Vec<InterfaceInfo>,
68}
69
70impl DeviceInfo {
71 pub(crate) fn build(env: &mut JNIEnv, dev: &JObject<'_>) -> Result<Self, jni::errors::Error> {
72 let num_interfaces = get_int_val(env, dev, "getInterfaceCount")? as u8;
73 let mut interface_refs = Vec::new();
74 for i in 0..num_interfaces {
75 interface_refs.push(
76 env.call_method(
77 dev,
78 "getInterface",
79 "(I)Landroid/hardware/usb/UsbInterface;",
80 &[(i as jint).into()],
81 )
82 .get_object(env)?,
83 );
84 }
85 let mut info = Self {
86 internal: env.new_global_ref(dev)?,
87
88 vendor_id: get_int_val(env, dev, "getVendorId")? as u16,
89 product_id: get_int_val(env, dev, "getProductId")? as u16,
90 class: get_int_val(env, dev, "getDeviceClass")? as u8,
91 subclass: get_int_val(env, dev, "getDeviceSubclass")? as u8,
92 protocol: get_int_val(env, dev, "getDeviceProtocol")? as u8,
93
94 path_name: get_string_val(env, dev, "getDeviceName")?,
95 manufacturer_string: None,
96 product_string: None,
97 version: None,
98 serial_number: None,
99
100 interfaces: {
101 let mut interfaces = Vec::new();
102 for interface in interface_refs.into_iter() {
103 interfaces.push(InterfaceInfo {
104 interface_number: get_int_val(env, &interface, "getId")? as u8,
105 class: get_int_val(env, &interface, "getInterfaceClass")? as u8,
106 sub_class: get_int_val(env, &interface, "getInterfaceSubclass")? as u8,
107 protocol: get_int_val(env, &interface, "getInterfaceProtocol")? as u8,
108 num_endpoints: get_int_val(env, &interface, "getEndpointCount")? as u8,
109 });
110 }
111 interfaces
112 },
113 };
114 if android_api_level() >= 21 {
115 info.version = Some(get_string_val(env, dev, "getVersion")?);
116 info.manufacturer_string = get_string_val(env, dev, "getManufacturerName").ok();
117 info.product_string = get_string_val(env, dev, "getProductName").ok();
118 info.serial_number = if android_api_level() < 29 {
119 get_string_val(env, dev, "getSerialNumber").ok()
120 } else {
121 env.call_method(dev, "getSerialNumber", "()Ljava/lang/String;", &[])
123 .map_err(jni_clear_ex_silent)
124 .get_object(env)
125 .and_then(|o| o.get_string(env))
126 .ok()
127 }
128 }
129 Ok(info)
130 }
131
132 pub fn interfaces(&self) -> impl Iterator<Item = &InterfaceInfo> {
134 self.interfaces.iter()
135 }
136}
137
138impl std::fmt::Debug for DeviceInfo {
139 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
140 let mut s = f.debug_struct("DeviceInfo");
141
142 s.field("vendor_id", &format_args!("0x{:04X}", self.vendor_id));
143 s.field("product_id", &format_args!("0x{:04X}", self.product_id));
144 s.field("class", &format_args!("0x{:02X}", self.class));
145 s.field("subclass", &format_args!("0x{:02X}", self.subclass));
146 s.field("protocol", &format_args!("0x{:02X}", self.protocol));
147
148 s.field("path_name", &self.path_name);
149 s.field("version", &self.version);
150 s.field("manufacturer_string", &self.manufacturer_string);
151 s.field("product_string", &self.product_string);
152 s.field("serial_number", &self.serial_number);
153
154 for intr in self.interfaces.iter() {
155 s.field("Interface", &intr);
156 }
157 s.finish()
158 }
159}
160
161impl PartialEq for DeviceInfo {
162 fn eq(&self, other: &Self) -> bool {
163 if let (Some(self_ser), Some(other_ser)) =
166 (self.serial_number.as_ref(), other.serial_number.as_ref())
167 {
168 if self_ser != other_ser {
169 return false;
170 }
171 }
172 self.vendor_id == other.vendor_id
173 && self.product_id == other.product_id
174 && self.path_name == other.path_name
175 }
176}
177
178#[derive(Clone, Copy, CopyGetters)]
180#[getset(get_copy = "pub")]
181pub struct InterfaceInfo {
182 interface_number: u8,
184 class: u8,
186 sub_class: u8,
188 protocol: u8,
190 num_endpoints: u8,
192}
193
194impl std::fmt::Debug for InterfaceInfo {
195 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
196 f.debug_struct("InterfaceInfo")
197 .field("interface_number", &self.interface_number)
198 .field("class", &format_args!("0x{:02X}", self.class))
199 .field("sub_class", &format_args!("0x{:02X}", self.sub_class))
200 .field("protocol", &format_args!("0x{:02X}", self.protocol))
201 .field("num_endpoints", &self.num_endpoints)
202 .finish()
203 }
204}
205
206#[inline(always)]
208fn get_int_val(
209 env: &mut JNIEnv,
210 dev: &JObject<'_>,
211 method: &str,
212) -> Result<jint, jni::errors::Error> {
213 env.call_method(dev, method, "()I", &[]).get_int()
214}
215#[inline(always)]
216fn get_string_val(
217 env: &mut JNIEnv,
218 dev: &JObject<'_>,
219 method: &str,
220) -> Result<String, jni::errors::Error> {
221 env.call_method(dev, method, "()Ljava/lang/String;", &[])
222 .get_object(env)
223 .and_then(|o| o.get_string(env))
224}