Android OTG: Accessing Unwritable USB Storage - Seeking Solutions

47 views Asked by At

When trying to communicate with the USB device using SCSI commands like INQUIRY and Test Unit Ready, I'm either not receiving any response or encountering errors. For instance, I've been receiving -1 from the bulkTransfer method, indicating a failure in communication. Notably, I haven't observed any explicit permission errors or other system logs that provide clear direction.

the code:

package com.example.myapplication

import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.hardware.usb.UsbConstants
import android.hardware.usb.UsbDevice
import android.hardware.usb.UsbDeviceConnection
import android.hardware.usb.UsbEndpoint
import android.hardware.usb.UsbManager
import android.os.Build
import android.os.Bundle
import android.widget.LinearLayout
import android.widget.ScrollView
import android.widget.TextView
import androidx.activity.ComponentActivity
import androidx.core.view.setPadding

class MainActivity : ComponentActivity() {
    private lateinit var usbManager: UsbManager
    private val actionUsbPermission = "com.example.USB_PERMISSION"
    private lateinit var logTextView: TextView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        initializeUI()
        usbManager = getSystemService(Context.USB_SERVICE) as UsbManager

        val permissionFilter = IntentFilter(actionUsbPermission)
        registerReceiver(usbPermissionReceiver, permissionFilter)

        discoverDevices()
    }

    private fun initializeUI() {
        val layout = LinearLayout(this).apply {
            orientation = LinearLayout.VERTICAL
            setPadding(16)
        }

        val scrollView = ScrollView(this).apply {
            layoutParams = LinearLayout.LayoutParams(
                LinearLayout.LayoutParams.MATCH_PARENT,
                LinearLayout.LayoutParams.MATCH_PARENT)
        }

        logTextView = TextView(this).apply {
            layoutParams = LinearLayout.LayoutParams(
                LinearLayout.LayoutParams.MATCH_PARENT,
                LinearLayout.LayoutParams.WRAP_CONTENT)
        }

        scrollView.addView(logTextView)
        layout.addView(scrollView)
        setContentView(layout)
    }

    private fun discoverDevices() {
        val deviceList: HashMap<String, UsbDevice> = usbManager.deviceList
        if (deviceList.isEmpty()) {
            logMessage("No USB devices found.")
            return
        }

        deviceList.values.forEach { device ->
            logMessage("Device: ${device.deviceName}")
            requestPermission(device)
        }
    }

    private fun requestPermission(device: UsbDevice) {
        val flags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            PendingIntent.FLAG_MUTABLE
        } else {
            0
        }
        val permissionIntent = PendingIntent.getBroadcast(this, 0, Intent(actionUsbPermission), flags)
        usbManager.requestPermission(device, permissionIntent)
    }

    private val usbPermissionReceiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent) {
            if (intent.action == actionUsbPermission) {
                synchronized(this) {
                    val device: UsbDevice? = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE)

                    if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
                        device?.apply {
                            logMessage("Permission granted for device $deviceName")
                            performSimpleUmsCommunication(this)
                        }
                    } else {
                        logMessage("Permission denied for device ")
                    }
                }
            }
        }
    }

    private fun performSimpleUmsCommunication(device: UsbDevice) {
        try {
            val connection: UsbDeviceConnection = usbManager.openDevice(device) ?: run {
                logMessage("Unable to open connection to device.")
                return
            }

            for (index in 0 until device.interfaceCount) {
                val usbInterface = device.getInterface(index)
                if (!connection.claimInterface(usbInterface, true)) {
                    logMessage("Unable to claim interface $index.")
                    continue
                }

                var epOut: UsbEndpoint? = null
                var epIn: UsbEndpoint? = null
                for (i in 0 until usbInterface.endpointCount) {
                    val ep = usbInterface.getEndpoint(i)
                    if (ep.direction == UsbConstants.USB_DIR_OUT && ep.type == UsbConstants.USB_ENDPOINT_XFER_BULK) {
                        epOut = ep
                    } else if (ep.direction == UsbConstants.USB_DIR_IN && ep.type == UsbConstants.USB_ENDPOINT_XFER_BULK) {
                        epIn = ep
                    }
                }

                if (epOut == null || epIn == null) {
                    logMessage("Cannot find IN/OUT endpoints for interface $index.")
                    continue
                }

                // Send SCSI Read Capacity (10) command
                val readCapacityCommand = byteArrayOf(
                    0x1A, // MODE SENSE (6)
                    0x00, // Reserved
                    0x3F, // Page Code (0x3F for return all mode pages)
                    0x00, // Reserved
                    0x1C, // Allocation Length (set to 28 bytes)
                    0x00  // Control
                )

                val response = ByteArray(8) // Typical response size for Read Capacity

                logMessage("Sending Read Capacity (10) command: ${bytesToHex(readCapacityCommand)}")
                val commandResult = connection.bulkTransfer(epOut, readCapacityCommand, readCapacityCommand.size, 5000)
                if (commandResult < 0) {
                    logMessage("Error sending Read Capacity (10) command: $commandResult")
                    continue
                }

                val received = connection.bulkTransfer(epIn, response, response.size, 5000)
                if (received >= 0) {
                    logMessage("Received response: ${bytesToHex(response.copyOf(received))}")
                } else {
                    logMessage("Error receiving response for Read Capacity (10) command: $received")
                }

                connection.releaseInterface(usbInterface)
            }

            connection.close()
        } catch (e: Exception) {
            logMessage("Exception during USB communication: ${e.message}")
        }
    }




    // Helper function to convert byte array to a hexadecimal string
    private fun bytesToHex(bytes: ByteArray): String {
        val hexChars = "0123456789ABCDEF"
        val result = StringBuilder(bytes.size * 2)
        for (byte in bytes) {
            val intVal = byte.toInt() and 0xff
            result.append(hexChars[intVal ushr 4])
            result.append(hexChars[intVal and 0x0f])
        }
        return result.toString()
    }


    private fun logMessage(message: String) {
        runOnUiThread { logTextView.append(message + "\n") }
    }

    override fun onDestroy() {
        super.onDestroy()
        unregisterReceiver(usbPermissionReceiver)
    }
}

