-
Notifications
You must be signed in to change notification settings - Fork 45
Support device probing and permission request on Android #150
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Excuse me, I just did a test of opening the same device in multiple threads: each thread opened the device successfully, but they got for i in 0..100 {
let dev = dev.clone();
std::thread::spawn(move || {
let conn = dev.open().wait().unwrap();
info!("*** Opened in thread {i}, printing USB configurations:");
std::thread::sleep(Duration::from_secs(1));
for conf in conn.configurations() {
info!("Thread {i}: {:?}", conf);
}
info!("Thread {i}: {:?}", conn.claim_interface(0).wait());
std::thread::sleep(Duration::from_secs(1));
info!("Thread {i} Closing the device.");
});
} |
|
The sloppy multi-thread Another test shows that getting the Java Click to expand // insert in `usb_manager()`
use std::sync::mpsc;
let usb_man_2 = usb_man.clone();
let (tx, rx) = mpsc::channel();
std::thread::spawn(move || {
jni_with_env(|env| {
let context = android_context();
let usb_service_id = USB_SERVICE.new_jobject(env)?;
let usb_man = env
.call_method(
context,
"getSystemService",
"(Ljava/lang/String;)Ljava/lang/Object;",
&[(&usb_service_id).into()],
)
.get_object(env)?;
tx.send(usb_man.is_same_object(&usb_man_2, env)).unwrap();
Ok(())
}).unwrap();
});
assert!(rx.recv().unwrap()); Yet another test shows that probing the same device in different threads gets different Java Click to expand // insert in `list_devices()`
if !devices.is_empty() {
use std::sync::mpsc;
let (tx, rx) = mpsc::channel();
std::thread::spawn(move || {
jni_with_env(|env| {
let ref_dev_list = env
.call_method(usb_man, "getDeviceList", "()Ljava/util/HashMap;", &[])
.get_object(env)?;
let map_dev = env.get_map(&ref_dev_list)?;
let mut iter_dev = map_dev.iter(env)?;
if let Some((_, dev)) = iter_dev.next(env)? {
tx.send(build_device_info(env, &dev)?).unwrap();
}
Ok(())
}).unwrap();
});
assert!(!rx.recv().unwrap().devinst.is_same_object(devices[0].devinst.as_ref(), env));
} |
I'm not sure if |
excited to see these changes land. I took a peek at the way you source context in jni-min and it strikes me as pretty sound. I was a bit worried about possible compatibility issues between ndk-glue and android_activity based crates - but I don't use ndk-glue (and it seems android_activity is generally better anyhow?) |
Well, I'm not familiar with I don't know if |
For v0.2, #158 |
src/platform/linux_usbfs/android.rs
Outdated
let ver_minor: u16 = ver_parser.next().unwrap(); | ||
|
||
Ok(DeviceInfo { | ||
devinst: Arc::new(env.new_global_ref(dev)?), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this called devinst
in other Android APIs, or should this be called jni_ref
or something more specific?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for the mismatch. The name is not from the Android API, and it's not a conventional name used in other Android support crate. I will change the name according to your suggestion.
src/platform/linux_usbfs/android.rs
Outdated
} | ||
self.vendor_id == other.vendor_id | ||
&& self.product_id == other.product_id | ||
&& self.id() == other.id() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The whole point of DeviceInfo::id
is to uniquely identify a device, so if self.id() == other.id()
is not sufficient alone then we need a different id
.
I'm wondering if UsbDevice::getDeviceId
would be a better ID than using the bus number and address like Linux, especially since the busnum and address are parsed from the path whose format is not explicitly documented.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just did a test and found that UsbDevice::getDeviceId()
returns integer decimal 1004
for /dev/bus/usb/001/004
; it became 1005
, 1006
... when plugging over and over again.
Description of UsbDevice.getDeviceName()
: "Returns the name of the device. In the standard implementation, this is the path of the device file for the device in the usbfs file system."
Despite of the bus number and device address number "contained" in the returned values of getDeviceId()
and getDeviceName()
, I may still ignore the possibility of extracting and providing these values since there might be non-standard implementations for the Android API.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, WebUSB also doesn't expose bus number and address, so I made limited them to Windows, Mac, Linux in #158. There is maybe some use case for the bus number on Android laptops or tablets with multiple USB ports (if they're even separate host controllers), but I think it would be fine to skip those fields.
Did I somehow push to your |
No, it's definitely not caused by your operation. I gave up merging conflicts online and synchronized my branch forcefully. I should consider my modifications again before reopening the PR. Of course I have an offline backup of my previous attempt. |
I just made a mistake: I added the "Android-specific note" for |
I'd lean towards making the permission request explicit, since it's an extra step that an Android developer will need to think about and figure out where it fits in their app's workflow. I was hoping we could add a common API to handle permission requests on both Android and WebUSB, but they work differently enough that I don't think that will be possible. On WebUSB, you can't list devices prior to requesting permission. The permission request API takes a set of filters, the user is prompted with a list of devices matching those filters (in browser-owned UI), and only the selected and approved device is returned. While on Android, it looks like the filtering and selection of the device is up to the app developer, and the permission is requested for a specific device. My plan was to add an API that accepts a set of WebUSB-style filters but potentially returns multiple devices. On desktop OSs it would just filter the |
Android runs on a lot more than just phones. I don't think we should assume there will only be a single USB device connected to an Android system. |
To implement the WebUSB style (device filtering and opening?) API for Android, https://crates.io/crates/jni-min-helper has provided an example of showing and receiving result of a chooser dialog on Android, it doesn't have to be provided by the application. Still I guess the current “Android style” API can be preserved too. |
Closes #86.
nusb::list_buses
(for rooted usage) is disabled because of some problems caused byport_chain
,device_version
,max_packet_size_0
unavailable on Android. Could they be changed toOption
s?I added
#![allow(dead_code)]
inplatform/linux_usbfs/mod.rs
because of the conditional compilation condition for Android caused many dead code warnings, maybe it can be solved in a better way.Sorry for being a bit sloppy, here's how I did the initial test:
Please make sure the JDK, Android SDK and NDK is installed and configured, and the Rust target
aarch64-linux-android
is installed. Installcargo-apk
and make sure the release keystore is configured.Save the code files posted below, run
cargo apk build -r
; connect the Android phone to the PC via USB, then configure the adbd TCP port (adb tcpip 5555
) and connect to it (adb connect xxx.xxx.xxx.xxx:5555
). Install the APK package withadb install -r <apk_file>
(the .apk file is located intarget/release/apk
).Run
adb logcat android_nusb_test:D '*:S'
On PC for tracing, start the installed "android_nusb_test" on the phone; try to connect and disconnect some USB devices via an OTG USB converter.(this draft code for testing can be changed in many ways in order to test all features thoroughly)
Cargo.toml
lib.rs
res/xml/device_filter.xml
:(this means no filtering, any device plugged into the phone can trigger the prompt asking whether or not to open this "app".)