androidqtqtandroidextrasrtl-sdr

QtAndroid: Grant Usb Permission in Android with Qt6


in android programming you need to get usb permission before working with connected device to client device.
i am trying to connect a hack-rf to android device and use it... but first of all i need to get usb permission and detect that hackrf.

setup:
1- qt6.3.0
2- ndk 21.3.6528147
3- ui with QML

this is my c++ code to get permission of storage:

bool checkAppPermissions(QString permission)
{
    auto result = QtAndroidPrivate::checkPermission(permission).result();
    if (result == QtAndroidPrivate::Denied) {
        auto result2 = QtAndroidPrivate::requestPermission(permission).result();
        qDebug() << permission << result2;

        if (result2 == QtAndroidPrivate::Denied)
            return false;
        else {
            return true;
        }
    }
    else {
        return true;
    }
}

and it's usage:

checkAppPermissions("android.permission.WRITE_EXTERNAL_STORAGE")

but i cannot get usb permisson this way.. its not working.

in AndroidManifest.xml i put these:

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-feature android:name="android.hardware.usb.host"/>

also i think the permission that i had to get is "com.android.example.USB_PERMISSION" but i dont know how should i Grant or request for that.

this kotlin code works fine... it detects device and gets the permission but i don't know how it is working! or how can i implement it in Qt with cpp.

i also see this answer but i dont know how he uses C++ native side code or MyActivity.java

could you help me to detect device and grant it's permission at run time.


Solution

  • To ask for USB permission you need to use the Java class called UsbManager.

    In Qt6, they changed how QJniObject works, so it is kinda hard to find how to use it but here is what you need to do :

    1. Get the context and usb manager object :

      QJniObject context = QtAndroidPrivate::context();
      if (context.isValid()) {
      QJniObject usbManager = context.callObjectMethod(
          "getSystemService",
          "(Ljava/lang/String;)Ljava/lang/Object;",
          QJniObject::getStaticObjectField(
              "android/content/Context",
              "USB_SERVICE",
              "Ljava/lang/String;"
          ).object<jstring>()
      );
      
    2. Iterate on the deviceList obtained from usbManager :

          QJniObject deviceList = usbManager.callObjectMethod(
              "getDeviceList",
              "()Ljava/util/HashMap;"
          );
      
          //Retrieve Java objects to be able to iterate deviceList
          QJniEnvironment env;
          jobject jMap = deviceList.object();
          jclass mapClass = env->GetObjectClass(jMap);
          jmethodID entrySet = env->GetMethodID(mapClass, "entrySet", "()Ljava/util/Set;");
          jobject jEntrySet = env->CallObjectMethod(jMap, entrySet);
          jclass setClass = env->FindClass("java/util/Set");
          jmethodID iterator = env->GetMethodID(setClass, "iterator", "()Ljava/util/Iterator;");
          jobject jIterator = env->CallObjectMethod(jEntrySet, iterator);
          jclass iteratorClass = env->FindClass("java/util/Iterator");
          jmethodID hasNext = env->GetMethodID(iteratorClass, "hasNext", "()Z");
          jmethodID next = env->GetMethodID(iteratorClass, "next", "()Ljava/lang/Object;");
          jclass entryClass = env->FindClass("java/util/Map$Entry");
          jmethodID getKey = env->GetMethodID(entryClass, "getKey", "()Ljava/lang/Object;");
          jmethodID getValue = env->GetMethodID(entryClass, "getValue", "()Ljava/lang/Object;");
      
    3. Get your device object and obtain all information needed (if you want to print for debug or something else)

       while (env->CallBooleanMethod(jIterator, hasNext)) {
              jobject jEntry = env->CallObjectMethod(jIterator, next);
              jstring jDeviceName = static_cast<jstring>(env->CallObjectMethod(jEntry, getKey));
              jobject jDevice = env->CallObjectMethod(jEntry, getValue);
              QJniObject device(jDevice);
      
              jint vendorId = device.callMethod<jint>("getVendorId", "()I");
              jint productId = device.callMethod<jint>("getProductId", "()I");
              QString manufacturer = (device.callMethod<jstring>("getManufacturerName", "()Ljava/lang/String;")).toString();
              QString productname = (device.callMethod<jstring>("getProductName", "()Ljava/lang/String;")).toString();
      
    4. And this is how you request permission to a specific device

              jstring permissionString = env->NewStringUTF("org.qtproject.example.USB_PERMISSION");
      
              QJniObject intent("android/content/Intent");
              jclass stringClass = env->FindClass("java/lang/String");
              jmethodID constructor = env->GetMethodID(stringClass, "<init>", "(Ljava/lang/String;)V");
              jobject stringObject = env->NewObject(stringClass, constructor, permissionString);
              intent.callMethod<void>("<init>", "(Ljava/lang/String;)V", stringObject);
      
              QJniObject permissionIntent("android/app/PendingIntent");
              jclass pendingIntentClass = env->FindClass("android/app/PendingIntent");
              jmethodID getBroadcastMethod = env->GetStaticMethodID(pendingIntentClass, "getBroadcast", "(Landroid/content/Context;ILandroid/content/Intent;I)Landroid/app/PendingIntent;");
              permissionIntent = permissionIntent.callStaticObjectMethod(pendingIntentClass, getBroadcastMethod, context.object(), 0, intent.object(), 0);
      
              usbManager.callMethod<void>(
                  "requestPermission",
                  "(Landroid/hardware/usb/UsbDevice;Landroid/app/PendingIntent;)V",
                  device.object(),
                  permissionIntent.object()
                  );