Skip to content

Conversation

@CraigHutchinson
Copy link
Contributor

@CraigHutchinson CraigHutchinson commented Jul 11, 2022

Rebase of work on #874:

This code implements connection and device listing on unrooted android devices, without requiring any user-provided java.
This PR is usable now 🎉

The java native interface library is used, which is always available when building on android, to do the sdk calls within libusb itself. The approach is modularised to aid expanding if new features are needed.

  • Device enumeration by generating usb descriptors from the android sdk, which requires no user permission for this information.
  • Detect if permission is needed when an unpermissioned device is opened, and perform the requestPermission call if necessary. This pops up a dialogue to the user asking if they will allow the connection.
    📓 Dispatching this request involves setting an action string on the intent for permission that the application can optionally subscribe to. The permission intent I dispatch has action string libusb.android.USB_PERMISSION.

The code only takes effect if the user provides their JNIEnv* or JavaVM* pointer via a new libusb option.

@CraigHutchinson
Copy link
Contributor Author

Integrated Android support for device relisting xloem#3

@CraigHutchinson CraigHutchinson marked this pull request as ready for review July 12, 2022 07:07
@CraigHutchinson CraigHutchinson mentioned this pull request Jul 12, 2022
15 tasks
@xloem
Copy link
Contributor

xloem commented Jul 12, 2022

I'm happy to have this replace #874 or merge and rebase it too, whatever works better for maintainers and contributors.

@CraigHutchinson
Copy link
Contributor Author

CraigHutchinson commented Jul 15, 2022

@xloem I'm not sure but one option could be to force push this onto #874 then the conversation history is maintained while adding a cleaner set of commits? I would be happy to have this replace that pull also. Primarily its amazing work you have made and it needs to get merged as has significant value.

Also, I would very much be in a position to aid/maintain for testing purposes.

Current identified issues(s):

  1. [Minor] When device is being polled the USB Permissions prompt can occur multiple-times i.e. First prompt gives permission but a second prompt still is also pending and must therefore click 'ok' twice.
    EDIT: This may be due to before permission being granted a "libusb generates fake descriptors for it from Android's API" and this may be using to generate the second request due to application level logic TBC

