I need to import / export some data from / to a usb stick.
Is there a way to get the path of the mounted usb devices?
My code looks something like this:
UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
HashMap<String, UsbDevice> deviceList = manager.getDeviceList();
Iterator<UsbDevice> deviceIterator = deviceList.values().iterator();
while(deviceIterator.hasNext()){
UsbDevice device = deviceIterator.next();
UsbInterface deviceInterface = device.getInterface(0);
if(deviceInterface != null && deviceInterface.getInterfaceClass() == UsbConstants.USB_CLASS_MASS_STORAGE){
// check if path "mnt/usb_storage/*" exist
}
}
I can search if the path "mnt/usb_storage/*" exists when the device is a mass storage.
This works fine if only one usb mass storage is attached or the app is in foreground when a second usb mass storaged is attached.
If you wonder, yes there are Android devices with multiple usb ports.
Is there no way to get the path when I have the UsbManager/UsbDevice/UsbInterface?
Is the path on all devices like "mnt/usb_storage/*"?
Update 1:
Target Android Version 5.1
Requirement: "Filechooser" has to be integrated inside the app, filter for folders, file names und file types have to be applied.
Update 2:
As suggested by #greenapps I gave "ACTION_OPEN_DOCUMENT_TREE" a try:
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
intent.putExtra("android.content.extra.SHOW_ADVANCED", true);
intent.putExtra("android.content.extra.FANCY", true);
intent.putExtra("android.content.extra.SHOW_FILESIZE", true);
startActivityForResult(intent, OPEN_DOCUMENT_TREE_REQUEST_CODE);
This does not show the usb stick neither was I able to filter the files and folders.
Same device: es-datei explorer is able to display the usb device.
My requirements are to display a custom file chooser where the user can select files from a customer specific cloud, internal storage or usb devices similar to the es datei explorer.
My solution with searching if the path: "mnt/usb_storage/*" exists works as required I am just wondering if there is a better way.
So I have searched quite a number of SO questions to get the path of a USB devices. I have not quite found an answer yet, but since this is top of the Google search and a fairly recent question, I will post what I have found.
Attempt (fleshed out)
So initially I tried (with my own implementation) as the question contains:
public static String getUSBProblematic(Context context){
UsbManager usbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
if (usbManager == null) {
Log.e(TAG, "Unable to get USB_SERVICE");
return "";
}
UsbAccessory[] accessoryList = usbManager.getAccessoryList();
if (accessoryList != null) {
for (UsbAccessory usbAccessory : accessoryList) {
// here we check the vendor
Log.d(TAG, "getUSBProblematic: " + usbAccessory.toString());
}
}
HashMap<String, UsbDevice> deviceList = usbManager.getDeviceList();
if(deviceList != null) {
List<UsbDevice> usbDeviceList = new ArrayList<>(deviceList.values());
for (Iterator<UsbDevice> iterator = usbDeviceList.iterator(); iterator.hasNext();) {
UsbDevice next = iterator.next();
boolean isMassStorage = false;
for (int i = 0; i < next.getInterfaceCount(); i++) {
// Check USB interface type is mass storage
UsbInterface usbInterface = next.getInterface(i);
if(usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_MASS_STORAGE && usbInterface.getEndpointCount() == 2) {
// Check endpoints support bulk transfer
for (int j = 0; j < usbInterface.getEndpointCount(); j++) {
UsbEndpoint endpoint = usbInterface.getEndpoint(j);
if(endpoint != null) {
if(endpoint.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK){
// Valid mass storage
isMassStorage = true;
}
}
}
}
}
if(!isMassStorage) {
iterator.remove();
}
}
for (UsbDevice usbDevice : usbDeviceList) {
Log.d(TAG, "getUSBProblematic: Device Name" + usbDevice.getDeviceName());
Log.d(TAG, "getUSBProblematic: Device Desc" + usbDevice.toString());
}
}
return "";
}
So the best I can get out of this is something like /dev/bus/usb/003, but nothing more than that.
So looking over my mounts file in /proc/mounts (found on the device, see Device File Explorer in Android Studio, I saw that USB specific storage devices are mounted at /mnt/media_rw/. But browsing to /storage, I saw the USB devices I expected or was looking for.
I am not sure if this is a good strategy or device specific (I use a Huawei Mate 10 Pro), but I am using
Possible Solution
public static String getUSB(){
File storageDirectory = new File("/storage");
if(!storageDirectory.exists()) {
Log.e(TAG, "getUSB: '/storage' does not exist on this device");
return "";
}
File[] files = storageDirectory.listFiles();
if(files == null) {
Log.e(TAG, "getUSB: Null when requesting directories inside '/storage'");
return "";
}
List<String> possibleUSBStorageMounts = new ArrayList<>();
for (File file : files) {
String path = file.getPath();
if (path.contains("emulated") ||
path.contains("sdcard") ||
path.contains("self")) {
Log.d(TAG, "getUSB: Found '" + path + "' - not USB");
} else {
possibleUSBStorageMounts.add(path);
}
}
if (possibleUSBStorageMounts.size() == 0) {
Log.e(TAG, "getUSB: Did not find any possible USB mounts");
return "";
}
if(possibleUSBStorageMounts.size() > 1) {
Log.d(TAG, "getUSB: Found multiple possible USB mount points, choosing the first one");
}
return possibleUSBStorageMounts.get(0);
}
EDIT 1
This may also be useful (search for USB)
There mostly is no path on modern Android systems. Nowadays you will try to get a document tree scheme.
There are several options.
You could have a look at the second or third item returned by getExternalFilesDirs(). (Will give a path. But writing not possible.)
For Android 6+ use Intent.ACTION_OPEN_DOCUMENT_TREE to let the user choose the drive. This will give you a content sheme. No file path.
For Android 7+ have a look at Storage Volumes.
I need your help, I'm trying to read some directory and trying to copy it to another place.
Now I can connect to usb host and read the usb specifications but i canĀ“t read the information on the usb to copy it.
this is my code
if (ACTION_USB_PERMISSION.equals(action)) {
synchronized (this) {
UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
if (device != null) {
//call method to set up device communication
byte[] bytes = new byte[0];
int TIMEOUT = 0;
boolean forceClaim = true;
intf = device.getInterface(0);
UsbEndpoint endpoint = intf.getEndpoint(0);
connection = manager.openDevice(device);
connection.claimInterface(intf, forceClaim);
bytes=connection.getRawDescriptors();
Toast.makeText(context,"PERMISO CONCEDIDO",Toast.LENGTH_SHORT).show();
ArrayList<String>directorios=new ArrayList<>();
directorios=ObtnerDirectorios("/storage/UsbDriveA");
String dir="";
for(int i=0; i<directorios.size();i++){
dir+=directorios.get(i)+"\n";
}
infotext.setText(dir);
}
} else {
Toast.makeText(context,"PERMISO DENEGADO",Toast.LENGTH_SHORT).show();
}
}
}
I read the google documentation and I think t have to use a bulktransfer and control transfer but I'm not sure.
Could you help me?
Thanks
I am new to USB and to Android so please forgive me if I don't explain myself clearly.
I have a USB HID device that I can communicate with in Windows. I am trying to establish communication using an Acer Iconia A500 tablet running Android 3.1.
I am able to find the device, enumerate it, get its only available interface, get the only available endpoint (0), and determine what type of endpoint it is (transfer interrupt from device to host).
My understanding of the USB spec is that all HID devices are required at a munimum to have a control endpoint (Endpoint 0) and an interrupt IN endpoint. But it seems that endpoint 0 here is the interrupt In endpoint, not the control endpoint.
Yet in order for the device to enumerate it must successfully transfer its descriptor data across the control endpoint. I deduce that the control endpoint therefore must be getting found (and used) because the host does, in fact, enumerate the device.
This is as far as I am able to proceed, as stated above, the only interface/endpoint presented to me at the application level is an interrupt type going from device to host. No endpoint available to my app going from host to device, interrupt or control. So the device waits to be told what to do and the host waits for something to happen in the device. Not very stimulating.
Bear in mind that this device responds properly when connected to Windows, e.g. I am able to send a report containing 13 bytes of data that causes the device to light an LED. So it seems to be complying with the USB HID spec. As an act of desperation I have tried using this one endpoint as both a control endpoint and as a interrupt OUT endpoint, using controltransfer() and UsbRequest() to submit the data to the device, no response in either case.
So my question is: "The control transfer endpoint is (?) being used to set up the device, why am I not able to find & use it?"
Thanks for any insight, below is the relevant code, I can include the rest in its entirety if needed:
private UsbManager mUsbManager;
private UsbDevice mDevice;
private UsbDeviceConnection mConnectionRead;
private UsbDeviceConnection mConnectionWrite;
private UsbEndpoint mEndpointRead;
private UsbEndpoint mEndpointWrite;
// check for existing devices
for (UsbDevice device : mUsbManager.getDeviceList().values())
{
//Need to filter for my device when other HIDs are also connected, but for now...
String devName = device.getDeviceName();
if (DEBUG == 1){
Toast.makeText(UsbHidDeviceTesterActivity.this, "My device got connected: " + devName, Toast.LENGTH_LONG).show();
}
//mDevice = device;
setHIDDevice(device);
}
private boolean setHIDDevice(UsbDevice device)
{
UsbInterface usbInterfaceRead = null;
UsbInterface usbInterfaceWrite = null;
UsbEndpoint ep1 = null;
UsbEndpoint ep2 = null;
boolean UsingSingleInterface = true;
mDevice = device;
//This HID device is using a single interface
if (UsingSingleInterface)
{
//usbInterfaceRead = device.getInterface(0x00);//only 1 EP on this interface
usbInterfaceRead = findInterface(device);
//Try getting an interface at next index
//usbInterfaceWrite = device.getInterface(0x01);//throws exception
// Try using the same interface for reading and writing
usbInterfaceWrite = usbInterfaceRead;
int endPointCount = usbInterfaceWrite.getEndpointCount();
if (DEBUG == 2)
{
Toast.makeText(UsbHidDeviceTesterActivity.this, "Endpoints: " + endPointCount, Toast.LENGTH_LONG).show();
//Toast.makeText(UsbHidDeviceTesterActivity.this, "Interface: " + usbInterfaceRead, Toast.LENGTH_LONG).show();
}
if (endPointCount == 1)//only getting 1 endpoint
{
ep1 = usbInterfaceRead.getEndpoint(0);
//As an act of desperation try equating ep2 to this read EP, so that we can later attempt to write to it anyway
ep2 = usbInterfaceRead.getEndpoint(0);
}
else if (endPointCount == 2)
{
ep1 = usbInterfaceRead.getEndpoint(0);
ep2 = usbInterfaceRead.getEndpoint(1);
}
}
else // ! UsingSingleInterface
{
usbInterfaceRead = device.getInterface(0x00);
usbInterfaceWrite = device.getInterface(0x01);
if ((usbInterfaceRead.getEndpointCount() == 1) && (usbInterfaceWrite.getEndpointCount() == 1))
{
ep1 = usbInterfaceRead.getEndpoint(0);
ep2 = usbInterfaceWrite.getEndpoint(0);
}
if (DEBUG == 3)
{
Toast.makeText(UsbHidDeviceTesterActivity.this, "Using Dual Interface", Toast.LENGTH_LONG).show();
}
}
//because ep1 = ep2 this will now not cause a return unless no ep is found at all
if ((ep1 == null) || (ep2 == null))
{
if (DEBUG == 4)
{
Toast.makeText(UsbHidDeviceTesterActivity.this, "One EP is null", Toast.LENGTH_LONG).show();
}
return false;
}
// Determine which endpoint is the read, and which is the write
if (ep1.getType() == UsbConstants.USB_ENDPOINT_XFER_INT)//I am getting a return of 3, which is an interrupt transfer
{
if (ep1.getDirection() == UsbConstants.USB_DIR_IN)//I am getting a return of 128, which is a device-to-host endpoint
{
mEndpointRead = ep1;
if (DEBUG == 5)
{
Toast.makeText(UsbHidDeviceTesterActivity.this, "EP1 type: " + ep1.getType(), Toast.LENGTH_LONG).show();
}
}
if (ep1.getDirection() == UsbConstants.USB_DIR_OUT)//nope
{
mEndpointWrite = ep1;
if (DEBUG == 6)
{
Toast.makeText(UsbHidDeviceTesterActivity.this, "EP1 is a write", Toast.LENGTH_LONG).show();
}
}
}
if (ep2.getType() == UsbConstants.USB_ENDPOINT_XFER_INT)
{
if (ep2.getDirection() == UsbConstants.USB_DIR_IN)
{
//Try treating it as a write anyway
//mEndpointRead = ep2;
mEndpointWrite = ep2;
}
else if (ep2.getDirection() == UsbConstants.USB_DIR_OUT)
{
//usbEndpointWrite = ep2;
mEndpointWrite = ep2;
}
}
//check that we should be able to read and write
if ((mEndpointRead == null) || (mEndpointWrite == null))
{
return false;
}
if (device != null)
{
UsbDeviceConnection connection = mUsbManager.openDevice(device);
if (connection != null && connection.claimInterface(usbInterfaceRead, true))
{
Log.d(TAG, "open SUCCESS");
mConnectionRead = connection;
// Start the read thread
//Comment out while desperately attempting to write on this connection/interface
//Thread thread = new Thread(this);
//thread.start();
}
else
{
Log.d(TAG, "open FAIL");
mConnectionRead = null;
}
}
if (UsingSingleInterface)
{
mConnectionWrite = mConnectionRead;
}
else //! UsingSingleInterface
{
mConnectionWrite = mUsbManager.openDevice(device);
mConnectionWrite.claimInterface(usbInterfaceWrite, true);
}
return true;
}
// searches for an interface on the given USB device
private UsbInterface findInterface(UsbDevice device) {
Log.d(TAG, "findInterface " + device);
int count = device.getInterfaceCount();
if (DEBUG == 7)
{
Toast.makeText(UsbHidDeviceTesterActivity.this, "Interface count: " + count, Toast.LENGTH_LONG).show();
}
for (int i = 0; i < count; i++) {
UsbInterface intf = device.getInterface(i);
String InterfaceInfo = intf.toString();
Log.d(TAG, "Interface: " + InterfaceInfo);
//Class below is 3 for USB_HID
if (intf.getInterfaceClass() == 3 && intf.getInterfaceSubclass() == 0 &&
intf.getInterfaceProtocol() == 0) {
return intf;
}
//....try just returning the interface regardless of class/subclass
//return intf;
}
return null;
}
private boolean sendControlTransfer(byte[] dataToSend)
{
synchronized (this)
{
if (mConnectionRead != null)
{
//byte[] message = new byte[13]; // or 14?
byte[] message = dataToSend;
if (DEBUG == 9)
{
Toast.makeText(UsbHidDeviceTesterActivity.this, "Sending Control Transfer", Toast.LENGTH_LONG).show();
}
//first field ox21 is bin 00100001 which splits into 0 01 00001 for direction(1bit)/type(2b)/recipient(5b)
//To set direction as 'host to Device' we need 0, To set type to HID we need 11 (3), and for recipient we want 00001
//second field 0x09 is class specific request code, 0x09 is listed as 'reserved for future use'
//third field 0x200 is value
//int transfer = mConnectionRead.controlTransfer(0x21, 0x9, 0x200, 0, message, message.length, 0);
//try with type set to HID
int transfer = mConnectionRead.controlTransfer(0xC1, 0x9, 0x200, 0, message, message.length, 0);
if (DEBUG == 10)
{
Toast.makeText(UsbHidDeviceTesterActivity.this, "Transfer returned " + transfer, Toast.LENGTH_LONG).show();
}
}
}
return true;
}
private boolean sendInterruptTransfer(byte[] dataToSend)
{
int bufferDataLength = mEndpointWrite.getMaxPacketSize();//The write endpoint is null unless we just copy the read endpoint
if (DEBUG == 12)
{
Toast.makeText(UsbHidDeviceTesterActivity.this, "Max Packet Size: " + bufferDataLength, Toast.LENGTH_LONG).show();
}
ByteBuffer buffer = ByteBuffer.allocate(bufferDataLength + 1);
UsbRequest request = new UsbRequest();
buffer.put(dataToSend);
request.initialize(mConnectionWrite, mEndpointWrite);
request.queue(buffer, bufferDataLength);
try
{
/* only use requestwait on a read
if (request.equals(mConnectionWrite.requestWait()))
{
return true;
}
*/
}
catch (Exception ex)
{
// An exception has occurred
if (DEBUG == 13)
{
Toast.makeText(UsbHidDeviceTesterActivity.this, "Caught Write Exception", Toast.LENGTH_LONG).show();
}
}
return true;
}
So, I have been researching similar things. I cannot confirm, but what I believe is happening is:
Android does not list the control endpoint when it enumerates it's endpoints. It only lists other endpoints.
A connection to any endpoint can send control transfers to endpoint 0, through the controlTransfer method, which (quoting from the api) "Performs a control transaction on endpoint zero for this device."
So, in your above code, I would use the 0th endpoint as an interrupt input endpoint, but it will still allow for control transfers.
An example of someone using a HID device is the Missle Launcher demo, the device it uses is a HID device with an interrupt endpoint.
You can get a full list of the details of interfaces and endpoint by using the following:
UsbManager mManager = (UsbManager) getSystemService(Context.USB_SERVICE);
HashMap<String, UsbDevice> deviceList = mManager.getDeviceList();
Iterator<UsbDevice> deviceIterator = deviceList.values().iterator();
while (deviceIterator.hasNext())
{
UsbDevice device = deviceIterator.next();
Log.i(TAG,"Model: " + device.getDeviceName());
Log.i(TAG,"ID: " + device.getDeviceId());
Log.i(TAG,"Class: " + device.getDeviceClass());
Log.i(TAG,"Protocol: " + device.getDeviceProtocol());
Log.i(TAG,"Vendor ID " + device.getVendorId());
Log.i(TAG,"Product ID: " + device.getProductId());
Log.i(TAG,"Interface count: " + device.getInterfaceCount());
Log.i(TAG,"---------------------------------------");
// Get interface details
for (int index = 0; index < device.getInterfaceCount(); index++)
{
UsbInterface mUsbInterface = device.getInterface(index);
Log.i(TAG," ***** *****");
Log.i(TAG," Interface index: " + index);
Log.i(TAG," Interface ID: " + mUsbInterface.getId());
Log.i(TAG," Inteface class: " + mUsbInterface.getInterfaceClass());
Log.i(TAG," Interface protocol: " + mUsbInterface.getInterfaceProtocol());
Log.i(TAG," Endpoint count: " + mUsbInterface.getEndpointCount());
// Get endpoint details
for (int epi = 0; epi < mUsbInterface.getEndpointCount(); epi++)
{
UsbEndpoint mEndpoint = mUsbInterface.getEndpoint(epi);
Log.i(TAG," ++++ ++++ ++++");
Log.i(TAG," Endpoint index: " + epi);
Log.i(TAG," Attributes: " + mEndpoint.getAttributes());
Log.i(TAG," Direction: " + mEndpoint.getDirection());
Log.i(TAG," Number: " + mEndpoint.getEndpointNumber());
Log.i(TAG," Interval: " + mEndpoint.getInterval());
Log.i(TAG," Packet size: " + mEndpoint.getMaxPacketSize());
Log.i(TAG," Type: " + mEndpoint.getType());
}
}
}
Log.i(TAG," No more devices connected.");
}
Control transfer doesn't show any interface descriptor and its endpoint number is 0 by default, for both in and out transfer.
if you have other interfaces the index of those interfaces should start from 0 i.e. default control transfer interface does not count.
So your interface 0 holds the endpoint 1 descriptor. use the UsbEndpoint methods to find the attributes of the endpoint whether it is interrupt type or not. if it is then endpoint type by UsbEndpoint.getType() should return 0x03 and endpoint number by UsbEndpoint.getEndpointNumber() should return 0x81 which is usual value for endpoint 1.
below your code is wrong:
//first field ox21 is bin 00100001 which splits into 0 01 00001 for direction(1bit)/type(2b)/recipient(5b)
//To set direction as 'host to Device' we need 0, **To set type to HID we need 11 (3)**, and for recipient we want 00001
//second field 0x09 is class specific request code, **0x09 is listed as 'reserved for future use'**
//**third field 0x200 is value**
//int transfer = mConnectionRead.controlTransfer(0x21, 0x9, 0x200, 0, message, message.length, 0);
//try with type set to HID
int transfer = mConnectionRead.controlTransfer(0xC1, 0x9, 0x200, 0, message, message.length, 0);
Type 2 bits is used to indicate class specific request, i.e. its value is 01,
0x09 is Hid class specific request SET_REPORT, not reserved.
value is the wValue which is used as Report ID for Hid class, for your case it is probably 0, if you have only one report at you HID descriptor.
and the 4 th parameter is wIndex which should be used to indicate the recipient, for your case it should be 0x01 for interface as recipient.
So your code for control transfer for Read or receive data form device should be:
int transfer = mConnectionRead.controlTransfer(0xA1, 0x01, 0x00, 0x01, message, message.length, 0);
where 0x01 in second parameter is GET_REPORT is Hid calls specific request.
And your code for control transfer for Write or send data to device should be:
int transfer = mConnectionWrite.controlTransfer(0x21, 0x09, 0x00, 0x01, message, message.length, 0);
Since you only have Interrupt IN endpoint 1, Bulk or Interrupt transfer should be like:
int transfer = bulkTransfer (ep1, message, message.length, 0);
to have the interrupt Out endpoint there should be a endpoint descriptor for that at the interface descriptor of firmware of your device.
Maybe it is a late answer or off topic. However, I hope someone will one day find this answer useful.
Github now contains great Android libraries to communicate with custom HID devices:
Mine in Kotlin
in Java
The good thing is that if you are lucky, then you only need to know the device VID:PID and the commands it accepts. You don't need to worry about USB protocol or communication details.
If interested about how USB works, then you can have a closer look at library source code.
What I want to do is to count 3G traffic and WiFi traffic respectively. Now I know how to do with WiFi. Below is the source code for WiFi. By this way I can count WiFi traffic for all the android phones of all manufactures. But I haven't found a similar way for 3g. Does anyone know?
//to get wifi interface
private static String getProp(String prop){
String output = "";
try{
Class<?> sp = Class.forName("android.os.SystemProperites");
Method get = sp.getMethod("get",String.class);
output = (String)get.invoke(null,prop);
}catch(Exception e){
e.printStackTrace();
}
return output;
}
//to get the traffic from system file
...
...
if (connectinTpe == ConnectivityManager.TYPE_WIFI){
String wifiInterface = getProp("wifi.interface");
if(wifiInterface == null || "".equals(wifiInterface)) wifiInterface = "eth0";
rxFile = "/sys/class/net/" +wifiInterface+ "/statistics/rx_bytes";
txFile = "/sys/class/net/" +wifiInterface+ "/statistics/tx_bytes";
}
...
...
Starting from API level 8 (Android 2.2) there is a class TrafficStats which provides what you need:
Class that provides network traffic statistics. These statistics
include bytes transmitted and received and network packets transmitted
and received, over all interfaces, over the mobile interface, and on a
per-UID basis.
On the older versions you can use the approach you mentioned (i.e. reading file content of /sys/class/net/... files). This blog post contains an excellent mapping between TrafficStats methods and file locations. And this SO post contains the source its author used to read those files values. According to it you should first try to read number from "/sys/class/net/rmnet0/statistics/rx_bytes" file (for "received bytes" value) and if it fails try "/sys/class/net/ppp0/statistics/rx_bytes" instead.
to get the current type of connection you can use the TelephonyManager: http://developer.android.com/reference/android/telephony/TelephonyManager.html
first check if the device is connected to the default mobile data connection and then check the connection type:
if (connectinTpe == ConnectivityManager.TYPE_MOBILE)
{
TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
int curConnectionType = tm.getNetworkType();
if(curConnectionType >= /*connection type you are looking for*/)
{
// do what you want
}
}