android_usbser/
usb_info.rs

1use crate::usb::{jerr, usb_manager, Error};
2use getset::*;
3use jni::{objects::JObject, sys::jint, JNIEnv};
4use jni_min_helper::*;
5
6/// Enumerates for all USB devices via Android Java API.
7pub 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/// Corresponds to `android.hardware.usb.UsbDevice`.
27/// Its fields and the `InterfaceInfo` list are read on creation and will not
28/// be updated automatically; however, `PartialEq` depends on these fields.
29#[derive(Clone, CopyGetters, Getters)]
30pub struct DeviceInfo {
31    pub(crate) internal: jni::objects::GlobalRef,
32
33    /// Equals `idVendor`.
34    #[getset(get_copy = "pub")]
35    vendor_id: u16,
36    /// Equals `idProduct`.
37    #[getset(get_copy = "pub")]
38    product_id: u16,
39    /// Equals `bDeviceClass`.
40    #[getset(get_copy = "pub")]
41    class: u8,
42    /// Equals `bDeviceSubClass`.
43    #[getset(get_copy = "pub")]
44    subclass: u8,
45    /// Equals `bDeviceProtocol`.
46    #[getset(get_copy = "pub")]
47    protocol: u8,
48
49    /// (usually) Path of the device in the usbfs file system.
50    #[getset(get = "pub")]
51    path_name: String,
52    /// Vendor name.
53    #[getset(get = "pub")]
54    manufacturer_string: Option<String>,
55    /// Product name.
56    #[getset(get = "pub")]
57    product_string: Option<String>,
58    /// USB protocol version.
59    #[getset(get = "pub")]
60    version: Option<String>,
61    /// Device serial ID string. FIXME: On Android 10 and above, this is always `None`
62    /// if this struct is created before gaining permission for the device. To read it,
63    /// call `list_devices()` and find the device again after the permission is granted.
64    #[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                // Avoid printing `java.lang.SecurityException: User has not given permission...`
122                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    /// Iterator over the device's interfaces.
133    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        // Check `android.hardware.usb.UsbDevice.equals()` source code:
164        // it may compare both `UsbDevice` only by name (`path_name`).
165        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/// Corresponds to `android.hardware.usb.UsbInterface`.
179#[derive(Clone, Copy, CopyGetters)]
180#[getset(get_copy = "pub")]
181pub struct InterfaceInfo {
182    /// Equals `bInterfaceNumber`.
183    interface_number: u8,
184    /// Equals `bInterfaceClass`.
185    class: u8,
186    /// Equals `bInterfaceSubClass`.
187    sub_class: u8,
188    /// Equals `bInterfaceProtocol`.
189    protocol: u8,
190    /// Equals `bNumEndpoints`.
191    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// These functions call java methods without parameter. Error::Other on failure.
207#[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}