@CraigHutchinson CraigHutchinson changed the title Better Android Support (Rebase 2022-07) JNI Android Support (Rebase 2022-07 #874) Aug 22, 2022
@mcuee
Copy link
Member

mcuee commented Nov 15, 2022

For people who are interested in better Android support, please come here and post your review or success/fail testing results. Thanks.

@CraigHutchinson CraigHutchinson changed the title JNI Android Support (Rebase 2022-07 #874) JNI Android Support on Unrooted devices Nov 15, 2022
@CraigHutchinson
Copy link
Contributor Author

CraigHutchinson commented Nov 17, 2022

NOTE: libusb now requires Signed-Commits on PR so has been rebased onto latest libusb/master. The code has not yet been retested after this update but no merge issues moving from master@2022-07 to master@2022-11

@tormodvolden
Copy link
Contributor

Thanks for the rebasing work!

For information, the maintainers typically rebase any PR locally when merging, regardless of Signed-Commits or not, so just ignore the GitHub warning about this (dunno how to turn it off). However, before this PR can be merged it should ideally be worked down to the smallest number of commits that makes sense, so e.g. without fixups commits. If there are multiple authors it can be correct to have their commits separated for credit attribution, but minor fixups of other people's commit can be amended in-place and commented in the commit message.

@mcuee
Copy link
Member

mcuee commented Aug 12, 2023

Its unclear to me whats preventing this from merging. Can someone summarize the action items needed ?

Need more reviews and tests.

Another thing, as of now this PR has conflicts with the git repo which needs to be fixed.

@ben1681
Copy link

ben1681 commented Sep 11, 2023

hello,I'm new here. Is there any Android demo using this branch? @CraigHutchinson @xloem

@CraigHutchinson
Copy link
Contributor Author

CraigHutchinson commented Sep 11, 2023

Not entirely sure what sort of demo you mean. But the Libusb should work as per any other example as long as you provide the JNI for the context configurable iirc.
Edit: actually, it may be the lack of a SIMPLE android demonstration app for test etc that prevents this getting merged as AFAIK as in use I didn't find any major bugs apart from hot plug issues earlier on.

@ben1681
Copy link

ben1681 commented Sep 11, 2023

@CraigHutchinson Thank you for your answer. I'm new to jni and I think need a sample program like this, where the author gets the file descriptor through usbmanager and passes it to jni, which works. Then I found your branch, and I was very interested in what you said about "to do the sdk calls within libusb itself."
Next I'll replace all the libusb code in this example with yours and try to modify the jni code. As I understand it, if I provide JNIEnv*, the code automatically runs the code that gets the permissions and file descriptors, calling the jni function directly. For a quick test, I simply changed the connect_device function in the example to look like this.
std::string connect_device(int fileDescriptor)
{
libusb_device **devs;
int r;
ssize_t cnt;

r = libusb_init(NULL);
if (r < 0)
return "r<0";

cnt = libusb_get_device_list(NULL, &devs);
if (cnt < 0){
libusb_exit(NULL);
return "fail";
}
if (cnt == 0){
libusb_exit(NULL);
return "cnt=0";
}

print_devs(devs);
libusb_free_device_list(devs, 1);

libusb_exit(NULL);
return "success";
}
When I connect my ipad to my Android phone using the Apple to type-c cable, and then run the program, no permission request window appears, I just get a "cnt=0". So I think an example like the one I provided, showing how to call your code to get device information, would be very helpful for beginners like me.

Rebase of work on #874:

This code implements connection and device listing on unrooted android devices, without requiring any user-provided java. This PR is usable now 🎉

The java native interface library is used, which is always available when building on android, to do the sdk calls within libusb itself. The approach is modularised to aid expanding if new features are needed.

  • Device enumeration by generating usb descriptors from the android sdk, which requires no user permission for this information.
  • Detect if permission is needed when an unpermissioned device is opened, and perform the requestPermission call if necessary. This pops up a dialogue to the user asking if they will allow the connection.
    📓 Dispatching this request involves setting an action string on the intent for permission that the application can optionally subscribe to. The permission intent I dispatch has action string libusb.android.USB_PERMISSION.

The code only takes effect if the user provides their JNIEnv* or JavaVM* pointer via a new libusb option.

@CraigHutchinson
Copy link
Contributor Author

I only rebased @xloem work but yes that is looking nearly there. Check the original PR for details which is linked on this rebase key bit is this (which I hope you gathered)

The code only takes effect if the user
provides their JNIEnv* or JavaVM*
pointer via a new libusb option.

I'm not at a PC nor working in this area for last year. As far as I recall the PR adds context options for setting these. It's upto your android code to choose where/how you get them but pass them in.

@ben1681
Copy link

ben1681 commented Sep 12, 2023

@CraigHutchinson Thank you so much for your answer, I have read this today#874, hard for me to fully understand by now. But finally I found out that @xloem has added an example to his libusb-android codehere, the guy I mentioned last time also wrote ademo program for this example, just I didn't find and understand it yesterday.

@sebinho
Copy link

sebinho commented Oct 16, 2024

I am bringing this topic back to life because I am trying to implement something similar on an Android device but I cannot get it to work.
To give a bit of background, I did build an app using Kivy Framework (the app is written in Python). I am able to talk to USB devices if I access the JAVA classes directly from Python using usb4a (via pyjnius).

But now I would like to access USB devices using a native C++ project. This project is using libusb to talk to USB devices. I tried to port the code from here. But I get the following errors in my logcat:

10-16 19:34:12.742  6950  6950 I SDLThread: type=1400 audit(0.0:2428): avc: denied { read } for name="usb" dev="tmpfs" ino=334 scontext=u:r:untrusted_app:s0:c103,c256,c512,c768 tcontext=u:object_r:usb_device:s0 tclass=dir permissive=1 app=org.test.myapp
10-16 19:34:12.742  6950  6950 I SDLThread: type=1400 audit(0.0:2429): avc: denied { open } for path="/dev/bus/usb" dev="tmpfs" ino=334 scontext=u:r:untrusted_app:s0:c103,c256,c512,c768 tcontext=u:object_r:usb_device:s0 tclass=dir permissive=1 app=org.test.myapp
10-16 19:34:12.742  6950  6950 I SDLThread: type=1400 audit(0.0:2431): avc: denied { open } for path="/sys/bus/usb/devices" dev="sysfs" ino=24050 scontext=u:r:untrusted_app:s0:c103,c256,c512,c768 tcontext=u:object_r:sysfs:s0 tclass=dir permissive=1 app=org.test.myapp

It seems to be some kind of permission issue from SELinux, but not sure what I can do....

Can someone help?
Is there any progress regarding libusb on Android and with a c++ project?

Many thanks in advance

@xloem
Copy link
Contributor

xloem commented Oct 17, 2024

  • make sure the project you are porting succeeds for you before the port, as-is
  • make sure you are including something like line 42 which provides the non-rooted device access

@sebinho
Copy link

sebinho commented Oct 17, 2024

@xloem, thanks for your reply.
Indeed I decided to try and port the project untouched with Android Studio. I had to update some version numbers to match my device (it has Android 12 so I put SDK version to 31). I also had to update gradle as it was a very old version referenced.
Here is the updated build.gradle:

apply plugin: 'com.android.application'

android {
    namespace "com.example.myapp"

    compileSdkVersion 31

    defaultConfig {
        applicationId = 'com.example.libusbNativeAndroidTest'
        minSdkVersion 31
        targetSdkVersion 31
        externalNativeBuild {
            cmake {
                arguments '-DANDROID_STL=c++_static'
            }
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'),
                    'proguard-rules.pro'
        }
    }
    externalNativeBuild {
        cmake {
            version '3.30.4'
            path 'src/main/cpp/CMakeLists.txt'
        }
    }
}

configurations.all {
    resolutionStrategy { force 'androidx.work:work-runtime:2.7.1' }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.4.2'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
    implementation 'androidx.startup:startup-runtime:1.1.1'
    implementation 'androidx.work:work-runtime:2.7.1'
    implementation 'androidx.work:work-runtime-ktx:2.7.1'
}

Not sure about the dependencies I added at the end of it, theses were trials to solve the issue I am facing. The issue I get when fetching logs with logcat is that the app shuts down as soon as it starts with following error:

10-18 17:27:04.939 10997 11023 F tiveAndroidTes: java_vm_ext.cc:579] JNI DETECTED ERROR IN APPLICATION: JNI CallVoidMethod called with pending exception java.lang.IllegalArgumentException: com.example.libusbNativeAndroidTest: Targeting S+ (version 31 and above) requires that one of FLAG_IMMUTABLE or FLAG_MUTABLE be specified when creating a PendingIntent.
10-18 17:27:04.939 10997 11023 F tiveAndroidTes: java_vm_ext.cc:579] Strongly consider using FLAG_IMMUTABLE, only use FLAG_MUTABLE if some functionality depends on the PendingIntent being mutable, e.g. if it needs to be used with inline replies or bubbles.
10-18 17:27:04.939 10997 11023 F tiveAndroidTes: java_vm_ext.cc:579]   at void android.app.PendingIntent.checkFlags(int, java.lang.String) (PendingIntent.java:375)
10-18 17:27:04.939 10997 11023 F tiveAndroidTes: java_vm_ext.cc:579]   at android.app.PendingIntent android.app.PendingIntent.getBroadcastAsUser(android.content.Context, int, android.content.Intent, int, android.os.UserHandle) (PendingIntent.java:645)
10-18 17:27:04.939 10997 11023 F tiveAndroidTes: java_vm_ext.cc:579]   at android.app.PendingIntent android.app.PendingIntent.getBroadcast(android.content.Context, int, android.content.Intent, int) (PendingIntent.java:632)

