0

I want to use libimobiledevice by Java Foreign API, This is my code, written by Kotlin:

class Device {
    private val arena: Arena = Arena.openConfined()
    private val udidAddress: MemorySegment = arena.allocate(C_POINTER)
    private val deviceAddress: MemorySegment = arena.allocate(C_POINTER)

    init {
        if (idevice_new_with_options(deviceAddress, NULL(), IDEVICE_LOOKUP_USBMUX()) != IDEVICE_E_SUCCESS()) {
            throw Exception("ERROR: No device found!")
        }
        if (idevice_get_udid(deviceAddress, udidAddress) != IDEVICE_E_SUCCESS()) {
            idevice_free(deviceAddress)
            throw Exception("ERROR: Get UDID failed")
        } else {
            println("connected")
            println(MemorySegment.ofAddress(udidAddress.address()).getUtf8String(0))
        }
    }
}

fun main() {
    Device()
}

But it got error while getting udid:

Exception in thread "main" java.lang.IndexOutOfBoundsException: Out of bound access on segment MemorySegment{ array: Optional.empty address:140202431639472 limit: 0 }; new offset = 0; new length = 1
    at java.base/jdk.internal.foreign.AbstractMemorySegmentImpl.outOfBoundException(AbstractMemorySegmentImpl.java:371)
    at java.base/jdk.internal.foreign.AbstractMemorySegmentImpl.apply(AbstractMemorySegmentImpl.java:357)
    at java.base/jdk.internal.foreign.AbstractMemorySegmentImpl.apply(AbstractMemorySegmentImpl.java:70)
    at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:98)
    at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:124)
    at java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:448)
    at java.base/jdk.internal.foreign.AbstractMemorySegmentImpl.checkBounds(AbstractMemorySegmentImpl.java:346)
    at java.base/jdk.internal.foreign.AbstractMemorySegmentImpl.checkAccess(AbstractMemorySegmentImpl.java:311)
    at java.base/java.lang.invoke.VarHandleSegmentAsBytes.checkAddress(VarHandleSegmentAsBytes.java:81)
    at java.base/java.lang.invoke.VarHandleSegmentAsBytes.get(VarHandleSegmentAsBytes.java:108)
    at java.base/java.lang.foreign.MemorySegment.get(MemorySegment.java:1388)
    at java.base/jdk.internal.foreign.abi.SharedUtils.strlen(SharedUtils.java:199)
    at java.base/jdk.internal.foreign.abi.SharedUtils.toJavaStringInternal(SharedUtils.java:190)
    at java.base/java.lang.foreign.MemorySegment.getUtf8String(MemorySegment.java:890)
    at Device.<init>(Device.kt:21)
    at DeviceKt.main(Device.kt:27)
    at DeviceKt.main(Device.kt)

The C language code i want:

static char *udid = NULL;

/* Device Handle */
idevice_t device = NULL;

/* Try to connect to first USB device */
if (idevice_new_with_options(&device, NULL, IDEVICE_LOOKUP_USBMUX) != IDEVICE_E_SUCCESS) {
  printf("ERROR: No device found!\n");
  return -1;
}

/* Retrieve the udid of the connected device */
if (idevice_get_udid(device, &udid) != IDEVICE_E_SUCCESS) {
  printf("ERROR: Unable to get the device UDID.\n");
  idevice_free(device);
  return -1;
}

/* Outputs device identifier */
printf("Connected with UDID: %s\n", udid);

how to solve this problem? (sorry, this is my first time using stackoverflow)

It should return a udid, but I got an error, the code I tried is above.

purofle
  • 1
  • 1
  • 2
    Shouldn't you read a C_Pointer first? Like `MemorySegment.ofAddress(udidAddress.get(C_POINTER, 0))` – Yahor Barkouski Jun 11 '23 at 09:48
  • Also please note that the getUtf8String() function might throw IndexOutOfBound if the string at udidPointer is not null-terminated, so you need to ensure that the string written by idevice_get_udid method is a null-terminated one – Yahor Barkouski Jun 11 '23 at 09:49
  • Note that `MemorySegment.ofAddress(udidAddress.address())` is essentially a no-op which just returns a new memory segment with the same address, but size 0. – Jorn Vernee Jun 11 '23 at 13:26

2 Answers2

0

There's no equivalent of the & operator in the Panama API (Simply because Java values don't have a stable native address). So, step 1 is to rewrite your C code to not use it. We get something like this:

static char *udid = NULL;

/* Device Handle */
idevice_t device = NULL;

/* Try to connect to first USB device */
idevice_t *device_ptr = malloc(sizeof *device_ptr);
if (idevice_new_with_options(device_ptr , NULL, IDEVICE_LOOKUP_USBMUX) != IDEVICE_E_SUCCESS) {
  printf("ERROR: No device found!\n");
  return -1;
}
device = *device_ptr;

/* Retrieve the udid of the connected device */
char **udid_ptr = malloc(sizeof *udid_ptr);
if (idevice_get_udid(device, udid_ptr) != IDEVICE_E_SUCCESS) {
  printf("ERROR: Unable to get the device UDID.\n");
  idevice_free(device);
  return -1;
}
udid = *uid_ptr;

/* Outputs device identifier */
printf("Connected with UDID: %s\n", udid);

free(device_ptr);
free(udid_ptr);

Now it should be easier to see how this should be translated into Kotlin (I hope this is correctly eyeballed, as I'm not super familiar with Kotlin):

class Device {
    private val uid: MemorySegment
    private val device: MemorySegment

    init {
        Arena.openConfined().use { arena ->
            // you could even have just one of these and re-use it
            val uidAddress: MemorySmegment = arena.allocate(C_POINTER)
            val deviceAddress: MemorySegment = arena.allocate(C_POINTER)

            if (idevice_new_with_options(deviceAddress, NULL(), IDEVICE_LOOKUP_USBMUX()) != IDEVICE_E_SUCCESS()) {
                throw Exception("ERROR: No device found!")
            }
            device = deviceAddress.get(C_POINTER, 0)

            if (idevice_get_udid(device, uidAddress) != IDEVICE_E_SUCCESS()) {
                idevice_free(device)
                throw Exception("ERROR: Get UDID failed")
            }
            uid = udidAddress.get(C_POINTER, 0)

            println("connected")
            println(uid.getUtf8String(0))
        }
    }
}

fun main() {
    Device()
}
Jorn Vernee
  • 31,735
  • 4
  • 76
  • 93
  • When I use `println(udid.get(C_POINTER, 0).getUtf8String(0))` it works, but thank you very much. – purofle Jun 11 '23 at 14:32
0

It worked when I write like this:

class Device {
    private var device: MemorySegment
    private var udid: MemorySegment

    init {
        Arena.openConfined().use { arena ->
            val udidAddress: MemorySegment = arena.allocate(C_POINTER)
            val deviceAddress: MemorySegment = arena.allocate(C_POINTER)

            if (idevice_new_with_options(deviceAddress, NULL(), IDEVICE_LOOKUP_USBMUX()) != IDEVICE_E_SUCCESS()) {
            throw Exception("ERROR: No device found!")
            }
            device = deviceAddress.get(C_POINTER, 0)

            if (idevice_get_udid(deviceAddress, udidAddress) != IDEVICE_E_SUCCESS()) {
                idevice_free(deviceAddress)
                throw Exception("ERROR: Get UDID failed")
            }
            
            udid = udidAddress.get(C_POINTER, 0).get(C_POINTER, 0)
            println("connected")
            println(udid.getUtf8String(0))
        }
    }
}
purofle
  • 1
  • 1