I used dynamic broadcast can receive UsbManager.ACTION_USB_DEVICE_ATTACHED and UsbManager.ACTION_USB_DEVICE_DETACHED event.
But when my app is not run,the usb device have been plugged,the dynamic broadcast is not receive it. So I want to when my app is first run, i can check it.
I used static broadcast can receive the event,but i don't want to use the method,is it have other method?
You can use UsbManager class and get list of connected device, Try using below method and call it in onCreate
private void findSerialPortDevice() {
// This snippet will try to open the first encountered usb device connected, excluding usb root hubs
UsbManager usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
HashMap<String, UsbDevice> usbDevices = usbManager.getDeviceList();
if (!usbDevices.isEmpty()) {
boolean keep = true;
for (Map.Entry<String, UsbDevice> entry : usbDevices.entrySet()) {
device = entry.getValue();
int deviceVID = device.getVendorId();
int devicePID = device.getProductId();
intf = device.getInterface(0);
if(device != null && !device.equals(""))
Utils.writeIntoFile(getBaseContext(),"device =============>"+device+"\n"+"getInterface Count =============>"+device.getInterfaceCount());
// There is a device connected to our Android device. Try to open it as a Serial Port.
} else {
connection = null;
device = null;
}
}
if (!keep)
break;
}
if (!keep) {
// There is no USB devices connected.
}
} else {
// There is no USB devices connected.
}
}
If you want to get your app started when a device is attached, you have to register the VID/PID of the USB devices in your manifest
<activity
android:name="..."
...>
<intent-filter>
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
</intent-filter>
<meta-data
android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
android:resource="#xml/device_filter" />
</activity>
and device_filter.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- 0x0403 / 0x6001: FTDI FT232R UART -->
<usb-device vendor-id="1027" product-id="24577" />
...
e.g. as mentioned here
Related
How can I detect a mounted device such as a Pen-Drive, that can be used for storage? How can I find the path for the mounted storage device so I may read files from it?
I've used following broadcast receiver taking the permission to access mounted device:
private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
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
Log.d(TAG, "onReceive: "+intent.getExtras().toString());
Log.d(TAG, "onReceive: "+intent.getData());
LinearLayout layoutUsbList = (LinearLayout)findViewById(R.id.layout_usb_list);
Button btn = new Button(MainActivity.this);
btn.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
layoutUsbList.addView(btn);
btn.setText(device.getDeviceId()+"\t"+device.getDeviceName());
Log.d(TAG, "onReceive: "+intent.getExtras().toString());
final String path = intent.getData().getPath();
Log.e(TAG, "onReceive: path of device received from intent: "+ path );
btn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
File file = new File(path);
Toast.makeText(MainActivity.this, "file exists --> "+file.exists()+"", Toast.LENGTH_SHORT).show();
Toast.makeText(MainActivity.this, "file is directory --> "+file.isDirectory()+"", Toast.LENGTH_SHORT).show();
Log.d(TAG, "onClick: file is directory --> "+file.isDirectory()+"");
try{
Toast.makeText(MainActivity.this, file.listFiles().length+"", Toast.LENGTH_LONG).show();
}catch(Exception e){
Toast.makeText(MainActivity.this, "error while showing total items", Toast.LENGTH_SHORT).show();
}
}
});
}
} else {
Log.d("ERROR", "permission denied for device " + device);
}
}
}
}
};
Manifest file:
<?xml version="1.0" encoding="utf-8"?>
<uses-feature android:name="android.hardware.usb.host"/>
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme">
<activity
android:name=".MainActivity"
android:configChanges="keyboard|orientation"
android:label="#string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<intent-filter>
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"/>
</intent-filter>
<meta-data
android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
android:resource="#xml/device_filter"/>
<intent-filter>
<action android:name="android.hardware.usb.action.USB_DEVICE_DETACHED"/>
</intent-filter>
<!---->
<!---->
<!---->
<intent-filter>
<action android:name="android.intent.action.MEDIA_MOUNTED"/>
<data android:scheme="file"/>
</intent-filter>
</activity>
</application>
Accordig to this pdf refrencing this library.
Every mass storage device has at least one interface descriptor with the class code 08h,
which stands for the mass storage class. The mass storage class is not defined in the device
descriptor! The USB interface has exactly two endpoint descriptors. One IN endpoint to
read from the device and one OUT endpoint to write to the device2. Reading and writing
in this case does not necessarily mean reading or writing on the actual storage medium,
this is described later.
There are two different types regarding the mass storage class. There is the bulk-only
transport (BBB) mechanism which is the most common one. All newer devices follow that
standard. Then there is the Control/Bulk/Interrupt (CBI) standard which is no longer
important, because the USB-IF recommends using the BBB approach
UsbDevice is recognized as massStorage Device If:
usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_MASS_STORAGE
|| usbInterface.getInterfaceSubclass() == INTERFACE_SUBCLASS // int 6
|| usbInterface.getInterfaceProtocol() == INTERFACE_PROTOCOL // int 80
and
usbInterface.getEndpointCount() == 2
where
one of endpoint must satisfy following:
endPoint direction == 0
endPoint type = UsbConstants.USB_ENDPOINT_XFER_BULK //int 2
How can I make Android not request for permission each time I reconnect a USB device? I want to make it to remember "Use by default" checkmark for the USB devices so that I don't have to give permission every time to the same device.
I programatically detect when USB devices (android phones) are attached to my host device (android phone) so that I can switch them to AOA mode and use them as accessories. Basically I have two android phones and an OTG cable and I want them to communicate between eachother.
I have a thread which constantly enumerates the attached USB devices:
UsbManager manager = (UsbManager)
context.getSystemService(Context.USB_SERVICE);
while (!m_stopRequested) {
boolean shouldNotify = false;
HashMap<String, UsbDevice> deviceMap = m_usbManager.getDeviceList();
for (Entry<String, UsbDevice> entry : deviceMap) {
UsbDevice device = entry.getValue();
if (m_usbManager.hasPermission(device)) {
int pid = device.getProductId();
if (device.getVendorId() == VID_GOOGLE(0x18D1) && (pid == ACCESSORY_PID(0x2D01) || pid == ACCESSORY_PID_ALT(0x2D00))) {
switchDeviceToAOAMode(device);
}
} else {
m_usbManager.requestPermission(device);
}
}
Thread.sleep(1000);
}
I also have a BroadcastReceiver registered to receive USB_PERMISSION intents:
private final class USBReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
MCSLogger.log(TAG, "Received permission result!");
String action = intent.getAction();
UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
if (ACTION_USB_PERMISSION.equals(action)) {
boolean res = intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false);
MCSLogger.log(TAG, "permission action for dev=" + device + " received " + res);
int pid = device.getProductId();
if (res && device.getVendorId() == VID_GOOGLE(0x18D1) && (pid == ACCESSORY_PID(0x2D01) || pid == ACCESSORY_PID_ALT(0x2D00))) {
connectAccessory()
}
}
}
};
This is how I switch to AOA mode:
private boolean switchDeviceToAOAMode(UsbDeviceConnection connection) {
byte ioBuffer[] = new byte[2];
int devVersion;
int response;
enter code here
response = connection.controlTransfer(0xC0, 51, 0, 0, ioBuffer, 2, 0);
if (response < 0) {
MCSLogger.log(TAG, "Error starting transfer control " + response);
return false;
}
devVersion = ioBuffer[1] << 8 | ioBuffer[0];
// sometimes hangs on the next transfer :( //WIN32 libusb only
// SystemClock.sleep(1000);
byte manufacturer[] = m_manufacturer.getBytes();
response = connection.controlTransfer(0x40, 52, 0, 0, manufacturer, manufacturer.length, 0);
if (response < 0) {
MCSLogger.log(TAG, "Error transfering manufacturer " + response);
return false;
}
byte modelName[] = m_modelName.getBytes();
response = connection.controlTransfer(0x40, 52, 0, 1, modelName, modelName.length, 0);
if (response < 0) {
MCSLogger.log(TAG, "Error transfering modelName " + response);
return false;
}
byte description[] = m_description.getBytes();
response = connection.controlTransfer(0x40, 52, 0, 2, description, description.length, 0);
if (response < 0) {
MCSLogger.log(TAG, "Error transfering description " + response);
return false;
}
byte version[] = m_version.getBytes();
response = connection.controlTransfer(0x40, 52, 0, 3, version, version.length, 0);
if (response < 0) {
MCSLogger.log(TAG, "Error transfering version " + response);
return false;
}
byte uri[] = m_uri.getBytes();
response = connection.controlTransfer(0x40, 52, 0, 4, uri, uri.length, 0);
if (response < 0) {
MCSLogger.log(TAG, "Error transfering uri " + response);
return false;
}
byte serialNumber[] = m_serialNumber.getBytes();
response = connection.controlTransfer(0x40, 52, 0, 5, serialNumber, serialNumber.length, 0);
if (response < 0) {
MCSLogger.log(TAG, "Error transfering serialNumber " + response);
return false;
}
MCSLogger.log(TAG, "Accessory Identification sent " + devVersion);
response = connection.controlTransfer(0x40, 53, 0, 0, null, 0, 0);
if (response < 0) {
MCSLogger.log(TAG, "Error ending transfer control " + response);
return false;
}
return true;
}
The answer provided by #Ender is correct, but there is one more thing you need to do on later versions of the Android Platform (7+).
You need to make sure that you have android:directBootAware="true" added to the activity tag that is responsible for responding to the USB_ACCESSORY_ATTACHED / USB_DEVICE_ATTACHED permissions.
Here is a valid manifest section for the activity:
<activity android:name=".MainActivity"
android:directBootAware="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<intent-filter>
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
android:resource="#xml/usb_device_filter" />
</intent-filter>
</activity>
Source:
https://github.com/dazza5000/USBPermissionTest/blob/master/app/src/main/AndroidManifest.xml
usb_device_filter.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<usb-device vendor-id="2049" product-id="25"/>
</resources>
Source:
https://github.com/dazza5000/USBPermissionTest/blob/master/app/src/main/res/xml/usb_device_filter.xml
The android:directBootAware="true" hint comes from the link below and I am very thankful for it.
https://www.sdgsystems.com/post/android-usb-permissions
More details can be found here:
https://issuetracker.google.com/issues/77658221
A full working project is here:
https://github.com/dazza5000/USBPermissionTest
Root Access
If you have root access, you can create the file and write it to disk and then reboot the device so that the default permission is read and set.
These are the basic steps:
private void grantUSBPermission() {
UsbManager usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
HashMap<String, UsbDevice> deviceList = usbManager.getDeviceList();
for (UsbDevice usbDevice : deviceList.values()) {
if (usbDevice.getManufacturerName() != null && usbDevice.getManufacturerName().equalsIgnoreCase(MANUFACTURER)) {
Boolean hasPermission = usbManager.hasPermission(usbDevice);
// Log if USB manager explicitly reports no permission.
if (!hasPermission) {
Log.i("DARRAN", "USB Manager reporting no permission to reader.");
DeviceFilter deviceFilter = new DeviceFilter(usbDevice);
writeSettingsFile(deviceFilter);
}
}
}
}
private void writeSettingsFile(DeviceFilter deviceFilter) {
PermissionUtil.writeSettingsLocked(getApplicationContext(), deviceFilter);
RootUtil.executeAsRoot(COMMAND_COPY_USB_FILE);
RootUtil.executeAsRoot(COMMAND_CHOWN_USB_FILE);
RootUtil.executeAsRoot("reboot");
}
Commands:
public static final String COMMAND_COPY_USB_FILE = "cp /sdcard/Android/data/com.whereisdarran.setusbdefault/files/usb_device_manager.xml /data/system/users/0/usb_device_manager.xml";
public static final String COMMAND_CHOWN_USB_FILE = "chown system:system /data/system/users/0/usb_device_manager.xml";
A full working project can be found here:
https://github.com/dazza5000/set-usb-default
Also, a blog article with a little more context:
http://whereisdarran.com/2019/12/wip-how-to-programmatically-set-your-app-as-the-default-app-for-a-usb-device-on-android-root-required/
In implementing AOA, there are two main ways to obtain device permission for USB data transfers.
One approach involves manually enumerating all connected devices, finding the desired device, directly requesting permission via the UsbManager.requestPermission(Device device) method, and handling the resulting broadcast with a BroadcastReceiver. This is the solution you've written. While functional and compliant, it prompts the user for permission every time a USB device is connected; a potential source of annoyance for the user.
The other approach is far simpler and allows for use-by-default functionality. It requires that an intent filter be defined in AndroidManifest.xml like so:
<activity ...>
...
<intent-filter>
<action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" />
</intent-filter>
<meta-data android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED"
android:resource="#xml/accessory_filter" />
Along with an xml file named "accessory_filter"(just a suggestion, you can name it whatever you want). Here's a sample accessory_filter.xml file:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<usb-accessory manufacturer="Google, Inc." model="DemoKit" version="1.0" /></resources>
The intent filter will automatically fire up the application in the event of a device connection and presents the user with the option to use your app as the default application for the specific device you are working with.
This link provides more information: https://developer.android.com/guide/topics/connectivity/usb/accessory#manifest-example
I'm trying to detect usb devices which are already connected to android.
I understand there are actions to detect when USB is either attached or detached.
But I don't really get how to check devices after connecting usb device to android.
Also, I've found that each USB device has it's device class code, but how do I figure out what kind of device is connected? For instance, I need to detect both usb mouse and keyboard; how do I differentiate them?
Try this:
First register Broadcast for USB connection.
manifest permission:
:
<intent-filter> <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" /> </intent-filter>
Get the List of USB Device with details by using this
public void getDetail() {
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();
manager.requestPermission(device, mPermissionIntent);
String Model = device.getDeviceName();
int DeviceID = device.getDeviceId();
int Vendor = device.getVendorId();
int Product = device.getProductId();
int Class = device.getDeviceClass();
int Subclass = device.getDeviceSubclass();
}}
Just to answer Benny's question here is what the mPermissionIntent could look like:
string actionString = context.PackageName + ".action.USB_PERMISSION";
PendingIntent mPermissionIntent = PendingIntent.GetBroadcast(context, 0, new
Intent(actionString), 0);
mUsbManager.RequestPermission(device, permissionIntent);
In my Android app, I read the values from a 3DConnexion SpaceNavigator via USB-OTG to control an AR.Drone.
Now I want to do the same with a mouse. However, Android is grabbing the mouse and presenting a mouse-cursor. When I write a device-filter with the vendor and product ID of the mouse, I do not get it like with the SpaceNavigator (strangely, both are HID -- I get no cursor with the SpaceNavigator).
Is there a way to get the raw mouse data without the cursor?
Would be perfect with stock Android. but I would also consider altering the ROM for that.
As soon as your Application claims the Mouse (as a USB HID device while being Host), Android should hide the cursor and you can read the raw data. This should work on stock android, but your device has to support USB Host mode and a USB OTG cable will be needed to connect the mouse.
Basic procedure:
enumerate devices
ask for permission to access the USB device
claim the device
read a data package from the HID endpoint
parse the X and Y position, button clicks and scroll wheel rotation from the data package
Example Code that works for me (Android 5.0):
UsbManager usbManager;
UsbDevice usbDevice;
private void connect() {
this.usbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
HashMap<String, UsbDevice> deviceList = usbManager.getDeviceList();
// just get the first enumerated USB device
Iterator<UsbDevice> deviceIterator = deviceList.values().iterator();
if (deviceIterator.hasNext()) {
this.usbDevice = deviceIterator.next();
}
if (usbDevice == null) {
Log.w(TAG, "no USB device found");
return;
}
// ask for permission
final String ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION";
final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
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
Log.i(TAG, "permission granted. access mouse.");
// repeat in a different thread
transfer(device);
}
}
else {
Log.d(TAG, "permission denied for device " + device);
}
}
} else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {
UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
if (device != null) {
// TODO:
// call your method that cleans up and closes communication with the device
// usbInterface.releaseInterface();
// usbDeviceConnection.close();
}
}
}
};
PendingIntent mPermissionIntent = PendingIntent.getBroadcast(context, 0, new Intent(ACTION_USB_PERMISSION), 0);
IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
context.registerReceiver(mUsbReceiver, filter);
usbManager.requestPermission(usbDevice, mPermissionIntent);
}
private void transfer(UsbDevice device) {
int TIMEOUT = 0;
boolean forceClaim = true;
// just grab the first endpoint
UsbInterface intf = device.getInterface(0);
UsbEndpoint endpoint = intf.getEndpoint(0);
UsbDeviceConnection connection = this.usbManager.openDevice(device);
connection.claimInterface(intf, forceClaim);
byte[] bytes = new byte[endpoint.getMaxPacketSize()];
connection.bulkTransfer(endpoint, bytes, bytes.length, TIMEOUT);
// depending on mouse firmware and vendor the information you're looking for may
// be in a different order or position. For some logitech devices the following
// is true:
int x = (int) bytes[1];
int y = (int) bytes[2];
int scrollwheel = (int) bytes[3]
// call a listener, process your data ...
}
I want to know when user has connected hands free accessories and hasn't blocked calls\sms. Is it possible to know when it is connected via hardware ports or bluetooth?
Try this in your onCreate or onResume.
BluetoothAdapter myLocalAdapter = BluetoothAdapter.getDefaultAdapter();
BluetoothDevice garniture;
Set<BluetoothDevice> connectedDevices = myLocalAdapter.getBondedDevices();
for (BluetoothDevice device : connectedDevices){
String name = device.getName();
//... check for the name you want
if( name.equals("whatnameisit"){
garniture = device
}
}
if (garniture != null){
// yay we found it, lets do our work with the device here
}