The message seems to claim that we have to use FLAG_IMMUTABLE or FLAG_MUTABLE, but these are parameters to be set inside JAVA source code and this project only has a C++ file.

Any idea on how to solve this?
Many thanks

@sebinho
Copy link

sebinho commented Oct 17, 2024

ok, so I was able to get one step further. I had to modify the source code of the updated libusb in linux_android_jni.c. This is because from API version 31 onwards, we have to use FLAG_IMMUTABLE or FLAG_MUTABLE be specified when creating a PendingIntent.
So I changed this:

	/* PendingIntent permission_intent =
		PendingIntent.getBroadcast(application_context, 0, intent, 0); */
	permission_intent =
		(*jni_env)->CallStaticObjectMethod(jni_env,
			jni->PendingIntent, jni->PendingIntent__getBroadcast,
			jni->application_context, 0, intent, 0);

into this:

	/* PendingIntent permission_intent =
		PendingIntent.getBroadcast(application_context, 0, intent, 0); */
	permission_intent =
		(*jni_env)->CallStaticObjectMethod(jni_env,
			jni->PendingIntent, jni->PendingIntent__getBroadcast,
			jni->application_context, 0, intent, 33554432);

33554432 is the value for FLAG_MUTABLE, see here: https://developer.android.com/reference/android/app/PendingIntent#FLAG_MUTABLE

