Это дополнение к ответу @Sagar о получении маунтов от /proc
. Обратите внимание на использование /proc/self/mountinfo вместо /proc/mountinfo или /proc/mounts. Вы можете прочитать больше о формате /proc/self/mountinfo
в man 5 procfs
. Хотя следующий код технически анализирует файлы, его безопасно запускать в основном потоке (поскольку /proc
— это файловая система в памяти).
private static final int SANE_SIZE_LIMIT = 200 * 1024 * 1024;
// some hashmap for mapping long values to objects
// personally I am using HPPC maps, the HashMap class is for simplicity
public final HashMap<String> mountMap = new HashMap<>();
public void parse() {
mountMap.clear();
CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder();
parseMounts(decoder, true);
}
private int measure(FileChannel fc) throws IOException {
final ByteBuffer buffer = ByteBuffer.allocate(1024 * 4);
int totalRead = 0, lastRead;
do {
buffer.clear();
lastRead = fc.read(buffer);
totalRead += lastRead;
if (totalRead > SANE_SIZE_LIMIT) {
throw new IOException("/proc/ file appears to be too big!!");
}
} while (lastRead != -1);
fc.position(0);
return totalRead;
}
private void parseMounts(CharsetDecoder d, boolean force) {
File file = new File("/proc/self/mountinfo");
int mode = ParcelFileDescriptor.MODE_READ_ONLY;
try (ParcelFileDescriptor pfd = ParcelFileDescriptor.open(file, mode));
FileChannel fc = new FileInputStream(pfd.getFileDescriptor()).getChannel()) {
// Measure size of file before reading from it.
// Virtual files in /proc/ appear to be zero-sized (because
// their contents are dynamic), but we want to attempt
// reading it in single read() call to avoid inconsistencies
final int totalRead = measure(fc);
try (FileInputStream fis = new FileInputStream(pfd.getFileDescriptor());
Reader r = Channels.newReader(fis.getChannel(), d, totalRead);
Scanner scanner = new Scanner(r)) {
while (scanner.hasNextLine()) {
scanner.nextInt();
scanner.nextInt();
final String[] mm = scanner.next().split(":");
final int major = Integer.parseInt(mm[0]);
final int minor = Integer.parseInt(mm[1]);
final long dev_t = makedev(major, minor);
final String source = scanner.next();
// ignore bind-mounts for now
if ("/".equals(source)) {
final String location = scanner.next();
// skip optional parts
scanner.skip("(.+ -)");
// type of file system (such as ext4)
// most useful filesystems can be distinguished by type
// but "fuse" is problematic (because most Android
// distributions implement dynamic permissions on
// external SD card via custom FUSE filesystem).
// To make matters worse, that SD card FUSE filesystem is
// often mounted in several places at once. The code below
// will throw away duplicate mounts by placing them in
// HashMap, keyed by uniqie filesystem type ID,
// but you can do it more cleverly be checking whether
// a mountpoint directory is accessible (via File#list).
// You can throw away rest of useless filesystems (such as
// /mnt/secure/asec) by permission checks and blacklisting
// well-known paths.
final String fsType = scanner.next().intern();
final String subject = scanner.next().intern();
String created = location + subject + fsType;
String prev = mountMap.put(dev_t, created);
if (prev != null) {
created.next = prev;
}
}
scanner.nextLine();
}
return;
} catch (NumberFormatException | NoSuchElementException nse) {
// oops.. either a new row type was introduced (not a big deal)
// or the file changed below our feet (because someone has mounted
// something). Let's retry once more
parseMounts(d, false);
} catch (IOException e) {
throw new WrappedIOException(e);
}
}
Вы можете найти более полезную информацию (например, общий путь к бесполезной файловой системе) в этом ответе. Обратите внимание, что форматы /proc/mounts и /proc/mountinfo отличаются, позже были введены после первого, чтобы улучшить его формат без нарушения обратной совместимости.
Приведенный выше код не является золотой пулей — он ничего не говорит вам об отдельных файловых системах, только их пути и имя файловой системы. Вы можете быть уверены, что "vfat" и "ext4" - полезные файловые системы, а "procfs" бесполезна, но что-то вроде "fuse" останется загадкой. Вы можете расширить вывод приведенного выше кода, используя android.os.storage.StorageManager
, чтобы получить более удобные для пользователя имена файловых систем (например, «SD-карта»), когда они доступны (соответствуют путям монтирования). Вы также можете использовать StatFs для получения свободного места на разделе — бесполезно виртуальные файловые системы обычно возвращают нулевое свободное пространство и нулевое доступное пространство при запросе. Наконец, если вы так склонны, вы можете рассмотреть параметры монтирования файловой системы при принятии решения о том, показывать ли файловую систему пользователю. Например. ro
против rw
— монтирование файловой системы только для чтения обычно будет намного менее полезным для ваших пользователей.
Когда я объясняю этот метод людям, они часто интересуются его надежностью... Будет ли он работать на каком-нибудь дрянном телефоне? Будет ли он доступен в будущих версиях ОС? Вот мой взгляд на это: этот метод все же лучше, чем многие советы, основанные на отражении, — в худшем случае чтение из /proc/file вернет вам IOException. Это не приведет к сбою вашего приложения и не приведет к непредсказуемому поведению, как некоторые хаки на основе отражения.
/proc
файловая система — это официальный API Linux, поддерживаемый разработчиками ядра Linux. Его невозможно удалить, указав другие параметры сборки ядра (например, он является обязательной частью ядра ОС). Он доступен уже много лет и сохраняет лучшую обратную совместимость, чем большинство Android API. В частности, /proc/self/mountinfo был создан более 10 лет назад и будет доступен в большинстве существующих версий Android, кроме самых старых.
Разработчики Android официально не поддерживают специфичные для Linux API. Но и ломать их тоже не собираются. Некоторые из недавних изменений SELinux в Android после Lollipop ограничили доступ к некоторым файлам в /proc/
, потому что они позволяли приложениям скрытно шпионить за другими приложениями. Эти изменения специально сделали /proc/self
доступным, потому что /proc/self предназначен для предоставления только собственной информации приложений (включая информацию о файловых системах, доступных приложению).
Если Google когда-нибудь перейдет с Linux на Fuchensa или какой-либо другой доморощенный форк BSD, /proc/ и другие специфичные для Linux API, вероятно, сломаются. Мне все равно? Не совсем.
person
user1643723
schedule
30.03.2018