In this article, we are going to see how to interact with USB in Android apps. What things are Required and Also 1 mini Project to Get a Clear Idea of How Things Work When We Need to Interact Android Apps With USB.
USB Endpoints and Interfaces
Endpoints:
Endpoints are the channels through which data is transferred between the USB device and the host (in this case our Android device is the host). They come in two types:
- In Endpoints: These are used by USB devices to send data to the host. For example, a camera might use an In Endpoint to send image data to a computer.
- Out Endpoints: These are used by USB devices to receive data from the host. For example, a printer might use an Out Endpoint to receive print jobs from a computer.
Interfaces:
The interface represents a set of endpoints that perform a specific function. A USB device can have multiple interfaces and each interface is identified by a unique number. For example, a printer might have one interface for printing and another for status information.
Required Components for USB Interaction and Implementing USB Communication in Android Apps
1. USB Host Mode Support:
For Android device to communicate with USB peripherals it must support USB Host Mode.Mostly modern Android devices have this capability.
2. USB Host API:
Android provides a set of APIs for interacting with USB devices. Primary classes for USB communication are UsbManager, UsbDevice, UsbDeviceConnection. These classes allow us to manage the USB devices connected to the host.
- UsbManager: This is the entry point for interacting with USB devices. It allows us to obtain a list of connected devices, request permission to communicate with a device, manage device connections.
- UsbDevice: Represents a connected USB device. It provides information about the device like its vendor and product IDs, interfaces,endpoints.
- UsbDeviceConnection: This class facilitates communication with a connected USB device. It allows us to send and receive data through the device’s endpoints.
3. USB Permissions:
Accessing USB devices is a sensitive operation we need to declare the necessary permissions in AndroidManifest.xml file. To ensure that only authorized apps can interact with connected USB devices.
XML
< uses-feature android:name = "android.hardware.usb.host" /> < uses-permission android:name = "android.permission.USB_PERMISSION" /> |
4. USB Device Enumeration:
When a USB device is connected Android performs device enumeration to identify its capabilities and characteristics. We can use the UsbManager to obtain list of connected devices and iterate through them to find the one that matches our app requirements.
Java
UsbManager usbManager = (UsbManager) getSystemService(Context.USB_SERVICE); HashMap<String, UsbDevice> connectedDevices = usbManager.getDeviceList(); for (UsbDevice device : connectedDevices.values()) { // You can check permissions and more here } |
Kotlin
val usbManager = getSystemService(Context.USB_SERVICE) as UsbManager val connectedDevices: HashMap<String, UsbDevice> = usbManager.deviceList for (device in connectedDevices.values) { } |
5. Requesting Permission:
Before communicating with a USB device we need to request permission from the user using pending intent. It is a crucial security measure to ensure that only authorized apps can access the device and it is required.
Java
PendingIntent permissionIntent = PendingIntent.getBroadcast( this , 0 , new Intent(ACTION_USB_PERMISSION), 0 ); usbManager.requestPermission(device, permissionIntent); |
Kotlin
val permissionIntent = PendingIntent.getBroadcast( this , 0 , Intent(ACTION_USB_PERMISSION), 0 ) usbManager.requestPermission(device, permissionIntent) |
6. Managing Endpoints and Interfaces:
Once we have obtained permission to communicate with a device we can access its endpoints and interfaces. Using the UsbDevice and UsbDeviceConnection classes to send and receive data through the appropriate endpoints.
Example Project : Creating an App To Enumerate All the USB Devices List
In this project we are going to create an app to Get all the usb devices list that are connected to our android device with OTG.
What we are going to do:
Step By Step Implementation:
Step 1: Create a new Project and Select Kotlin/java As the programming language
Step 2: We have created a simple UI by adding one Button at Bottom
Step 3: Add Permissions To Interact With Usb that we have seen Earlier
XML
< uses-permission android:name = "android.permission.USB_PERMISSION" /> < uses-feature android:name = "android.hardware.usb.host" /> |
Step 4: Working on Kotlin File and When the button is clicked it retrieves a list of connected USB devices and displays them in bottom sheet dialog that i have created showing information like device name, vendor ID, manufacturer name, product name,you can add more things. Each device item is clickable and clicking on it displays a toast message indicating the selected device with device name and Vendor Id.
Java
package com.ayush.gfgapp; import android.content.Context; import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbManager; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.widget.Button; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import com.google.android.material.bottomsheet.BottomSheetDialog; import java.util.HashMap; public class MainActivity2 extends AppCompatActivity { private Button getDevice; private UsbManager usbManager; @Override protected void onCreate( @Nullable Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main2); // Initialize button and USB manager getDevice = findViewById(R.id.getDevice); usbManager = (UsbManager) getSystemService(Context.USB_SERVICE); // Set OnClickListener for the button getDevice.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { // Retrieve the list of connected USB devices HashMap<String, UsbDevice> deviceList = usbManager.getDeviceList(); showDevicesDialog(deviceList); } }); } private void showDevicesDialog(HashMap<String, UsbDevice> deviceList) { if (deviceList.isEmpty()) { // Show a toast if no USB devices are found Toast.makeText( this , "No USB devices found" , Toast.LENGTH_SHORT).show(); } else { // Inflate the bottom sheet layout to display the list of devices View bottomSheetView = LayoutInflater.from( this ).inflate(R.layout.bottom_sheet_device_list, null ); LinearLayout devicesLayout = bottomSheetView.findViewById(R.id.devicesLayout); for (UsbDevice device : deviceList.values()) { // Inflate the layout for each device item View deviceItemLayout = LayoutInflater.from( this ).inflate(R.layout.device_list_item, null ); TextView deviceNameTextView = deviceItemLayout.findViewById(R.id.deviceNameTextView); TextView vendorIdTextView = deviceItemLayout.findViewById(R.id.vendorIdTextView); TextView manufacturerTextView = deviceItemLayout.findViewById(R.id.manufacturerTextView); TextView productNameTextView = deviceItemLayout.findViewById(R.id.ProductNameTextView); TextView emptyTV = deviceItemLayout.findViewById(R.id.emptyTV); // Set information about the device in the corresponding TextViews deviceNameTextView.setText( "Device Name: " + device.getDeviceName()); vendorIdTextView.setText( "Vendor ID: " + device.getVendorId()); manufacturerTextView.setText( "Manufacturer Name: " + device.getManufacturerName()); productNameTextView.setText( "Product Name: " + device.getProductName()); emptyTV.setText( "Testing:- " + device.getProductId()); // Add an OnClickListener to handle device selection deviceItemLayout.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(MainActivity2. this , "Device Selected: " + device.getDeviceName() + " (Vendor ID: " + device.getVendorId() + ")" , Toast.LENGTH_SHORT).show(); } }); // Add the device item to the layout devicesLayout.addView(deviceItemLayout); } // Create and display the bottom sheet dialog with the list of devices BottomSheetDialog bottomSheetDialog = new BottomSheetDialog( this ); bottomSheetDialog.setContentView(bottomSheetView); bottomSheetDialog.show(); } } } |
Kotlin
package com.ayush.gfgapp import android.content.Context import android.content.Intent import android.hardware.usb.UsbDevice import android.hardware.usb.UsbManager import android.os.Bundle import android.view.LayoutInflater import android.widget.Button import android.widget.LinearLayout import android.widget.TextView import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import com.google.android.material.bottomsheet.BottomSheetDialog class MainActivity2 : AppCompatActivity() { private lateinit var getDevice: Button private lateinit var usbManager: UsbManager override fun onCreate(savedInstanceState: Bundle?) { super .onCreate(savedInstanceState) setContentView(R.layout.activity_main2) getDevice = findViewById(R.id.getDevice) // Initialize the USB manager to access USB functionality usbManager = getSystemService(Context.USB_SERVICE) as UsbManager getDevice.setOnClickListener { // Retrieve the list of connected USB devices val deviceList = usbManager.deviceList // Call function to show the list of devices showDevicesDialog(deviceList) } } private fun showDevicesDialog(deviceList: HashMap<String, UsbDevice>) { if (deviceList.isEmpty()) { // Show a toast if no USB devices are found Toast.makeText( this , "No USB devices found" , Toast.LENGTH_SHORT).show() } else { // Inflate the bottom sheet layout to display the list of devices val bottomSheetView = LayoutInflater.from( this ).inflate(R.layout.bottom_sheet_device_list, null ) val devicesLayout = bottomSheetView.findViewById<LinearLayout>(R.id.devicesLayout) for (device in deviceList.values) { // Inflate the layout for each device item val deviceItemLayout = LayoutInflater.from( this ).inflate(R.layout.device_list_item, null ) as LinearLayout val deviceNameTextView = deviceItemLayout.findViewById<TextView>(R.id.deviceNameTextView) val vendorIdTextView = deviceItemLayout.findViewById<TextView>(R.id.vendorIdTextView) val manufacturerTextView = deviceItemLayout.findViewById<TextView>(R.id.manufacturerTextView) val productNameTextView = deviceItemLayout.findViewById<TextView>(R.id.ProductNameTextView) val emptyTV = deviceItemLayout.findViewById<TextView>(R.id.emptyTV) // Set information about the device in the corresponding TextViews deviceNameTextView.text = "Device Name: ${device.deviceName}" vendorIdTextView.text = "Vendor ID: ${device.vendorId}" manufacturerTextView.text = "Manufacturer Name: ${device.manufacturerName}" productNameTextView.text = "Product Name: ${device.productName}" emptyTV.text = "Testing:- ${device.productId}" // Add an onClickListener to handle device selection deviceItemLayout.setOnClickListener { Toast.makeText( this , "Device Selected: ${device.deviceName} (Vendor ID: ${device.vendorId})" , Toast.LENGTH_SHORT).show() } // Add the device item to the layout devicesLayout.addView(deviceItemLayout) } val bottomSheetDialog = BottomSheetDialog( this ) bottomSheetDialog.setContentView(bottomSheetView) // Display the bottom sheet // dialog with the list of devices bottomSheetDialog.show() } } } |
Layout For Bottom Sheets:
bottom_sheet_device_list.xml:
XML
android:layout_width = "match_parent" android:layout_height = "wrap_content" android:background = "@color/cardview_shadow_start_color" android:orientation = "vertical" > < LinearLayout android:id = "@+id/devicesLayout" android:layout_width = "match_parent" android:background = "@color/cardview_shadow_start_color" android:layout_height = "wrap_content" android:orientation = "vertical" /> </ LinearLayout > |
device_list_item.xml:
XML
android:layout_width = "match_parent" android:layout_height = "wrap_content" android:orientation = "vertical" android:background = "@color/cardview_shadow_start_color" android:padding = "16dp" > < TextView android:id = "@+id/deviceNameTextView" android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:textColor = "#E11919" android:textAppearance = "?android:textAppearanceMedium" /> < TextView android:id = "@+id/vendorIdTextView" android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:textColor = "#0EEF12" android:layout_marginTop = "4dp" android:textAppearance = "?android:textAppearanceSmall" /> < TextView android:id = "@+id/manufacturerTextView" android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:textColor = "#0EEF12" android:layout_marginTop = "4dp" android:textAppearance = "?android:textAppearanceSmall" /> < TextView android:id = "@+id/ProductNameTextView" android:layout_width = "wrap_content" android:textColor = "#0EEF12" android:layout_height = "wrap_content" android:layout_marginTop = "4dp" android:textAppearance = "?android:textAppearanceSmall" /> < TextView android:id = "@+id/emptyTV" android:layout_width = "wrap_content" android:textColor = "#3F51B5" android:layout_height = "wrap_content" android:layout_marginTop = "4dp" android:textAppearance = "?android:textAppearanceSmall" /> </ LinearLayout > |
Output: