xamarin android write file to external usb drive

Phil 166 Reputation points
2022-11-21T17:12:48.843+00:00

Hi there
I have a requirement where a client would like to be able to take an existing file from a usb drive.
Do some processing on android.
Then save a new version of the file back to the external usb drive (not on the tablet itself).

This can be a separate apk so doesn't necessarily have to go on the play store (obviously it would be good if it could).

I have used the xamarin file picker in order to select the file from drive.

I am now trying to save it back and it is proving more difficult then I anticipated.

I have looked over some similar questions such as :
https://stackoverflow.com/questions/46690925/xamarin-android-read-and-write-files-on-usb-stick

and at the android documentation:
https://developer.android.com/guide/topics/connectivity/usb/host#java

I think I almost have it working.
I am able to identify the drive using the USBManager and get the details for it.
I am then able to get interface and endpoint.
If I then try to write to the USB drive the tablet (a Samsung) tells me the drive has been ejected(I guess me writing to the drive is causing it to be ejected?).

I don't get the warning that the drive was ejected if I remove the line:

 deviceConnection.ClaimInterface(usbInterface, true);  

It seems like it should work. but I can't see the file on the drive. (I am just using file manager from the Samsung tablet, it can seem to find all other files so I thought it should find my newly written file).

Below is the code for saving the file:

private void SaveFileToUSBDevice(byte[] file)  
        {  
            if (file != null && file.Length > 0)  
            {  
                UsbManager manager = (UsbManager)Android.App.Application.Context.GetSystemService(Context.UsbService);  
  
                string ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION";  
                var mPermissionIntent = PendingIntent.GetBroadcast(Android.App.Application.Context, 0, new Intent(ACTION_USB_PERMISSION), 0);  
  
                if (manager != null)  
                {  
                    if (manager.DeviceList != null && manager.DeviceList.Count > 0)  
                    {  
                        var usbDevice = manager.DeviceList.FirstOrDefault().Value;  
                        if (usbDevice != null)  
                        {  
                            Toast.MakeText(Android.App.Application.Context, $"usb device found", ToastLength.Long).Show();  
  
                            manager.RequestPermission(usbDevice, mPermissionIntent);  
                            //List<UsbInterface> usbInterfaces = new List<UsbInterface>();  
                            //List<UsbEndpoint> usbConfigurations = new List<UsbEndpoint>();  
  
                            //for (int i = 0; i < usbDevice.InterfaceCount; i++)  
                            //{  
                            //    usbInterfaces.Add(usbDevice.GetInterface(i));  
                            //}  
  
                            //if (usbInterfaces != null && usbInterfaces.Count > 0)  
                            //{  
                            //    for (int i = 0; i < usbInterfaces.Count; i++)  
                            //    {  
                            //        for (int j = 0; j < usbInterfaces[i].EndpointCount; j++)  
                            //        {  
                            //            usbConfigurations.Add(usbInterfaces[i].GetEndpoint(i));  
                            //        }  
                            //    }  
  
  
                            //    if (usbConfigurations != null && usbConfigurations.Count > 0)  
                            //    {  
  
                            //    }  
  
                            UsbInterface usbInterface = usbDevice.GetInterface(0);  
                            if (usbInterface != null)  
                            {  
                                Toast.MakeText(Android.App.Application.Context, $"usb interface found {usbInterface.Id}", ToastLength.Long).Show();  
                                UsbEndpoint usbEndpoint = usbInterface.GetEndpoint(0);  
  
                                if (usbEndpoint != null)  
                                {  
                                    Toast.MakeText(Android.App.Application.Context, $"usb endpoint found {usbEndpoint.EndpointNumber}", ToastLength.Long).Show();  
                                    try  
                                    {  
                                        var deviceConnection = manager.OpenDevice(usbDevice);  
  
                                        if (deviceConnection != null)  
                                        {  
                                            deviceConnection.ClaimInterface(usbInterface, true);  
  
                                            Toast.MakeText(Android.App.Application.Context, $"File length = {file.Length}", ToastLength.Long).Show();  
  
                                            deviceConnection.BulkTransfer(usbEndpoint, file, file.Length, 0);  
  
                                        }  
                                        else  
                                        {  
                                            Toast.MakeText(Android.App.Application.Context, $"device connection is null", ToastLength.Long).Show();  
                                        }  
                                    }  
                                    catch (Exception ex)  
                                    {  
                                        Toast.MakeText(Android.App.Application.Context, $"USB ERROR: {ex.Message}", ToastLength.Long).Show();  
                                    }  
                                }  
                            }  
                        }  
                    }  
                }  
            }  
        }  

I added the toasts in for easier debugging on the tablet, and have removed the looping of the interfaces and endpoints. (I know that I only have 1 as I got this info).

It hits all the toasts after the first run (I have to give it permission the first time which I will tidy up once it's working.)

Are there any other thoughts on how best to achieve saving items to an external USB without having user input?

I have been investigating the StorageManager but I can't seem to get the drive using that method.

If there is a way to get the URI for the drive and then use that to store the file, but I have been doing a lot of searching and have not found a definitive way to get the drive directory.

Another idea was to open a file manager but only show the directory for the external drive.

Any thoughts would be appreciated.

Xamarin
Xamarin
A Microsoft open-source app platform for building Android and iOS apps with .NET and C#.
5,325 questions
{count} votes

1 answer

Sort by: Most helpful
  1. Phil 166 Reputation points
    2022-11-24T15:59:15.297+00:00

    It looks like the Storage manager should be able to achieve what I was trying to do but there may be a bug stopping it from working correctly.

    SEE:
    https://stackoverflow.com/questions/73713567/access-usb-storage-otg-in-android-11

    and
    https://issuetracker.google.com/issues/246857526#comment9

    As a result I have a file manager that comes up once. Then the USB directory is selected. This provides a Uri for the device. This is saved in secure storage and the dialogue is not sown again.
    This allows us to assign a USB drive to an android device when it is being configured.

    Once configured the tablet will only save to the provided drive, or provide an error message asking the user to insert the drive if wiring fails.

    0 comments No comments