Now the problem I have is that the application does not do anything regarding USB devices. If my understanding is correct, when launching the app it should print a list of the USB devices connected but nothing happens.
I put some debug log messages and it seems that the function void android_main(struct android_app * state) is never entered...

Here is the log, if it can help.

10-18 19:10:00.291   596   610 I ActivityTaskManager: START u0 {act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.example.libusbNativeAndroidTest/android.app.NativeActivity bnds=[1276,301][1592,449]} from uid 10079
10-18 19:10:00.308   596   625 I ActivityManager: Start proc 15935:com.example.libusbNativeAndroidTest/u0a111 for pre-top-activity {com.example.libusbNativeAndroidTest/android.app.NativeActivity}
10-18 19:10:00.312   397   397 D [email protected]: got new private_handle_t instance for buffer 'ViewRootImpl[libusbNativeAndroidTest]#140(BLAST Consumer)140'. share_fd : 9, share_attr_fd : 10, width : 1920, height : 1080, req_format : 0x1, producer_usage : 0xb00, consumer_usage : 0xb00, , stride : 0, alloc_format : 1, size : 8486912, layer_count : 1
10-18 19:10:00.312   397   397 D [email protected]: got new private_handle_t instance for buffer 'ViewRootImpl[libusbNativeAndroidTest]#140(BLAST Consumer)140'. share_fd : 9, share_attr_fd : 10, width : 1920, height : 1080, req_format : 0x1, producer_usage : 0xb00, consumer_usage : 0xb00, , stride : 0, alloc_format : 1, size : 8486912, layer_count : 1
10-18 19:10:00.312   397   397 D [email protected]: got new private_handle_t instance for buffer 'ViewRootImpl[libusbNativeAndroidTest]#140(BLAST Consumer)140'. share_fd : 9, share_attr_fd : 10, width : 1920, height : 1080, req_format : 0x1, producer_usage : 0xb00, consumer_usage : 0xb00, , stride : 0, alloc_format : 1, size : 8486912, layer_count : 1
10-18 19:10:00.346 15935 15935 V GraphicsEnvironment: ANGLE Developer option for 'com.example.libusbNativeAndroidTest' set to: 'default'
10-18 19:10:00.346 15935 15935 V GraphicsEnvironment: ANGLE GameManagerService for com.example.libusbNativeAndroidTest: false
10-18 19:10:00.409 15935 15935 I tiveAndroidTest: type=1400 audit(0.0:9627): avc: denied { read } for name="usb" dev="tmpfs" ino=357 scontext=u:r:untrusted_app:s0:c111,c256,c512,c768 tcontext=u:object_r:usb_device:s0 tclass=dir permissive=1 app=com.example.libusbNativeAndroidTest
10-18 19:10:00.409 15935 15935 I tiveAndroidTest: type=1400 audit(0.0:9628): avc: denied { open } for path="/dev/bus/usb" dev="tmpfs" ino=357 scontext=u:r:untrusted_app:s0:c111,c256,c512,c768 tcontext=u:object_r:usb_device:s0 tclass=dir permissive=1 app=com.example.libusbNativeAndroidTest
10-18 19:10:00.412 15935 15935 I Thread-2: type=1400 audit(0.0:9629): avc: denied { search } for name="usb" dev="tmpfs" ino=357 scontext=u:r:untrusted_app:s0:c111,c256,c512,c768 tcontext=u:object_r:usb_device:s0 tclass=dir permissive=1 app=com.example.libusbNativeAndroidTest
10-18 19:10:10.333   596   618 V WindowManager: getPackagePerformanceMode -- ComponentInfo{com.example.libusbNativeAndroidTest/android.app.NativeActivity} -- com.example.libusbNativeAndroidTest -- mode=0

any idea @xloem ?
Thx

@xloem
Copy link
Contributor

xloem commented Oct 18, 2024

Does anybody have a working example using one of these pull requests? I imagine it would be for an older libusb and an older android sdk.

If new to the code, it might probably make sense to forward-port something working one version at a time.

I might have time to help iterate issues if tagged elsewhere (maybe drop a link here) to not clog this thread too much. I was excited to do the first coding for this work but am stuck on iOS myself right now.

@ila-embsys
Copy link

Does anybody have a working example using one of these pull requests?

I had a working demo application that was connecting to AR glasses by libusb version from exactly this MR. This demo is an android application based on 'NativeActivity' with c++ code and CMake project.

This is a link where I saved code changes and description about what were necessary to port the demo application. The description is not fully about libusb usage, though the libusb related parts still could be useful.

