Android accessory protocol is slow (3.7 MB/s)

168 Views Asked by At

I want to send large image data over USB from a PC to my Android Phone. I learned about the Android accessory protocol https://developer.android.com/develop/connectivity/usb/accessory and can now send data from my Mac to my App. The code is almost identical to Android USB Accessory mode can't read/write with host PC using libusb

However, I only get data rates of about 3.7 MB/s which is much to slow. I wanted to use USB to be fast.

The relevant receiving part in kotlin is this one:

            val fd: FileDescriptor = mFileDescriptor.getFileDescriptor()
            mInputStream = FileInputStream(fd)
            mOutputStream = FileOutputStream(fd)

            val bytes_length: ByteArray = ByteArray(4)
            mInputStream.read(bytes_length, 0, 4)

            val length = byteArrayToUnsignedInt(bytes_length.toUByteArray()).toInt()
            val bytes = ByteArray(length)
            val package_size = 16384
            val timeTaken = measureTime {
                val num_chunks = length / package_size
                var offset = 0
                for (i: Int in 1..num_chunks) {
                    val bytes_read = mInputStream.read(bytes, offset, package_size)
                    offset += bytes_read
                }
                val remaining = length - offset
                mInputStream.read(bytes, offset, remaining)
            }

            val mb_length = length/1024.0/1024
            val bps = mb_length/(timeTaken.inWholeMilliseconds*1e-3)
            text_view.append("\nReading: ${mb_length} MB in ${timeTaken.inWholeMilliseconds} ms = ${bps} MB/s")

Does anybody have a clue why it is so slow? With usb c I expected at least 100 MB/s or more.

1

There are 1 best solutions below

0
Flo On

I think I found another workaround. During my research I came across https://immersed.zendesk.com/hc/en-us/articles/14823473330957-USB-C-Mode-w-Meta-Quest-BETA-

I wondered, how they could achieve it. And I noticed, that they required that the android device has the debugging mode enabled. Therefore, I searched for a solution via the adb bridge. And I found the adb forward command. Using this command and just communicating over a TCP sockets I get now transfer rates around 209 MB/s. That's much more usable.

ADB command:

./adb forward tcp:3001 tcp:3001

Python code to send an image:

import socket
import struct

def send_bitmap(filename, server_address=('127.0.0.1', 3001)):
    try:
        # Open the bitmap file in binary mode
        with open(filename, 'rb') as file:
            # Read the binary data
            bitmap_data = file.read()

        # Get the size of the bitmap data
        file_size = len(bitmap_data)
        print(file_size)

        # Create a TCP socket
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as client_socket:
            # Connect to the server
            client_socket.connect(server_address)

            # Send the file size as a 4-byte integer
            client_socket.sendall(struct.pack("!I", file_size))

            # Send the bitmap data
            client_socket.sendall(bitmap_data)

        print(f"Bitmap file '{filename}' sent successfully to {server_address}")

    except Exception as e:
        print(f"Error: {e}")

# Replace 'your_bitmap_file.bmp' with the actual filename of your bitmap file
send_bitmap('../output.bmp')

Kotlin code to receive the image:

private inner class ServerThread : Thread() {
    override fun run() {
        var running = true
        while (running) {
            val server = ServerSocket(3001)
            println("Server running on port ${server.localPort}")
            val client = server.accept()
            println("Client connected : ${client.inetAddress.hostAddress}")

            val inputStream = BufferedInputStream(client.getInputStream())
            try {
                // Step 1: Read the integer (4 bytes) for file length
                val lengthBytes = ByteArray(4)
                inputStream.read(lengthBytes)
                var fileLength = byteArrayToInt(lengthBytes)

                // Step 2: Read the full bitmap file using the obtained length
                val bitmapBytes = ByteArray(fileLength)

                var package_size: Int = 16384

                val timeTaken = measureTime {
                    val num_chunks = fileLength / package_size
                    var offset = 0

                    for (i: Int in 1..num_chunks) {

                        val bytes_read = inputStream.read(bitmapBytes, offset, package_size)
                        offset += bytes_read
                    }
                    val remaining = fileLength - offset
                    inputStream.read(bitmapBytes, offset, remaining)
                }

                val mb_length = fileLength/1024.0/1024
                val bps = mb_length/(timeTaken.inWholeMilliseconds*1e-3)
                val bitmap: Bitmap = BitmapFactory.decodeByteArray(bitmapBytes, 0, bitmapBytes.size)
                runOnUiThread {
                    text_view.append("\nReading: ${mb_length} MB in ${timeTaken.inWholeMilliseconds} ms = ${bps} MB/s")
                    preview_image.setImageBitmap(bitmap)
                }
                
            } catch (e: Exception) {
                e.printStackTrace()
            }

            // Close resources when done
            inputStream.close()
            client.close()
            server.close()
        }
    }
}

Maybe this helps somebody if you are looking for some fast USB transfer between a PC/Mac and an android device.