And the adb error:

2023-12-23 22:29:03.316  1517-2592  StorageManagerService   system_server E  
android.os.ServiceSpecificException:  (code -5)
at android.os.Parcel.createExceptionOrNull(Parcel.java:3037)
at android.os.Parcel.createException(Parcel.java:3007)
at android.os.Parcel.readException(Parcel.java:2990)
at android.os.Parcel.readException(Parcel.java:2932)
at android.os.IVold$Stub$Proxy.mount(IVold.java:2205)
at com.android.server.StorageManagerService.mount(StorageManagerService.java:2819)
at com.android.server.StorageManagerService.-$$Nest$mmount(Unknown Source:0)
at com.android.server.StorageManagerService$StorageManagerServiceHandler.handleMessage(StorageManagerService.java:972)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:226)
at android.os.Looper.loop(Looper.java:313)
at android.os.HandlerThread.run(HandlerThread.java:67)  

usbui

2023-12-23 22:29:04.655  1517-2528  UsbUI                   system_server                        D  onUEvent(Host Interface): {SUBSYSTEM=usb, SEQNUM=909705, ACTION=unbind, INTERFACE=8/6/80, DEVTYPE=usb_interface, PRODUCT=3337/301/f000, DEVPATH=/devices/platform/soc/a600000.ssusb/a600000.dwc3/xhci-hcd.8.auto/usb2/2-1/2-1:1.0, TYPE=0/0/0}
2023-12-23 22:29:04.656  1517-2528  UsbUI                   system_server                        D  onUEvent(Host Interface): {SUBSYSTEM=usb, SEQNUM=909706, ACTION=bind, INTERFACE=8/6/80, DEVTYPE=usb_interface, PRODUCT=3337/301/f000, DRIVER=usbfs, MODALIAS=usb:v3337p0301dF000dc00dsc00dp00ic08isc06ip50in00, DEVPATH=/devices/platform/soc/a600000.ssusb/a600000.dwc3/xhci-hcd.8.auto/usb2/2-1/2-1:1.0, TYPE=0/0/0}
2023-12-23 22:29:04.656  1517-2528  UsbUI                   system_server                        D  onUEvent(Host Interface): {SUBSYSTEM=usb, SEQNUM=909707, ACTION=unbind, INTERFACE=8/6/80, DEVTYPE=usb_interface, PRODUCT=3337/301/f000, DEVPATH=/devices/platform/soc/a600000.ssusb/a600000.dwc3/xhci-hcd.8.auto/usb2/2-1/2-1:1.0, TYPE=0/0/0}

I want the device to mount on Android (with Windows currently running), or to send SCSI commands, but I can't do either.

Many thanks for sharing your knowledge.

QAQ

0

There are 0 answers