As I see now, the important steps I did in the code were next:

  1. Create libusb context with set option LIBUSB_OPTION_ANDROID_JAVAVM
libusb_context *ctx;
libusb_set_option(ctx, LIBUSB_OPTION_ANDROID_JAVAVM, app->activity->vm);
libusb_init(&ctx);
  1. Set USB permission in Android manifest
<uses-permission android:name="android.hardware.usb.host" />
  1. Link application with libusb prebuilt from this MR
find_package(Libusb1 REQUIRED MODULE)

target_include_directories(xrgears PUBLIC ${LIBUSB1_INCLUDE_DIRS})
target_link_libraries(xrgears ${LIBUSB1_LIBRARIES})

Hope this information would be helpful.

@calvin2021y
Copy link

@CraigHutchinson

more than 3 years, the patch not able to work with libusb 1.0.27. any plan to update or merge this?

@Lorenzooone
Copy link

Lorenzooone commented Apr 30, 2025

Just wanted to drop by to say that I have used this successfully on Android, when targeting Android API 31.
I have used jagheterfredrik's branch (which currently has a PR going into CraigHutchinson's branch to fix a couple of compilation issues with newer NDKs), and it works flawlessly!

The only real "issue" is that it seems to be unable to compile for armeabi-v7a with the lowest Android API (16) due to the setup missing some timerfd functions, an error which does not happen when compiling regular libusb.
The fix to that is simply targeting a higher Android API (I did 31 for my setup since that is what the docker I use is based on).

Besides that, it really is as simple as giving the JavaVM pointer to libusb when you init the library. And then, it just works.

@YFogRain
Copy link

我倒是有个其他方案,不使用jni反调安卓的方法。

  1. 使用usbmanager获取到usbdevice的文件描述符
  2. 传递这个fd到native层,native层新增get_Device_with_fd的方法,将fd赋值给底层的struct缓存
  3. 打开设备时,通过这个缓存的fd,来赋值给对应的配置
    因为fd已经申请到权限,所以,使用ioctl跟linux底层交互时,这个fd就能够正常通讯
    。现在问题是,我使用当前方案,在安卓上表现正常,但是在鸿蒙上,因为获取当前fd绑定的驱动问题,会导致执行IOCTL_USBFS_IOCTL这个方法在没有驱动绑定时造成ANR问题,当前在研究解决方案

@mcuee
Copy link
Member

mcuee commented Sep 7, 2025

@CraigHutchinson

Just wondering if you can update this PR to resolve the conflicts with master. Thanks.

@mcuee mcuee mentioned this pull request Sep 7, 2025
laran002 added a commit to laran002/libusb that referenced this pull request Sep 25, 2025
…TABLE when creating a PendingIntent

- fixed the input flag to #FLAG_MUTABLE
- for more information on this pull request, check here: libusb#1164
@kubaraczkowski-spectricity

Hi all,

Excellent work, we've used libusb as Android USB solution for years now, this could make it even easier!

However, it seems that Android 15 breaks the whole game... It seems (from tests and e.g. Gemini) that it introduces stricter SELinux policies that disallow operations on the USB devices from Native libs. ONLY SDK (Java) transfers are allowed.

Could somebody confirm this perhaps?

@bearsh
Copy link

bearsh commented Nov 14, 2025

However, it seems that Android 15 breaks the whole game... It seems (from tests and e.g. Gemini) that it introduces stricter SELinux policies that disallow operations on the USB devices from Native libs. ONLY SDK (Java) transfers are allowed.

Could somebody confirm this perhaps?

I have an application (Kotlin (UI) -> golang (high-level driver) -> libusb) which works on my Pixel with Android 16. I open the usb handle through the Kontlin API, perhaps this makes the difference...

@Lorenzooone
Copy link

Hi all,

Excellent work, we've used libusb as Android USB solution for years now, this could make it even easier!

However, it seems that Android 15 breaks the whole game... It seems (from tests and e.g. Gemini) that it introduces stricter SELinux policies that disallow operations on the USB devices from Native libs. ONLY SDK (Java) transfers are allowed.

Could somebody confirm this perhaps?

I have an Android 15 phone, and this fork of libusb works on it.

All I do is this: https://github.com/Lorenzooone/cc3dsfs/blob/79116a27fdf56b3f73443c4a2c1450ee6f1dfa29/source/CaptureDeviceSpecific/usb_generic.cpp#L20

@kubaraczkowski-spectricity

Thanks for confirming! Indeed, it was a USB hub issue, and Gemini was hallucinating the root cause...

NICE!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.