USBdevice recognise as storage device and find path - android

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

Related

Filtering services by intent-filter category

I'm learning android by writing a launcher on top of AOSP and looking in the ways to get all services from installed packages that have certain category in their intent-filter
My current approach is
final PackageManager pm = getPackageManager();
int flags = PackageManager.GET_INTENT_FILTERS | PackageManager.GET_SERVICES;
List<PackageInfo> packages = pm.getInstalledPackages(flags);
for (PackageInfo packageInfo : packages) {
Log.d(TAG, "Installed package: " + packageInfo.packageName);
for (ServiceInfo service : packageInfo.services) {
Log.d(TAG, "Service: " + service.packageName);
// ???
}
}
An example of services defined in Manifest.xml:
<service
android:name=".ComplicationIntentService"
android:exported="true">
<intent-filter>
<category android:name="my.intent.CATEGORY" />
</intent-filter>
</service>
Sadly ServiceInfo object doesn't seem to contain information about the intent filters and a few searches seems to hint that this is not implemented
Any ideas if it's possible?

Check usb device if have been pluged

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

Android USB_DEVICE_ATTACHED persistent permission

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

Android emulator craches when Using SmsManager to send SMS

I click on send button in my app and emulator craches with following error message :
HAX is working and emulator runs in fast virt mode
dyld: lazy symbol binding failed: Symbol not found: _utf8_write
Referenced from: /Users/Nabil/Documents/Development/Android/sdk/tools/emulator64-x86
Expected in: flat namespace
dyld: Symbol not found: _utf8_write
Referenced from: /Users/Nabil/Documents/Development/Android/sdk/tools/emulator64-x86
Expected in: flat namespace
Here is my class code :
public void Send (View view){
txtnum = (EditText)findViewById(R.id.txtNum);
txtmsg = (EditText)findViewById(R.id.txtMessage);
String phoneNo = txtnum.getText().toString();
String message = txtmsg.getText().toString();
try {
SmsManager smsManager = SmsManager.getDefault();
smsManager.sendTextMessage(phoneNo, null, message, null, null);
Toast.makeText(getApplicationContext(), "SMS sent.",
Toast.LENGTH_LONG).show();
} catch (Exception e) {
Toast.makeText(getApplicationContext(),
"SMS faild, please try again.",
Toast.LENGTH_LONG).show();
e.printStackTrace();
}
}
and my manifest file:
<uses-permission android:name="android.permission.SEND_SMS" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
First know android simulators are not real devices. Use any real devices, because real devices only interact with hardware config(Sim Card).

can't receive messages on device using GCM

i've followed google instruction on initiating a gcm server and client.
my problem is though i get a successful gcm from server :
MulticastResult(multicast_id=8287827393174436535,total=1,success=1,failure=0,canonical_ids=0,results:
[[ messageId=0:1366468335181772%8e1522ac00000031 ]]
i'm not able to see any movement of receiving a message on the client - onMessage() isn't called.
client id as well as server id are correct as far as i can tell.
i'd appreciate any help... :)
the server side code :
try {
Sender sender = new Sender(API_KEY);
Message message = new Message.Builder().collapseKey("1")
.timeToLive(3)
.delayWhileIdle(true)
.addData("message",
"this text will be seen in notification bar!!").build();
ArrayList<String> devices = new ArrayList<String>();
devices.add(DEVICE_ID1);
//devices.add(DEVICE_ID2);
MulticastResult result = sender.send(message, devices, 2);
//Result result = sender.send(message, DEVICE_ID1, 2);
System.out.println(result.toString());
if (result.getResults() != null) {
int canonicalRegId = result.getCanonicalIds();
if (canonicalRegId != 0) {
}
} else {
int error = result.getFailure();
System.out.println(error);
}
} catch (Exception e) {
// TODO: handle exception
}
the client code :
registering :
GCMRegistrar.checkDevice(this);
GCMRegistrar.checkManifest(this);
final String regId = GCMRegistrar.getRegistrationId(this);
if (regId.equals("")) {
GCMRegistrar.register(this, SENDER_ID); // Note: get the sender id from configuration.
String regIdString = GCMRegistrar.getRegistrationId(this);
Log.v(TAG, "RegisterId, regId: " + regIdString);
} else {
Log.v(TAG, "Already registered, regId: " + regId);
}
and gcmIntentServiceCreated (partly) :
public class GCMIntentService extends GCMBaseIntentService {
public GCMIntentService(){
super(SENDER_ID);
}
#Override
protected void onMessage(Context context, Intent intent) {
Log.i("Registration", "Got a message!");
Log.i("Registration", context.toString() + " " + intent.toString());
}
...
}
client's manifest :
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.myapp"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="17" />
<permission android:protectionLevel="signature" android:name="com.example.myapp.permission.C2D_MESSAGE"></permission>
<uses-permission android:name="com.example.myapp.permission.C2D_MESSAGE"/>
<!-- App receives GCM messages. -->
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<!-- GCM connects to Google Services. -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- GCM requires a Google account. -->
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<!-- Keeps the processor from sleeping when a message is received. -->
<uses-permission android:name="android.permission.WAKE_LOCK" />
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name="com.example.myapp.MainActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name="com.google.android.gcm.GCMBroadcastReceiver"
android:permission="com.google.android.c2dm.permission.SEND" >
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="com.example.myapp" />
</intent-filter>
</receiver>
<service android:name=".GCMIntentService" />
</application>
</manifest>
To maximise your chances of receiving the message, I would not set the time_to_live at a mere 3 seconds. I'd omit this parameter altogether so that it takes the default value of 4 weeks. I would also set delay_while to false, so that the phone gets the message even whilst idle.
Well your message is surely getting sent to your device since gcm sends a success status of 1. Try to check the GCMReceiver code of your app.
Change the following code in your onMessage method:
Log.i("Registration", "Got a message!");
Log.i("Registration", context.toString() + " " + intent.toString());
to this:
String message=intent.getStringExtra("message");
Log.w("The message received is", message);
hope this help

Categories

Resources