Created
March 6, 2015 15:42
-
-
Save sr105/345e2096f158e3195c0f to your computer and use it in GitHub Desktop.
Class that discovers USB mass storage devices on Android. See getAll() as a starting point.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.rdm.storagetests; | |
import com.rdm.storagetests.StorageHelper.StorageVolume; | |
import java.io.BufferedReader; | |
import java.io.File; | |
import java.io.FileFilter; | |
import java.io.FileReader; | |
import java.io.IOException; | |
import java.util.ArrayList; | |
import java.util.Arrays; | |
import java.util.List; | |
/* | |
* Create a list of USBDevice objects | |
* - sysfs path | |
* - device number (e.g. 8:13) | |
* - size (in Bytes) | |
* - readonly (get from parent) | |
* - removable (get from parent) | |
* | |
* optional: | |
* - manufacturer | |
* - device name | |
* - file system label | |
* | |
*/ | |
/* | |
* - How will we match these to StorageVolumes? | |
* - volume size | |
* - vold device path (in /proc/mounts, device name is /dev/block/vold/8:13) | |
* The 8:13 is the device number. | |
* | |
* - might be other ways using /proc/diskstats or some other /proc file | |
*/ | |
public class UsbDevice | |
{ | |
private static List<UsbDevice> sDeviceCache; | |
public final File mPath; | |
public String mDeviceNumber; | |
public long mSize; | |
public boolean mReadonly; | |
public boolean mRemovable; | |
public String mVendor; | |
public String mModel; | |
public StorageVolume mVolume; | |
public UsbDevice(File sysfsPath) | |
{ | |
this(sysfsPath, null); | |
} | |
public UsbDevice(File sysfsPath, UsbDevice parent) | |
{ | |
mPath = sysfsPath; | |
if (parent == null) | |
{ | |
mReadonly = "1".equals(readFirstLineFromFile(new File(mPath, "ro"))); | |
mRemovable = "1".equals(readFirstLineFromFile(new File(mPath, "removable"))); | |
mVendor = readFirstLineFromFile(new File(mPath, "../../vendor")); | |
mModel = readFirstLineFromFile(new File(mPath, "../../model")); | |
} | |
else | |
{ | |
mReadonly = parent.mReadonly; | |
mRemovable = parent.mRemovable; | |
mVendor = parent.mVendor; | |
mModel = parent.mModel; | |
} | |
mDeviceNumber = readFirstLineFromFile(new File(mPath, "dev")); | |
try | |
{ | |
mSize = Long.valueOf(readFirstLineFromFile(new File(mPath, "size"))) * 512; | |
} | |
catch (NumberFormatException e) | |
{ | |
// should never happen | |
mSize = 0; | |
} | |
} | |
public String toString() | |
{ | |
return "[UsbDevice " | |
+ mPath.getName() | |
+ " " + mDeviceNumber | |
+ " " + mVendor | |
+ " " + mModel | |
+ (mReadonly ? " ro" : " rw") | |
+ (mRemovable ? " removable" : " not_removable") | |
+ " " + StorageUnit.humanReadable(mSize) | |
+ " " + (mVolume == null ? "not_mounted" : mVolume.getPath()) | |
+ "]"; | |
} | |
@SuppressWarnings("unused") | |
public static List<UsbDevice> getAll() | |
{ | |
boolean reload; | |
return getAll(reload = false); | |
} | |
public static List<UsbDevice> getAll(boolean reload) | |
{ | |
if (!reload && sDeviceCache != null) | |
{ | |
return sDeviceCache; | |
} | |
// patterns must match the entire string, so adding ^ or $ is meaningless | |
String[] patterns = new String[] {"usb[-_]storage", "[0-9].*", "host.*", "target.*", "[0-9][0-9:]+", "block", ".*"}; | |
List<File> dirs = new ArrayList<>(); | |
dirs.add(new File("/sys/bus/usb/drivers")); | |
for (String pattern : patterns) | |
{ | |
dirs = findNamedDirs(pattern, dirs); | |
} | |
List<UsbDevice> usbBlockDevices = new ArrayList<>(); | |
for (File disk : dirs) | |
{ | |
UsbDevice parent = new UsbDevice(disk); | |
usbBlockDevices.add(parent); | |
for (File part : disk.listFiles(dirNameFilter("^" + disk.getName() + "[0-9]+"))) | |
{ | |
usbBlockDevices.add(new UsbDevice(part, parent)); | |
} | |
} | |
synchronizeWithStorageVolumes(usbBlockDevices); | |
sDeviceCache = usbBlockDevices; | |
return sDeviceCache; | |
} | |
private static void synchronizeWithStorageVolumes(List<UsbDevice> usbDevices) | |
{ | |
List<StorageVolume> volumes = StorageHelper.getStorages(true); | |
for (UsbDevice u : usbDevices) | |
{ | |
for (StorageVolume v : volumes) | |
{ | |
// Device Numbers are very reliable, use them first | |
if (v.device.equals("/dev/block/vold/" + u.mDeviceNumber)) | |
{ | |
v.setUsbDevice(u); | |
u.mVolume = v; | |
break; | |
} | |
// Next, try looking for the device name as a component in the volume paths | |
for (String path : v.getPaths()) | |
{ | |
if (StorageHelper.pathContainsDir(path, new String[] {u.mPath.getName()})) | |
{ | |
v.setUsbDevice(u); | |
u.mVolume = v; | |
break; | |
} | |
} | |
if (v.getUsbDevice() == u) | |
{ | |
break; | |
} | |
/* | |
* TODO: this is not reliable. What if I insert two identically sized USB drives? | |
* TODO: need a better way to sync up when vold is not in use | |
* Ideas: | |
* - Quickly check to see if all of the volume sizes are unique? | |
* - /proc/diskstats? | |
* - we could perform a *lot* of I/O and watch the stats :) | |
* - busybox stat command to get the device number? (not always available) | |
* | |
*/ | |
if (v.file.getTotalSpace() == u.mSize) | |
{ | |
v.setUsbDevice(u); | |
u.mVolume = v; | |
break; | |
} | |
} | |
} | |
} | |
/** | |
* ******* utility methods ********* | |
*/ | |
private String readFirstLineFromFile(File file) | |
{ | |
try | |
{ | |
return new BufferedReader(new FileReader(file)).readLine(); | |
} | |
catch (IOException e) | |
{ | |
// If the file is missing or unavailable, we return an empty string | |
} | |
return ""; | |
} | |
private static FileFilter dirNameFilter(final String pattern) | |
{ | |
return new FileFilter() | |
{ | |
@Override | |
public boolean accept(File pathname) | |
{ | |
return pathname.isDirectory() && pathname.getName().matches(pattern); | |
} | |
}; | |
} | |
private static List<File> findNamedDirs(final String pattern, List<File> dirs) | |
{ | |
List<File> matchingDirs = new ArrayList<>(); | |
for (File d : dirs) | |
{ | |
matchingDirs.addAll(Arrays.asList(d.listFiles(dirNameFilter(pattern)))); | |
} | |
return matchingDirs; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment