📜 ⬆️ ⬇️

Getting the path to the SD Card on Android

Developing an application for the competition, I was faced with the problem of storing the database. The problem was how do I identify an external memory card. In general, the search in the network did not give an exact answer. Therefore, combining all the results found, I gathered my class. If anyone is interested, we look under the cut.

So let's start with the theory.

Terminology


Google tells us that there are the following concepts:
  1. Internal memory is a part of the memory card built into the phone. When it is used by default, the application folder is protected from access by other applications ( Using the Internal Storage ).
  2. External ( external ) memory is a common “external storage”, i.e. This can be either a part of the internal memory or a removable device. Usually this is a part of the internal memory, as the device I removed last time I saw on Android 2.2, where the internal memory was about 2GB, and the connected memory became External ( Using the External Storage ).
  3. Removable ( removable ) memory - all storages that can be removed from the device without “surgical” interventions.


Prior to KitKat 4.4, the API did not provide functionality for retrieving external memory paths. Starting with this version (API 19), the function public abstract File [] getExternalFilesDirs (String type) has appeared, which returns an array of strings with paths to internal and external memory. But what about our SD Card, which is inserted into the slot? The path to it again, we can not get.
')

searching results


To answer the question I asked the all-knowing Google. But he did not give me a clear answer. A number of definitions were considered, ranging from the use of standard functions that lead to external memory, but they have nothing in common with removable storage devices before processing device mount rules (Android also works on the Linux kernel). In the latter cases, the “wired” paths to the folder with mounted devices were used (in different versions this directory is different). Do not forget that from version to version of the mount rule change.

In the end, I decided to combine all the knowledge gained and wrote my class, which can give us back the way to external and removable devices.

Code Description


The MountDevice class was created, which contains the path to the device, the device type and a certain hash.
There are two types of devices (I did not touch the internal memory, since it can be accessed through the system API).

public enum MountDeviceType { EXTERNAL_SD_CARD, REMOVABLE_SD_CARD } 

And the StorageHelper class was created, which searches for available memory cards.

The StorageHelper class implements two search methods — through the system environment ( Environment ) and using the Linux mount utility, or rather, the result of its execution.

Method One - Environment

When working with the environment, I use the standard getExternalStorageDirectory () function to get information about external memory. To get information about deleted memory, I use the environment variable " SECONDARY_STORAGE ".

External memory is always the same and usually is always there, so we check it for readability, calculate the hash and remember. There can be a lot of memory to be deleted, so you need to split the resulting string by separator and check each value.

FillDevicesEnvirement function
 String path = android.os.Environment.getExternalStorageDirectory() .getAbsolutePath(); if (!path.trim().isEmpty() && android.os.Environment.getExternalStorageState().equals( android.os.Environment.MEDIA_MOUNTED)) { testAndAdd(path, MountDeviceType.EXTERNAL_SD_CARD); } //   String rawSecondaryStoragesStr = System.getenv("SECONDARY_STORAGE"); if (rawSecondaryStoragesStr != null && !rawSecondaryStoragesStr.isEmpty()) { // All Secondary SD-CARDs splited into array final String[] rawSecondaryStorages = rawSecondaryStoragesStr .split(File.pathSeparator); for (String rawSecondaryStorage : rawSecondaryStorages) { testAndAdd(rawSecondaryStorage, MountDeviceType.REMOVABLE_SD_CARD); } } 


The solution is taken from stackoverflow . The answer is somewhere down there.

Method two - mount

Since I haven’t been able to get the system to tell me the path to the deleted memory for a long time, I decided to look in the direction of the mounted devices. The system has configuration files that describe the rules for mounting external devices. Everything would be fine, but on Android version 4 * there are no mere mortals access to this file, so I will not consider this method.

Let's go back to the mount utility. When started without parameters, the command returns a list of mounted file systems. Deleted devices usually have the format of the FAT file system, then we will select lines in which there is a characteristic " fat ". External memory will be characterized by the " fuse " parameter.

Note: using this method is not always correct (most likely I did not take into account something) the types of installed devices are determined. Noticed the difference on different versions of Android. Therefore, this method can be used as an additional.

FillDevicesProcess function
 try { Runtime runtime = Runtime.getRuntime(); proc = runtime.exec("mount"); try { is = proc.getInputStream(); isr = new InputStreamReader(is); br = new BufferedReader(isr); while ((line = br.readLine()) != null) { if (line.contains("secure")) continue; if (line.contains("asec")) continue; if (line.contains("fat")) {// TF card String columns[] = line.split(" "); if (columns != null && columns.length > 1) { testAndAdd(columns[1], MountDeviceType.REMOVABLE_SD_CARD); } } else if (line.contains("fuse")) {// internal(External) // storage String columns[] = line.split(" "); if (columns != null && columns.length > 1) { // mount = mount.concat(columns[1] + "\n"); testAndAdd(columns[1], MountDeviceType.EXTERNAL_SD_CARD); } } } } finally { ... } } catch (Exception e) { ... } 



The solution is taken from stackoverflow . There are several answers about the same.

About duplication

Many people have noticed the following picture in the device mounting directory:

 /storage/sdcard0/ /storage/emulated/0/ /storage/emulated/legacy/ 

And most interestingly, all this is the same external memory card. Such fragmentation begins with the version of Jelly Bean and it is done to support multi-user mode of the system. More details here . And so, in order not to receive one and the same memory card as different devices, a method of determining identity is necessary. If there was access to the mount configuration, then there were no questions. But there is no access. Therefore, I peeped the solution with the calculation of the hash for each device:
  1. create stringbuilder
  2. write to it the total size of the device and the size of the used space of the device
  3. go around the contents of the root device
  4. write the name of the directory
  5. write file name and size
  6. calculate hash


Its calculation function hash calcHash
 private int calcHash(File dir) { StringBuilder tmpHash = new StringBuilder(); tmpHash.append(dir.getTotalSpace()); tmpHash.append(dir.getUsableSpace()); File[] list = dir.listFiles(); for (File file : list) { tmpHash.append(file.getName()); if (file.isFile()) { tmpHash.append(file.length()); } } return tmpHash.toString().hashCode(); } 


Usage example


 /*    */ if (!mPreferences.contains(PREFS_BASEBATH)) { //      ,     //  ArrayList<MountDevice> storages = StorageHelper.getInstance() .getRemovableMountedDevices(); //     if (storages.size() != 0) { setBasePath(storages.get(0).getPath() + mAppPath); } else if ((storages = StorageHelper.getInstance() //  //  //  .getExternalMountedDevices()).size() != 0) { setBasePath(storages.get(0).getPath() + mAppPath); } } else { //     mBasePath = mPreferences.getString(PREFS_BASEBATH, context .getFilesDir().getParent()); } 

Conclusion


Detailed reasoning on the issue of understanding the memory in Android, some tips can be found here .

The source code of the entire class is located nowhere else. I will try to post the other day on gitHub.

Who else uses what methods?

UPD1: Bitbucket class source code

Source: https://habr.com/ru/post/254813/


All Articles